# Local Runtime Onboarding Implementation Plan

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Make the local AIPROS Lite student package connect, test, select, and run Codex, Qoder, CodeBuddy, Trae Agent, or a custom local CLI, while exposing all conversation artifacts and a complete ZIP download.

**Architecture:** Keep the existing Node/Hono backend as the only browser-to-CLI bridge. Add a shared runtime preset catalog and setup script, enrich the runtime APIs with discovery/test state, correct provider adapters, and reuse the session directory as the artifact source of truth. Refactor the settings and artifact UI around small pure helpers so behavior is testable without a browser framework.

**Tech Stack:** Node.js 22+, React 18, Hono, SQLite/D1 migrations, Node `child_process`, Node test runner, Vite, JSZip.

---

### Task 1: Runtime preset catalog and student setup command

**Files:**
- Create: `scripts/runtime-presets.mjs`
- Create: `scripts/aipros-runtime-setup.mjs`
- Create: `scripts/runtime-presets.test.mjs`
- Modify: `package.json`

**Step 1: Write the failing catalog tests**

Add tests that assert:

```js
assert.equal(getRuntimePreset('codex').command, 'codex')
assert.match(getRuntimePreset('qoder').install.darwin, /@qoder-ai\/qodercli/)
assert.deepEqual(getRuntimePreset('codebuddy').healthcheckArgs, ['--version'])
assert.deepEqual(renderRuntimeArgs('codebuddy', { prompt: 'hello', workdir: '/tmp/w' }), [
  '-p', 'hello', '--output-format', 'text', '--dangerously-skip-permissions'
])
```

Also assert that unknown runtime codes return `null` and every preset has install, login, health-check, execution, and purpose metadata.

**Step 2: Run the test and verify RED**

Run: `node --test scripts/runtime-presets.test.mjs`

Expected: FAIL because `runtime-presets.mjs` does not exist.

**Step 3: Implement the catalog and setup CLI**

Implement pure helpers for preset lookup, platform-specific install command selection, and argument rendering. Add a setup script that:

1. validates the preset code;
2. prints the selected install command;
3. checks executable candidates with `spawnSync(..., ['--version'], { shell: false })`;
4. with `--install`, runs only the catalog-owned installer command;
5. prints login guidance and the next UI action.

Add:

```json
"runtime:setup": "node scripts/aipros-runtime-setup.mjs"
```

to root scripts.

**Step 4: Run the test and verify GREEN**

Run: `node --test scripts/runtime-presets.test.mjs`

Expected: PASS.

**Step 5: Commit**

```bash
git add scripts/runtime-presets.mjs scripts/aipros-runtime-setup.mjs scripts/runtime-presets.test.mjs package.json
git commit -m "feat: add local runtime setup catalog"
```

### Task 2: Runtime presets, discovery, and connection tests in the Node backend

**Files:**
- Create: `cf-worker/src/runtime/local-runtime-presets.js`
- Create: `cf-worker/src/runtime/local-runtime-presets.test.js`
- Modify: `cf-worker/src/runtime/runtime-registry.js`
- Modify: `cf-worker/src/routes/runtimes.js`
- Create: `cf-worker/migrations/0017_local_runtime_onboarding.sql`
- Test: `cf-worker/src/routes/providers-agentrun-runtime.test.js`

**Step 1: Write failing backend tests**

Test pure behavior:

```js
assert.equal(resolveRuntimeCommand({ local_command: 'qoder' }, { qoder: '/usr/local/bin/qodercli' }), '/usr/local/bin/qodercli')
assert.equal(classifyRuntimeFailure(new Error('command not found')).code, 'not_installed')
assert.equal(classifyRuntimeFailure(new Error('Please login first')).code, 'auth_required')
```

Add a route assertion that runtime list payloads expose `setup`, `connectionState`, `canActivate`, and the new `local-codebuddy` runtime.

**Step 2: Run the tests and verify RED**

Run:

```bash
node --test cf-worker/src/runtime/local-runtime-presets.test.js cf-worker/src/routes/providers-agentrun-runtime.test.js
```

Expected: FAIL because the discovery helpers and payload fields do not exist.

**Step 3: Implement discovery and state**

Add catalog metadata for `local-codex`, `local-qoder`, `local-codebuddy`, `local-trae`, and `local-custom`. Search `PATH` plus safe known user-bin locations with `fs.access`, never with a user-constructed shell string.

Enrich `rowToRuntime` with:

```js
{
  setup: { command, installCommand, loginHint, docsUrl },
  connectionState: 'not_installed' | 'installed' | 'auth_required' | 'connected' | 'error',
  detectedCommand: '',
  lastTest: null,
  canActivate: false,
}
```

