diff --git a/examples/autopilot-quickstart/CHANGELOG.md b/examples/autopilot-quickstart/CHANGELOG.md deleted file mode 100644 index 61a91f87..00000000 --- a/examples/autopilot-quickstart/CHANGELOG.md +++ /dev/null @@ -1,133 +0,0 @@ -# @gemstack/example-autopilot-quickstart - -## 0.0.12 - -### Patch Changes - -- Updated dependencies [cd121f4] -- Updated dependencies [780ef3e] -- Updated dependencies [23c1fb7] -- Updated dependencies [decace4] - - @gemstack/ai-sdk@0.6.1 - - @gemstack/ai-autopilot@0.12.0 - -## 0.0.11 - -### Patch Changes - -- Updated dependencies [46c79a6] -- Updated dependencies [8bf9d20] -- Updated dependencies [f6efb7d] -- Updated dependencies [f38f80b] - - @gemstack/ai-sdk@0.6.0 - - @gemstack/ai-autopilot@0.11.1 - -## 0.0.10 - -### Patch Changes - -- Updated dependencies [6f7cf23] -- Updated dependencies [6f7cf23] -- Updated dependencies [6f7cf23] -- Updated dependencies [66c7aeb] -- Updated dependencies [da79ec8] - - @gemstack/ai-sdk@0.5.1 - - @gemstack/ai-autopilot@0.11.0 - -## 0.0.9 - -### Patch Changes - -- Updated dependencies [734da1a] -- Updated dependencies [df15f71] -- Updated dependencies [9442761] -- Updated dependencies [5e24797] - - @gemstack/ai-autopilot@0.10.0 - -## 0.0.8 - -### Patch Changes - -- Updated dependencies [08f5710] - - @gemstack/ai-autopilot@0.9.0 - -## 0.0.7 - -### Patch Changes - -- Updated dependencies [4a6311e] -- Updated dependencies [8c3e7d0] -- Updated dependencies [03e06aa] -- Updated dependencies [3c72f14] -- Updated dependencies [e45e4d0] -- Updated dependencies [396dc7f] -- Updated dependencies [24944b9] -- Updated dependencies [d2acba4] - - @gemstack/ai-autopilot@0.8.0 - -## 0.0.6 - -### Patch Changes - -- Updated dependencies [6f7e7e3] - - @gemstack/ai-autopilot@0.7.0 - -## 0.0.5 - -### Patch Changes - -- Updated dependencies [1db19e2] -- Updated dependencies [c3e7e9e] -- Updated dependencies [6625ca7] -- Updated dependencies [bd13fcf] -- Updated dependencies [11f76da] -- Updated dependencies [c79f567] -- Updated dependencies [93892d7] -- Updated dependencies [de37e7e] -- Updated dependencies [d98d4ad] -- Updated dependencies [f1d11d9] - - @gemstack/ai-autopilot@0.6.0 - -## 0.0.4 - -### Patch Changes - -- Updated dependencies [d72accd] - - @gemstack/ai-autopilot@0.5.0 - -## 0.0.3 - -### Patch Changes - -- Updated dependencies [fc21943] -- Updated dependencies [87e6804] -- Updated dependencies [8d913dd] - - @gemstack/ai-autopilot@0.4.0 - -## 0.0.2 - -### Patch Changes - -- Updated dependencies [0823cfa] -- Updated dependencies [7ce5bae] -- Updated dependencies [493cc11] - - @gemstack/ai-autopilot@0.3.0 - -## 0.0.1 - -### Patch Changes - -- Updated dependencies [70168c5] -- Updated dependencies [11dce73] -- Updated dependencies [aa1925f] -- Updated dependencies [c9b4d0d] -- Updated dependencies [57cbba4] -- Updated dependencies [04965c5] -- Updated dependencies [07cc623] -- Updated dependencies [7fde5fc] -- Updated dependencies [55b3697] -- Updated dependencies [8f780f3] -- Updated dependencies [b0c0647] -- Updated dependencies [d261873] -- Updated dependencies [481c2f0] - - @gemstack/ai-autopilot@0.2.0 diff --git a/examples/autopilot-quickstart/README.md b/examples/autopilot-quickstart/README.md deleted file mode 100644 index 4e35b32f..00000000 --- a/examples/autopilot-quickstart/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# @gemstack/example-autopilot-quickstart - -A runnable, end-to-end quickstart for [`@gemstack/ai-autopilot`](../../packages/ai-autopilot) — the four layers of the epic composed into one "build a feature" flow: - -``` -workers → Supervisor → runner (sandbox) → surfaces -``` - -A lead planner decomposes the task **"Add a paginated Orders page backed by an orders table"** and routes each subtask to one of the example's own **worker** agents (`data-modeler`, `page-builder`, `ui-designer`). The **Supervisor** dispatches them; each worker acts inside a **runner** sandbox — writing files through `runnerTools` — and progress is rendered through the **surfaces** (a terminal sink for live output, plus a background handle exposing events + result). - -The roster is the app's own: autopilot orchestrates the agents you hand it and adds nothing to their instructions. - -It runs **offline**: `AiFake` scripts the model, so there's no API key and the output is deterministic. - -## Run it - -```bash -pnpm install && pnpm build # from the repo root, to build the packages first -pnpm --filter @gemstack/example-autopilot-quickstart start -``` - -You'll see the live plan, the files written into the sandbox, a build + preview URL, and the synthesized result. - -## Test it - -```bash -pnpm --filter @gemstack/example-autopilot-quickstart test -``` - -## Going real - -The only fakes here are `AiFake` (the model) and `FakeRunner` (the sandbox). To run it for real, drop `AiFake` and give the workers real model strings, and swap `FakeRunner` for a real runner adapter (a `FlueRunner`, WebContainer, or Docker sandbox) — the `Runner` interface is the same, so nothing else in the flow changes. - -## What each file shows - -- **`src/autopilot.ts`** — the composition: the worker roster + `runnerTools`, `agentPlanner` fed that roster, a `Supervisor`, and `launchAutopilot` + `terminalSink` for the surfaces. -- **`src/main.ts`** — the runnable demo. -- **`src/autopilot.test.ts`** — asserts the four layers actually compose (plan routes by role, files land in the sandbox, surfaces capture events). diff --git a/examples/autopilot-quickstart/package.json b/examples/autopilot-quickstart/package.json deleted file mode 100644 index 737a43d9..00000000 --- a/examples/autopilot-quickstart/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "@gemstack/example-autopilot-quickstart", - "version": "0.0.12", - "private": true, - "description": "Runnable end-to-end quickstart for @gemstack/ai-autopilot: workers + Supervisor + runner + surfaces composed into one build-a-feature flow, offline via AiFake.", - "type": "module", - "scripts": { - "typecheck": "tsc --noEmit", - "test": "tsc -p tsconfig.test.json && cd dist-test && node --test", - "clean": "rm -rf dist-test", - "start": "tsx src/main.ts" - }, - "dependencies": { - "@gemstack/ai-autopilot": "workspace:^", - "@gemstack/ai-sdk": "workspace:^", - "zod": "^4.0.0" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "tsx": "^4.19.0", - "typescript": "^5.4.0" - } -} diff --git a/examples/autopilot-quickstart/src/autopilot.test.ts b/examples/autopilot-quickstart/src/autopilot.test.ts deleted file mode 100644 index 5ed2d6c8..00000000 --- a/examples/autopilot-quickstart/src/autopilot.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { describe, it } from 'node:test' -import assert from 'node:assert/strict' -import { runQuickstart, TASK } from './autopilot.js' - -describe('autopilot quickstart: the four layers compose end-to-end', () => { - it('plans by role, dispatches, acts in the sandbox, and surfaces progress', async () => { - const lines: string[] = [] - const result = await runQuickstart(line => lines.push(line)) - - // Supervisor: three subtasks, each routed to a worker, all succeeded. - assert.equal(result.run.plan.length, 3) - assert.deepEqual(result.run.plan.map(s => s.worker).sort(), ['data-modeler', 'page-builder', 'ui-designer']) - assert.ok(result.run.results.every(r => r.ok), 'every subtask succeeded') - - // Runner: each worker wrote its file into the sandbox via runnerTools. - assert.ok('database/schema.ts' in result.files) - assert.ok('pages/orders/+Page.jsx' in result.files) - assert.match(result.files['database/schema.ts']!, /orders/) - // the seed file is still there - assert.ok('package.json' in result.files) - - // Runner: the post-build exec ran and a preview URL was exposed. - assert.equal(result.build.exitCode, 0) - assert.match(result.build.stdout, /built/) - assert.match(result.previewUrl, /:5173$/) - - // Surfaces: the terminal sink printed a plan line and per-subtask lines; - // the background handle captured the same events. - assert.ok(lines.some(l => l.includes('plan:')), 'terminal printed the plan') - assert.ok(lines.some(l => l.includes('✓')), 'terminal printed a completed subtask') - assert.equal(result.events[0]!.type, 'plan') - assert.equal(result.events.at(-1)!.type, 'synthesize') - }) - - it('exposes the task constant for the runnable demo', () => { - assert.match(TASK, /Orders/) - }) -}) diff --git a/examples/autopilot-quickstart/src/autopilot.ts b/examples/autopilot-quickstart/src/autopilot.ts deleted file mode 100644 index 29904573..00000000 --- a/examples/autopilot-quickstart/src/autopilot.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { AiFake, agent, type ToolCall } from '@gemstack/ai-sdk' -import { - Supervisor, - agentPlanner, - FakeRunner, - runnerTools, - terminalSink, - launchAutopilot, - type RunnerSession, - type SupervisorEvent, - type SupervisorRun, -} from '@gemstack/ai-autopilot' - -/** - * The end-to-end shape of the ai-autopilot epic, in one flow: - * - * workers → Supervisor → runner (sandbox) → surfaces - * - * A lead planner decomposes a build task and routes each subtask to one of the - * **worker** agents defined below; the **Supervisor** dispatches them; each - * worker acts inside a **runner** sandbox (writing files via `runnerTools`); - * progress is rendered through the **surfaces** (a terminal sink plus a - * background handle with a live stream). - * - * The roster is the app's own: autopilot orchestrates whatever agents you hand - * it and never injects instructions of its own. - * - * It runs offline: `AiFake` scripts the model, so there is no API key and the - * output is deterministic. Swapping `FakeRunner` for a real runner and dropping - * the fake is the only change needed to run it for real. - */ - -/** The feature we ask autopilot to build. */ -export const TASK = 'Add a paginated Orders page backed by an orders table' - -/** The worker roster: the role name the planner routes to, and how that agent is briefed. */ -const WORKERS = [ - { name: 'data-modeler', role: 'Design database schemas and migrations.' }, - { name: 'page-builder', role: 'Build pages and their routing.' }, - { name: 'ui-designer', role: 'Design the UI for a page.' }, -] as const - -/** Each subtask, the worker that should own it, and the file it writes. */ -const WORK = [ - { - worker: 'data-modeler', - description: 'Define the orders schema and a migration', - file: 'database/schema.ts', - contents: "export const orders = table('orders', { id: id(), total: integer(), createdAt: timestamp() })\n", - }, - { - worker: 'page-builder', - description: 'Build the /orders page that lists orders, paginated', - file: 'pages/orders/+Page.jsx', - contents: "export default function Page({ orders }) { return }\n", - }, - { - worker: 'ui-designer', - description: 'Express the orders list as intent, not hardcoded markup', - file: 'pages/orders/+config.js', - contents: "export default { meta: { OrderList: { env: { server: true, client: true } } } }\n", - }, -] as const - -/** Script the fake: step 0 is the planner's JSON; then each worker writes its file. */ -function scriptModel(fake: AiFake): void { - const plannerOutput = JSON.stringify(WORK.map(w => ({ description: w.description, worker: w.worker }))) - const workerSteps = WORK.flatMap((w, i) => { - const toolCalls: ToolCall[] = [ - { id: `write-${i}`, name: 'write_file', arguments: { path: w.file, contents: w.contents } }, - ] - return [{ toolCalls }, { text: `Wrote ${w.file}` }] - }) - // concurrency: 1 makes the provider-call order deterministic: - // 0 = planner, then each worker's (tool-call, final-text) pair in plan order. - fake.respondWithSequence([{ text: plannerOutput }, ...workerSteps]) -} - -/** The roster, as a line per worker, so the planner knows who it can route to. */ -function roster(): string { - return WORKERS.map(w => `- ${w.name}: ${w.role}`).join('\n') -} - -/** Build one worker agent per role, each with hands inside the sandbox. */ -function workersWithSandbox(session: RunnerSession): Record> { - const sandbox = runnerTools(session) - return Object.fromEntries(WORKERS.map(w => [w.name, agent({ instructions: w.role, tools: sandbox })])) -} - -export interface QuickstartResult { - run: SupervisorRun - /** Every event, in order (from the background handle). */ - events: SupervisorEvent[] - /** Files the workers wrote into the sandbox. */ - files: Record - /** Output of the post-build `exec`. */ - build: { stdout: string; exitCode: number } - /** The preview URL the sandbox exposed. */ - previewUrl: string -} - -/** - * Run the whole flow once and return everything the surfaces exposed. - * - * @param write where terminal-surface lines go (default: no-op; `main.ts` prints). - */ -export async function runQuickstart(write: (line: string) => void = () => {}): Promise { - const fake = AiFake.fake() - scriptModel(fake) - try { - // Runner: an in-memory sandbox seeded with a minimal project. - const runner = new FakeRunner({ - onExec: cmd => - cmd.includes('build') - ? { stdout: 'orders page built', stderr: '', exitCode: 0 } - : { stdout: '', stderr: '', exitCode: 0 }, - }) - const session = await runner.boot({ files: { 'package.json': '{ "name": "shop" }\n' } }) - - // Workers → Supervisor. The planner is told the roster so it routes by role. - const planner = agentPlanner( - agent(`You are the lead engineer. Decompose the task and route each subtask to a worker.\n\n${roster()}`), - ) - const start = (onEvent: (e: SupervisorEvent) => void) => - new Supervisor({ - plan: planner, - workers: workersWithSandbox(session), - concurrency: 1, - onEvent, - }).run(TASK) - - // Surfaces: one run feeds both the terminal (live) and a background handle. - const terminal = terminalSink({ write }) - const handle = launchAutopilot(onEvent => - start(e => { - terminal(e) - onEvent(e) - }), - ) - const run = await handle.result() - - // Runner again: build the app and grab a preview URL. - const build = await session.exec('pnpm build') - const preview = session.preview ? await session.preview({ port: 5173 }) : { url: '' } - - return { - run, - events: handle.events(), - files: session.snapshot(), - build: { stdout: build.stdout, exitCode: build.exitCode }, - previewUrl: preview.url, - } - } finally { - fake.restore() - } -} diff --git a/examples/autopilot-quickstart/src/main.ts b/examples/autopilot-quickstart/src/main.ts deleted file mode 100644 index 7c300bc6..00000000 --- a/examples/autopilot-quickstart/src/main.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { runQuickstart, TASK } from './autopilot.js' - -/** Run the quickstart and print what each surface exposed. Offline (AiFake). */ -async function main(): Promise { - console.log(`Task: ${TASK}\n`) - console.log('--- live progress (terminal surface) ---') - const result = await runQuickstart(line => process.stdout.write(line + '\n')) - - console.log('\n--- files written into the sandbox (runner) ---') - for (const path of Object.keys(result.files).sort()) console.log(` ${path}`) - - console.log('\n--- build + preview (runner) ---') - console.log(` build: exit ${result.build.exitCode} — ${result.build.stdout}`) - console.log(` preview: ${result.previewUrl}`) - - console.log('\n--- synthesized result (Supervisor) ---') - console.log(result.run.text.replace(/^/gm, ' ')) -} - -main().catch(err => { - console.error(err) - process.exitCode = 1 -}) diff --git a/examples/autopilot-quickstart/tsconfig.json b/examples/autopilot-quickstart/tsconfig.json deleted file mode 100644 index 404aab40..00000000 --- a/examples/autopilot-quickstart/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { "noEmit": true, "rootDir": "src" }, - "include": ["src"] -} diff --git a/examples/autopilot-quickstart/tsconfig.test.json b/examples/autopilot-quickstart/tsconfig.test.json deleted file mode 100644 index eebda2fc..00000000 --- a/examples/autopilot-quickstart/tsconfig.test.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { "outDir": "dist-test", "rootDir": "src" }, - "include": ["src"] -} diff --git a/examples/bootstrap-quickstart/CHANGELOG.md b/examples/bootstrap-quickstart/CHANGELOG.md deleted file mode 100644 index 9136daa5..00000000 --- a/examples/bootstrap-quickstart/CHANGELOG.md +++ /dev/null @@ -1,133 +0,0 @@ -# @gemstack/example-bootstrap-quickstart - -## 0.0.12 - -### Patch Changes - -- Updated dependencies [cd121f4] -- Updated dependencies [780ef3e] -- Updated dependencies [23c1fb7] -- Updated dependencies [decace4] - - @gemstack/ai-sdk@0.6.1 - - @gemstack/ai-autopilot@0.12.0 - -## 0.0.11 - -### Patch Changes - -- Updated dependencies [46c79a6] -- Updated dependencies [8bf9d20] -- Updated dependencies [f6efb7d] -- Updated dependencies [f38f80b] - - @gemstack/ai-sdk@0.6.0 - - @gemstack/ai-autopilot@0.11.1 - -## 0.0.10 - -### Patch Changes - -- Updated dependencies [6f7cf23] -- Updated dependencies [6f7cf23] -- Updated dependencies [6f7cf23] -- Updated dependencies [66c7aeb] -- Updated dependencies [da79ec8] - - @gemstack/ai-sdk@0.5.1 - - @gemstack/ai-autopilot@0.11.0 - -## 0.0.9 - -### Patch Changes - -- Updated dependencies [734da1a] -- Updated dependencies [df15f71] -- Updated dependencies [9442761] -- Updated dependencies [5e24797] - - @gemstack/ai-autopilot@0.10.0 - -## 0.0.8 - -### Patch Changes - -- Updated dependencies [08f5710] - - @gemstack/ai-autopilot@0.9.0 - -## 0.0.7 - -### Patch Changes - -- Updated dependencies [4a6311e] -- Updated dependencies [8c3e7d0] -- Updated dependencies [03e06aa] -- Updated dependencies [3c72f14] -- Updated dependencies [e45e4d0] -- Updated dependencies [396dc7f] -- Updated dependencies [24944b9] -- Updated dependencies [d2acba4] - - @gemstack/ai-autopilot@0.8.0 - -## 0.0.6 - -### Patch Changes - -- Updated dependencies [6f7e7e3] - - @gemstack/ai-autopilot@0.7.0 - -## 0.0.5 - -### Patch Changes - -- Updated dependencies [1db19e2] -- Updated dependencies [c3e7e9e] -- Updated dependencies [6625ca7] -- Updated dependencies [bd13fcf] -- Updated dependencies [11f76da] -- Updated dependencies [c79f567] -- Updated dependencies [93892d7] -- Updated dependencies [de37e7e] -- Updated dependencies [d98d4ad] -- Updated dependencies [f1d11d9] - - @gemstack/ai-autopilot@0.6.0 - -## 0.0.4 - -### Patch Changes - -- Updated dependencies [d72accd] - - @gemstack/ai-autopilot@0.5.0 - -## 0.0.3 - -### Patch Changes - -- Updated dependencies [fc21943] -- Updated dependencies [87e6804] -- Updated dependencies [8d913dd] - - @gemstack/ai-autopilot@0.4.0 - -## 0.0.2 - -### Patch Changes - -- Updated dependencies [0823cfa] -- Updated dependencies [7ce5bae] -- Updated dependencies [493cc11] - - @gemstack/ai-autopilot@0.3.0 - -## 0.0.1 - -### Patch Changes - -- Updated dependencies [70168c5] -- Updated dependencies [11dce73] -- Updated dependencies [aa1925f] -- Updated dependencies [c9b4d0d] -- Updated dependencies [57cbba4] -- Updated dependencies [04965c5] -- Updated dependencies [07cc623] -- Updated dependencies [7fde5fc] -- Updated dependencies [55b3697] -- Updated dependencies [8f780f3] -- Updated dependencies [b0c0647] -- Updated dependencies [d261873] -- Updated dependencies [481c2f0] - - @gemstack/ai-autopilot@0.2.0 diff --git a/examples/bootstrap-quickstart/README.md b/examples/bootstrap-quickstart/README.md deleted file mode 100644 index d773450a..00000000 --- a/examples/bootstrap-quickstart/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# Bootstrap quickstart (capstone) - -The whole `@gemstack/ai-autopilot` AI-framework epic in one offline flow — from a -project's dependencies to a scaffolded, production-grade, deploy-decided app. - -``` -detect framework (preset) - → Bootstrap: scope → build → full-fledged loop → deploy - → scale mode: CODE-OVERVIEW.md -``` - -## Run it - -```bash -pnpm --filter @gemstack/example-bootstrap-quickstart start -``` - -No API key: `AiFake` scripts the model and `FakeRunner` is an in-memory sandbox, -so the run is deterministic. You'll see the narration stream live, the files the -workers wrote, the checklist blocking once and then clearing, the app -shipped to a Cloudflare URL, and the generated `CODE-OVERVIEW.md`. The deploy runs -the real `cloudflareTarget` adapter over a simulated `wrangler`, so the whole flow -ends at a live-looking URL with no credentials. - -## What it shows - -- **Presets (#115)** — the framework is detected from the project deps (Vike here), - so the build's workers are briefed on the right one. -- **Bootstrap (#116)** — one scoping question, then a **build** scaffolds the app - with those workers inside a **runner**, the **full-fledged loop** repeats the - production-grade checklist until its `{ blockers }` verdict is empty, and a - **deploy** is decided and shipped through the `DeployTarget` seam — here the real - `cloudflareTarget` (SSR → Workers), run over a simulated `wrangler` offline. - What stack to build on is the build agent's call, not the framework's. -- **Surfaces (#100/#120)** — every phase streams as narration over the generic - `launchAutopilot` handle. -- **Scale mode (#114)** — `CODE-OVERVIEW.md` is generated from the scaffold. - -## Live verification (#124) - -The offline run above verifies the flow structurally. `src/live.ts` runs the same -flow for real — a real model via `@gemstack/ai-sdk` and a real `LocalRunner` -workspace on the host filesystem, so the build workers and the deploy decision are -all model-driven and the workers write **real files to disk**: - -```bash -ANTHROPIC_API_KEY=sk-... pnpm --filter @gemstack/example-bootstrap-quickstart start:live -# override the model with any provider ai-sdk knows: -GEMSTACK_MODEL=anthropic/claude-haiku-4-5-20251001 ... start:live -``` - -Swapping the fakes (`AiFake` + `FakeRunner`) for a real model + `LocalRunner` is the -only difference from `main.ts`; the orchestration is identical. A sample live run -scaffolded a 9-file Vike + universal-orm orders app (schema + migration, `pages/orders/` -with `+Page`/`+data`/`+config`, a UI-intent renderer) from the intent, blocked the -checklist once on missing auth, then decided SSR → Cloudflare. - -### Real deploy to Cloudflare - -Add a Cloudflare token and the live run ships for real — `cloudflareTarget` -installs, builds, and deploys the scaffold (Workers for SSR, Pages for SSG/SPA) -and reports the live URL: - -```bash -ANTHROPIC_API_KEY=sk-... \ -CLOUDFLARE_API_TOKEN=... CLOUDFLARE_ACCOUNT_ID=... CLOUDFLARE_PROJECT=my-app \ -pnpm --filter @gemstack/example-bootstrap-quickstart start:live -``` - -Without `CLOUDFLARE_API_TOKEN` the deploy falls back to `planOnlyTarget` (decide + -narrate, no ship), so the live run works with only a model key. Note the deploy -runs `npm install && npm run build` against the scaffold, so a real ship needs the -generated app to actually build — that is what the full-fledged loop (and an -opt-in `serveCheck`, below) are for. - -Scoped for a bounded proof, the production-grade **loop** keeps the scripted verdict -so the run stays deterministic and cheap; making the checklist a real reviewer agent -is the natural follow-up. - -### Giving the loop teeth: `serveCheck` - -The scripted checklist only *asks* whether the app is production-grade. To gate a pass -on whether the app actually **boots and serves**, compose a `serveCheck` into the -checklist — it runs install → start the dev server → `preview` → fetch a health path, -turning failures into blockers the improve loop then fixes: - -```js -import { serveCheck, mergeChecklists, loopChecklist } from '@gemstack/ai-autopilot' - -// A pass is production-grade only when the prompt says so AND the app really runs. -checklist: mergeChecklists( - loopChecklist({ loop }), - serveCheck(session, { install: 'npm install', serve: 'npm run dev', port: 3000 }), -), -``` - -It's left out of the default live run because a model-scaffolded app may not install or -boot first try (that's the loop's job to fix) — enable it when you want the run to prove -the app serves, not just that files were written. diff --git a/examples/bootstrap-quickstart/package.json b/examples/bootstrap-quickstart/package.json deleted file mode 100644 index 9ff4e4b8..00000000 --- a/examples/bootstrap-quickstart/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "@gemstack/example-bootstrap-quickstart", - "version": "0.0.12", - "private": true, - "description": "Runnable capstone for @gemstack/ai-autopilot: preset detection + Bootstrap (scope → build → full-fledged loop → deploy) + scale mode, streamed over surfaces, offline via AiFake + FakeRunner.", - "type": "module", - "scripts": { - "typecheck": "tsc --noEmit", - "test": "tsc -p tsconfig.test.json && cd dist-test && node --test", - "clean": "rm -rf dist-test", - "start": "tsx src/main.ts", - "start:live": "tsx src/main-live.ts" - }, - "dependencies": { - "@gemstack/ai-autopilot": "workspace:^", - "@gemstack/ai-sdk": "workspace:^", - "zod": "^4.0.0" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "tsx": "^4.19.0", - "typescript": "^5.4.0" - } -} diff --git a/examples/bootstrap-quickstart/src/bootstrap.test.ts b/examples/bootstrap-quickstart/src/bootstrap.test.ts deleted file mode 100644 index a0f0c47b..00000000 --- a/examples/bootstrap-quickstart/src/bootstrap.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { describe, it } from 'node:test' -import assert from 'node:assert/strict' -import { runCapstone, INTENT } from './bootstrap.js' - -describe('bootstrap capstone: the whole epic composes end-to-end (offline)', () => { - it('detects the preset, runs scope → build → loop → deploy, and maps the code', async () => { - const lines: string[] = [] - const { detection, result, events, files, overview } = await runCapstone(line => lines.push(line)) - - // Preset: the Vike framework was detected from the project deps. - assert.equal(detection.preset?.name, 'vike') - assert.equal(detection.framework, 'Vike') - - // Build: each worker wrote its file into the sandbox. - assert.ok('database/schema.ts' in files) - assert.ok('pages/orders/+Page.jsx' in files) - assert.ok('pages/orders/+config.js' in files) - assert.ok('package.json' in files) // the seed survived - assert.equal(result.run.results.length, 3) - assert.ok(result.run.results.every(r => r.ok)) - - // Full-fledged loop: blocked on pass 1, clean on pass 2 → production-grade. - assert.equal(result.passes, 2) - assert.deepEqual(result.blockers, []) - assert.equal(result.productionGrade, true) - - // Deploy: decided SSR → Cloudflare and shipped via the real cloudflareTarget - // adapter (over a simulated wrangler), reporting the live URL it printed. - assert.equal(result.deploy?.plan.render, 'ssr') - assert.equal(result.deploy?.plan.target, 'cloudflare') - assert.equal(result.deploy?.result.deployed, true) - assert.equal(result.deploy?.result.url, 'https://orders-app.gemstack.workers.dev') - - // Surface: the stream ran scope-first, done-last, and the terminal printed it. - assert.equal(events[0]?.type, 'scope') - assert.equal(events.at(-1)?.type, 'done') - assert.ok(lines.some(l => l.includes('scope:'))) - assert.ok(lines.some(l => l.includes('deploy:'))) - - // Scale mode: an overview was generated from the scaffold. - assert.match(overview?.summary ?? '', /orders app/) - assert.ok((overview?.sections.length ?? 0) >= 1) - }) - - it('exposes the intent constant for the runnable demo', () => { - assert.match(INTENT, /Orders page/) - }) -}) diff --git a/examples/bootstrap-quickstart/src/bootstrap.ts b/examples/bootstrap-quickstart/src/bootstrap.ts deleted file mode 100644 index 9c4d284b..00000000 --- a/examples/bootstrap-quickstart/src/bootstrap.ts +++ /dev/null @@ -1,231 +0,0 @@ -import { AiFake, agent, type ToolCall } from '@gemstack/ai-sdk' -import { - Bootstrap, - supervisorBuild, - loopChecklist, - loopImprove, - agentDeploy, - cloudflareTarget, - LoopEngine, - definePrompt, - defineLoop, - builtinFrameworkPresetRegistry, - CodeOverviewMaintainer, - FakeRunner, - runnerTools, - launchAutopilot, - type BootstrapEvent, - type BootstrapResult, - type CodeOverview, - type FrameworkDetection, - type Planner, - type RunnerSession, -} from '@gemstack/ai-autopilot' - -/** - * The capstone: the whole AI-framework epic in one offline flow. - * - * detect framework (preset) → Bootstrap - * scope → build → full-fledged loop → deploy - * → scale mode (CODE-OVERVIEW.md) - * - * A **preset** detects the project's framework from its dependencies, so the - * build's workers are briefed on the right one. **Bootstrap** then sequences the - * flow: it asks one scoping question, **builds** the app with those workers inside - * a **runner** sandbox, runs the **full-fledged loop** until the production-grade - * checklist's `{ blockers }` verdict is empty, and **decides a deploy** behind the - * `DeployTarget` seam. What stack to build on is the build agent's call, not ours. - * Every phase streams as narration over the generic **surface**. Finally **scale - * mode** generates `CODE-OVERVIEW.md` from the scaffold. - * - * It runs offline: `AiFake` scripts the model and `FakeRunner` is an in-memory - * sandbox, so there is no API key and the output is deterministic. `live.ts` runs - * the same flow for real (a real model + `LocalRunner` writing files to disk) — the - * live proof for #124. - */ - -/** What the user wants built (the one thing scope asks about). */ -export const INTENT = 'A paginated Orders page backed by an orders table, with sign-in.' - -/** The project we detect a framework from — Vike here, so the Vike preset wins. */ -const PROJECT_DEPS = { 'vike-react': '1.0.0', react: '18.0.0', '@prisma/client': '1.0.0' } - -/** Each build subtask, the worker that owns it, and the file it writes. */ -const WORK = [ - { - worker: 'data-modeler', - description: 'Define the orders schema and a migration', - file: 'database/schema.ts', - contents: "export const orders = table('orders', { id: id(), total: integer(), createdAt: timestamp() })\n", - }, - { - worker: 'page-builder', - description: 'Build the /orders page that lists orders, paginated', - file: 'pages/orders/+Page.jsx', - contents: "export default function Page({ orders }) { return }\n", - }, - { - worker: 'ui-designer', - description: 'Express the orders list as intent, not hardcoded markup', - file: 'pages/orders/+config.js', - contents: "export default { meta: { OrderList: { env: { server: true, client: true } } } }\n", - }, -] as const - -/** The deploy decision (what `agentDeploy` parses). SSR → Cloudflare Workers. */ -const DEPLOY_DECISION = { render: 'ssr', target: 'cloudflare', reason: 'per-request orders data + server-side auth' } - -/** The URL the simulated `wrangler` prints, so the offline demo ends at a live-looking URL. */ -const DEPLOY_URL = 'https://orders-app.gemstack.workers.dev' - -/** - * Script the fake provider. Order (concurrency 1) is: each build worker's - * (write-file tool call, final text) pair, then the deploy decision. The - * full-fledged loop uses scripted local prompts, not the model. - */ -function scriptModel(fake: AiFake): void { - const workerSteps = WORK.flatMap((w, i) => { - const toolCalls: ToolCall[] = [ - { id: `write-${i}`, name: 'write_file', arguments: { path: w.file, contents: w.contents } }, - ] - return [{ toolCalls }, { text: `Wrote ${w.file}` }] - }) - fake.respondWithSequence([...workerSteps, { text: JSON.stringify(DEPLOY_DECISION) }]) -} - -/** A static planner: the build subtasks, in the order the fake scripts them. */ -const staticPlanner: Planner = () => WORK.map(w => ({ description: w.description, worker: w.worker })) - -/** The worker roster: the role name the plan routes to, and how that agent is briefed. */ -function roster(framework: string) { - return [ - { name: 'data-modeler', role: 'Design database schemas and migrations.' }, - { name: 'page-builder', role: `Build pages and their routing for ${framework}.` }, - { name: 'ui-designer', role: 'Design the UI for a page.' }, - ] -} - -/** Build one worker agent per role, each with hands inside the sandbox. */ -function buildWorkers(session: RunnerSession, framework: string) { - const sandbox = runnerTools(session) - return Object.fromEntries(roster(framework).map(w => [w.name, agent({ instructions: w.role, tools: sandbox })])) -} - -/** The full-fledged loop: the checklist blocks once, then clears after the fix. */ -function buildLoop(): LoopEngine { - const verdicts = [ - '```json\n{ "blockers": ["No authentication on the orders page yet"] }\n```', - '```json\n{ "blockers": [] }\n```', - ] - let pass = 0 - return new LoopEngine({ - loops: [ - defineLoop({ on: 'production-check', run: ['production-grade'] }), - defineLoop({ on: 'major-change', run: ['address-blockers'] }), - ], - prompts: [ - definePrompt({ id: 'production-grade', run: () => verdicts[Math.min(pass++, verdicts.length - 1)]! }), - definePrompt({ id: 'address-blockers', run: () => 'Added a +guard to the orders page (vike-auth).' }), - ], - }) -} - -/** Everything the capstone exposes, for the runnable demo and the smoke test. */ -export interface CapstoneResult { - detection: FrameworkDetection - result: BootstrapResult - events: BootstrapEvent[] - files: Record - overview: CodeOverview | undefined -} - -/** Render a bootstrap event as one human-readable narration line. */ -export function formatBootstrapEvent(event: BootstrapEvent): string { - switch (event.type) { - case 'scope': - return `▶ scope: ${event.scope} — "${event.intent}"` - case 'narrate': - return ` ${event.message}` - case 'build': - return ` build/${event.event.type}` - case 'checklist': - return event.passing - ? ` ✓ checklist pass ${event.pass}: production-grade` - : ` ✗ checklist pass ${event.pass}: ${event.blockers.join('; ')}` - case 'improve': - return ` → improving: ${event.blockers.join('; ')}` - case 'deploy': - return `▶ deploy: ${event.plan.render.toUpperCase()} → ${event.plan.target} (${event.plan.reason})` - case 'done': - return `✓ done: ${event.result.productionGrade ? 'production-grade' : 'prototype'} in ${event.result.passes} pass(es)` - } -} - -/** - * Run the whole capstone once and return everything the surfaces exposed. - * - * @param write where narration lines go (default: no-op; `main.ts` prints). - */ -export async function runCapstone(write: (line: string) => void = () => {}): Promise { - const fake = AiFake.fake() - scriptModel(fake) - try { - // 0. FrameworkPreset: detect the framework from the project's deps to brief the workers. - const { preset, detection } = builtinFrameworkPresetRegistry().select({ dependencies: PROJECT_DEPS }) - - // Runner: an in-memory sandbox seeded with a minimal project. `wrangler` is - // simulated (prints a live-looking URL) so the real cloudflareTarget adapter - // runs its full path — install → build → deploy → parse URL — offline. - const runner = new FakeRunner({ - onExec: cmd => { - if (cmd.includes('wrangler')) return { stdout: `Published orders-app\n${DEPLOY_URL}`, stderr: '', exitCode: 0 } - return { stdout: cmd.includes('build') ? 'built' : '', stderr: '', exitCode: 0 } - }, - }) - const session = await runner.boot({ files: { 'package.json': JSON.stringify({ name: 'orders-app' }) + '\n' } }) - - const loop = buildLoop() - // The real Cloudflare adapter, run over the simulated wrangler above. A fake - // token lets it proceed offline; the live capstone passes a real one. - const deployTarget = cloudflareTarget({ - session, - apiToken: process.env['CLOUDFLARE_API_TOKEN'] ?? 'demo-token', - projectName: 'orders-app', - }) - - // Surfaces: run bootstrap detached; the terminal prints as events stream. - const handle = launchAutopilot(onEvent => - new Bootstrap({ - onEvent: e => { - write(formatBootstrapEvent(e)) - onEvent(e) - }, - steps: { - scope: () => ({ scope: 'full', intent: INTENT }), - build: supervisorBuild({ plan: staticPlanner, workers: buildWorkers(session, preset.framework), concurrency: 1 }), - checklist: loopChecklist({ loop }), - improve: loopImprove({ loop }), - deploy: agentDeploy(agent({ instructions: 'deployer' }), { target: deployTarget }), - }, - }).run(), - ) - const result = await handle.result() - const files = session.snapshot() - - // Scale mode: generate CODE-OVERVIEW.md from the scaffold (a material change). - const maintainer = new CodeOverviewMaintainer({ - regenerate: () => ({ - summary: 'A server-rendered orders app on Vike + Prisma.', - sections: [ - { title: 'Structure', body: '- `pages/orders/` — the paginated orders page\n- `database/` — the orders schema + migrations' }, - { title: 'Conventions', body: 'Data goes through the Prisma client; pages stay thin.' }, - ], - }), - }) - await maintainer.handle({ kind: 'major-change', summary: 'scaffolded the app', paths: [...Object.keys(files), 'package.json'] }) - - return { detection, result, events: handle.events(), files, overview: maintainer.get() } - } finally { - fake.restore() - } -} diff --git a/examples/bootstrap-quickstart/src/live.ts b/examples/bootstrap-quickstart/src/live.ts deleted file mode 100644 index 2a097efd..00000000 --- a/examples/bootstrap-quickstart/src/live.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { AiRegistry, AnthropicProvider, agent } from '@gemstack/ai-sdk' -import { - Bootstrap, - supervisorBuild, - loopChecklist, - loopImprove, - agentDeploy, - cloudflareTarget, - LoopEngine, - definePrompt, - defineLoop, - builtinFrameworkPresetRegistry, - CodeOverviewMaintainer, - LocalRunner, - runnerTools, - launchAutopilot, - type BootstrapEvent, - type BootstrapResult, - type Planner, - type RunnerSession, -} from '@gemstack/ai-autopilot' -import { INTENT, formatBootstrapEvent, type CapstoneResult } from './bootstrap.js' - -/** - * The LIVE half of the capstone (#124): the same flow as `bootstrap.ts`, but with - * the fakes swapped for real infra — a real model via `@gemstack/ai-sdk` and a real - * `LocalRunner` sandbox on the host filesystem. The build workers and the deploy - * decision are all model-driven; the workers write REAL files into a real temp - * workspace. This is the honest "zero to a scaffolded app" proof that the offline - * run (AiFake + FakeRunner) can only assert structurally. - * - * Scoped for a first, bounded, cheap proof: the production-grade loop keeps the same - * scripted verdict as the offline example (blocks once on "no auth", then clears), - * so the run is deterministic and does not spend model budget on the checklist. - * Making the checklist a real reviewer agent is the natural follow-up. - * - * Run it: `ANTHROPIC_API_KEY=… pnpm start:live` (any provider ai-sdk knows works via - * GEMSTACK_MODEL, e.g. `GEMSTACK_MODEL=anthropic/claude-haiku-4-5-20251001`). - */ - -/** The model to drive the live run. Cheap + fast by default; override via env. */ -const MODEL = process.env['GEMSTACK_MODEL'] ?? 'anthropic/claude-haiku-4-5-20251001' - -/** The project we detect a framework from — Vike here, so the Vike preset wins. */ -const PROJECT_DEPS = { 'vike-react': '1.0.0', react: '18.0.0', '@prisma/client': '1.0.0' } - -/** The build plan: three subtasks, each owned by a worker (by name). The real - * worker decides the file contents; only the decomposition is fixed. */ -const WORK = [ - { worker: 'data-modeler', description: 'Define the orders schema and a migration in database/schema.ts' }, - { worker: 'page-builder', description: 'Build pages/orders/+Page.jsx: a server-rendered, paginated list of orders' }, - { worker: 'ui-designer', description: 'Express the orders list as intent (a +config.js meta), not hardcoded markup' }, -] as const -const livePlanner: Planner = () => WORK.map(w => ({ description: w.description, worker: w.worker })) - -/** Register the Anthropic provider from the environment key. Throws with a clear - * message if the key is missing, so a bad env fails loudly rather than at request time. */ -export function registerModel(): void { - const apiKey = process.env['ANTHROPIC_API_KEY'] - if (!apiKey) throw new Error('[live capstone] set ANTHROPIC_API_KEY (a model key) in the environment') - AiRegistry.register(new AnthropicProvider({ apiKey })) -} - -/** The worker roster: the role name the plan routes to, and how that agent is briefed. */ -function roster(framework: string) { - return [ - { name: 'data-modeler', role: 'You design database schemas and migrations.' }, - { name: 'page-builder', role: `You build pages and their routing for ${framework}.` }, - { name: 'ui-designer', role: 'You design the UI for a page.' }, - ] -} - -/** One real worker agent per role, each with hands (runner tools) in the sandbox. */ -function buildWorkers(session: RunnerSession, framework: string) { - const sandbox = runnerTools(session) - return Object.fromEntries( - roster(framework).map(w => [ - w.name, - agent({ model: MODEL, instructions: `${w.role} Write your work to disk with your tools.`, tools: sandbox }), - ]), - ) -} - -/** The full-fledged loop: the checklist blocks once, then clears after the fix (scripted). */ -function buildLoop(): LoopEngine { - const verdicts = [ - '```json\n{ "blockers": ["No authentication on the orders page yet"] }\n```', - '```json\n{ "blockers": [] }\n```', - ] - let pass = 0 - return new LoopEngine({ - loops: [ - defineLoop({ on: 'production-check', run: ['production-grade'] }), - defineLoop({ on: 'major-change', run: ['address-blockers'] }), - ], - prompts: [ - definePrompt({ id: 'production-grade', run: () => verdicts[Math.min(pass++, verdicts.length - 1)]! }), - definePrompt({ id: 'address-blockers', run: () => 'Added a +guard to the orders page (vike-auth).' }), - ], - }) -} - -/** - * The deploy step. With `CLOUDFLARE_API_TOKEN` set, it ships for real: the model - * decides SSR/SSG/SPA and `cloudflareTarget` installs → builds → deploys the app - * to Cloudflare (Workers for SSR, Pages for SSG/SPA) and reports the live URL. - * Without a token it falls back to `planOnlyTarget` (decide + narrate, no ship), - * so the run works with only a model key. - */ -function deployStep(session: RunnerSession, deployer: ReturnType) { - if (process.env['CLOUDFLARE_API_TOKEN']) { - return agentDeploy(deployer, { - targets: ['cloudflare'], - target: cloudflareTarget({ session, projectName: process.env['CLOUDFLARE_PROJECT'] ?? 'gemstack-orders-demo' }), - }) - } - return agentDeploy(deployer) -} - -/** Snapshot the real workspace into a { path: contents } map. */ -async function snapshot(session: RunnerSession): Promise> { - const files: Record = {} - for (const path of await session.fs.list()) { - files[path] = await session.fs.read(path) - } - return files -} - -/** - * Run the live capstone once against a real model + LocalRunner and return everything - * the surfaces exposed. Disposes the temp workspace when done. - */ -export async function runLiveCapstone(write: (line: string) => void = () => {}): Promise { - registerModel() - - // 0. FrameworkPreset: detect the framework from the project's deps to brief the workers. - const { preset, detection } = builtinFrameworkPresetRegistry().select({ dependencies: PROJECT_DEPS }) - - // Runner: a REAL isolated workspace on the host filesystem, seeded with a minimal project. - const runner = new LocalRunner() - const session = await runner.boot({ files: { 'package.json': JSON.stringify({ name: 'orders-app' }) + '\n' } }) - - try { - const loop = buildLoop() - - const handle = launchAutopilot(onEvent => - new Bootstrap({ - onEvent: e => { - write(formatBootstrapEvent(e)) - onEvent(e) - }, - steps: { - scope: () => ({ scope: 'full', intent: INTENT }), - build: supervisorBuild({ plan: livePlanner, workers: buildWorkers(session, preset.framework), concurrency: 1 }), - checklist: loopChecklist({ loop }), - improve: loopImprove({ loop }), - // Real Cloudflare deploy when CLOUDFLARE_API_TOKEN is set, else plan-only. - deploy: deployStep(session, agent({ model: MODEL, instructions: 'deployer' })), - }, - }).run(), - ) - const result = await handle.result() - const files = await snapshot(session) - - // Scale mode: generate CODE-OVERVIEW.md from the real scaffold (a material change). - const maintainer = new CodeOverviewMaintainer({ - regenerate: () => ({ - summary: 'A server-rendered orders app on Vike + Prisma.', - sections: [{ title: 'Structure', body: Object.keys(files).map(f => `- \`${f}\``).join('\n') }], - }), - }) - await maintainer.handle({ kind: 'major-change', summary: 'scaffolded the app', paths: Object.keys(files) }) - - return { detection, result, events: handle.events(), files, overview: maintainer.get() } - } finally { - await session.dispose() - } -} diff --git a/examples/bootstrap-quickstart/src/main-live.ts b/examples/bootstrap-quickstart/src/main-live.ts deleted file mode 100644 index e5dc94a3..00000000 --- a/examples/bootstrap-quickstart/src/main-live.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { runLiveCapstone } from './live.js' -import { INTENT } from './bootstrap.js' - -/** Run the LIVE capstone (real model + LocalRunner) and print what each phase produced. - * Needs ANTHROPIC_API_KEY in the environment (see live.ts). */ -async function main(): Promise { - console.log(`Bootstrap (LIVE): "${INTENT}"\n`) - console.log('--- live narration (surface stream) ---') - const { detection, result, files, overview } = await runLiveCapstone(line => process.stdout.write(line + '\n')) - - console.log('\n--- preset ---') - console.log(` detected: ${detection.framework} (confidence ${detection.confidence})`) - - console.log('\n--- app scaffolded (REAL LocalRunner workspace) ---') - for (const path of Object.keys(files).sort()) console.log(` ${path} (${files[path]?.length ?? 0} bytes)`) - - console.log('\n--- outcome ---') - console.log(` scope: ${result.scope}`) - console.log(` production-grade: ${result.productionGrade} (after ${result.passes} checklist pass(es))`) - console.log(` deploy: ${result.deploy?.plan.render.toUpperCase()} → ${result.deploy?.plan.target}`) - - console.log('\n--- scale mode: CODE-OVERVIEW.md ---') - console.log(` ${overview?.summary}`) -} - -main().catch(err => { - console.error(err) - process.exitCode = 1 -}) diff --git a/examples/bootstrap-quickstart/src/main.ts b/examples/bootstrap-quickstart/src/main.ts deleted file mode 100644 index 2c9ec707..00000000 --- a/examples/bootstrap-quickstart/src/main.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { runCapstone, INTENT } from './bootstrap.js' - -/** Run the capstone and print what each phase and surface exposed. Offline (AiFake). */ -async function main(): Promise { - console.log(`Bootstrap: "${INTENT}"\n`) - console.log('--- live narration (surface stream) ---') - const { detection, result, files, overview } = await runCapstone(line => process.stdout.write(line + '\n')) - - console.log('\n--- preset ---') - console.log(` detected: ${detection.framework} (confidence ${detection.confidence})`) - - console.log('\n--- app scaffolded (runner sandbox) ---') - for (const path of Object.keys(files).sort()) console.log(` ${path}`) - - console.log('\n--- outcome ---') - console.log(` scope: ${result.scope}`) - console.log(` production-grade: ${result.productionGrade} (after ${result.passes} checklist pass(es))`) - console.log(` deploy: ${result.deploy?.plan.render.toUpperCase()} → ${result.deploy?.plan.target}, url ${result.deploy?.result.url}`) - - console.log('\n--- scale mode: CODE-OVERVIEW.md ---') - console.log(` ${overview?.summary}`) - for (const s of overview?.sections ?? []) console.log(` ## ${s.title}`) -} - -main().catch(err => { - console.error(err) - process.exitCode = 1 -}) diff --git a/examples/bootstrap-quickstart/tsconfig.json b/examples/bootstrap-quickstart/tsconfig.json deleted file mode 100644 index 404aab40..00000000 --- a/examples/bootstrap-quickstart/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { "noEmit": true, "rootDir": "src" }, - "include": ["src"] -} diff --git a/examples/bootstrap-quickstart/tsconfig.test.json b/examples/bootstrap-quickstart/tsconfig.test.json deleted file mode 100644 index eebda2fc..00000000 --- a/examples/bootstrap-quickstart/tsconfig.test.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { "outDir": "dist-test", "rootDir": "src" }, - "include": ["src"] -} diff --git a/examples/framework-demo/CHANGELOG.md b/examples/framework-demo/CHANGELOG.md deleted file mode 100644 index 281ee614..00000000 --- a/examples/framework-demo/CHANGELOG.md +++ /dev/null @@ -1,395 +0,0 @@ -# @gemstack/example-framework-demo - -## 0.0.9 - -### Patch Changes - -- Updated dependencies [9fd8951] -- Updated dependencies [c6548ca] -- Updated dependencies [61451a1] -- Updated dependencies [ecf2ce4] -- Updated dependencies [053db85] -- Updated dependencies [7c70533] -- Updated dependencies [a1552ac] -- Updated dependencies [3166507] -- Updated dependencies [6740853] -- Updated dependencies [38bb24a] -- Updated dependencies [fd8e13f] -- Updated dependencies [969b9bb] -- Updated dependencies [96f7b6d] -- Updated dependencies [f20add1] -- Updated dependencies [c9864a6] -- Updated dependencies [85c5f73] -- Updated dependencies [deb130e] -- Updated dependencies [d3cd883] -- Updated dependencies [8a8cd75] -- Updated dependencies [decace4] -- Updated dependencies [ca48f85] -- Updated dependencies [30e94f9] -- Updated dependencies [38ef437] -- Updated dependencies [1c8d520] -- Updated dependencies [3bc2a2e] -- Updated dependencies [b2d23ed] -- Updated dependencies [df6ac36] -- Updated dependencies [9202800] -- Updated dependencies [6601c44] -- Updated dependencies [ef4f007] -- Updated dependencies [c55dcca] -- Updated dependencies [3dd90eb] -- Updated dependencies [c2c5798] -- Updated dependencies [6e4eb69] -- Updated dependencies [8154e20] - - @gemstack/the-framework@2.0.0 - - @gemstack/ai-autopilot@0.12.0 - -## 0.0.8 - -### Patch Changes - -- Updated dependencies [38c8e80] -- Updated dependencies [4c89b8a] -- Updated dependencies [02619cc] -- Updated dependencies [296b559] -- Updated dependencies [ee78ce1] -- Updated dependencies [8857670] -- Updated dependencies [a0f5a64] -- Updated dependencies [6f7cf23] -- Updated dependencies [0749c76] -- Updated dependencies [770591a] -- Updated dependencies [6ed8f90] -- Updated dependencies [788e033] -- Updated dependencies [2e46392] -- Updated dependencies [5904116] -- Updated dependencies [e41b392] -- Updated dependencies [aeb4f09] -- Updated dependencies [a2a35be] -- Updated dependencies [b5300a6] -- Updated dependencies [137aecd] -- Updated dependencies [3fc09a2] -- Updated dependencies [3c8a606] -- Updated dependencies [1589150] -- Updated dependencies [c7a0bde] -- Updated dependencies [5c2679b] -- Updated dependencies [1c97dcb] -- Updated dependencies [b6e6c82] -- Updated dependencies [312f993] -- Updated dependencies [0d15eb6] -- Updated dependencies [0d15eb6] -- Updated dependencies [504626d] -- Updated dependencies [7201a61] -- Updated dependencies [f99424f] -- Updated dependencies [836b5c7] -- Updated dependencies [6f2901f] -- Updated dependencies [e834f82] -- Updated dependencies [a42c0b7] -- Updated dependencies [c9e777e] -- Updated dependencies [c9e777e] -- Updated dependencies [5dfa1a1] -- Updated dependencies [b9128e0] -- Updated dependencies [773ca7d] -- Updated dependencies [7041dc4] -- Updated dependencies [cab77e9] -- Updated dependencies [8ecbac6] -- Updated dependencies [867e66f] -- Updated dependencies [a64c29a] -- Updated dependencies [4e32eba] -- Updated dependencies [136ee9a] -- Updated dependencies [bcb4299] -- Updated dependencies [8dc742f] -- Updated dependencies [b05663f] -- Updated dependencies [7e77c58] -- Updated dependencies [314995f] -- Updated dependencies [95e6d5a] -- Updated dependencies [12b1653] -- Updated dependencies [a95f52c] -- Updated dependencies [ae9ecd6] -- Updated dependencies [6412128] -- Updated dependencies [50a548c] -- Updated dependencies [9069628] -- Updated dependencies [d2373cb] -- Updated dependencies [726ed95] -- Updated dependencies [7a9699c] -- Updated dependencies [a7582e1] -- Updated dependencies [5b7b2c8] -- Updated dependencies [afcbfce] -- Updated dependencies [c02f317] -- Updated dependencies [66c7aeb] -- Updated dependencies [135f210] -- Updated dependencies [206fc61] -- Updated dependencies [e5e662a] -- Updated dependencies [8df2f95] -- Updated dependencies [daef9a5] -- Updated dependencies [bf35985] -- Updated dependencies [bf70326] -- Updated dependencies [728d833] -- Updated dependencies [862ed73] -- Updated dependencies [500bec7] -- Updated dependencies [1e3647b] -- Updated dependencies [9a3327b] -- Updated dependencies [45d22aa] -- Updated dependencies [513f3ba] -- Updated dependencies [698bc1f] -- Updated dependencies [181c7b5] -- Updated dependencies [d3d470e] -- Updated dependencies [b214e85] -- Updated dependencies [92eec90] -- Updated dependencies [ff5792c] -- Updated dependencies [e8dab8e] -- Updated dependencies [c17e550] -- Updated dependencies [c7803cb] -- Updated dependencies [2fad888] -- Updated dependencies [bc5b90d] -- Updated dependencies [24b900a] -- Updated dependencies [7fced75] -- Updated dependencies [eff5f40] -- Updated dependencies [bcecc7b] -- Updated dependencies [30f41ee] -- Updated dependencies [e62b60d] -- Updated dependencies [b4dce07] -- Updated dependencies [0f16b3e] -- Updated dependencies [a47d2d9] -- Updated dependencies [58786ca] - - @gemstack/framework@1.0.0 - - @gemstack/ai-autopilot@0.11.0 - -## 0.0.7 - -### Patch Changes - -- Updated dependencies [68555e4] -- Updated dependencies [ca2b719] -- Updated dependencies [0faa297] -- Updated dependencies [e65e16a] -- Updated dependencies [0c922a6] -- Updated dependencies [5844526] -- Updated dependencies [aac6e5d] -- Updated dependencies [6721c0f] -- Updated dependencies [8c576df] -- Updated dependencies [be0a58c] -- Updated dependencies [4788a22] -- Updated dependencies [9e71fc8] -- Updated dependencies [b22337a] -- Updated dependencies [558bdb8] -- Updated dependencies [7a94b48] -- Updated dependencies [016fb8d] -- Updated dependencies [112c3a6] -- Updated dependencies [aac6e5d] -- Updated dependencies [87d67c8] -- Updated dependencies [76c1bfa] -- Updated dependencies [6524e0a] -- Updated dependencies [d0fe851] -- Updated dependencies [9a27125] -- Updated dependencies [cdfe508] -- Updated dependencies [e808793] -- Updated dependencies [d4fe2e5] -- Updated dependencies [f61a367] -- Updated dependencies [5646b16] -- Updated dependencies [65e27fd] -- Updated dependencies [06cb0ce] -- Updated dependencies [7ca71be] -- Updated dependencies [e0404cf] -- Updated dependencies [1dbc02a] -- Updated dependencies [dcea89b] -- Updated dependencies [f82e220] -- Updated dependencies [18de94b] -- Updated dependencies [27f522a] -- Updated dependencies [900efbb] -- Updated dependencies [4ed510f] -- Updated dependencies [a345a83] -- Updated dependencies [63b2a73] -- Updated dependencies [1bb66cf] -- Updated dependencies [48aba07] -- Updated dependencies [3091bc2] -- Updated dependencies [44988d7] -- Updated dependencies [dabdf0f] -- Updated dependencies [c26159d] -- Updated dependencies [16e86c4] -- Updated dependencies [43d4f50] -- Updated dependencies [90c15bf] -- Updated dependencies [388f3ad] -- Updated dependencies [48f25cd] -- Updated dependencies [4e43d76] -- Updated dependencies [72fb351] -- Updated dependencies [ed25ab8] -- Updated dependencies [f9add6d] -- Updated dependencies [e4b38b3] -- Updated dependencies [131f349] -- Updated dependencies [f1ff0d2] -- Updated dependencies [4a741f6] -- Updated dependencies [734da1a] -- Updated dependencies [df15f71] -- Updated dependencies [affa3d8] -- Updated dependencies [c05a186] -- Updated dependencies [1f588aa] -- Updated dependencies [5882932] -- Updated dependencies [eec009d] -- Updated dependencies [5d54b64] -- Updated dependencies [99229db] -- Updated dependencies [ee075ec] -- Updated dependencies [1f6a0d3] -- Updated dependencies [06fefbe] -- Updated dependencies [c584b16] -- Updated dependencies [b183dc0] -- Updated dependencies [aa5870e] -- Updated dependencies [28fff61] -- Updated dependencies [4aaa00a] -- Updated dependencies [f0a024c] -- Updated dependencies [4746188] -- Updated dependencies [f496a54] -- Updated dependencies [e370b41] -- Updated dependencies [79af200] -- Updated dependencies [164771a] -- Updated dependencies [4d456c2] -- Updated dependencies [c06532e] -- Updated dependencies [43bae91] -- Updated dependencies [f50f0d5] -- Updated dependencies [a743cd4] -- Updated dependencies [72533fc] -- Updated dependencies [7e1ea76] -- Updated dependencies [5709703] -- Updated dependencies [21fe373] -- Updated dependencies [89cedff] -- Updated dependencies [cfdbd59] -- Updated dependencies [5bd0489] -- Updated dependencies [721f539] -- Updated dependencies [db95caa] -- Updated dependencies [d1331a2] -- Updated dependencies [d834af8] -- Updated dependencies [c4a992a] -- Updated dependencies [b7de2a1] -- Updated dependencies [3f12815] -- Updated dependencies [4067614] -- Updated dependencies [5108aea] -- Updated dependencies [32e9d3e] -- Updated dependencies [c48af6d] -- Updated dependencies [96870d2] -- Updated dependencies [9d1951b] -- Updated dependencies [5d1653b] -- Updated dependencies [d10d515] -- Updated dependencies [18c9352] -- Updated dependencies [cbe1898] -- Updated dependencies [68d0df4] -- Updated dependencies [f736b55] -- Updated dependencies [1f1a2a3] -- Updated dependencies [1e1b4dc] -- Updated dependencies [28c3330] -- Updated dependencies [632f0df] -- Updated dependencies [734da1a] -- Updated dependencies [9442761] -- Updated dependencies [2a12ec8] -- Updated dependencies [eb1a0f1] -- Updated dependencies [5e24797] -- Updated dependencies [dac7613] -- Updated dependencies [7f9c514] -- Updated dependencies [98f44e2] -- Updated dependencies [aafbb55] -- Updated dependencies [4a70c5a] -- Updated dependencies [43d7fa0] -- Updated dependencies [b8c45a7] -- Updated dependencies [a76ace7] -- Updated dependencies [4d4d77c] -- Updated dependencies [68d53ff] -- Updated dependencies [8910ed3] -- Updated dependencies [03ca1b0] -- Updated dependencies [34d3ec2] -- Updated dependencies [c762529] -- Updated dependencies [c72d155] -- Updated dependencies [c6d005f] -- Updated dependencies [2fc612a] -- Updated dependencies [5c83bc2] -- Updated dependencies [9345476] -- Updated dependencies [59e3707] -- Updated dependencies [6b561fc] -- Updated dependencies [883d974] -- Updated dependencies [83e6a1f] -- Updated dependencies [5dcd8a4] -- Updated dependencies [3bd0478] -- Updated dependencies [d1202dc] -- Updated dependencies [e453bba] -- Updated dependencies [437618f] -- Updated dependencies [e4b518a] -- Updated dependencies [caf8a0b] -- Updated dependencies [bc3586b] -- Updated dependencies [7db5a9c] -- Updated dependencies [3302b0f] -- Updated dependencies [5417558] -- Updated dependencies [8d396f7] - - @gemstack/framework@0.9.0 - - @gemstack/ai-autopilot@0.10.0 - -## 0.0.6 - -### Patch Changes - -- Updated dependencies [385c953] - - @gemstack/framework@0.8.0 - -## 0.0.5 - -### Patch Changes - -- Updated dependencies [cc6a8db] -- Updated dependencies [5f319ff] -- Updated dependencies [9f62be7] -- Updated dependencies [08f5710] - - @gemstack/framework@0.7.0 - - @gemstack/ai-autopilot@0.9.0 - -## 0.0.4 - -### Patch Changes - -- Updated dependencies [4a6311e] -- Updated dependencies [b81e563] -- Updated dependencies [c28c373] -- Updated dependencies [74a9907] -- Updated dependencies [8c3e7d0] -- Updated dependencies [03e06aa] -- Updated dependencies [c24ae22] -- Updated dependencies [3c72f14] -- Updated dependencies [e45e4d0] -- Updated dependencies [396dc7f] -- Updated dependencies [edd242b] -- Updated dependencies [24944b9] -- Updated dependencies [d2acba4] - - @gemstack/ai-autopilot@0.8.0 - - @gemstack/framework@0.6.0 - -## 0.0.3 - -### Patch Changes - -- Updated dependencies [6f7e7e3] - - @gemstack/ai-autopilot@0.7.0 - - @gemstack/framework@0.5.1 - -## 0.0.2 - -### Patch Changes - -- Updated dependencies [16f6bb8] -- Updated dependencies [c7eae83] - - @gemstack/framework@0.5.0 - -## 0.0.1 - -### Patch Changes - -- Updated dependencies [1db19e2] -- Updated dependencies [c3e7e9e] -- Updated dependencies [5288ca6] -- Updated dependencies [6625ca7] -- Updated dependencies [bd13fcf] -- Updated dependencies [11f76da] -- Updated dependencies [c79f567] -- Updated dependencies [f156cf8] -- Updated dependencies [97b2943] -- Updated dependencies [93892d7] -- Updated dependencies [de37e7e] -- Updated dependencies [c9cf96b] -- Updated dependencies [d98d4ad] -- Updated dependencies [a06e845] -- Updated dependencies [c79f567] -- Updated dependencies [f1d11d9] - - @gemstack/ai-autopilot@0.6.0 - - @gemstack/framework@0.4.0 diff --git a/examples/framework-demo/README.md b/examples/framework-demo/README.md deleted file mode 100644 index 25afd8ee..00000000 --- a/examples/framework-demo/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# The Framework — end-to-end demo - -One prompt, taken all the way to a **running, deployed app** — offline and -deterministic, in a couple of seconds. - -This runs the real product (`@gemstack/the-framework`'s `runFramework`) with the -built-in **fake driver**: no Claude Code, no model, no API keys. Same code a live -run executes — preset detection, build, the serve gate, deploy — just with -scripted agent turns so it is instant -and repeatable. Two things are genuinely real, not narrated: - -- **the app boots and serves** — the serve gate starts a real HTTP server and the - run leaves it running, so the demo `fetch`es it and prints what it served; -- **deploy runs the real `cloudflareTarget` adapter** (over a simulated wrangler), - so it ends at a real-looking `workers.dev` URL. - -## Run it - -```bash -pnpm --filter @gemstack/example-framework-demo start -``` - -## What you see - -``` -Prompt: "A paginated orders page backed by an orders table, with sign-in." - ---- live narration --- -◆ fake in /tmp/framework-demo-xxxx - Detected Vike (confidence 2) -▶ scope: full — "A paginated orders page backed by an orders table, with sign-in." - Built the orders app: an orders schema and migration, a paginated /orders page, and a sign-in stub. - serve: start: node server.js - serve: fetch http://localhost:50106/ -> 200 - ✓ checklist pass 1: no blockers -▶ deploy: SSR → cloudflare (per-request orders data + server-side auth) -✓ review passed after 1 pass(es) -▶ your app is running at http://localhost:50106 -✓ finished - ---- outcome --- - preset detected: Vike - serve gate: passed (in 1 pass(es)) - deployed to: cloudflare → https://orders-app.gemstack.workers.dev - running locally: http://localhost:50106 - it served: Orders

