@ilya-lesikov/pi-pi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/3p/pi-ask-user/index.ts +1835 -0
- package/3p/pi-ask-user/package.json +50 -0
- package/3p/pi-ask-user/single-select-layout.ts +203 -0
- package/3p/pi-lsp/extensions/lsp/client.ts +542 -0
- package/3p/pi-lsp/extensions/lsp/config.ts +135 -0
- package/3p/pi-lsp/extensions/lsp/effects/command.ts +42 -0
- package/3p/pi-lsp/extensions/lsp/effects/filesystem.ts +50 -0
- package/3p/pi-lsp/extensions/lsp/effects/runtime.ts +21 -0
- package/3p/pi-lsp/extensions/lsp/errors.ts +113 -0
- package/3p/pi-lsp/extensions/lsp/formatting.ts +315 -0
- package/3p/pi-lsp/extensions/lsp/index.ts +231 -0
- package/3p/pi-lsp/extensions/lsp/protocol.ts +209 -0
- package/3p/pi-lsp/extensions/lsp/retry.ts +39 -0
- package/3p/pi-lsp/extensions/lsp/tools/programs.ts +293 -0
- package/3p/pi-lsp/extensions/lsp/tools.ts +89 -0
- package/3p/pi-lsp/extensions/lsp/types.ts +243 -0
- package/3p/pi-lsp/package.json +54 -0
- package/3p/pi-plannotator/AGENTS.md +583 -0
- package/3p/pi-plannotator/apps/pi-extension/README.md +228 -0
- package/3p/pi-plannotator/apps/pi-extension/assistant-message.ts +128 -0
- package/3p/pi-plannotator/apps/pi-extension/config.test.ts +166 -0
- package/3p/pi-plannotator/apps/pi-extension/config.ts +318 -0
- package/3p/pi-plannotator/apps/pi-extension/current-pi-session.ts +147 -0
- package/3p/pi-plannotator/apps/pi-extension/index.ts +1279 -0
- package/3p/pi-plannotator/apps/pi-extension/package.json +56 -0
- package/3p/pi-plannotator/apps/pi-extension/plannotator-browser.test.ts +13 -0
- package/3p/pi-plannotator/apps/pi-extension/plannotator-browser.ts +616 -0
- package/3p/pi-plannotator/apps/pi-extension/plannotator-events.ts +342 -0
- package/3p/pi-plannotator/apps/pi-extension/plannotator.json +12 -0
- package/3p/pi-plannotator/apps/pi-extension/server/agent-jobs.ts +515 -0
- package/3p/pi-plannotator/apps/pi-extension/server/ai-runtime.ts +169 -0
- package/3p/pi-plannotator/apps/pi-extension/server/annotations.ts +85 -0
- package/3p/pi-plannotator/apps/pi-extension/server/external-annotations.ts +189 -0
- package/3p/pi-plannotator/apps/pi-extension/server/handlers.ts +210 -0
- package/3p/pi-plannotator/apps/pi-extension/server/helpers.ts +78 -0
- package/3p/pi-plannotator/apps/pi-extension/server/ide.ts +46 -0
- package/3p/pi-plannotator/apps/pi-extension/server/integrations.ts +195 -0
- package/3p/pi-plannotator/apps/pi-extension/server/network.test.ts +158 -0
- package/3p/pi-plannotator/apps/pi-extension/server/network.ts +268 -0
- package/3p/pi-plannotator/apps/pi-extension/server/pr.ts +126 -0
- package/3p/pi-plannotator/apps/pi-extension/server/project.ts +64 -0
- package/3p/pi-plannotator/apps/pi-extension/server/reference.ts +362 -0
- package/3p/pi-plannotator/apps/pi-extension/server/serverAnnotate.ts +199 -0
- package/3p/pi-plannotator/apps/pi-extension/server/serverPlan.ts +505 -0
- package/3p/pi-plannotator/apps/pi-extension/server/serverReview.ts +1229 -0
- package/3p/pi-plannotator/apps/pi-extension/server/vcs.ts +121 -0
- package/3p/pi-plannotator/apps/pi-extension/server.test.ts +966 -0
- package/3p/pi-plannotator/apps/pi-extension/server.ts +44 -0
- package/3p/pi-plannotator/apps/pi-extension/tool-scope.test.ts +88 -0
- package/3p/pi-plannotator/apps/pi-extension/tool-scope.ts +39 -0
- package/3p/pi-plannotator/apps/pi-extension/tsconfig.json +16 -0
- package/3p/pi-plannotator/apps/pi-extension/vendor.sh +48 -0
- package/3p/pi-plannotator/package.json +53 -0
- package/3p/pi-plannotator/packages/ai/ai.test.ts +1407 -0
- package/3p/pi-plannotator/packages/ai/base-session.ts +94 -0
- package/3p/pi-plannotator/packages/ai/context.ts +250 -0
- package/3p/pi-plannotator/packages/ai/endpoints.ts +326 -0
- package/3p/pi-plannotator/packages/ai/index.ts +105 -0
- package/3p/pi-plannotator/packages/ai/package.json +21 -0
- package/3p/pi-plannotator/packages/ai/provider.ts +103 -0
- package/3p/pi-plannotator/packages/ai/providers/claude-agent-sdk.ts +447 -0
- package/3p/pi-plannotator/packages/ai/providers/codex-sdk.ts +430 -0
- package/3p/pi-plannotator/packages/ai/providers/command-path.ts +115 -0
- package/3p/pi-plannotator/packages/ai/providers/opencode-sdk.ts +546 -0
- package/3p/pi-plannotator/packages/ai/providers/pi-events.ts +110 -0
- package/3p/pi-plannotator/packages/ai/providers/pi-sdk-node.ts +425 -0
- package/3p/pi-plannotator/packages/ai/providers/pi-sdk.ts +469 -0
- package/3p/pi-plannotator/packages/ai/session-manager.ts +195 -0
- package/3p/pi-plannotator/packages/ai/tsconfig.json +15 -0
- package/3p/pi-plannotator/packages/ai/types.ts +379 -0
- package/3p/pi-plannotator/packages/server/agent-jobs.ts +538 -0
- package/3p/pi-plannotator/packages/server/agent-review-message.test.ts +135 -0
- package/3p/pi-plannotator/packages/server/agent-review-message.ts +243 -0
- package/3p/pi-plannotator/packages/server/ai-runtime.ts +108 -0
- package/3p/pi-plannotator/packages/server/annotate.ts +401 -0
- package/3p/pi-plannotator/packages/server/browser.test.ts +87 -0
- package/3p/pi-plannotator/packages/server/browser.ts +245 -0
- package/3p/pi-plannotator/packages/server/claude-review.ts +352 -0
- package/3p/pi-plannotator/packages/server/code-nav.ts +73 -0
- package/3p/pi-plannotator/packages/server/codex-review-schema.json +41 -0
- package/3p/pi-plannotator/packages/server/codex-review.ts +330 -0
- package/3p/pi-plannotator/packages/server/config.ts +8 -0
- package/3p/pi-plannotator/packages/server/draft.ts +1 -0
- package/3p/pi-plannotator/packages/server/editor-annotations.ts +76 -0
- package/3p/pi-plannotator/packages/server/external-annotations.test.ts +18 -0
- package/3p/pi-plannotator/packages/server/external-annotations.ts +207 -0
- package/3p/pi-plannotator/packages/server/git.ts +141 -0
- package/3p/pi-plannotator/packages/server/goal-setup.test.ts +55 -0
- package/3p/pi-plannotator/packages/server/goal-setup.ts +248 -0
- package/3p/pi-plannotator/packages/server/ide.ts +43 -0
- package/3p/pi-plannotator/packages/server/image.test.ts +63 -0
- package/3p/pi-plannotator/packages/server/image.ts +66 -0
- package/3p/pi-plannotator/packages/server/index.ts +648 -0
- package/3p/pi-plannotator/packages/server/integrations.test.ts +172 -0
- package/3p/pi-plannotator/packages/server/integrations.ts +214 -0
- package/3p/pi-plannotator/packages/server/jj.test.ts +69 -0
- package/3p/pi-plannotator/packages/server/jj.ts +86 -0
- package/3p/pi-plannotator/packages/server/p4.ts +417 -0
- package/3p/pi-plannotator/packages/server/package.json +40 -0
- package/3p/pi-plannotator/packages/server/path-utils.ts +18 -0
- package/3p/pi-plannotator/packages/server/pr.ts +146 -0
- package/3p/pi-plannotator/packages/server/project.test.ts +115 -0
- package/3p/pi-plannotator/packages/server/project.ts +45 -0
- package/3p/pi-plannotator/packages/server/reference-handlers.ts +404 -0
- package/3p/pi-plannotator/packages/server/remote.test.ts +150 -0
- package/3p/pi-plannotator/packages/server/remote.ts +74 -0
- package/3p/pi-plannotator/packages/server/repo.ts +80 -0
- package/3p/pi-plannotator/packages/server/resolve-file.test.ts +310 -0
- package/3p/pi-plannotator/packages/server/review-workspace.test.ts +1032 -0
- package/3p/pi-plannotator/packages/server/review-workspace.ts +48 -0
- package/3p/pi-plannotator/packages/server/review.ts +1318 -0
- package/3p/pi-plannotator/packages/server/sessions.ts +111 -0
- package/3p/pi-plannotator/packages/server/share-url.ts +53 -0
- package/3p/pi-plannotator/packages/server/shared-handlers.test.ts +43 -0
- package/3p/pi-plannotator/packages/server/shared-handlers.ts +177 -0
- package/3p/pi-plannotator/packages/server/storage.test.ts +176 -0
- package/3p/pi-plannotator/packages/server/storage.ts +17 -0
- package/3p/pi-plannotator/packages/server/tour/tour-review.test.ts +146 -0
- package/3p/pi-plannotator/packages/server/tour/tour-review.ts +604 -0
- package/3p/pi-plannotator/packages/server/tsconfig.json +15 -0
- package/3p/pi-plannotator/packages/server/vcs.test.ts +48 -0
- package/3p/pi-plannotator/packages/server/vcs.ts +80 -0
- package/3p/pi-plannotator/packages/shared/agent-jobs.ts +132 -0
- package/3p/pi-plannotator/packages/shared/agents.ts +53 -0
- package/3p/pi-plannotator/packages/shared/annotate-args.test.ts +386 -0
- package/3p/pi-plannotator/packages/shared/annotate-args.ts +107 -0
- package/3p/pi-plannotator/packages/shared/at-reference.test.ts +99 -0
- package/3p/pi-plannotator/packages/shared/at-reference.ts +52 -0
- package/3p/pi-plannotator/packages/shared/checklist.ts +52 -0
- package/3p/pi-plannotator/packages/shared/code-file.test.ts +112 -0
- package/3p/pi-plannotator/packages/shared/code-file.ts +41 -0
- package/3p/pi-plannotator/packages/shared/code-nav.test.ts +515 -0
- package/3p/pi-plannotator/packages/shared/code-nav.ts +436 -0
- package/3p/pi-plannotator/packages/shared/compress.ts +51 -0
- package/3p/pi-plannotator/packages/shared/config.ts +262 -0
- package/3p/pi-plannotator/packages/shared/crypto.test.ts +172 -0
- package/3p/pi-plannotator/packages/shared/crypto.ts +97 -0
- package/3p/pi-plannotator/packages/shared/data-dir.ts +42 -0
- package/3p/pi-plannotator/packages/shared/diff-paths.test.ts +30 -0
- package/3p/pi-plannotator/packages/shared/diff-paths.ts +137 -0
- package/3p/pi-plannotator/packages/shared/draft.ts +64 -0
- package/3p/pi-plannotator/packages/shared/external-annotation.ts +397 -0
- package/3p/pi-plannotator/packages/shared/extract-code-paths.test.ts +59 -0
- package/3p/pi-plannotator/packages/shared/extract-code-paths.ts +66 -0
- package/3p/pi-plannotator/packages/shared/favicon.ts +5 -0
- package/3p/pi-plannotator/packages/shared/feedback-templates.test.ts +65 -0
- package/3p/pi-plannotator/packages/shared/feedback-templates.ts +29 -0
- package/3p/pi-plannotator/packages/shared/goal-setup.test.ts +231 -0
- package/3p/pi-plannotator/packages/shared/goal-setup.ts +336 -0
- package/3p/pi-plannotator/packages/shared/html-to-markdown.test.ts +62 -0
- package/3p/pi-plannotator/packages/shared/html-to-markdown.ts +32 -0
- package/3p/pi-plannotator/packages/shared/improvement-hooks.test.ts +135 -0
- package/3p/pi-plannotator/packages/shared/improvement-hooks.ts +115 -0
- package/3p/pi-plannotator/packages/shared/integrations-common.ts +243 -0
- package/3p/pi-plannotator/packages/shared/jj-core.test.ts +236 -0
- package/3p/pi-plannotator/packages/shared/jj-core.ts +433 -0
- package/3p/pi-plannotator/packages/shared/package.json +55 -0
- package/3p/pi-plannotator/packages/shared/pfm-reminder.test.ts +88 -0
- package/3p/pi-plannotator/packages/shared/pfm-reminder.ts +80 -0
- package/3p/pi-plannotator/packages/shared/pr-github.ts +661 -0
- package/3p/pi-plannotator/packages/shared/pr-gitlab.test.ts +202 -0
- package/3p/pi-plannotator/packages/shared/pr-gitlab.ts +620 -0
- package/3p/pi-plannotator/packages/shared/pr-provider.test.ts +266 -0
- package/3p/pi-plannotator/packages/shared/pr-provider.ts +123 -0
- package/3p/pi-plannotator/packages/shared/pr-stack.test.ts +104 -0
- package/3p/pi-plannotator/packages/shared/pr-stack.ts +194 -0
- package/3p/pi-plannotator/packages/shared/pr-types.ts +326 -0
- package/3p/pi-plannotator/packages/shared/project.ts +71 -0
- package/3p/pi-plannotator/packages/shared/prompts-integration.test.ts +421 -0
- package/3p/pi-plannotator/packages/shared/prompts.test.ts +504 -0
- package/3p/pi-plannotator/packages/shared/prompts.ts +247 -0
- package/3p/pi-plannotator/packages/shared/reference-common.ts +87 -0
- package/3p/pi-plannotator/packages/shared/repo.ts +71 -0
- package/3p/pi-plannotator/packages/shared/resolve-file.test.ts +113 -0
- package/3p/pi-plannotator/packages/shared/resolve-file.ts +509 -0
- package/3p/pi-plannotator/packages/shared/review-args.test.ts +64 -0
- package/3p/pi-plannotator/packages/shared/review-args.ts +85 -0
- package/3p/pi-plannotator/packages/shared/review-core.test.ts +286 -0
- package/3p/pi-plannotator/packages/shared/review-core.ts +895 -0
- package/3p/pi-plannotator/packages/shared/review-workspace-node.ts +230 -0
- package/3p/pi-plannotator/packages/shared/review-workspace.ts +436 -0
- package/3p/pi-plannotator/packages/shared/semantic-diff-types.ts +76 -0
- package/3p/pi-plannotator/packages/shared/semantic-diff.test.ts +322 -0
- package/3p/pi-plannotator/packages/shared/semantic-diff.ts +520 -0
- package/3p/pi-plannotator/packages/shared/storage.ts +378 -0
- package/3p/pi-plannotator/packages/shared/tour.ts +61 -0
- package/3p/pi-plannotator/packages/shared/tsconfig.json +15 -0
- package/3p/pi-plannotator/packages/shared/types.ts +29 -0
- package/3p/pi-plannotator/packages/shared/url-to-markdown.test.ts +177 -0
- package/3p/pi-plannotator/packages/shared/url-to-markdown.ts +351 -0
- package/3p/pi-plannotator/packages/shared/vcs-core.test.ts +332 -0
- package/3p/pi-plannotator/packages/shared/vcs-core.ts +482 -0
- package/3p/pi-plannotator/packages/shared/worktree-pool.test.ts +162 -0
- package/3p/pi-plannotator/packages/shared/worktree-pool.ts +103 -0
- package/3p/pi-plannotator/packages/shared/worktree.ts +119 -0
- package/3p/pi-subagents/package.json +50 -0
- package/3p/pi-subagents/src/agent-manager.ts +413 -0
- package/3p/pi-subagents/src/agent-runner.ts +502 -0
- package/3p/pi-subagents/src/agent-types.ts +248 -0
- package/3p/pi-subagents/src/context.ts +58 -0
- package/3p/pi-subagents/src/cross-extension-rpc.ts +135 -0
- package/3p/pi-subagents/src/custom-agents.ts +137 -0
- package/3p/pi-subagents/src/default-agents.ts +144 -0
- package/3p/pi-subagents/src/env.ts +33 -0
- package/3p/pi-subagents/src/group-join.ts +141 -0
- package/3p/pi-subagents/src/index.ts +1811 -0
- package/3p/pi-subagents/src/invocation-config.ts +40 -0
- package/3p/pi-subagents/src/memory.ts +165 -0
- package/3p/pi-subagents/src/model-resolver.ts +81 -0
- package/3p/pi-subagents/src/output-file.ts +77 -0
- package/3p/pi-subagents/src/prompts.ts +85 -0
- package/3p/pi-subagents/src/skill-loader.ts +79 -0
- package/3p/pi-subagents/src/types.ts +111 -0
- package/3p/pi-subagents/src/ui/agent-widget.ts +496 -0
- package/3p/pi-subagents/src/ui/conversation-viewer.ts +250 -0
- package/3p/pi-subagents/src/worktree.ts +162 -0
- package/3p/pi-tasks/package.json +52 -0
- package/3p/pi-tasks/src/auto-clear.ts +91 -0
- package/3p/pi-tasks/src/index.ts +1150 -0
- package/3p/pi-tasks/src/process-tracker.ts +140 -0
- package/3p/pi-tasks/src/reminder-cadence.ts +90 -0
- package/3p/pi-tasks/src/task-store.ts +324 -0
- package/3p/pi-tasks/src/tasks-config.ts +27 -0
- package/3p/pi-tasks/src/types.ts +40 -0
- package/3p/pi-tasks/src/ui/settings-menu.ts +152 -0
- package/3p/pi-tasks/src/ui/task-widget.ts +296 -0
- package/AGENTS.md +28 -0
- package/LICENSE +201 -0
- package/extensions/orchestrator/agents/brainstorm-reviewer.ts +77 -0
- package/extensions/orchestrator/agents/code-reviewer.ts +89 -0
- package/extensions/orchestrator/agents/explore.ts +33 -0
- package/extensions/orchestrator/agents/librarian.ts +43 -0
- package/extensions/orchestrator/agents/plan-reviewer.ts +75 -0
- package/extensions/orchestrator/agents/planner.ts +61 -0
- package/extensions/orchestrator/agents/registry.ts +156 -0
- package/extensions/orchestrator/agents/task.ts +50 -0
- package/extensions/orchestrator/agents/tool-routing.ts +84 -0
- package/extensions/orchestrator/ast-search.ts +85 -0
- package/extensions/orchestrator/cbm.ts +330 -0
- package/extensions/orchestrator/command-handlers.test.ts +163 -0
- package/extensions/orchestrator/command-handlers.ts +116 -0
- package/extensions/orchestrator/commands.test.ts +81 -0
- package/extensions/orchestrator/commands.ts +75 -0
- package/extensions/orchestrator/config.test.ts +221 -0
- package/extensions/orchestrator/config.ts +230 -0
- package/extensions/orchestrator/context.test.ts +293 -0
- package/extensions/orchestrator/context.ts +206 -0
- package/extensions/orchestrator/event-handlers.test.ts +190 -0
- package/extensions/orchestrator/event-handlers.ts +1416 -0
- package/extensions/orchestrator/exa.ts +104 -0
- package/extensions/orchestrator/flant-infra.ts +486 -0
- package/extensions/orchestrator/index.ts +80 -0
- package/extensions/orchestrator/integration.test.ts +1214 -0
- package/extensions/orchestrator/orchestrator.test.ts +252 -0
- package/extensions/orchestrator/orchestrator.ts +538 -0
- package/extensions/orchestrator/phases/brainstorm.test.ts +15 -0
- package/extensions/orchestrator/phases/brainstorm.ts +273 -0
- package/extensions/orchestrator/phases/implementation.ts +40 -0
- package/extensions/orchestrator/phases/machine.test.ts +293 -0
- package/extensions/orchestrator/phases/machine.ts +209 -0
- package/extensions/orchestrator/phases/planning.ts +255 -0
- package/extensions/orchestrator/phases/review.ts +193 -0
- package/extensions/orchestrator/plannotator.ts +56 -0
- package/extensions/orchestrator/pp-menu.ts +866 -0
- package/extensions/orchestrator/state.test.ts +343 -0
- package/extensions/orchestrator/state.ts +237 -0
- package/extensions/orchestrator/validate-artifacts.test.ts +88 -0
- package/extensions/orchestrator/validate-artifacts.ts +272 -0
- package/extensions/orchestrator/vendor.d.ts +26 -0
- package/package.json +73 -0
- package/scripts/postinstall.sh +18 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { readFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import type { PiPiConfig } from "../config.js";
|
|
5
|
+
import { registerAgentDefinitions, spawnViaRpc, waitForCompletion } from "../agents/registry.js";
|
|
6
|
+
import { createBrainstormReviewerAgent } from "../agents/brainstorm-reviewer.js";
|
|
7
|
+
import type { TaskType } from "../state.js";
|
|
8
|
+
|
|
9
|
+
export function brainstormSystemPrompt(taskType: TaskType, taskDescription: string, taskDir: string): string {
|
|
10
|
+
if (taskType === "debug") {
|
|
11
|
+
return [
|
|
12
|
+
"[PI-PI — DEBUG PHASE]",
|
|
13
|
+
`Problem: ${taskDescription}`,
|
|
14
|
+
"",
|
|
15
|
+
"Read-only diagnosis mode.",
|
|
16
|
+
"",
|
|
17
|
+
"# FORBIDDEN — do NOT do any of these:",
|
|
18
|
+
"- Do NOT modify project source code (no write or edit tools on project files)",
|
|
19
|
+
"- Do NOT create or modify any files outside the task directory",
|
|
20
|
+
"- Do NOT implement fixes — only diagnose and recommend",
|
|
21
|
+
"- If the user asks you to implement a fix or start coding — call pp_phase_complete instead. It will offer \"Implement a fix\" as an option. Do NOT implement directly in this session.",
|
|
22
|
+
"",
|
|
23
|
+
"# Your job:",
|
|
24
|
+
"1. Clarify the problem with the user if needed",
|
|
25
|
+
'2. Spawn explore subagents via Agent(subagent_type="Explore", ...) for codebase research',
|
|
26
|
+
"3. Use bash to run commands, check logs, reproduce issues",
|
|
27
|
+
"4. Use tools to trace the bug:",
|
|
28
|
+
" - cbm_search/cbm_search_code: find relevant code by concept or text",
|
|
29
|
+
" - lsp goToDefinition, findReferences, hover: precise navigation and type info",
|
|
30
|
+
" - lsp goToImplementation: check interface implementors",
|
|
31
|
+
" - lsp diagnostics: find type errors",
|
|
32
|
+
" - cbm_trace: trace call chains to/from suspect functions",
|
|
33
|
+
" - ast_search: find structural patterns (e.g. error handling, goroutines)",
|
|
34
|
+
"",
|
|
35
|
+
"IMPORTANT: Always specify subagent_type. Use Explore for codebase, Librarian for external docs.",
|
|
36
|
+
"",
|
|
37
|
+
"Produce two files:",
|
|
38
|
+
`- ${taskDir}/USER_REQUEST.md — the fix request. MUST follow this exact structure:`,
|
|
39
|
+
" # User Request",
|
|
40
|
+
" <1-3 sentence distillation of the fix needed>",
|
|
41
|
+
" ## Problem",
|
|
42
|
+
" <What's broken, from the user's perspective / your diagnosis>",
|
|
43
|
+
" ## Constraints",
|
|
44
|
+
" <User-stated boundaries or critical constraints discovered during diagnosis>",
|
|
45
|
+
`- ${taskDir}/RESEARCH.md — MUST follow this exact structure:`,
|
|
46
|
+
" ## Affected Code",
|
|
47
|
+
" <file:symbol — one-line role, per line>",
|
|
48
|
+
" ## Architecture Context",
|
|
49
|
+
" <Dense bullets. How affected pieces connect. Sub-group by subsystem for complex tasks.>",
|
|
50
|
+
" ## Constraints & Edge Cases",
|
|
51
|
+
" - MUST: <hard requirements discovered from code>",
|
|
52
|
+
" - RISK: <things that could break>",
|
|
53
|
+
" ## Open Questions",
|
|
54
|
+
" <Unresolved items needing user input. Omit section if none.>",
|
|
55
|
+
"",
|
|
56
|
+
"These files are validated programmatically. Missing sections or unexpected sections will be rejected.",
|
|
57
|
+
"",
|
|
58
|
+
"# Optional: focused analysis artifacts",
|
|
59
|
+
`You may also write additional analysis files to ${taskDir}/artifacts/<name>.md`,
|
|
60
|
+
"for deep dives on specific topics (e.g. architecture analysis, API comparison, risk assessment).",
|
|
61
|
+
"Each artifact must start with # <Title>. Content is freeform. These are reviewed alongside USER_REQUEST.md and RESEARCH.md.",
|
|
62
|
+
"Do NOT duplicate content already in RESEARCH.md — artifacts are for supplementary deep dives.",
|
|
63
|
+
"",
|
|
64
|
+
"When both required files are complete, call pp_phase_complete with a brief summary.",
|
|
65
|
+
].join("\n");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (taskType === "brainstorm") {
|
|
69
|
+
return [
|
|
70
|
+
"[PI-PI — BRAINSTORM]",
|
|
71
|
+
`Topic: ${taskDescription}`,
|
|
72
|
+
"",
|
|
73
|
+
"# This is a conversation, not a task.",
|
|
74
|
+
"Your primary job is to TALK WITH THE USER. Explore ideas, analyze tradeoffs, answer questions, discuss approaches.",
|
|
75
|
+
"Do NOT rush to produce artifacts or finish. Stay in the conversation until the user is satisfied.",
|
|
76
|
+
"",
|
|
77
|
+
"# How to work:",
|
|
78
|
+
"- Discuss the topic with the user. Ask clarifying questions. Propose approaches. Analyze tradeoffs.",
|
|
79
|
+
'- Spawn subagents for research: Agent(subagent_type="Explore", ...) for codebase, Agent(subagent_type="Librarian", ...) for external docs.',
|
|
80
|
+
" IMPORTANT: Always specify subagent_type. Do NOT omit it.",
|
|
81
|
+
"- Use tools directly for quick lookups (cbm_search, lsp, ast_search, grep, etc.)",
|
|
82
|
+
"- Present findings to the user and discuss them. Don't just dump raw results.",
|
|
83
|
+
"- Do NOT modify project source code.",
|
|
84
|
+
"",
|
|
85
|
+
"# When to finish:",
|
|
86
|
+
"Do NOT call pp_phase_complete on your own. The user will tell you when they're done,",
|
|
87
|
+
"or use /pp to advance. Keep the conversation going until then.",
|
|
88
|
+
"If the user asks you to implement, write code, or start building — tell them to use /pp which will offer \"Start implementation\" as an option. Do NOT implement directly in this session.",
|
|
89
|
+
"",
|
|
90
|
+
"# Optional artifacts (only when the conversation naturally produces them):",
|
|
91
|
+
"If the discussion leads to a clear action plan or the user asks you to capture conclusions,",
|
|
92
|
+
"write them to:",
|
|
93
|
+
`- ${taskDir}/USER_REQUEST.md — MUST use structure: # User Request, ## Problem, ## Constraints`,
|
|
94
|
+
`- ${taskDir}/RESEARCH.md — MUST use structure: ## Affected Code, ## Architecture Context, ## Constraints & Edge Cases, ## Open Questions (optional)`,
|
|
95
|
+
"These files are validated. Missing or unexpected sections will be rejected.",
|
|
96
|
+
"Do NOT create these files preemptively. Only write them when there's substance to capture.",
|
|
97
|
+
"",
|
|
98
|
+
"# Optional: focused analysis artifacts",
|
|
99
|
+
`You may also write additional analysis files to ${taskDir}/artifacts/<name>.md`,
|
|
100
|
+
"for deep dives on specific topics (e.g. architecture analysis, API comparison, risk assessment).",
|
|
101
|
+
"Each artifact must start with # <Title>. Content is freeform. These are reviewed alongside USER_REQUEST.md and RESEARCH.md.",
|
|
102
|
+
"Do NOT duplicate content already in RESEARCH.md — artifacts are for supplementary deep dives.",
|
|
103
|
+
"",
|
|
104
|
+
"Do NOT modify any files except .md files in the task directory.",
|
|
105
|
+
].join("\n");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return [
|
|
109
|
+
"[PI-PI — BRAINSTORM PHASE]",
|
|
110
|
+
`Task: ${taskDescription}`,
|
|
111
|
+
"",
|
|
112
|
+
"Your job is to produce USER_REQUEST.md and RESEARCH.md — complete enough that",
|
|
113
|
+
"downstream agents can work without re-exploring the codebase or re-interviewing the user.",
|
|
114
|
+
"",
|
|
115
|
+
"# FORBIDDEN — do NOT do any of these:",
|
|
116
|
+
"- Do NOT modify project source code (no write or edit tools on project files)",
|
|
117
|
+
"- Do NOT create or modify any files outside the task directory",
|
|
118
|
+
"- Do NOT start implementing — only research and document",
|
|
119
|
+
"- If the user asks you to implement or start coding — tell them to use /pp which will offer phase advancement. Do NOT implement directly in this session.",
|
|
120
|
+
"",
|
|
121
|
+
"# Steps:",
|
|
122
|
+
"1. Clarify requirements with the user if anything is ambiguous",
|
|
123
|
+
'2. Spawn explore subagents via Agent(subagent_type="Explore", ...) for codebase research',
|
|
124
|
+
'3. Spawn librarian subagents via Agent(subagent_type="Librarian", ...) for external docs/library research',
|
|
125
|
+
"4. Use tools to understand code structure:",
|
|
126
|
+
" - cbm_search: natural-language search across all symbols",
|
|
127
|
+
" - cbm_search_code: graph-augmented grep (deduplicates into functions)",
|
|
128
|
+
" - lsp documentSymbol, goToDefinition, findReferences, goToImplementation, hover",
|
|
129
|
+
" - cbm_trace: trace call chains for dependency understanding",
|
|
130
|
+
" - ast_search: find structural patterns across the codebase",
|
|
131
|
+
"5. Ask the user follow-up questions as needed",
|
|
132
|
+
"6. Write findings into RESEARCH.md as results come back — don't wait for all subagents",
|
|
133
|
+
"",
|
|
134
|
+
"IMPORTANT: Always specify subagent_type. Use Explore for codebase, Librarian for external docs.",
|
|
135
|
+
"",
|
|
136
|
+
"Produce two files:",
|
|
137
|
+
`- ${taskDir}/USER_REQUEST.md — MUST follow this exact structure:`,
|
|
138
|
+
" # User Request",
|
|
139
|
+
" <1-3 sentence distillation of what the user wants>",
|
|
140
|
+
" ## Problem",
|
|
141
|
+
" <What's broken / what's missing, in the user's words. Issue link if provided.>",
|
|
142
|
+
" ## Constraints",
|
|
143
|
+
" <Boundaries the user explicitly stated. Only user-stated info, no agent findings.>",
|
|
144
|
+
`- ${taskDir}/RESEARCH.md — MUST follow this exact structure:`,
|
|
145
|
+
" ## Affected Code",
|
|
146
|
+
" <file:symbol — one-line role, per line>",
|
|
147
|
+
" ## Architecture Context",
|
|
148
|
+
" <Dense bullets. How affected pieces connect. Sub-group by subsystem for complex tasks.>",
|
|
149
|
+
" ## Constraints & Edge Cases",
|
|
150
|
+
" - MUST: <hard requirements discovered from code>",
|
|
151
|
+
" - RISK: <things that could break>",
|
|
152
|
+
" ## Open Questions",
|
|
153
|
+
" <Unresolved items needing user input. Omit section if none.>",
|
|
154
|
+
"",
|
|
155
|
+
"These files are validated programmatically. Missing sections or unexpected sections will be rejected.",
|
|
156
|
+
"",
|
|
157
|
+
"# Optional: focused analysis artifacts",
|
|
158
|
+
`You may also write additional analysis files to ${taskDir}/artifacts/<name>.md`,
|
|
159
|
+
"for deep dives on specific topics (e.g. architecture analysis, API comparison, risk assessment).",
|
|
160
|
+
"Each artifact must start with # <Title>. Content is freeform. These are reviewed alongside USER_REQUEST.md and RESEARCH.md.",
|
|
161
|
+
"Do NOT duplicate content already in RESEARCH.md — artifacts are for supplementary deep dives.",
|
|
162
|
+
"",
|
|
163
|
+
"Do NOT modify any files except .md files in the task directory.",
|
|
164
|
+
"When both files are produced and thorough, call pp_phase_complete with a brief summary.",
|
|
165
|
+
].join("\n");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export async function spawnBrainstormReviewers(
|
|
169
|
+
pi: ExtensionAPI,
|
|
170
|
+
cwd: string,
|
|
171
|
+
taskDir: string,
|
|
172
|
+
taskId: string,
|
|
173
|
+
config: PiPiConfig,
|
|
174
|
+
round: number,
|
|
175
|
+
): Promise<{ spawned: number; files: string[]; agentIds: string[]; failedVariants: string[] }> {
|
|
176
|
+
const urPath = join(taskDir, "USER_REQUEST.md");
|
|
177
|
+
const resPath = join(taskDir, "RESEARCH.md");
|
|
178
|
+
if (!existsSync(urPath) || !existsSync(resPath)) return { spawned: 0, files: [], agentIds: [], failedVariants: [] };
|
|
179
|
+
|
|
180
|
+
const userRequest = readFileSync(urPath, "utf-8");
|
|
181
|
+
const research = readFileSync(resPath, "utf-8");
|
|
182
|
+
|
|
183
|
+
const artifactsDir = join(taskDir, "artifacts");
|
|
184
|
+
const artifacts: { name: string; content: string }[] = [];
|
|
185
|
+
if (existsSync(artifactsDir)) {
|
|
186
|
+
for (const f of readdirSync(artifactsDir).filter((f) => f.endsWith(".md")).sort()) {
|
|
187
|
+
artifacts.push({ name: `artifacts/${f}`, content: readFileSync(join(artifactsDir, f), "utf-8") });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const reviewsDir = join(taskDir, "brainstorm-reviews");
|
|
192
|
+
if (!existsSync(reviewsDir)) {
|
|
193
|
+
mkdirSync(reviewsDir, { recursive: true });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
197
|
+
const enabledVariants = Object.entries(config.brainstormReviewers).filter(([, v]) => v.enabled);
|
|
198
|
+
const reviewFiles: string[] = [];
|
|
199
|
+
const agentIds: string[] = [];
|
|
200
|
+
const failedVariants: string[] = [];
|
|
201
|
+
const results: Promise<void>[] = [];
|
|
202
|
+
|
|
203
|
+
for (const [variant] of enabledVariants) {
|
|
204
|
+
const outputPath = join(reviewsDir, `${timestamp}_${variant}_round-${round}.md`);
|
|
205
|
+
reviewFiles.push(outputPath);
|
|
206
|
+
const agent = createBrainstormReviewerAgent(variant, config, { userRequest, research, artifacts: artifacts.length > 0 ? artifacts : undefined }, outputPath);
|
|
207
|
+
|
|
208
|
+
registerAgentDefinitions(pi, [{ type: "brainstorm_reviewer", variant, ...agent }]);
|
|
209
|
+
|
|
210
|
+
results.push(
|
|
211
|
+
(async () => {
|
|
212
|
+
try {
|
|
213
|
+
const { id } = await spawnViaRpc(pi, `brainstorm_reviewer_${variant}`, "Begin brainstorm artifact review.", {
|
|
214
|
+
description: `Brainstorm reviewer (${variant})`,
|
|
215
|
+
validateCompletion: () => {
|
|
216
|
+
if (!existsSync(outputPath) || statSync(outputPath).size === 0) {
|
|
217
|
+
return `You finished without writing your review file. Write your review to: ${outputPath}`;
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
agentIds.push(id);
|
|
222
|
+
await waitForCompletion(pi, id);
|
|
223
|
+
} catch (err: any) {
|
|
224
|
+
failedVariants.push(variant);
|
|
225
|
+
pi.sendMessage(
|
|
226
|
+
{
|
|
227
|
+
customType: "pp-brainstorm-reviewer-error",
|
|
228
|
+
content: `Brainstorm reviewer variant "${variant}" failed: ${err.message}`,
|
|
229
|
+
display: true,
|
|
230
|
+
},
|
|
231
|
+
{ deliverAs: "steer" },
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
})(),
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
await Promise.allSettled(results);
|
|
239
|
+
|
|
240
|
+
const reviewOutputFiles = existsSync(reviewsDir)
|
|
241
|
+
? readdirSync(reviewsDir).filter((f) => f.includes(`round-${round}`) && f.endsWith(".md"))
|
|
242
|
+
: [];
|
|
243
|
+
|
|
244
|
+
if (reviewOutputFiles.length > 0) {
|
|
245
|
+
pi.sendMessage(
|
|
246
|
+
{
|
|
247
|
+
customType: "pp-brainstorm-reviews-done",
|
|
248
|
+
content: [
|
|
249
|
+
`${reviewOutputFiles.length} brainstorm reviewer(s) completed (round ${round}). Reviews in ${reviewsDir}:`,
|
|
250
|
+
...reviewOutputFiles.map((f) => ` - ${f}`),
|
|
251
|
+
"",
|
|
252
|
+
"Read all reviews and update USER_REQUEST.md and RESEARCH.md if needed.",
|
|
253
|
+
].join("\n"),
|
|
254
|
+
display: true,
|
|
255
|
+
},
|
|
256
|
+
{ deliverAs: "steer" },
|
|
257
|
+
);
|
|
258
|
+
} else if (enabledVariants.length > 0) {
|
|
259
|
+
pi.sendMessage(
|
|
260
|
+
{
|
|
261
|
+
customType: "pp-brainstorm-reviews-error",
|
|
262
|
+
content: [
|
|
263
|
+
`All brainstorm reviewer variants failed (round ${round}) — no reviews were produced.`,
|
|
264
|
+
"Proceeding without automatic brainstorm review.",
|
|
265
|
+
].join("\n"),
|
|
266
|
+
display: true,
|
|
267
|
+
},
|
|
268
|
+
{ deliverAs: "steer" },
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return { spawned: enabledVariants.length, files: reviewFiles, agentIds, failedVariants };
|
|
273
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function implementationSystemPrompt(taskDir: string): string {
|
|
2
|
+
return [
|
|
3
|
+
"[PI-PI — IMPLEMENT PHASE]",
|
|
4
|
+
"",
|
|
5
|
+
"The plan has been approved. Implement it.",
|
|
6
|
+
"",
|
|
7
|
+
"# FORBIDDEN — do NOT do any of these:",
|
|
8
|
+
"- Do NOT skip plan items without the user's approval",
|
|
9
|
+
"",
|
|
10
|
+
"USER_REQUEST.md, RESEARCH.md, and the synthesized plan are already provided in your context above — do NOT re-read them from disk.",
|
|
11
|
+
"",
|
|
12
|
+
"# Instructions:",
|
|
13
|
+
"1. Review the synthesized plan in your context",
|
|
14
|
+
"2. Implement each item in order",
|
|
15
|
+
"3. Check off items in the plan (change - [ ] to - [x]) as you complete them",
|
|
16
|
+
"4. After editing files, run lsp diagnostics to check for errors; use lsp codeActions for auto-fixes",
|
|
17
|
+
"5. Before modifying code, understand it first:",
|
|
18
|
+
" - cbm_search/cbm_search_code: find relevant functions and their context",
|
|
19
|
+
" - lsp goToDefinition, findReferences, hover: precise navigation",
|
|
20
|
+
" - lsp goToImplementation: check interface implementors",
|
|
21
|
+
" - lsp incomingCalls/outgoingCalls: understand call chains",
|
|
22
|
+
" - cbm_trace: trace dependencies across the codebase",
|
|
23
|
+
" - ast_search: find structural patterns (e.g. error handling conventions)",
|
|
24
|
+
"6. After completing changes, run cbm_changes to verify blast radius",
|
|
25
|
+
'7. For parallelizable, self-contained subtasks, delegate via Agent(subagent_type="Task", ...)',
|
|
26
|
+
'8. For codebase research, use Agent(subagent_type="Explore", ...)',
|
|
27
|
+
'9. For external docs research, use Agent(subagent_type="Librarian", ...)',
|
|
28
|
+
"",
|
|
29
|
+
"If a fix fails 3 times, STOP and re-plan the approach — do not keep pushing.",
|
|
30
|
+
"",
|
|
31
|
+
"# Commits:",
|
|
32
|
+
"After completing a logical unit of work (a plan item, a bug fix, a test), call pp_commit",
|
|
33
|
+
"with a descriptive message (what changed and why). Don't batch all changes into one commit.",
|
|
34
|
+
"",
|
|
35
|
+
"Constraints:",
|
|
36
|
+
"- Fix issues found by lsp diagnostics before moving on",
|
|
37
|
+
"",
|
|
38
|
+
"When all plan items are checked off, call pp_phase_complete with a brief summary.",
|
|
39
|
+
].join("\n");
|
|
40
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
2
|
+
import { tmpdir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { canTransition, nextPhase, phasePipeline, validateExitCriteria } from "./machine.js";
|
|
6
|
+
|
|
7
|
+
const tempDirs: string[] = [];
|
|
8
|
+
|
|
9
|
+
const VALID_USER_REQUEST = `# User Request
|
|
10
|
+
Fix the auth bug.
|
|
11
|
+
|
|
12
|
+
## Problem
|
|
13
|
+
Auth tokens expire incorrectly.
|
|
14
|
+
|
|
15
|
+
## Constraints
|
|
16
|
+
Must be backward compatible.
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
const VALID_RESEARCH = `## Affected Code
|
|
20
|
+
src/auth.ts:validateToken — validates JWT tokens
|
|
21
|
+
|
|
22
|
+
## Architecture Context
|
|
23
|
+
- Auth middleware calls validateToken on every request
|
|
24
|
+
|
|
25
|
+
## Constraints & Edge Cases
|
|
26
|
+
- MUST: Existing tokens must remain valid
|
|
27
|
+
- RISK: Token refresh flow may break
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
function makeValidPlan(checklist: string[]): string {
|
|
31
|
+
return `# Plan
|
|
32
|
+
|
|
33
|
+
## Scope
|
|
34
|
+
Fix token validation in auth middleware. Does not change token format.
|
|
35
|
+
|
|
36
|
+
## Checklist
|
|
37
|
+
${checklist.join("\n")}
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function makeTempDir(): string {
|
|
42
|
+
const dir = mkdtempSync(join(tmpdir(), "pi-pi-machine-"));
|
|
43
|
+
tempDirs.push(dir);
|
|
44
|
+
return dir;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
afterEach(() => {
|
|
48
|
+
while (tempDirs.length > 0) {
|
|
49
|
+
const dir = tempDirs.pop();
|
|
50
|
+
if (dir) rmSync(dir, { recursive: true, force: true });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("canTransition", () => {
|
|
55
|
+
it("handles implement transitions", () => {
|
|
56
|
+
expect(canTransition("implement", "brainstorm", "plan")).toBe(true);
|
|
57
|
+
expect(canTransition("implement", "plan", "implement")).toBe(true);
|
|
58
|
+
expect(canTransition("implement", "implement", "done")).toBe(true);
|
|
59
|
+
|
|
60
|
+
expect(canTransition("implement", "brainstorm", "done")).toBe(false);
|
|
61
|
+
expect(canTransition("implement", "plan", "done")).toBe(false);
|
|
62
|
+
expect(canTransition("implement", "done", "implement")).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("handles debug transitions", () => {
|
|
66
|
+
expect(canTransition("debug", "debug", "done")).toBe(true);
|
|
67
|
+
expect(canTransition("debug", "debug", "plan")).toBe(false);
|
|
68
|
+
expect(canTransition("debug", "done", "debug")).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("handles brainstorm transitions", () => {
|
|
72
|
+
expect(canTransition("brainstorm", "brainstorm", "done")).toBe(true);
|
|
73
|
+
expect(canTransition("brainstorm", "brainstorm", "plan")).toBe(false);
|
|
74
|
+
expect(canTransition("brainstorm", "done", "brainstorm")).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("nextPhase", () => {
|
|
79
|
+
it("returns implement next phase and terminal null", () => {
|
|
80
|
+
expect(nextPhase("implement", "brainstorm")).toBe("plan");
|
|
81
|
+
expect(nextPhase("implement", "plan")).toBe("implement");
|
|
82
|
+
expect(nextPhase("implement", "implement")).toBe("done");
|
|
83
|
+
expect(nextPhase("implement", "done")).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("returns debug next phase and terminal null", () => {
|
|
87
|
+
expect(nextPhase("debug", "debug")).toBe("done");
|
|
88
|
+
expect(nextPhase("debug", "done")).toBeNull();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("returns brainstorm next phase and terminal null", () => {
|
|
92
|
+
expect(nextPhase("brainstorm", "brainstorm")).toBe("done");
|
|
93
|
+
expect(nextPhase("brainstorm", "done")).toBeNull();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("phasePipeline", () => {
|
|
98
|
+
it("returns implement pipeline", () => {
|
|
99
|
+
expect(phasePipeline("implement")).toEqual(["brainstorm", "plan", "implement", "done"]);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("returns debug pipeline", () => {
|
|
103
|
+
expect(phasePipeline("debug")).toEqual(["debug", "done"]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("returns brainstorm pipeline", () => {
|
|
107
|
+
expect(phasePipeline("brainstorm")).toEqual(["brainstorm", "done"]);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe("validateExitCriteria", () => {
|
|
112
|
+
it("validates brainstorm artifacts", () => {
|
|
113
|
+
const missing = makeTempDir();
|
|
114
|
+
expect(validateExitCriteria(missing, "implement", "brainstorm")).toEqual({
|
|
115
|
+
ok: false,
|
|
116
|
+
reason: "USER_REQUEST.md does not exist or is empty",
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const emptyResearch = makeTempDir();
|
|
120
|
+
writeFileSync(join(emptyResearch, "USER_REQUEST.md"), VALID_USER_REQUEST, "utf-8");
|
|
121
|
+
writeFileSync(join(emptyResearch, "RESEARCH.md"), "\n \n", "utf-8");
|
|
122
|
+
expect(validateExitCriteria(emptyResearch, "implement", "brainstorm")).toEqual({
|
|
123
|
+
ok: false,
|
|
124
|
+
reason: "RESEARCH.md does not exist or is empty",
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const pass = makeTempDir();
|
|
128
|
+
writeFileSync(join(pass, "USER_REQUEST.md"), VALID_USER_REQUEST, "utf-8");
|
|
129
|
+
writeFileSync(join(pass, "RESEARCH.md"), VALID_RESEARCH, "utf-8");
|
|
130
|
+
expect(validateExitCriteria(pass, "implement", "brainstorm")).toEqual({ ok: true });
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("validates plan artifacts", () => {
|
|
134
|
+
const missing = makeTempDir();
|
|
135
|
+
expect(validateExitCriteria(missing, "implement", "plan")).toEqual({
|
|
136
|
+
ok: false,
|
|
137
|
+
reason: "No synthesized plan found in plans/",
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const noSynth = makeTempDir();
|
|
141
|
+
mkdirSync(join(noSynth, "plans"), { recursive: true });
|
|
142
|
+
writeFileSync(join(noSynth, "plans", "draft.md"), "draft", "utf-8");
|
|
143
|
+
expect(validateExitCriteria(noSynth, "implement", "plan")).toEqual({
|
|
144
|
+
ok: false,
|
|
145
|
+
reason: "No synthesized plan found in plans/",
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const pass = makeTempDir();
|
|
149
|
+
mkdirSync(join(pass, "plans"), { recursive: true });
|
|
150
|
+
writeFileSync(
|
|
151
|
+
join(pass, "plans", "synthesized-plan.md"),
|
|
152
|
+
makeValidPlan(["- [ ] P1. Fix token expiry check — Done when: expired tokens are rejected consistently"]),
|
|
153
|
+
"utf-8",
|
|
154
|
+
);
|
|
155
|
+
expect(validateExitCriteria(pass, "implement", "plan")).toEqual({ ok: true });
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("validates implement checkboxes", () => {
|
|
159
|
+
const missing = makeTempDir();
|
|
160
|
+
expect(validateExitCriteria(missing, "implement", "implement")).toEqual({
|
|
161
|
+
ok: false,
|
|
162
|
+
reason: "No synthesized plan found",
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const noSynth = makeTempDir();
|
|
166
|
+
mkdirSync(join(noSynth, "plans"), { recursive: true });
|
|
167
|
+
writeFileSync(join(noSynth, "plans", "plan.md"), "content", "utf-8");
|
|
168
|
+
expect(validateExitCriteria(noSynth, "implement", "implement")).toEqual({
|
|
169
|
+
ok: false,
|
|
170
|
+
reason: "No synthesized plan found",
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const unchecked = makeTempDir();
|
|
174
|
+
mkdirSync(join(unchecked, "plans"), { recursive: true });
|
|
175
|
+
writeFileSync(
|
|
176
|
+
join(unchecked, "plans", "2026-04-20_synthesized.md"),
|
|
177
|
+
makeValidPlan([
|
|
178
|
+
"- [ ] P1. First item — Done when: first item remains unchecked",
|
|
179
|
+
"- [ ] P2. Second item — Done when: second item remains unchecked",
|
|
180
|
+
"- [x] P3. Completed item — Done when: completed item is checked",
|
|
181
|
+
]),
|
|
182
|
+
"utf-8",
|
|
183
|
+
);
|
|
184
|
+
expect(validateExitCriteria(unchecked, "implement", "implement")).toEqual({
|
|
185
|
+
ok: false,
|
|
186
|
+
reason: "2 plan items still unchecked",
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const checked = makeTempDir();
|
|
190
|
+
mkdirSync(join(checked, "plans"), { recursive: true });
|
|
191
|
+
writeFileSync(
|
|
192
|
+
join(checked, "plans", "synthesized-plan.md"),
|
|
193
|
+
makeValidPlan(["- [x] P1. Done item — Done when: all checklist items are checked"]),
|
|
194
|
+
"utf-8",
|
|
195
|
+
);
|
|
196
|
+
expect(validateExitCriteria(checked, "implement", "implement")).toEqual({ ok: true });
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("picks the numerically latest synthesized plan for implement validation", () => {
|
|
200
|
+
const dir = makeTempDir();
|
|
201
|
+
mkdirSync(join(dir, "plans"), { recursive: true });
|
|
202
|
+
writeFileSync(
|
|
203
|
+
join(dir, "plans", "2_synthesized.md"),
|
|
204
|
+
makeValidPlan(["- [x] P1. Older checked item — Done when: old plan stays checked"]),
|
|
205
|
+
"utf-8",
|
|
206
|
+
);
|
|
207
|
+
writeFileSync(
|
|
208
|
+
join(dir, "plans", "11_synthesized.md"),
|
|
209
|
+
makeValidPlan(["- [ ] P1. Latest unchecked item — Done when: latest plan has an unchecked item"]),
|
|
210
|
+
"utf-8",
|
|
211
|
+
);
|
|
212
|
+
expect(validateExitCriteria(dir, "implement", "implement")).toEqual({
|
|
213
|
+
ok: false,
|
|
214
|
+
reason: "1 plan items still unchecked",
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("passes implement validation when the numerically latest plan is fully checked", () => {
|
|
219
|
+
const dir = makeTempDir();
|
|
220
|
+
mkdirSync(join(dir, "plans"), { recursive: true });
|
|
221
|
+
writeFileSync(
|
|
222
|
+
join(dir, "plans", "2_synthesized.md"),
|
|
223
|
+
makeValidPlan(["- [ ] P1. Older unchecked item — Done when: old plan keeps one unchecked item"]),
|
|
224
|
+
"utf-8",
|
|
225
|
+
);
|
|
226
|
+
writeFileSync(
|
|
227
|
+
join(dir, "plans", "11_synthesized.md"),
|
|
228
|
+
makeValidPlan(["- [x] P1. Latest checked item — Done when: latest plan is fully checked"]),
|
|
229
|
+
"utf-8",
|
|
230
|
+
);
|
|
231
|
+
expect(validateExitCriteria(dir, "implement", "implement")).toEqual({ ok: true });
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("counts unchecked items only within checklist section", () => {
|
|
235
|
+
const dir = makeTempDir();
|
|
236
|
+
mkdirSync(join(dir, "plans"), { recursive: true });
|
|
237
|
+
writeFileSync(
|
|
238
|
+
join(dir, "plans", "synthesized-plan.md"),
|
|
239
|
+
`# Plan
|
|
240
|
+
|
|
241
|
+
## Scope
|
|
242
|
+
Fix bug.
|
|
243
|
+
|
|
244
|
+
## Checklist
|
|
245
|
+
- [x] Main task — Done when: implemented
|
|
246
|
+
|
|
247
|
+
## Blockers
|
|
248
|
+
- [ ] waiting on external team
|
|
249
|
+
`,
|
|
250
|
+
"utf-8",
|
|
251
|
+
);
|
|
252
|
+
expect(validateExitCriteria(dir, "implement", "implement")).toEqual({ ok: true });
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("handles brainstorm phase for brainstorm task — always passes", () => {
|
|
256
|
+
const dir = makeTempDir();
|
|
257
|
+
expect(validateExitCriteria(dir, "brainstorm", "brainstorm")).toEqual({ ok: true });
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("validates brainstorm phase for implement task — requires artifacts", () => {
|
|
261
|
+
const missing = makeTempDir();
|
|
262
|
+
expect(validateExitCriteria(missing, "implement", "brainstorm")).toEqual({
|
|
263
|
+
ok: false,
|
|
264
|
+
reason: "USER_REQUEST.md does not exist or is empty",
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const pass = makeTempDir();
|
|
268
|
+
writeFileSync(join(pass, "USER_REQUEST.md"), VALID_USER_REQUEST, "utf-8");
|
|
269
|
+
writeFileSync(join(pass, "RESEARCH.md"), VALID_RESEARCH, "utf-8");
|
|
270
|
+
expect(validateExitCriteria(pass, "implement", "brainstorm")).toEqual({ ok: true });
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("validates debug artifacts", () => {
|
|
274
|
+
const missing = makeTempDir();
|
|
275
|
+
expect(validateExitCriteria(missing, "debug", "debug")).toEqual({
|
|
276
|
+
ok: false,
|
|
277
|
+
reason: "USER_REQUEST.md does not exist or is empty",
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const pass = makeTempDir();
|
|
281
|
+
writeFileSync(join(pass, "USER_REQUEST.md"), VALID_USER_REQUEST, "utf-8");
|
|
282
|
+
writeFileSync(join(pass, "RESEARCH.md"), VALID_RESEARCH, "utf-8");
|
|
283
|
+
expect(validateExitCriteria(pass, "debug", "debug")).toEqual({ ok: true });
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("returns unknown phase error for done", () => {
|
|
287
|
+
const dir = makeTempDir();
|
|
288
|
+
expect(validateExitCriteria(dir, "implement", "done")).toEqual({
|
|
289
|
+
ok: false,
|
|
290
|
+
reason: "Unknown phase: done",
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
});
|