The test route performs version detection followed by a harmless real prompt. Persist only status, version, test timestamp, and a sanitized message. Require a passed real test before activation for local presets.

Migration `0017` inserts/corrects CodeBuddy, Qoder, Codex, Trae, and custom rows with accurate commands and argument templates while preserving existing user-edited values where possible.

**Step 4: Run tests and verify GREEN**

Run the command from Step 2.

Expected: PASS.

**Step 5: Commit**

```bash
git add cf-worker/src/runtime/local-runtime-presets.js cf-worker/src/runtime/local-runtime-presets.test.js cf-worker/src/runtime/runtime-registry.js cf-worker/src/routes/runtimes.js cf-worker/migrations/0017_local_runtime_onboarding.sql cf-worker/src/routes/providers-agentrun-runtime.test.js
git commit -m "feat: discover and test local runtimes"
```

### Task 3: Reliable local CLI execution adapters

**Files:**
- Modify: `cf-worker/src/runtime/runtime-registry.js`
- Create: `cf-worker/src/runtime/local-runtime-execution.test.js`
- Modify: `cf-worker/src/routes/skill-run.js`

**Step 1: Write failing adapter tests**

Use a temporary Node fixture executable to assert:

- stdin prompts reach Codex-style adapters;
- `{prompt}` and `{workdir}` placeholders render as single arguments without shell interpolation;
- stdout becomes assistant text;
- stderr-only non-zero exits are classified and truncated;
- timeout terminates the child once;
- spawned processes receive the session directory as `cwd`.

**Step 2: Run and verify RED**

Run: `node --test cf-worker/src/runtime/local-runtime-execution.test.js`

Expected: FAIL on the new adapter contract.

**Step 3: Implement minimal adapter behavior**

Extract command resolution, argument rendering, prompt transport, timeout cleanup, and sanitized failure mapping into testable functions. Keep `spawn(..., { shell: false })`. Return structured execution metadata while preserving `stdout` compatibility for the current Skill job.

**Step 4: Run and verify GREEN**

Run:

```bash
node --test cf-worker/src/runtime/local-runtime-execution.test.js cf-worker/src/routes/skill-run-run-mode.test.js
```

Expected: PASS.

**Step 5: Commit**

```bash
git add cf-worker/src/runtime/runtime-registry.js cf-worker/src/runtime/local-runtime-execution.test.js cf-worker/src/routes/skill-run.js
git commit -m "fix: harden local CLI execution adapters"
```

### Task 4: Safe artifact manifest, nested downloads, and session ZIP

**Files:**
- Create: `cf-worker/src/runtime/session-artifacts.js`
- Create: `cf-worker/src/runtime/session-artifacts.test.js`
- Modify: `cf-worker/src/routes/skill-run.js`
- Create: `cf-worker/src/routes/skill-run-artifacts.test.js`

**Step 1: Write failing artifact tests**

Create a temporary session with:

```text
report.docx
images/chart.png
data/result.xlsx
.skill-run-events.jsonl
.skill-run-state.json
```

Assert the manifest includes the first three with normalized paths, MIME/type metadata, and excludes both internal files. Route tests must prove nested file downloads succeed, `../` traversal fails, and ZIP contains only the three visible artifacts.

**Step 2: Run and verify RED**

Run:

```bash
node --test cf-worker/src/runtime/session-artifacts.test.js cf-worker/src/routes/skill-run-artifacts.test.js
```

Expected: FAIL because nested output paths are flattened by the current route.

**Step 3: Implement the artifact contract**

Add safe relative-path normalization and containment checks. Emit artifact objects while accepting legacy string arrays on the frontend. Update the output route to a wildcard relative path or encoded-path query contract. Refactor repack to use the same visible artifact manifest and name the archive `aipros-artifacts-<session>.zip`.

**Step 4: Run and verify GREEN**

Run the command from Step 2.

Expected: PASS.

**Step 5: Commit**

```bash
git add cf-worker/src/runtime/session-artifacts.js cf-worker/src/runtime/session-artifacts.test.js cf-worker/src/routes/skill-run.js cf-worker/src/routes/skill-run-artifacts.test.js
git commit -m "feat: expose safe session artifacts and zip"
```

### Task 5: Guided Runtime Registry UI

**Files:**
- Create: `src/services/runtimeOnboarding.js`
- Create: `src/services/runtimeOnboarding.test.js`
- Modify: `src/pages/LlmSettings.jsx`
- Modify: `src/components/ProviderIcon.jsx`

**Step 1: Write failing UI-state tests**

Test pure helpers:

