diff --git a/.codecarto/findings/vision-capture/INTERVIEW.md b/.codecarto/findings/vision-capture/INTERVIEW.md new file mode 100644 index 0000000..9110c65 --- /dev/null +++ b/.codecarto/findings/vision-capture/INTERVIEW.md @@ -0,0 +1,80 @@ +--- +name: guided-vision-interview +description: Conduct a structured product discovery interview to extract a rich vision brief from the user before the synthesis pipeline's vision-capture phase. +--- + +# Guided Vision Interview + +Your job is to conduct a conversational product discovery interview that draws out the user's product idea and writes it into `inputs/vision.md` as a rich brief. The synthesis pipeline's vision-capture phase will then structure this brief into a testable vision document. + +## Interview structure + +Ask questions in this order. Do not dump all questions at once — ask one or two at a time, wait for the answer, then probe deeper based on what the user said. Skip questions the user has already answered unprompted. + +### 1. Audience and problem (2-3 questions) +- Who specifically is this for? (Not "everyone" — get them to name a concrete persona or team) +- What problem does this solve for them? What are they doing today, and why is it painful? +- Why are existing solutions insufficient? What's the gap? + +### 2. Desired outcomes (2-3 questions) +- What should the user be able to do that they can't today? +- What does success look like? How would they know it worked? +- What's the most important outcome — the one thing that must work? + +### 3. Scope and non-goals (2-3 questions) +- What's explicitly in scope for this version? +- What are you deliberately NOT building? What's a non-goal? +- Is there anything you're tempted to include but know you should defer? + +### 4. Constraints (2-3 questions) +- Any hard technical constraints? (language, platform, integration, performance) +- Any operational constraints? (self-hosted, cloud, offline, privacy, latency) +- Any timeline or team-size constraints? + +### 5. Success measures and acceptance (1-2 questions) +- How would you measure success? What metric or observation would tell you it's working? +- Can you describe one specific scenario where someone uses this and has a good experience? + +## Writing the brief + +After the interview, synthesize the user's answers into `inputs/vision.md`. Use this structure: + +```markdown +# Vision brief + +## Audience +- Who: [specific persona] +- Current approach: [what they do today] +- Pain: [why it's painful] + +## Problem +[2-3 sentences describing the core problem] + +## Desired outcomes +- [outcome 1] +- [outcome 2] +- [outcome 3, if any] + +## Scope +- In scope: [list] +- Non-goals: [list] + +## Constraints +- [constraint 1] +- [constraint 2, if any] + +## Success measures +- [measure 1] +- [acceptance scenario, if provided] + +## Assumptions and open decisions +- [anything the user was unsure about — mark as assumption or needs-decision] +``` + +## Rules + +- Do not invent product decisions the user didn't make. If something is unclear, ask. If they're unsure, record it as an assumption. +- Keep the interview conversational — not a form. Follow up on vague answers with a specific probe. +- If the user gives a very detailed answer up front, skip the questions they already covered and go deeper on the gaps. +- Do not select library entries or discuss implementation in this interview. That comes later in the synthesis pipeline. +- After writing the brief, tell the user they can now run `/codecarto-init synthesis` followed by `/codecarto-next` to start the synthesis pipeline. \ No newline at end of file diff --git a/README.md b/README.md index f1b7f20..dc28588 100644 --- a/README.md +++ b/README.md @@ -287,6 +287,7 @@ Beyond the slash commands, the Pi extension layers on: |---|---| | `/codecarto-init [variant]` | Copy `.codecarto/` into the current repository, select pipeline variant | | `/codecarto-open` | Activate an existing `.codecarto/` workspace in a new Pi session without resetting durable state | +| `/codecarto-vision` | Run a guided product discovery interview to produce `inputs/vision.md` for the synthesis pipeline | | `/codecarto-switch-pipeline ` | Switch the active pipeline in-place without losing findings or progress | | `/codecarto-status` | Current phase, progress, open questions | | `/codecarto-next [--auto [--strict]] [--llm-steer \| --no-llm-steer]` | Spawn the next eligible phase as a sub-agent. After the sub-agent finishes, auto-validates and auto-completes the phase so `status.yaml` advances without manual steps. `--auto` walks the full pipeline end-to-end (same validate + complete + advance loop, repeated); `--strict` flips the `PASS WITH GAPS` rule from "advance" to "pause". | diff --git a/extensions/codecarto/index.ts b/extensions/codecarto/index.ts index 48603c8..bacd84b 100644 --- a/extensions/codecarto/index.ts +++ b/extensions/codecarto/index.ts @@ -250,6 +250,40 @@ export default function codeCartographerExtension(pi: ExtensionAPI) { }, }); + pi.registerCommand("codecarto-vision", { + description: "Run a guided product discovery interview to produce inputs/vision.md for the synthesis pipeline", + handler: async (_args, ctx) => { + const interviewPath = join(ctx.cwd, ".codecarto", "findings", "vision-capture", "INTERVIEW.md"); + if (!(await pathExists(interviewPath))) { + ctx.ui.notify("Vision interview skill not found. Run /codecarto-init synthesis first.", "warning"); + return; + } + + const interviewSkill = await readFile(interviewPath, "utf8"); + const inputsDir = join(ctx.cwd, ".codecarto", "inputs"); + const visionPath = join(inputsDir, "vision.md"); + const visionExists = await pathExists(visionPath); + + const prompt = [ + "Read the interview skill below and conduct a guided product discovery interview with the user.", + "", + interviewSkill, + "", + `The vision brief should be written to .codecarto/inputs/vision.md${visionExists ? " (it already exists — review and improve it based on the interview)" : " (it does not exist yet — create it)"}.`, + "After the interview, write the synthesized brief and tell the user to run /codecarto-init synthesis followed by /codecarto-next to start the pipeline.", + ].join("\n"); + + if (ctx.isIdle()) { + pi.sendUserMessage(prompt); + } else { + pi.sendUserMessage(prompt, { deliverAs: "followUp" }); + } + + lastFeedbackLines = ["Vision interview started — answer the questions in chat."]; + ctx.ui.notify("Vision interview queued — answer the questions in the chat.", "info"); + }, + }); + pi.registerCommand("codecarto-init", { description: "Initialize .codecarto/ in the current repository", getArgumentCompletions: (prefix) => { diff --git a/mcp-server/server.ts b/mcp-server/server.ts index 6fc67be..c3d8021 100644 --- a/mcp-server/server.ts +++ b/mcp-server/server.ts @@ -657,6 +657,37 @@ export async function handleLibraryInit(args: { library_path: string; name?: str }); } +export async function handleVision(args: { cwd: string; raw_text: string }) { + const cwd = await validateCwd(args.cwd); + const workspaceDir = join(cwd, ".codecarto"); + const interviewPath = join(workspaceDir, "findings", "vision-capture", "INTERVIEW.md"); + const visionPath = join(workspaceDir, "inputs", "vision.md"); + + if (!(await pathExists(interviewPath))) { + throw new McpError(ErrorCode.InvalidRequest, "Vision interview skill not found. Run codecarto_init with the synthesis pipeline first."); + } + + const interviewSkill = await readFile(interviewPath, "utf8"); + const prompt = [ + "Read the interview skill below and use it to structure the user's raw product text into a vision brief.", + "", + interviewSkill, + "", + "The user's raw product text is:", + "", + args.raw_text, + "", + `Write the synthesized brief to ${visionPath}.`, + ].join("\n"); + + return textResult(prompt, { + cwd, + visionPath, + interviewPath, + note: "Feed this prompt to your agent. The agent will write the structured vision brief to inputs/vision.md.", + }); +} + export async function handleConfig(args: { cwd?: string }) { const config = args.cwd ? await loadCodecartoConfig(join(args.cwd, ".codecarto")) @@ -870,6 +901,19 @@ const TOOLS = [ }, }, }, + { + name: "codecarto_vision", + description: + "Generate a structured vision brief from raw product text using the guided interview skill. Returns a prompt the host should feed to its agent to write inputs/vision.md. Requires a synthesis workspace (run codecarto_init with the synthesis pipeline first).", + inputSchema: { + type: "object", + properties: { + cwd: { type: "string", description: "Absolute path to the target repository with a .codecarto/ synthesis workspace." }, + raw_text: { type: "string", description: "The user's raw product text — audience, problem, desired outcomes, constraints, non-goals." }, + }, + required: ["cwd", "raw_text"], + }, + }, { name: "codecarto_library_init", description: @@ -911,6 +955,7 @@ const HANDLERS: Record Promise> = { codecarto_library_reindex: handleLibraryReindex, codecarto_library_init: handleLibraryInit, codecarto_config: handleConfig, + codecarto_vision: handleVision, }; // ---------- server bootstrap ---------- diff --git a/tests/default-pipeline.test.mjs b/tests/default-pipeline.test.mjs index 3fefb5f..7cbd246 100644 --- a/tests/default-pipeline.test.mjs +++ b/tests/default-pipeline.test.mjs @@ -61,7 +61,7 @@ test("every PIPELINE_ALIASES target resolves to a real file", async () => { test("Pi extension registers a command for every CodeCartographer operation", async () => { // The set of operations the framework exposes; both wrappers must surface them. - const expected = ["init", "open", "switch-pipeline", "status", "next", "phase", "validate", "complete", "skill", "publish", "library-init", "config", "usage", "dashboard"]; + const expected = ["init", "open", "vision", "switch-pipeline", "status", "next", "phase", "validate", "complete", "skill", "publish", "library-init", "config", "usage", "dashboard"]; const indexSrc = await readFile(join(REPO_ROOT, "extensions", "codecarto", "index.ts"), "utf8"); const missing = expected.filter((op) => !indexSrc.includes(`pi.registerCommand("codecarto-${op}"`)); assert.deepEqual(