Orders

… -``` - -The run does not take the agent's word for it: the serve gate **boots the app and -fetches it** (the `serve:` lines) and blocks until the real server responds. That is -the only checklist here — the agent is treated as a black box, with no built-in -review prompts layered on top (#1372). When it finishes, the app is left running so -you can open it — that is the localhost link, live until the run stops. - -## The real thing - -Same flow, driven against Claude Code instead of the fake driver: - -```bash -npx @gemstack/the-framework "a paginated orders page with sign-in" -``` - -That opens the localhost dashboard, wraps Claude Code as the coding agent, and runs -the identical scope → build → serve gate → deploy flow — writing real files and, -with `--serve`, leaving the real app running behind a preview link. diff --git a/examples/framework-demo/package.json b/examples/framework-demo/package.json deleted file mode 100644 index 35964d8d..00000000 --- a/examples/framework-demo/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "@gemstack/example-framework-demo", - "version": "0.0.9", - "private": true, - "description": "Showable end-to-end demo of @gemstack/the-framework: one prompt → scope → architect → build → full-fledged loop → deploy → a real running app, offline and deterministic via the fake driver.", - "type": "module", - "scripts": { - "typecheck": "tsc --noEmit", - "test": "tsc -p tsconfig.test.json && cd dist-test && node --test", - "clean": "rm -rf dist-test", - "start": "tsx src/main.ts" - }, - "dependencies": { - "@gemstack/the-framework": "workspace:^", - "@gemstack/ai-autopilot": "workspace:^" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "tsx": "^4.19.0", - "typescript": "^5.4.0" - } -} diff --git a/examples/framework-demo/src/demo.test.ts b/examples/framework-demo/src/demo.test.ts deleted file mode 100644 index 9237312a..00000000 --- a/examples/framework-demo/src/demo.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { describe, it } from 'node:test' -import assert from 'node:assert/strict' -import { DEMO_INTENT, runDemo } from './demo.js' - -describe('framework demo: one prompt → a running, deployed app (offline)', () => { - it('runs the whole product flow and ends at a real serving app', async () => { - const lines: string[] = [] - const out = await runDemo(line => lines.push(line)) - - // Preset detection picked Vike from the demo's deps signals. - assert.equal(out.framework, 'Vike') - - // The serve gate is the only checklist (#1372: no built-in agent review) and the - // app booted first try, so the loop cleared in one pass. - assert.equal(out.productionGrade, true) - assert.equal(out.passes, 1) - - // Deploy ran the real cloudflareTarget adapter → a live workers.dev URL. - assert.equal(out.deployTarget, 'cloudflare') - assert.equal(out.deployUrl, 'https://orders-app.gemstack.workers.dev') - - // The app was actually booted and served (not just narrated). - assert.match(out.previewUrl ?? '', /^http:\/\/localhost:\d+$/) - assert.match(out.served, /Orders/) - assert.match(out.served, /Ada Lovelace/) - - // The narration told the whole story: the prompt, the serve gate clearing, the end. - assert.ok(lines.some(l => l.includes(DEMO_INTENT))) - assert.ok(lines.some(l => l.includes('review passed'))) - assert.ok(lines.some(l => l.includes('your app is running at'))) - }) -}) diff --git a/examples/framework-demo/src/demo.ts b/examples/framework-demo/src/demo.ts deleted file mode 100644 index b320e6f5..00000000 --- a/examples/framework-demo/src/demo.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { mkdtemp, rm, writeFile } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { createServer } from 'node:net' -import { cloudflareTarget } from '@gemstack/ai-autopilot' -import { - FAKE_DEPLOY, - FAKE_INTENT, - FAKE_SIGNALS, - fakeDriver, - formatFrameworkEvent, - runFramework, - type AppPreview, -} from '@gemstack/the-framework' - -/** - * The showable end-to-end demo for `@gemstack/the-framework`: one prompt taken all - * the way to a *running, deployed* app, offline and deterministic. - * - * It drives the real product (`runFramework`) with the built-in **fake driver** - * (no Claude Code, no model, no keys) so the whole flow — preset detection, - * architect decisions, build, the serve gate (#1372: the only checklist without - * a domain preset), and deploy — runs the same code a live run does, just with - * scripted agent turns. Two things are genuinely real, not narrated: - * - * - the app **boots and serves**: the serve gate starts a real HTTP server and - * the run leaves it running, so the demo can `fetch` it and prove it works; - * - the **deploy** runs the real `cloudflareTarget` adapter over a simulated - * wrangler, so it ends at a real-looking `workers.dev` URL deterministically. - * - * This is the artifact to point people at (README / the-framework.ai / Discord): - * a single command that goes from an idea to an app you can open. - */ - -/** The one prompt the demo builds from. */ -export const DEMO_INTENT = FAKE_INTENT - -/** What {@link runDemo} reports back after the flow completes. */ -export interface DemoOutcome { - /** The detected framework preset (Vike, from the demo's deps signals). */ - framework: string | undefined - /** Whether the serve-gate loop cleared, and in how many passes. */ - productionGrade: boolean - passes: number - /** The deploy result: the target and the (simulated) live URL. */ - deployTarget: string | undefined - deployUrl: string | undefined - /** The localhost URL the app was left running at. */ - previewUrl: string | undefined - /** The first bytes the running app actually served (proof it works). */ - served: string -} - -/** An ephemeral free port so the demo never collides with something already bound. */ -function freePort(): Promise { - return new Promise((resolvePromise, rejectPromise) => { - const srv = createServer() - srv.on('error', rejectPromise) - srv.listen(0, '127.0.0.1', () => { - const addr = srv.address() - const port = typeof addr === 'object' && addr ? addr.port : 0 - srv.close(() => resolvePromise(port)) - }) - }) -} - -/** The tiny but real "orders app" the serve gate boots — a stand-in for what the agent builds. */ -function ordersAppSource(port: number): string { - return [ - `const http = require('http')`, - `const page = \`Orders`, - `

Orders

`, - ``, - `
#CustomerTotal
1001Ada Lovelace$42.00
1002Alan Turing$17.50
\``, - `http.createServer((_, res) => res.end(page)).listen(${port})`, - ].join('\n') -} - -/** The simulated Cloudflare: every command succeeds; the deploy prints a workers.dev URL. */ -const simulatedCloudflare = { - exec: async (command: string) => ({ - stdout: /wrangler|deploy/.test(command) - ? 'Published framework-demo\nhttps://orders-app.gemstack.workers.dev' - : '', - stderr: '', - exitCode: 0, - }), -} - -/** - * Run the whole demo and stream one narration line per phase to `onLine`. - * Deterministic and offline; leaves no processes or temp files behind. - */ -export async function runDemo(onLine: (line: string) => void): Promise { - const dir = await mkdtemp(join(tmpdir(), 'framework-demo-')) - const port = await freePort() - await writeFile(join(dir, 'server.js'), ordersAppSource(port) + '\n') - await writeFile(join(dir, 'package.json'), JSON.stringify({ name: 'orders-app', private: true }) + '\n') - - let preview: AppPreview | undefined - try { - const run = await runFramework({ - intent: DEMO_INTENT, - driver: fakeDriver(), - cwd: dir, - signals: FAKE_SIGNALS, - serve: { command: 'node server.js', port, waitMs: 8000, keepAlive: true }, - deploy: FAKE_DEPLOY, - deployTarget: cloudflareTarget({ - session: simulatedCloudflare, - apiToken: 'demo-token', - accountId: 'demo-account', - projectName: 'framework-demo', - }), - onEvent: event => onLine(formatFrameworkEvent(event)), - }) - preview = run.preview - - // Prove the app the loop signed off on is actually serving right now. - let served = '' - if (preview) { - const res = await fetch(preview.url) - served = (await res.text()).replace(/\s+/g, ' ').trim() - } - - return { - framework: run.detection.framework, - productionGrade: run.result.productionGrade, - passes: run.result.passes, - deployTarget: run.result.deploy?.plan.target, - deployUrl: run.result.deploy?.result.url, - previewUrl: preview?.url, - served, - } - } finally { - if (preview) await preview.stop() - await rm(dir, { recursive: true, force: true }) - } -} diff --git a/examples/framework-demo/src/main.ts b/examples/framework-demo/src/main.ts deleted file mode 100644 index 4b2a92e0..00000000 --- a/examples/framework-demo/src/main.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { DEMO_INTENT, runDemo } from './demo.js' - -/** Run the demo and print the whole story: the live narration, then the outcome. */ -async function main(): Promise { - console.log('The Framework — end-to-end demo (offline, deterministic)\n') - console.log(`Prompt: "${DEMO_INTENT}"\n`) - console.log('--- live narration ---') - const out = await runDemo(line => console.log(line)) - - console.log('\n--- outcome ---') - console.log(` preset detected: ${out.framework}`) - console.log(` serve gate: ${out.productionGrade ? 'passed' : 'not clean'} (in ${out.passes} pass(es))`) - console.log(` deployed to: ${out.deployTarget} → ${out.deployUrl}`) - console.log(` running locally: ${out.previewUrl}`) - console.log(` it served: ${out.served.slice(0, 72)}${out.served.length > 72 ? '…' : ''}`) - - console.log('\nThat is the fake driver. To do it for real against Claude Code:') - console.log(' npx @gemstack/the-framework "a paginated orders page with sign-in"') -} - -main().catch(err => { - console.error(err) - process.exitCode = 1 -}) diff --git a/examples/framework-demo/tsconfig.json b/examples/framework-demo/tsconfig.json deleted file mode 100644 index 404aab40..00000000 --- a/examples/framework-demo/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { "noEmit": true, "rootDir": "src" }, - "include": ["src"] -} diff --git a/examples/framework-demo/tsconfig.test.json b/examples/framework-demo/tsconfig.test.json deleted file mode 100644 index eebda2fc..00000000 --- a/examples/framework-demo/tsconfig.test.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { "outDir": "dist-test", "rootDir": "src" }, - "include": ["src"] -} diff --git a/packages/ai-autopilot/package.json b/packages/ai-autopilot/package.json index 6c4087ea..d87d46ba 100644 --- a/packages/ai-autopilot/package.json +++ b/packages/ai-autopilot/package.json @@ -63,7 +63,6 @@ "devDependencies": { "@types/node": "^20.0.0", "@webcontainer/api": "^1.6.4", - "playwright-core": "^1.49.0", "typescript": "^5.4.0" }, "author": "Suleiman Shahbari" diff --git a/packages/ai-sdk/src/conversation-persistence.ts b/packages/ai-sdk/src/conversation-persistence.ts index d0a8a6a2..27903679 100644 --- a/packages/ai-sdk/src/conversation-persistence.ts +++ b/packages/ai-sdk/src/conversation-persistence.ts @@ -221,7 +221,7 @@ async function runValidation( * agent loop produced. Mirrors the old `ConversableAgent.prompt` * persistence shape exactly so downstream stores see no behavioral change. */ -export function newMessagesFromTurn(input: string, response: AgentResponse): AiMessage[] { +function newMessagesFromTurn(input: string, response: AgentResponse): AiMessage[] { const out: AiMessage[] = [{ role: 'user', content: input }] for (const step of response.steps) { out.push(step.message) diff --git a/packages/ai-sdk/src/providers/anthropic.ts b/packages/ai-sdk/src/providers/anthropic.ts index 177ffe9b..084e6557 100644 --- a/packages/ai-sdk/src/providers/anthropic.ts +++ b/packages/ai-sdk/src/providers/anthropic.ts @@ -325,7 +325,7 @@ export function fromAnthropicResponse(response: any): ProviderResponse { * `max_tokens`/`refusal` cases a truncated or refused answer reports a clean * `stop`, so a caller cannot tell a complete answer from a cut-off one. */ -export function mapAnthropicStopReason(reason: string | null | undefined): FinishReason { +function mapAnthropicStopReason(reason: string | null | undefined): FinishReason { switch (reason) { case 'tool_use': return 'tool_calls' case 'max_tokens': return 'length' diff --git a/packages/ai-sdk/src/tool-helpers.ts b/packages/ai-sdk/src/tool-helpers.ts index 49c9876d..31465d35 100644 --- a/packages/ai-sdk/src/tool-helpers.ts +++ b/packages/ai-sdk/src/tool-helpers.ts @@ -93,7 +93,7 @@ export function validateToolArgs( * tool has no `toModelOutput` transform: pass through strings, JSON-encode * everything else. */ -export function defaultStringify(value: unknown): string { +function defaultStringify(value: unknown): string { return typeof value === 'string' ? value : JSON.stringify(value) } diff --git a/packages/the-framework/src/auto-pm.ts b/packages/the-framework/src/auto-pm.ts index d8ff0862..28863b6f 100644 --- a/packages/the-framework/src/auto-pm.ts +++ b/packages/the-framework/src/auto-pm.ts @@ -227,7 +227,7 @@ export interface PlanAssignment { * assignment is a snapshot, and a human may retire the entry between the sweep's read and the * run's own. */ -export function pinnedDrainJob(job: AutoPmJob, entry: string): AutoPmJob { +function pinnedDrainJob(job: AutoPmJob, entry: string): AutoPmJob { return { ...job, entry, diff --git a/packages/the-framework/src/ci-watch.ts b/packages/the-framework/src/ci-watch.ts index 7f0f52f9..27515a07 100644 --- a/packages/the-framework/src/ci-watch.ts +++ b/packages/the-framework/src/ci-watch.ts @@ -21,7 +21,7 @@ import type { Cached } from './dashboard/cache.js' // feed the same handlers from a webhook receiver and only the trigger changes. /** How long between sweeps: the ~1 min CI latency agreed on #1418. */ -export const DEFAULT_CI_WATCH_INTERVAL_MS = 60 * 1000 +const DEFAULT_CI_WATCH_INTERVAL_MS = 60 * 1000 /** * How long a run's watched PR stays on the sweep's list: long enough to survive a weekend of the @@ -42,7 +42,7 @@ export const NO_CHECKS_GRACE_MS = 3 * 60 * 1000 * re-arms on a new head commit, so two attempts means: the original failure, and one more after * a fix that did not take. Past that the failure is evidently not agent-shaped. */ -export const MAX_CI_FIX_ATTEMPTS = 2 +const MAX_CI_FIX_ATTEMPTS = 2 /** * The marker a CI-fix run's prompt opens with, so attempts are discoverable from run metas. The diff --git a/packages/the-framework/src/claude-trust.ts b/packages/the-framework/src/claude-trust.ts index 65f15d79..174c84ed 100644 --- a/packages/the-framework/src/claude-trust.ts +++ b/packages/the-framework/src/claude-trust.ts @@ -17,7 +17,7 @@ import { join } from 'node:path' */ /** Where the Claude Code CLI records per-directory trust. */ -export function claudeConfigPath(home: string = homedir()): string { +function claudeConfigPath(home: string = homedir()): string { return join(home, '.claude.json') } diff --git a/packages/the-framework/src/cli.ts b/packages/the-framework/src/cli.ts index da21c582..d5868705 100644 --- a/packages/the-framework/src/cli.ts +++ b/packages/the-framework/src/cli.ts @@ -835,7 +835,7 @@ export function runLogEntry(input: { } /** This run's own flags as the nearest config layer (#841). A flag left off says nothing. */ -export function flagConfigLayer(opts: RunConfigFlags): ConfigLayer { +function flagConfigLayer(opts: RunConfigFlags): ConfigLayer { return { name: 'flag', values: { diff --git a/packages/the-framework/src/config.ts b/packages/the-framework/src/config.ts index 30ce9246..7e72ffea 100644 --- a/packages/the-framework/src/config.ts +++ b/packages/the-framework/src/config.ts @@ -47,7 +47,7 @@ export interface FrameworkFileConfig { export const FRAMEWORK_CONFIG_FILES = ['the-framework.yml', 'the-framework.yaml'] as const /** The string-valued config keys, parsed and copied across layers as-is. */ -export const STRING_CONFIG_KEYS = ['preset', 'event'] as const +const STRING_CONFIG_KEYS = ['preset', 'event'] as const /** * The boolean-valued mode keys. This is the canonical mode list: parsing, the config-layer copy, * resolution, and the resolved-config summary all iterate it, so a new mode is added here once and diff --git a/packages/the-framework/src/conversations.ts b/packages/the-framework/src/conversations.ts index 38f86aab..c6556f42 100644 --- a/packages/the-framework/src/conversations.ts +++ b/packages/the-framework/src/conversations.ts @@ -82,7 +82,7 @@ export function unescapeBody(text: string): string { } /** The one-time first line of a conversation file. */ -export function conversationHeader(runId: string): string { +function conversationHeader(runId: string): string { return `# Conversation ${runId}\n` } diff --git a/packages/the-framework/src/daemon-runtime.ts b/packages/the-framework/src/daemon-runtime.ts index 1a8d67c5..37c4f081 100644 --- a/packages/the-framework/src/daemon-runtime.ts +++ b/packages/the-framework/src/daemon-runtime.ts @@ -235,10 +235,10 @@ export function lastRunFailureDetail(eventsJsonl: string): string | undefined { export const MAX_TRANSIENT_RETRIES = 2 /** The pause before a retry (#1281): long enough for a dropped connection to be worth re-trying. */ -export const TRANSIENT_RETRY_DELAY_MS = 15_000 +const TRANSIENT_RETRY_DELAY_MS = 15_000 /** What the continued session is told (#1281), in the #923 resume prompt's shape. */ -export const RETRY_PROMPT = +const RETRY_PROMPT = 'This session died to a transient connection error, not because anyone asked it to stop. Look at what you had already done, then carry on from there and finish the work.' /** diff --git a/packages/the-framework/src/daemon.ts b/packages/the-framework/src/daemon.ts index 0fa31e3c..e13ae4a0 100644 --- a/packages/the-framework/src/daemon.ts +++ b/packages/the-framework/src/daemon.ts @@ -69,7 +69,7 @@ export interface DaemonState { } /** The `.the-framework/` directory for a workspace. */ -export function daemonDir(cwd: string): string { +function daemonDir(cwd: string): string { return join(cwd, FRAMEWORK_DIR) } diff --git a/packages/the-framework/src/dashboard/bridge-store.ts b/packages/the-framework/src/dashboard/bridge-store.ts index 71ac4804..3318f056 100644 --- a/packages/the-framework/src/dashboard/bridge-store.ts +++ b/packages/the-framework/src/dashboard/bridge-store.ts @@ -18,7 +18,7 @@ export interface BridgeAnswer { } /** Most transcript entries kept per session, oldest dropped first. */ -export const MAX_SESSION_EVENTS = 300 +const MAX_SESSION_EVENTS = 300 /** * The questions a Claude web session is parked on, keyed by its cloud session id (#1237). diff --git a/packages/the-framework/src/dashboard/browser-proxy.ts b/packages/the-framework/src/dashboard/browser-proxy.ts index 7706de35..b98ca904 100644 --- a/packages/the-framework/src/dashboard/browser-proxy.ts +++ b/packages/the-framework/src/dashboard/browser-proxy.ts @@ -60,7 +60,7 @@ export type BrowserPortLookup = (projectId: string, runId: string) => Promise { +const defaultBrowserPortLookup: BrowserPortLookup = async (projectId, runId) => { const cwd = await defaultProjectsProvider().resolvePath(projectId) if (!cwd) return undefined const live = await readLiveMetas(cwd).catch(() => []) diff --git a/packages/the-framework/src/dashboard/gh.ts b/packages/the-framework/src/dashboard/gh.ts index fec325ad..aeaeb594 100644 --- a/packages/the-framework/src/dashboard/gh.ts +++ b/packages/the-framework/src/dashboard/gh.ts @@ -416,7 +416,7 @@ export interface OpenPrFilePatch { * queue file deeper than that simply contributes no claim, which is today's behavior anyway. * Resolves `[]` when gh is missing/unauthed. */ -export async function ghOpenPrFilePatches(cwd: string, file: string, prs?: OpenPr[]): Promise { +async function ghOpenPrFilePatches(cwd: string, file: string, prs?: OpenPr[]): Promise { const open = prs ?? (await ghPrList(cwd)) const patches: OpenPrFilePatch[] = [] for (const pr of open) { diff --git a/packages/the-framework/src/dashboard/interventions.ts b/packages/the-framework/src/dashboard/interventions.ts index 4c1fc2ef..be6b2a29 100644 --- a/packages/the-framework/src/dashboard/interventions.ts +++ b/packages/the-framework/src/dashboard/interventions.ts @@ -69,7 +69,7 @@ export interface InterventionsDeps { } /** How many recent finished runs are inspected per project by default. */ -export const HANDOFF_LIMIT = 5 +const HANDOFF_LIMIT = 5 /** * Build the cross-project interventions queue: every registered project's open PRs, plus any run diff --git a/packages/the-framework/src/dashboard/open-in-app.ts b/packages/the-framework/src/dashboard/open-in-app.ts index 415d7b26..f5f8beec 100644 --- a/packages/the-framework/src/dashboard/open-in-app.ts +++ b/packages/the-framework/src/dashboard/open-in-app.ts @@ -41,7 +41,7 @@ export type EditorProbe = (bin: string) => Promise * An {@link EditorProbe} that looks each launcher up on the real PATH: an executable match in any * PATH dir (trying PATHEXT suffixes on Windows). Pure lookup, nothing is spawned. */ -export function nodeEditorProbe(env: NodeJS.ProcessEnv = process.env, os: NodeJS.Platform = platform()): EditorProbe { +function nodeEditorProbe(env: NodeJS.ProcessEnv = process.env, os: NodeJS.Platform = platform()): EditorProbe { const dirs = (env.PATH ?? '').split(delimiter).filter(Boolean) const exts = os === 'win32' ? (env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';').filter(Boolean) : [''] return async bin => { @@ -78,7 +78,7 @@ export type SpawnRunner = (command: string, args: string[]) => Promise * long-lived editor (or `explorer`, which exits non-zero even on success) does not block or * error; rejects on the `error` event (e.g. ENOENT when the command is not on PATH). */ -export function nodeSpawnRunner(): SpawnRunner { +function nodeSpawnRunner(): SpawnRunner { return (command, args) => new Promise((resolve, reject) => { void import('node:child_process').then(({ spawn }) => { diff --git a/packages/the-framework/src/dashboard/run-handoff.ts b/packages/the-framework/src/dashboard/run-handoff.ts index 2304fc8f..b97ad055 100644 --- a/packages/the-framework/src/dashboard/run-handoff.ts +++ b/packages/the-framework/src/dashboard/run-handoff.ts @@ -121,7 +121,7 @@ export function runBranchFor(run: { id: string; branch?: string; sessionName?: s } /** What every branch a session creates for itself is named under. */ -export const SESSION_BRANCH_PREFIX = 'the-framework/' +const SESSION_BRANCH_PREFIX = 'the-framework/' /** * The branch's PR as it applies to *this* run: the injected seam when the caller gave one, else @@ -547,7 +547,7 @@ export interface HandoffIntent { } /** Both halves armed — the default a session starts from. */ -export const ARMED_HANDOFF: HandoffIntent = { push: true, pr: true } +const ARMED_HANDOFF: HandoffIntent = { push: true, pr: true } /** * Whether an armed merge may actually run (#1363), and if not, why. diff --git a/packages/the-framework/src/dashboard/server.ts b/packages/the-framework/src/dashboard/server.ts index a14f17b3..9191b81b 100644 --- a/packages/the-framework/src/dashboard/server.ts +++ b/packages/the-framework/src/dashboard/server.ts @@ -302,7 +302,7 @@ const DAEMON_COOKIE = 'fw_daemon' * Channel, and the MJPEG `` screencast alike (all same-origin), which a bearer header cannot * reach. Returns true to admit the request, false once it has answered (401 or the redirect). */ -export function authorizeDaemonRequest(req: IncomingMessage, res: ServerResponse, token: string): boolean { +function authorizeDaemonRequest(req: IncomingMessage, res: ServerResponse, token: string): boolean { // Safe to re-parse: requestPathname already parsed this same url without throwing (#938). const url = new URL(req.url ?? '/', 'http://localhost') const queryToken = url.searchParams.get('token') diff --git a/packages/the-framework/src/discord/gateway.ts b/packages/the-framework/src/discord/gateway.ts index dd300bd0..646befb0 100644 --- a/packages/the-framework/src/discord/gateway.ts +++ b/packages/the-framework/src/discord/gateway.ts @@ -39,7 +39,7 @@ export const INTENTS = { export const CHAT_INTENTS = INTENTS.guildMessages | INTENTS.directMessages | INTENTS.messageContent /** The default gateway endpoint. v10, JSON encoding (no `zlib-stream`, no `etf`). */ -export const GATEWAY_URL = 'wss://gateway.discord.gg/?v=10&encoding=json' +const GATEWAY_URL = 'wss://gateway.discord.gg/?v=10&encoding=json' /** * The socket seam. Mirrors the slice of `WebSocket` this uses, so a test drives the protocol @@ -68,8 +68,8 @@ export type IntervalFactory = (fn: () => void, ms: number) => Timer export type DelayFactory = (fn: () => void, ms: number) => Timer /** Reconnect backoff bounds. A failed connect must not become a tight loop against Discord. */ -export const RECONNECT_MS = 1_000 -export const RECONNECT_MAX_MS = 60_000 +const RECONNECT_MS = 1_000 +const RECONNECT_MAX_MS = 60_000 /** One inbound chat message, narrowed to what routing needs. */ export interface DiscordMessage { @@ -103,7 +103,7 @@ export interface GatewayDeps { } /** A {@link SocketFactory} over the global `WebSocket` (node >= 22 ships one). */ -export function nodeSocketFactory(): SocketFactory { +function nodeSocketFactory(): SocketFactory { return url => { const ws = new WebSocket(url) return { @@ -117,7 +117,7 @@ export function nodeSocketFactory(): SocketFactory { } /** The default {@link IntervalFactory}: unref'd, so a heartbeat never keeps the daemon alive. */ -export function nodeIntervalFactory(): IntervalFactory { +function nodeIntervalFactory(): IntervalFactory { return (fn, ms) => { const timer = setInterval(fn, ms) timer.unref?.() @@ -126,7 +126,7 @@ export function nodeIntervalFactory(): IntervalFactory { } /** The default {@link DelayFactory}: unref'd, so a pending reconnect never keeps the daemon alive. */ -export function nodeDelayFactory(): DelayFactory { +function nodeDelayFactory(): DelayFactory { return (fn, ms) => { const timer = setTimeout(fn, ms) timer.unref?.() diff --git a/packages/the-framework/src/discord/reply-mirror.ts b/packages/the-framework/src/discord/reply-mirror.ts index d850b67e..040d753e 100644 --- a/packages/the-framework/src/discord/reply-mirror.ts +++ b/packages/the-framework/src/discord/reply-mirror.ts @@ -86,7 +86,7 @@ interface Mirrored extends RunBinding { } /** How often answers are checked for. A chat reply has to feel like a reply. */ -export const REPLY_POLL_MS = 3_000 +const REPLY_POLL_MS = 3_000 /** * How many consecutive unresolvable polls release a binding (#941). Generous on purpose: a diff --git a/packages/the-framework/src/logs.ts b/packages/the-framework/src/logs.ts index face4957..3b80537d 100644 --- a/packages/the-framework/src/logs.ts +++ b/packages/the-framework/src/logs.ts @@ -69,13 +69,13 @@ const SEP = ' · ' * newline spills the rest of the prompt into the file, where a `## ` line forges an entry and a * `- status: ` line rewrites one. Reversed by {@link decodeField}. */ -export function encodeField(value: string): string { +function encodeField(value: string): string { return value.replace(/\\/g, '\\\\').replace(/\r\n|\r|\n/g, '\\n') } /** Reverse {@link encodeField}. Entries written before #897 are unescaped, so a literal `\n` in * one of them decodes to a newline; harmless next to reading the rest of the prompt as entries. */ -export function decodeField(value: string): string { +function decodeField(value: string): string { return value.replace(/\\(\\|n)/g, (_, char) => (char === 'n' ? '\n' : '\\')) } diff --git a/packages/the-framework/src/maintenance.ts b/packages/the-framework/src/maintenance.ts index 946129db..e38d3357 100644 --- a/packages/the-framework/src/maintenance.ts +++ b/packages/the-framework/src/maintenance.ts @@ -67,7 +67,7 @@ export interface MaintenanceFs { } /** A {@link MaintenanceFs} backed by `node:fs/promises`. See {@link nodeFs}. */ -export function nodeMaintenanceFs(): MaintenanceFs { +function nodeMaintenanceFs(): MaintenanceFs { const { read, write, mkdir } = nodeFs() return { read, write, mkdir } } diff --git a/packages/the-framework/src/merged-worktrees.ts b/packages/the-framework/src/merged-worktrees.ts index 56f02fb1..3c7e9c72 100644 --- a/packages/the-framework/src/merged-worktrees.ts +++ b/packages/the-framework/src/merged-worktrees.ts @@ -147,7 +147,7 @@ export async function removeMergedWorktrees(cwd: string, deps: MergedSweepDeps = * the sweep costs — a `gh pr view` per retained worktree, behind a 60s cache — so a minute-poll * would spend an order of magnitude more `gh` on an answer that changes a few times a day. */ -export const DEFAULT_MERGED_SWEEP_INTERVAL_MS = 10 * 60 * 1000 +const DEFAULT_MERGED_SWEEP_INTERVAL_MS = 10 * 60 * 1000 /** A running sweep, in the shape the daemon's other background services use. */ export interface MergedWorktreeSweep { diff --git a/packages/the-framework/src/queue-promote.ts b/packages/the-framework/src/queue-promote.ts index 72dd7b17..90fe0d51 100644 --- a/packages/the-framework/src/queue-promote.ts +++ b/packages/the-framework/src/queue-promote.ts @@ -43,7 +43,7 @@ export type QueuePromotion = } /** The commit message a promotion writes. Names the run so the history says where it came from. */ -export function promotionMessage(runId: string): string { +function promotionMessage(runId: string): string { return `[The Framework] queue updates from ${runId}` } diff --git a/packages/the-framework/src/registry.ts b/packages/the-framework/src/registry.ts index 6825d165..244b5ecc 100644 --- a/packages/the-framework/src/registry.ts +++ b/packages/the-framework/src/registry.ts @@ -49,7 +49,7 @@ export interface CustomPreset { /** The cap on saved custom presets, and the per-field lengths — enough for real prompts, bounded * so a hand-edited or hostile registry can't bloat the home file. */ -export const CUSTOM_PRESET_LIMITS = { count: 30, label: 80, prompt: 20_000 } as const +const CUSTOM_PRESET_LIMITS = { count: 30, label: 80, prompt: 20_000 } as const export interface Preferences { autopilot?: boolean diff --git a/packages/the-framework/src/steps.ts b/packages/the-framework/src/steps.ts index 02104ad7..4e89b9ef 100644 --- a/packages/the-framework/src/steps.ts +++ b/packages/the-framework/src/steps.ts @@ -76,7 +76,7 @@ export function extendPrompt(intent: string): string { * {@link improvePrompt} ("smallest changes / no unrelated features") would * wrongly discourage scaffolding (#182). */ -export function scaffoldPrompt(intent: string): string { +function scaffoldPrompt(intent: string): string { return [ `The workspace is empty — no app exists here yet. You must create the entire app now from scratch: ${intent}`, 'This is a from-scratch build, not an edit: do not wait for existing code, and do', diff --git a/packages/the-framework/src/store/run-store.ts b/packages/the-framework/src/store/run-store.ts index e206eab2..3fcab203 100644 --- a/packages/the-framework/src/store/run-store.ts +++ b/packages/the-framework/src/store/run-store.ts @@ -475,7 +475,7 @@ function writeMetaFile(fs: StoreFs, path: string, meta: RunMeta): Promise * off a single `end` event, so a death that skipped it left the run's last question rendering * as answerable forever while its picks were read by nobody. */ -export function orphanEndEvent(): FrameworkEvent { +function orphanEndEvent(): FrameworkEvent { return { kind: 'end', ok: false, stopped: true, detail: 'its process died without reporting an end' } } diff --git a/packages/the-framework/src/ticket-locks.ts b/packages/the-framework/src/ticket-locks.ts index 31f6bd63..47956127 100644 --- a/packages/the-framework/src/ticket-locks.ts +++ b/packages/the-framework/src/ticket-locks.ts @@ -25,7 +25,7 @@ import type { PlanAssignment } from './auto-pm.js' // watching for dead agents is the user's job, with the dashboard button as the tool. /** The first line of a lock file: the claim, naming the agent that holds it. */ -export const TICKET_LOCK_PREFIX = 'CLAIMED:' +const TICKET_LOCK_PREFIX = 'CLAIMED:' /** A ticket filename's lock sibling, e.g. `a.md` → `a.lock.md`, without the directory. */ export function ticketLockName(ticket: string): string { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 33e6cbb8..e96897dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,69 +18,6 @@ importers: specifier: ^5.4.0 version: 5.9.3 - examples/autopilot-quickstart: - dependencies: - '@gemstack/ai-autopilot': - specifier: workspace:^ - version: link:../../packages/ai-autopilot - '@gemstack/ai-sdk': - specifier: workspace:^ - version: link:../../packages/ai-sdk - zod: - specifier: ^4.0.0 - version: 4.4.3 - devDependencies: - '@types/node': - specifier: ^20.0.0 - version: 20.19.43 - tsx: - specifier: ^4.19.0 - version: 4.22.4 - typescript: - specifier: ^5.4.0 - version: 5.9.3 - - examples/bootstrap-quickstart: - dependencies: - '@gemstack/ai-autopilot': - specifier: workspace:^ - version: link:../../packages/ai-autopilot - '@gemstack/ai-sdk': - specifier: workspace:^ - version: link:../../packages/ai-sdk - zod: - specifier: ^4.0.0 - version: 4.4.3 - devDependencies: - '@types/node': - specifier: ^20.0.0 - version: 20.19.43 - tsx: - specifier: ^4.19.0 - version: 4.22.4 - typescript: - specifier: ^5.4.0 - version: 5.9.3 - - examples/framework-demo: - dependencies: - '@gemstack/ai-autopilot': - specifier: workspace:^ - version: link:../../packages/ai-autopilot - '@gemstack/the-framework': - specifier: workspace:^ - version: link:../../packages/the-framework - devDependencies: - '@types/node': - specifier: ^20.0.0 - version: 20.19.43 - tsx: - specifier: ^4.19.0 - version: 4.22.4 - typescript: - specifier: ^5.4.0 - version: 5.9.3 - packages/ai-autopilot: dependencies: '@gemstack/ai-sdk': @@ -99,9 +36,6 @@ importers: '@webcontainer/api': specifier: ^1.6.4 version: 1.6.4 - playwright-core: - specifier: ^1.49.0 - version: 1.61.1 typescript: specifier: ^5.4.0 version: 5.9.3 @@ -2518,11 +2452,6 @@ packages: resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} - playwright-core@1.61.1: - resolution: {integrity: sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==} - engines: {node: '>=18'} - hasBin: true - postcss@8.5.16: resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} engines: {node: ^10 || ^12 || >=14} @@ -5571,8 +5500,6 @@ snapshots: pkce-challenge@5.0.1: optional: true - playwright-core@1.61.1: {} - postcss@8.5.16: dependencies: nanoid: 3.3.15