```js
assert.equal(runtimeStep({ connectionState: 'not_installed' }), 'install')
assert.equal(runtimeStep({ connectionState: 'auth_required' }), 'login')
assert.equal(runtimeStep({ connectionState: 'connected', isActive: false }), 'select')
assert.equal(runtimeStep({ connectionState: 'connected', isActive: true }), 'ready')
```

Also test copy-command labels and activation disabled reasons.

**Step 2: Run and verify RED**

Run: `node --test src/services/runtimeOnboarding.test.js`

Expected: FAIL because the helper does not exist.

**Step 3: Implement the guided panel**

Replace the local-runtime form-first experience with:

- provider cards;
- a four-step progress row;
- copyable setup command;
- login hint;
- “重新检测并测试” action;
- “选择此运行时” action gated by `canActivate`;
- concise status/error feedback.

Move current command, arguments, health check, working directory, timeout, environment, scopes, and notes under “高级设置”. Keep AgentRun UI unchanged.

**Step 4: Run and verify GREEN**

Run:

```bash
node --test src/services/runtimeOnboarding.test.js src/services/systemSkillsUi.test.js
npm run build
```

Expected: tests PASS and Vite build exits 0.

**Step 5: Commit**

```bash
git add src/services/runtimeOnboarding.js src/services/runtimeOnboarding.test.js src/pages/LlmSettings.jsx src/components/ProviderIcon.jsx
git commit -m "feat: add guided local runtime onboarding"
```

### Task 6: Shared conversation artifact cards and ZIP action

**Files:**
- Create: `src/services/sessionArtifacts.js`
- Create: `src/services/sessionArtifacts.test.js`
- Modify: `src/components/AgentEventLine.jsx`
- Modify: `src/pages/CreateSkillChat.jsx`
- Modify: `src/components/EvaluationPanel.jsx`
- Modify: `src/pages/SkillSandboxPage.jsx`

**Step 1: Write failing URL and normalization tests**

Assert legacy strings and new artifact objects normalize to one shape, nested paths are encoded safely, preview eligibility is type-driven, and ZIP URL is:

```text
/api/skill-run/sessions/<sessionId>/repack
```

**Step 2: Run and verify RED**

Run: `node --test src/services/sessionArtifacts.test.js`

Expected: FAIL because the shared service does not exist.

**Step 3: Implement one shared artifact card**

Refactor `FilesDownloadBox` to display file type/name/path, preview supported browser types, download every type, and show “下载全部 ZIP”. All three conversation surfaces continue to render the same component from their existing `files` events.

**Step 4: Run and verify GREEN**

Run:

```bash
node --test src/services/sessionArtifacts.test.js src/services/skillRunStream.test.js src/services/skillRunStreamReconnect.test.js
npm run build
```

Expected: tests PASS and build exits 0.

**Step 5: Commit**

```bash
git add src/services/sessionArtifacts.js src/services/sessionArtifacts.test.js src/components/AgentEventLine.jsx src/pages/CreateSkillChat.jsx src/components/EvaluationPanel.jsx src/pages/SkillSandboxPage.jsx
git commit -m "feat: add conversation artifact downloads"
```

### Task 7: Student documentation and full verification

**Files:**
- Modify: `README.md`
- Modify: `README-STUDENT.md`
- Modify: `scripts/aipros-student-check.mjs`
- Modify: `scripts/student-release-utils.test.mjs`

**Step 1: Write the failing release check**

Require the student package check to verify the runtime setup script, preset catalog, runtime migration, and documented `runtime:setup` command.

**Step 2: Run and verify RED**

Run: `npm run test:student`

Expected: FAIL until the release checker knows the new required files.

**Step 3: Update documentation and checker**

Document the exact student flow, supported providers, login caveats, local-only security boundary, custom CLI escape hatch, artifact downloads, and troubleshooting commands.

**Step 4: Run complete verification**

Run:

```bash
npm run test:student
node --test scripts/*.test.mjs
node --test src/services/*.test.js
node --test cf-worker/src/runtime/*.test.js cf-worker/src/routes/*runtime*.test.js cf-worker/src/routes/skill-run-*.test.js
npm run build
npm run check
git diff --check
```

Expected: every command exits 0 with no failing tests.

Perform a local smoke test using an installed supported CLI:

```bash
npm run runtime:setup -- codex
npm run local -- --no-browser
```

Then verify from the API/UI that detection, prompt test, activation, a generated nested file, individual download, and ZIP download work.

**Step 5: Commit**

```bash
git add README.md README-STUDENT.md scripts/aipros-student-check.mjs scripts/student-release-utils.test.mjs
git commit -m "docs: explain local runtime onboarding"
```
