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
| # | Customer | Total |
`,
- `| 1001 | Ada Lovelace | $42.00 |
`,
- `| 1002 | Alan 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