diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3a8e7cb
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Jordan Partridge
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/app/Commands/InspireCommand.php b/app/Commands/InspireCommand.php
deleted file mode 100644
index 934fe72..0000000
--- a/app/Commands/InspireCommand.php
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
Laravel Zero
-
- Simplicity is the ultimate sophistication.
-
-
- HTML);
- }
-
- /**
- * Define the command's schedule.
- */
- public function schedule(Schedule $schedule): void
- {
- // $schedule->command(static::class)->everyMinute();
- }
-}
diff --git a/composer.json b/composer.json
index 69c29ae..56e74c3 100644
--- a/composer.json
+++ b/composer.json
@@ -34,9 +34,7 @@
},
"autoload": {
"psr-4": {
- "App\\": "app/",
- "Database\\Factories\\": "database/factories/",
- "Database\\Seeders\\": "database/seeders/"
+ "App\\": "app/"
}
},
"autoload-dev": {
diff --git a/config/app.php b/config/app.php
index 802e2c1..1a14cd7 100644
--- a/config/app.php
+++ b/config/app.php
@@ -15,7 +15,7 @@
|
*/
- 'name' => 'Cf',
+ 'name' => 'Cloudflare',
/*
|--------------------------------------------------------------------------
diff --git a/specs/scaffold-cleanup/spec.md b/specs/scaffold-cleanup/spec.md
new file mode 100644
index 0000000..ac64611
--- /dev/null
+++ b/specs/scaffold-cleanup/spec.md
@@ -0,0 +1,43 @@
+# SPEC: Scaffold cleanup (LICENSE, drop Inspire, app name Cloudflare)
+
+Status: DRAFT
+
+**Story:** README and `composer.json` say this is a Cloudflare CLI under MIT. `cf list` still ships Laravel Zero's `inspire` and the app name `Cf`, and there is no `LICENSE` file. Cursor PR https://github.com/conduit-ui/cloudflare/pull/3 almost fixed that, then sat one commit behind `#17` (quality-runners) with a body that still claims a CI/lock rewrite.
+
+## Locked
+
+- Apply the cleanup **on current `main`** (`e60415d` / `#17`). Rebase, do not replay the pre-`#17` tree.
+- Add MIT `LICENSE` (Copyright (c) 2026 Jordan Partridge).
+- Delete `app/Commands/InspireCommand.php` and `tests/Feature/InspireCommandTest.php`.
+- `config/app.php` `'name'` → `Cloudflare`. Binary stays `cf`. Keep the `#17` `AppServiceProvider` import.
+- Drop nonexistent `Database\\Factories\\` / `Database\\Seeders\\` autoload paths. There is no `database/` directory.
+- Replace `tests/Unit/ExampleTest.php` with a `CloudflareConnector` smoke test.
+- `composer.json` `config.platform.php` stays `"8.3.33"`. `composer.lock` `platform-overrides.php` stays `"8.3.33"`. Host PHP here is 8.5.x; unconstrained `composer update` resolves past 8.3 and breaks the 8.3 CI runner (closed PR `#12`).
+- Keep `phpstan/phpstan` and `rector/rector` in `require-dev` (added by `#17` for `./bin/ci`). Autoload-only edits do **not** change Composer content-hash; do not regenerate the lock.
+- Keep `tunnel:expose`: `app/Commands/TunnelExposeCommand.php` + `tests/Feature/TunnelExposeCommandTest.php`.
+- Keep `tests/Feature/QualityRunnersCiTest.php` and the residual `.github/workflows/ci.yml` from `#17` (Pint `--test` + Pest; not a fake `quality-runners/gate` job).
+- Research owns `specs/` and `~/.grok/tmp/3-notes.md`. Build owns LICENSE / Inspire delete / `config/app.php` / composer autoload. Test owns `tests/Unit/CloudflareConnectorTest.php`.
+- Do not merge. Do not deploy.
+
+## Done when
+
+- [ ] `test ! -f app/Commands/InspireCommand.php && test ! -f tests/Feature/InspireCommandTest.php`
+- [ ] `python3 -c "import json; assert json.load(open('composer.json'))['config']['platform']['php']=='8.3.33'"`
+- [ ] `python3 -c "import json; c=json.load(open('composer.json')); assert 'phpstan/phpstan' in c['require-dev'] and 'rector/rector' in c['require-dev']"`
+- [ ] `test -f app/Commands/TunnelExposeCommand.php && test -f tests/Feature/TunnelExposeCommandTest.php && test -f tests/Feature/QualityRunnersCiTest.php && test -f LICENSE`
+- [ ] `rg -n "'name' => 'Cloudflare'" config/app.php`
+- [ ] After `composer install`: `vendor/bin/pest --compact` exits 0. `php cf list` does not show `inspire`. `php cf tunnel:expose --help` exits 0.
+
+## Out of scope
+
+- Merging PR `#3` or deploying a PHAR
+- Regenerating `composer.lock`, bumping Symfony, pinning PHP 8.4/8.5
+- Restoring a full GHA quality workflow (PR body is stale; `#17` already retired that)
+- `dns:upsert` / `tunnel:status` / `--dry-run` (open issues `#14` `#15` `#16`)
+- Daily clone under `~/Projects/conduit-ui/repos/cloudflare`
+
+## Open decisions
+
+- `tests/Feature/.gitkeep` is empty noise (`fc7ba34` commit message still says "regenerate composer.lock"; the file is the whole commit). Optional delete. Do not empty `tests/Feature/` to justify it.
+- Smoke test title says "auth header" but only asserts base URL + account id. Optional rename or assert `Authorization` via Saloon public headers. Not blocking.
+- PR body still claims "Add CI workflow" and "Refresh composer.lock". Update the body; do not implement those claims.
diff --git a/tests/Feature/.gitkeep b/tests/Feature/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/tests/Feature/InspireCommandTest.php b/tests/Feature/InspireCommandTest.php
deleted file mode 100755
index 57642b2..0000000
--- a/tests/Feature/InspireCommandTest.php
+++ /dev/null
@@ -1,5 +0,0 @@
-artisan('inspire')->assertExitCode(0);
-});
diff --git a/tests/Feature/TunnelExposeListedTest.php b/tests/Feature/TunnelExposeListedTest.php
new file mode 100644
index 0000000..c96ecf5
--- /dev/null
+++ b/tests/Feature/TunnelExposeListedTest.php
@@ -0,0 +1,19 @@
+toBe(0)
+ ->and(Artisan::output())->toContain('tunnel:expose');
+});
+
+it('prints tunnel:expose help', function () {
+ $exit = Artisan::call('tunnel:expose', ['--help' => true]);
+
+ expect($exit)->toBe(0)
+ ->and(Artisan::output())->toContain('Create a tunnel and print cloudflared steps to expose a local app');
+});
diff --git a/tests/Unit/CloudflareConnectorTest.php b/tests/Unit/CloudflareConnectorTest.php
new file mode 100644
index 0000000..c481ee0
--- /dev/null
+++ b/tests/Unit/CloudflareConnectorTest.php
@@ -0,0 +1,22 @@
+resolveBaseUrl())->toBe('https://api.cloudflare.com/client/v4')
+ ->and($connector->getAccountId())->toBe('acct-123');
+});
+
+it('does not ship the Laravel Zero InspireCommand scaffold', function () {
+ expect(is_file(dirname(__DIR__, 2).'/app/Commands/InspireCommand.php'))->toBeFalse();
+});
+
+it('pins composer platform php to 8.3.33', function () {
+ $composer = json_decode((string) file_get_contents(dirname(__DIR__, 2).'/composer.json'), true, 512, JSON_THROW_ON_ERROR);
+
+ expect($composer['config']['platform']['php'])->toBe('8.3.33');
+});
diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php
deleted file mode 100644
index 61cd84c..0000000
--- a/tests/Unit/ExampleTest.php
+++ /dev/null
@@ -1,5 +0,0 @@
-toBeTrue();
-});