Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions .codecarto/findings/vision-capture/INTERVIEW.md
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <variant>` | 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". |
Expand Down
34 changes: 34 additions & 0 deletions extensions/codecarto/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
45 changes: 45 additions & 0 deletions mcp-server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -911,6 +955,7 @@ const HANDLERS: Record<string, (args: any) => Promise<unknown>> = {
codecarto_library_reindex: handleLibraryReindex,
codecarto_library_init: handleLibraryInit,
codecarto_config: handleConfig,
codecarto_vision: handleVision,
};

// ---------- server bootstrap ----------
Expand Down
2 changes: 1 addition & 1 deletion tests/default-pipeline.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down