Commit
Expand scope: non-technical users, folder browser, export
commit
66c04bc
3 changed files with +51 and −15
Jump to a changed file
- spec/PLAN.md +15 −2
- spec/SPEC.md +21 −13
- spec/TASKS.md +15 −0
modified spec/PLAN.md +15 −2
| @@ -15,9 +15,11 @@No other runtime dependencies without a BLOCKED.md entry. | ||
| 15 | 15 | |
| 16 | 16 | ``` |
| 17 | 17 | shared/ types + zod schemas (Session, Segment, Coverage, API payloads) |
| 18 | + transcript.ts: formatTranscript(segments) — plain-text rendering shared by | |
| 19 | + the transcript download route and the client's copy-to-clipboard button | |
| 18 | 20 | server/ |
| 19 | 21 | index.ts Fastify bootstrap, serves client build in prod |
| 20 | - routes/ sessions.ts, audio.ts, generate.ts, blockers.ts | |
| 22 | + routes/ sessions.ts, audio.ts, generate.ts, blockers.ts, fs.ts, export.ts | |
| 21 | 23 | store/ sessionStore.ts (JSON files under data/sessions/<id>/session.json) |
| 22 | 24 | providers/ |
| 23 | 25 | types.ts SttProvider, InterviewLlm interfaces |
| @@ -34,12 +36,16 @@server/ | ||
| 34 | 36 | provenance.ts marker validation (FR-013) |
| 35 | 37 | blockers/ |
| 36 | 38 | parse.ts BLOCKED.md parser |
| 39 | + exporter/ | |
| 40 | + zip.ts dependency-free ZIP (store method) writer used by the export route | |
| 37 | 41 | client/ |
| 38 | 42 | src/App.tsx session list/create, interview view |
| 39 | 43 | src/api.ts typed fetch wrappers over shared types |
| 40 | 44 | src/audio.ts MediaRecorder push-to-talk |
| 41 | 45 | src/tts.ts speechSynthesis wrapper |
| 42 | - src/components/ Transcript, CoveragePanel, QuestionCard, GeneratePanel | |
| 46 | + src/labels.ts plain-language copy for coverage category ids | |
| 47 | + src/components/ Transcript, CoveragePanel, QuestionCard, GeneratePanel, FolderBrowser, | |
| 48 | + TranscriptExport | |
| 43 | 49 | ``` |
| 44 | 50 | |
| 45 | 51 | ## Coverage categories (fixed, order matters for tie-breaking) |
| @@ -95,6 +101,10 @@POST /api/sessions/:id/audio multipart audio -> {segment, turn: InterviewTu | ||
| 95 | 101 | POST /api/sessions/:id/answer {text} -> {segment, turn: InterviewTurn} |
| 96 | 102 | POST /api/sessions/:id/generate {overwrite?: boolean} -> {files: string[], warnings: string[]} |
| 97 | 103 | POST /api/sessions/:id/blockers {} -> {questions: string[]} (reads <targetDir>/BLOCKED.md) |
| 104 | +GET /api/fs/browse ?path=<abs path, default home dir> -> {path, parent, directories: [{name, path}]} | |
| 105 | +POST /api/fs/mkdir {path, name} -> {path} (creates one subdirectory) | |
| 106 | +GET /api/sessions/:id/export/spec-pack.zip -> application/zip download of the generated spec pack | |
| 107 | +GET /api/sessions/:id/export/transcript.md -> text/markdown download of formatTranscript(session.segments) | |
| 98 | 108 | ``` |
| 99 | 109 | |
| 100 | 110 | All request/response bodies validated with the shared zod schemas. Errors: `{error: string}` with appropriate status codes. |
| @@ -106,3 +116,6 @@All request/response bodies validated with the shared zod schemas. Errors: `{err | ||
| 106 | 116 | - One LLM call per generated file instead of one giant call: keeps each output small enough to be reliable and lets provenance validation run per file. |
| 107 | 117 | - Browser TTS over API TTS: zero cost and zero latency-sensitive infra for v1. |
| 108 | 118 | - The generated spec pack intentionally mirrors the structure of this very spec pack (SPEC/PLAN/TASKS/VERIFICATION/HANDOFF); templates in `generator/prompts.ts` should be derived from these files. |
| 119 | +- Zip export is hand-rolled (`exporter/zip.ts`, STORE method only, no compression) instead of adding a zip dependency: the format is small and well-specified, and it keeps the "no dependency without a BLOCKED.md entry" rule intact for a feature this contained. | |
| 120 | +- `/api/fs/browse` and `/api/fs/mkdir` expose the local filesystem over HTTP with no auth check beyond what the rest of the app already assumes (single local user, no auth by design). This is acceptable only because the app is local-only per the Out of scope section; it must never ship if that decision changes. | |
| 121 | +- Coverage category labels shown in the UI are looked up from `client/src/labels.ts`, kept separate from the `CategoryId` values in `shared/types.ts` so the wire format/category ids never change, only the display text. |
modified spec/SPEC.md +21 −13
| @@ -2,39 +2,41 @@ | ||
| 2 | 2 | |
| 3 | 3 | ## Goal |
| 4 | 4 | |
| 5 | -A local tool for a solo developer who thinks out loud. The user talks about something they want to build. An AI interviewer conducts a spoken Socratic interview: one targeted question at a time, driven by a coverage model of what a good spec needs. When coverage is sufficient (or the user says done), the tool writes a spec pack (SPEC.md, PLAN.md, TASKS.md, VERIFICATION.md, HANDOFF.md) into a target project directory, ready to hand to a sandboxed Claude Code session. Every requirement in the generated spec carries provenance markers pointing to the exact transcript segments it came from, so the user can always tell "I said this" apart from "the AI invented this". | |
| 5 | +A local tool for anyone who thinks out loud about something they want built, technical or not. The user talks about their idea. An AI interviewer conducts a spoken Socratic interview in plain language: one targeted question at a time, driven by a coverage model of what a good spec needs, with no jargon assumed. When coverage is sufficient (or the user says done), the tool writes a spec pack (SPEC.md, PLAN.md, TASKS.md, VERIFICATION.md, HANDOFF.md) into a target project directory, ready to hand to a sandboxed Claude Code session, and lets the user download a zip of that pack (or the raw transcript) to send to whoever is going to build it. Every requirement in the generated spec carries provenance markers pointing to the exact transcript segments it came from, so the user can always tell "I said this" apart from "the AI invented this". | |
| 6 | 6 | |
| 7 | 7 | Differentiators over existing tools (verified 2026-07): ChatPRD interviews but has no voice; WhisperCode takes voice but does not interview; Spec Kit / Kiro clarify in text only; no tool provides requirement-to-utterance provenance or closes the loop from a coding agent's blockers back into a voice interview. |
| 8 | 8 | |
| 9 | 9 | ## Users |
| 10 | 10 | |
| 11 | -Single user, the developer themselves, on their own machine. No auth, no multi-tenancy. | |
| 11 | +Single user on their own machine. No auth, no multi-tenancy, no accounts. The primary user is not assumed to be a developer — they may be a non-technical founder, product person, or stakeholder describing an idea that someone else (a developer, an agency, a sandboxed coding agent) will build. Anything shown to the user — coverage category names, interviewer tone, empty states — is written in plain language, not developer jargon. A technical user who wants direct control (typing an exact path, editing files by hand afterward) is never blocked from doing so. | |
| 12 | 12 | |
| 13 | 13 | ## User stories |
| 14 | 14 | |
| 15 | 15 | P1 (must have): |
| 16 | -- US-1: As a developer, I hold a button, talk about my idea, and see my words transcribed into the session. | |
| 17 | -- US-2: As a developer, I am asked one concrete question at a time, chosen to fill the biggest gap in the spec, and I can answer by voice or by typing. | |
| 18 | -- US-3: As a developer, I can see which spec areas are covered and which are still open. | |
| 19 | -- US-4: As a developer, I can generate a spec pack into a target directory and every requirement shows where in my own words it came from. | |
| 16 | +- US-1: As a user, I hold a button, talk about my idea, and see my words transcribed into the session. | |
| 17 | +- US-2: As a user, I am asked one concrete question at a time, in plain language, chosen to fill the biggest gap in the spec, and I can answer by voice or by typing. | |
| 18 | +- US-3: As a user, I can see which spec areas are covered and which are still open, described in terms I understand without a technical background. | |
| 19 | +- US-4: As a user, I can generate a spec pack into a project folder and every requirement shows where in my own words it came from. | |
| 20 | +- US-9: As a user, I can pick or create the project folder by browsing, without knowing what a file path is. | |
| 21 | +- US-10: As a user, once the spec pack is ready, I can download it as a single zip file, or copy/download the full transcript, so I can send it to whoever is going to build it. | |
| 20 | 22 | |
| 21 | 23 | P2 (should have): |
| 22 | -- US-5: As a developer, I hear the interviewer's question spoken aloud so I can keep my eyes off the screen. | |
| 23 | -- US-6: As a developer, when I contradict something I said earlier, the interviewer points at both statements and asks which one holds. | |
| 24 | -- US-7: As a developer, I can close the tool and resume the same session later. | |
| 24 | +- US-5: As a user, I hear the interviewer's question spoken aloud so I can keep my eyes off the screen. | |
| 25 | +- US-6: As a user, when I contradict something I said earlier, the interviewer points at both statements and asks which one holds. | |
| 26 | +- US-7: As a user, I can close the tool and resume the same session later. | |
| 25 | 27 | |
| 26 | 28 | P3 (nice to have): |
| 27 | -- US-8: As a developer, after a sandboxed Claude Code run leaves questions in BLOCKED.md, I can import them and answer them in a new voice round, and the spec pack is regenerated with the answers. | |
| 29 | +- US-8: As a user, after a sandboxed Claude Code run leaves questions in BLOCKED.md, I can import them and answer them in a new voice round, and the spec pack is regenerated with the answers. | |
| 28 | 30 | |
| 29 | 31 | ## Functional requirements |
| 30 | 32 | |
| 31 | 33 | Interview session: |
| 32 | -- FR-001: The user can create a session with a project name and a target directory path. The session gets a unique id. | |
| 34 | +- FR-001: The user can create a session with a project name and a target directory path. The session gets a unique id. The target directory can be chosen by browsing the local filesystem (list subfolders, navigate up/down, create a new folder in place) instead of typing a path; a technical user may still type or paste a path directly into the same field. | |
| 33 | 35 | - FR-002: The client records audio push-to-talk style (press and hold, or click to start/stop) using the browser microphone and sends the recording to the server when released. |
| 34 | 36 | - FR-003: The server transcribes audio through the configured STT provider and appends a transcript segment `{id, timestamp, speaker: "user", text}` to the session. Segment ids are sequential (S1, S2, ...). |
| 35 | 37 | - FR-004: A text input is always available as a fallback; a typed answer goes through the same pipeline as a transcribed one and also becomes a transcript segment. |
| 36 | 38 | - FR-005: After each user answer, the interview engine produces exactly one next question. The question is stored as a transcript segment with `speaker: "interviewer"`. |
| 37 | -- FR-006: The engine maintains a coverage state over the nine categories listed in PLAN.md, each rated `missing`, `partial`, or `clear`, updated after every answer. The next question targets the weakest category. | |
| 39 | +- FR-006: The engine maintains a coverage state over the nine categories listed in PLAN.md, each rated `missing`, `partial`, or `clear`, updated after every answer. The next question targets the weakest category. Category names shown in the UI are plain-language (e.g. "What's NOT included" rather than "Non-goals"); the underlying category ids in PLAN.md are unchanged. | |
| 38 | 40 | - FR-007: If a new answer conflicts with an earlier statement, the next question must surface the contradiction, quoting or referencing both segments, instead of a coverage question. |
| 39 | 41 | - FR-008: The user can end the interview at any time by clicking Done or by saying/typing "done". If categories are still `missing`, generation proceeds but the UI first shows which categories are missing and asks for confirmation. |
| 40 | 42 | - FR-009: Each interviewer question is displayed as text and, when the TTS toggle is on, spoken via the browser `speechSynthesis` API. |
| @@ -46,6 +48,10 @@Spec pack generation: | ||
| 46 | 48 | - FR-013: After generation, the server validates all `[S<n>]` markers against the session transcript. Markers pointing at nonexistent segments are removed and the requirement is suffixed with `[unverified]`. |
| 47 | 49 | - FR-014: Every task in the generated TASKS.md includes at least one verification command, and the generated HANDOFF.md contains a ready-to-copy command for launching a Claude Code session against the pack. |
| 48 | 50 | |
| 51 | +Sharing and export: | |
| 52 | +- FR-019: Once a spec pack has been generated, the user can download it as a single zip file (SPEC.md, PLAN.md, TASKS.md, VERIFICATION.md, HANDOFF.md, sources.json) without needing filesystem access to the target directory. | |
| 53 | +- FR-020: At any point in a session, the user can copy the full transcript to the clipboard or download it as a text file, formatted as a readable back-and-forth (not raw JSON). | |
| 54 | + | |
| 49 | 55 | Persistence and resume: |
| 50 | 56 | - FR-015: Sessions are persisted to disk after every turn. Opening the app lists existing sessions and lets the user resume one, with full transcript and coverage state restored. |
| 51 | 57 | |
| @@ -65,10 +71,12 @@Modes and safety: | ||
| 65 | 71 | - "done" as an ordinary word inside a longer answer must not end the interview; only an answer that is exactly "done" (case-insensitive, trimmed) or the Done button ends it. |
| 66 | 72 | - Very long sessions: the engine prompt includes at most the last 40 transcript segments verbatim plus a running summary of earlier ones (summary maintained by the engine). |
| 67 | 73 | - BLOCKED.md missing or empty on import: informative message, nothing changes. |
| 74 | +- Folder browser: a directory the user can't read (permissions) shows an inline error and keeps the browser at the last folder that worked; it never crashes the picker. | |
| 75 | +- Zip download requested before the spec pack has been generated: a clear "generate the spec pack first" error, no partial download. | |
| 68 | 76 | |
| 69 | 77 | ## Out of scope (v1) |
| 70 | 78 | |
| 71 | -- Multi-user support, auth, or any cloud deployment. Local only. | |
| 79 | +- Multi-user support, auth, or any cloud deployment. Local only. Sharing means the user downloads a file and sends it themselves (email, Slack, upload) — not a hosted link to a session or spec pack. | |
| 72 | 80 | - Real-time streaming conversation (barge-in, VAD). Push-to-talk only. |
| 73 | 81 | - Paid TTS voices. Browser `speechSynthesis` only. |
| 74 | 82 | - Running or supervising Claude Code from inside the app. The app only writes files and prints the handoff command. |
modified spec/TASKS.md +15 −0
| @@ -61,3 +61,18 @@Work strictly in order unless a task's Depends line allows otherwise. One task a | ||
| 61 | 61 | - Single vitest test: boot the server with mocks, create session (temp target dir), answer until done via the answer route, generate, assert all six files exist, all remaining `[S<n>]` markers resolve against sources.json, and HANDOFF.md contains the string `claude`. |
| 62 | 62 | - Depends: T9, T10 |
| 63 | 63 | - Verify: `npm test` runs it green; then `npm run typecheck`, `npm test`, `npm run build` all exit 0 as the final full check. |
| 64 | + | |
| 65 | +- [ ] T13 Non-technical onboarding pass | |
| 66 | + - `client/src/labels.ts` maps each `CategoryId` to a plain-language label and one-line explanation used by `CoveragePanel`. Rewrite session-list and interview-screen copy (headings, empty states, button text) to not assume the reader is a developer. Update `buildInterviewSystemPrompt` in `llmAnthropic.ts` to instruct plain-language, jargon-free questions per updated SPEC FR-006. | |
| 67 | + - Depends: T7 | |
| 68 | + - Verify: `npm run typecheck` and `npm run build` exit 0. Manual: every category in CoveragePanel shows a plain-language label, not a `CategoryId`. | |
| 69 | + | |
| 70 | +- [ ] T14 Folder browser | |
| 71 | + - `server/routes/fs.ts`: `GET /api/fs/browse` (defaults to the OS home directory, lists subdirectories, returns `{path, parent, directories}`), `POST /api/fs/mkdir` (creates one subdirectory). Shared zod schemas for both. `client/src/components/FolderBrowser.tsx`: modal that navigates the tree, creates a folder, and returns the chosen path to the session-create form; the raw text input stays available alongside it. | |
| 72 | + - Depends: T7 | |
| 73 | + - Verify: `npm test` (route tests: browse lists directories and a parent, browsing an unreadable path returns a 4xx, mkdir creates a directory and 4xxs on a duplicate name) and `npm run typecheck`. Manual: browsing, creating a folder, and selecting it fills the target directory field. | |
| 74 | + | |
| 75 | +- [ ] T15 Export: spec pack zip and transcript | |
| 76 | + - `shared/transcript.ts`: `formatTranscript(segments)` plain-text renderer. `server/exporter/zip.ts`: dependency-free ZIP (STORE method) writer. `GET /api/sessions/:id/export/spec-pack.zip` (404 with a clear error if `spec/` doesn't exist yet in the target directory) and `GET /api/sessions/:id/export/transcript.md`. Client: a transcript "Copy" / "Download" control usable at any time, and a "Download spec pack (.zip)" button in `GeneratePanel` that appears once a pack exists. | |
| 77 | + - Depends: T9, T10 | |
| 78 | + - Verify: `npm test` (zip writer roundtrips through Node's own unzip via a temp-file check or a byte-level structural check; export route: 404 before generate, 200 with correct `Content-Type`/`Content-Disposition` after) and `npm run typecheck`. Manual: after generating, the zip downloads and contains all six files; transcript copy/download works mid-interview. |