@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,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CODE_PATH_BARE_REGEX,
|
|
3
|
+
isCodeFilePath,
|
|
4
|
+
isCodeFilePathStrict,
|
|
5
|
+
} from "./code-file";
|
|
6
|
+
|
|
7
|
+
const FENCED_CODE_BLOCK = /(^|\n)([ \t]*)(```|~~~)[\s\S]*?\n\2\3/g;
|
|
8
|
+
const HTML_COMMENT = /<!--[\s\S]*?-->/g;
|
|
9
|
+
// Match InlineMarkdown.tsx's bare-URL regex exactly so URL ranges excised
|
|
10
|
+
// here mirror the ranges the renderer would consume.
|
|
11
|
+
const URL_REGEX = /https?:\/\/[^\s<>"']+/g;
|
|
12
|
+
const BACKTICK_SPAN = /`([^`\n]+)`/g;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Extract candidate code-file paths from markdown text. Mirrors the renderer's
|
|
16
|
+
* detection precedence so the validator only sees paths the renderer would
|
|
17
|
+
* actually linkify:
|
|
18
|
+
* 1. fenced code blocks and HTML comments are stripped first;
|
|
19
|
+
* 2. URL ranges are excised before the bare-prose scan (URLs win);
|
|
20
|
+
* 3. backtick spans matching `isCodeFilePath` are collected;
|
|
21
|
+
* 4. bare-prose paths matching `CODE_PATH_BARE_REGEX` and
|
|
22
|
+
* `isCodeFilePathStrict` are collected.
|
|
23
|
+
*
|
|
24
|
+
* Hash anchors (`#L42`) are stripped from results to match the renderer's
|
|
25
|
+
* `cleanPath` transform. Returns deduped candidate strings.
|
|
26
|
+
*/
|
|
27
|
+
export function extractCandidateCodePaths(markdown: string): string[] {
|
|
28
|
+
const stripped = markdown
|
|
29
|
+
.replace(FENCED_CODE_BLOCK, "")
|
|
30
|
+
.replace(HTML_COMMENT, "");
|
|
31
|
+
|
|
32
|
+
const candidates = new Set<string>();
|
|
33
|
+
|
|
34
|
+
let m: RegExpExecArray | null;
|
|
35
|
+
const backtickRe = new RegExp(BACKTICK_SPAN.source, "g");
|
|
36
|
+
while ((m = backtickRe.exec(stripped)) !== null) {
|
|
37
|
+
const inner = m[1].trim();
|
|
38
|
+
if (isCodeFilePath(inner)) {
|
|
39
|
+
candidates.add(inner.replace(/#.*$/, ""));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const line of stripped.split("\n")) {
|
|
44
|
+
const urlRanges: Array<[number, number]> = [];
|
|
45
|
+
const urlRe = new RegExp(URL_REGEX.source, "g");
|
|
46
|
+
while ((m = urlRe.exec(line)) !== null) {
|
|
47
|
+
urlRanges.push([m.index, m.index + m[0].length]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const pathRe = new RegExp(CODE_PATH_BARE_REGEX.source, "g");
|
|
51
|
+
while ((m = pathRe.exec(line)) !== null) {
|
|
52
|
+
const start = m.index;
|
|
53
|
+
const end = start + m[0].length;
|
|
54
|
+
const prev = start === 0 ? "" : line[start - 1];
|
|
55
|
+
if (/\w/.test(prev)) continue;
|
|
56
|
+
const overlapsUrl = urlRanges.some(
|
|
57
|
+
([s, e]) => start < e && end > s,
|
|
58
|
+
);
|
|
59
|
+
if (overlapsUrl) continue;
|
|
60
|
+
if (!isCodeFilePathStrict(m[0])) continue;
|
|
61
|
+
candidates.add(m[0].replace(/#.*$/, ""));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return Array.from(candidates);
|
|
66
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const FAVICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
2
|
+
<rect width="64" height="64" rx="14" fill="#070b14"/>
|
|
3
|
+
<rect x="12" y="28" width="40" height="14" rx="3" fill="#E0BA55" opacity="0.35"/>
|
|
4
|
+
<text x="32" y="46" text-anchor="middle" font-family="Inter,system-ui,sans-serif" font-weight="800" font-size="42" fill="white">P</text>
|
|
5
|
+
</svg>`;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { planDenyFeedback } from "./feedback-templates";
|
|
3
|
+
|
|
4
|
+
describe("feedback-templates", () => {
|
|
5
|
+
/**
|
|
6
|
+
* The whole point of this module: all three integrations (hook, opencode, pi)
|
|
7
|
+
* produce identical output except for the tool name. If this test fails,
|
|
8
|
+
* the templates have diverged — which is what we're trying to prevent.
|
|
9
|
+
*/
|
|
10
|
+
test("plan deny is identical across integrations (modulo tool name)", () => {
|
|
11
|
+
const normalize = (s: string) =>
|
|
12
|
+
s.replace(/ExitPlanMode|submit_plan|exit_plan_mode|plannotator_submit_plan/g, "TOOL");
|
|
13
|
+
|
|
14
|
+
const feedback = "## 1. Remove auth section\n> Not needed anymore.";
|
|
15
|
+
const hook = normalize(planDenyFeedback(feedback, "ExitPlanMode"));
|
|
16
|
+
const opencode = normalize(planDenyFeedback(feedback, "submit_plan"));
|
|
17
|
+
const pi = normalize(planDenyFeedback(feedback, "plannotator_submit_plan"));
|
|
18
|
+
|
|
19
|
+
expect(hook).toBe(opencode);
|
|
20
|
+
expect(opencode).toBe(pi);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The deny template must embed the user's feedback verbatim — no truncation,
|
|
25
|
+
* no escaping, no wrapping. The agent needs the raw annotation output.
|
|
26
|
+
*/
|
|
27
|
+
test("plan deny preserves feedback content verbatim", () => {
|
|
28
|
+
const feedback = "## 1. Change auth\n**From:**\n```\nold code\n```\n**To:**\n```\nnew code\n```";
|
|
29
|
+
const result = planDenyFeedback(feedback);
|
|
30
|
+
expect(result).toContain(feedback);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Empty feedback should not produce a broken message — the agent needs
|
|
35
|
+
* something actionable even if the user didn't write annotations.
|
|
36
|
+
*/
|
|
37
|
+
test("plan deny handles empty feedback gracefully", () => {
|
|
38
|
+
const result = planDenyFeedback("");
|
|
39
|
+
expect(result.length).toBeGreaterThan(50);
|
|
40
|
+
expect(result).toBe(result.trimEnd());
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Version history is keyed by the plan's first # heading + date.
|
|
45
|
+
* If the agent renames the heading on resubmission, the version chain breaks
|
|
46
|
+
* and the user loses diffs (#296). The deny template must instruct the agent
|
|
47
|
+
* to preserve the title.
|
|
48
|
+
*/
|
|
49
|
+
test("plan deny instructs agent to preserve plan title", () => {
|
|
50
|
+
const result = planDenyFeedback("feedback");
|
|
51
|
+
expect(result.toLowerCase()).toContain("title");
|
|
52
|
+
expect(result.toLowerCase()).toContain("heading");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("plan deny can include a plan file hint for file-based integrations", () => {
|
|
56
|
+
const result = planDenyFeedback("feedback", "plannotator_submit_plan", {
|
|
57
|
+
planFilePath: "plans/auth.md",
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(result).toContain("plans/auth.md");
|
|
61
|
+
expect(result).toContain("edit this file");
|
|
62
|
+
expect(result).toContain("plannotator_submit_plan");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared feedback templates for all agent integrations.
|
|
3
|
+
*
|
|
4
|
+
* The plan deny template was tuned in #224 / commit 3dca977 to use strong
|
|
5
|
+
* directive framing — Claude was ignoring softer phrasing.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: This module is imported by packages/ui/utils/parser.ts which is
|
|
8
|
+
* bundled into the browser SPA. It must NOT import from ./prompts or ./config
|
|
9
|
+
* (which depend on node:fs, node:os, node:child_process). Keep it self-contained.
|
|
10
|
+
*
|
|
11
|
+
* Server-side call sites use getPlanDeniedPrompt() from ./prompts directly.
|
|
12
|
+
* This module is only kept for the browser's wrapFeedbackForAgent clipboard feature.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export interface PlanDenyFeedbackOptions {
|
|
16
|
+
planFilePath?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const planDenyFeedback = (
|
|
20
|
+
feedback: string,
|
|
21
|
+
toolName: string = "ExitPlanMode",
|
|
22
|
+
options?: PlanDenyFeedbackOptions,
|
|
23
|
+
): string => {
|
|
24
|
+
const planFileRule = options?.planFilePath
|
|
25
|
+
? `- Your plan is saved at: ${options.planFilePath}\n You can edit this file to make targeted changes, then pass its path to ${toolName}.\n`
|
|
26
|
+
: "";
|
|
27
|
+
|
|
28
|
+
return `YOUR PLAN WAS NOT APPROVED.\n\nYou MUST revise the plan to address ALL of the feedback below before calling ${toolName} again.\n\nRules:\n${planFileRule}- Do not resubmit the same plan unchanged.\n- Do NOT change the plan title (first # heading) unless the user explicitly asks you to.\n\n${feedback || "Plan changes requested"}`;
|
|
29
|
+
};
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
createFactsResult,
|
|
4
|
+
createInterviewResult,
|
|
5
|
+
factsResultToMarkdown,
|
|
6
|
+
filterReviewableFacts,
|
|
7
|
+
normalizeFactsBundle,
|
|
8
|
+
normalizeGoalSetupBundle,
|
|
9
|
+
normalizeInterviewBundle,
|
|
10
|
+
} from "./goal-setup";
|
|
11
|
+
|
|
12
|
+
describe("goal setup model", () => {
|
|
13
|
+
test("normalizes bundled interview questions", () => {
|
|
14
|
+
const bundle = normalizeInterviewBundle({
|
|
15
|
+
title: "Goal setup",
|
|
16
|
+
questions: [
|
|
17
|
+
{
|
|
18
|
+
id: "scope",
|
|
19
|
+
prompt: "What is in scope?",
|
|
20
|
+
answerMode: "multi-custom",
|
|
21
|
+
options: [{ id: "ui", label: "UI" }],
|
|
22
|
+
recommendedAnswer: "Ship the interactive flow.",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(bundle.stage).toBe("interview");
|
|
28
|
+
expect(bundle.questions[0]).toMatchObject({
|
|
29
|
+
id: "scope",
|
|
30
|
+
answerMode: "multi-custom",
|
|
31
|
+
recommendedAnswer: "Ship the interactive flow.",
|
|
32
|
+
required: true,
|
|
33
|
+
});
|
|
34
|
+
expect(bundle.questions[0].options?.[0].label).toBe("UI");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("creates complete interview result from selected options and text", () => {
|
|
38
|
+
const bundle = normalizeGoalSetupBundle(
|
|
39
|
+
{
|
|
40
|
+
stage: "interview",
|
|
41
|
+
questions: [
|
|
42
|
+
{ id: "audience", prompt: "Who uses this?", answerMode: "single" },
|
|
43
|
+
{ id: "risk", prompt: "Main risk?" },
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
"interview"
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (bundle.stage !== "interview") throw new Error("expected interview");
|
|
50
|
+
|
|
51
|
+
const result = createInterviewResult(bundle, [
|
|
52
|
+
{
|
|
53
|
+
questionId: "audience",
|
|
54
|
+
selectedOptionIds: ["agents"],
|
|
55
|
+
customAnswer: "",
|
|
56
|
+
answer: "Agents",
|
|
57
|
+
completed: false,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
questionId: "risk",
|
|
61
|
+
selectedOptionIds: [],
|
|
62
|
+
customAnswer: "",
|
|
63
|
+
answer: "Slow one-at-a-time prompting",
|
|
64
|
+
completed: false,
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
expect(result.answers.map((answer) => answer.completed)).toEqual([
|
|
69
|
+
true,
|
|
70
|
+
true,
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("preserves skipped interview answers with notes", () => {
|
|
75
|
+
const bundle = normalizeGoalSetupBundle(
|
|
76
|
+
{
|
|
77
|
+
stage: "interview",
|
|
78
|
+
questions: [
|
|
79
|
+
{ id: "scope", prompt: "What is out of scope?" },
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
"interview"
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (bundle.stage !== "interview") throw new Error("expected interview");
|
|
86
|
+
|
|
87
|
+
const result = createInterviewResult(bundle, [
|
|
88
|
+
{
|
|
89
|
+
questionId: "scope",
|
|
90
|
+
selectedOptionIds: [],
|
|
91
|
+
customAnswer: "",
|
|
92
|
+
answer: "",
|
|
93
|
+
note: "I need more context before answering.",
|
|
94
|
+
completed: false,
|
|
95
|
+
skipped: true,
|
|
96
|
+
},
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
expect(result.answers[0]).toMatchObject({
|
|
100
|
+
questionId: "scope",
|
|
101
|
+
completed: false,
|
|
102
|
+
skipped: true,
|
|
103
|
+
note: "I need more context before answering.",
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("filters previously accepted facts unless the bundle opts in", () => {
|
|
108
|
+
const bundle = normalizeFactsBundle({
|
|
109
|
+
stage: "facts",
|
|
110
|
+
facts: [
|
|
111
|
+
{ id: "old", text: "Already accepted.", accepted: true },
|
|
112
|
+
{ id: "new", text: "Needs review." },
|
|
113
|
+
],
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
expect(filterReviewableFacts(bundle).map((fact) => fact.id)).toEqual([
|
|
117
|
+
"new",
|
|
118
|
+
]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("preserves recommended automated verification as preselected state", () => {
|
|
122
|
+
const bundle = normalizeFactsBundle({
|
|
123
|
+
stage: "facts",
|
|
124
|
+
facts: [
|
|
125
|
+
{
|
|
126
|
+
id: "build",
|
|
127
|
+
text: "The build must pass.",
|
|
128
|
+
recommendedAutomatedVerification: true,
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
expect(bundle.facts[0].automatedVerification).toBe(true);
|
|
134
|
+
expect(bundle.facts[0].recommendedAutomatedVerification).toBe(true);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("serializes accepted facts as flat markdown while metadata stays structured", () => {
|
|
138
|
+
const markdown = factsResultToMarkdown([
|
|
139
|
+
{
|
|
140
|
+
id: "fact-1",
|
|
141
|
+
text: "The skill batches all interview questions.",
|
|
142
|
+
accepted: true,
|
|
143
|
+
removed: false,
|
|
144
|
+
comment: "Important for speed.",
|
|
145
|
+
automatedVerification: true,
|
|
146
|
+
recommendedAutomatedVerification: true,
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: "fact-2",
|
|
150
|
+
text: "A removed fact is omitted.",
|
|
151
|
+
accepted: true,
|
|
152
|
+
removed: true,
|
|
153
|
+
automatedVerification: false,
|
|
154
|
+
},
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
expect(markdown).toContain(
|
|
158
|
+
"- The skill batches all interview questions."
|
|
159
|
+
);
|
|
160
|
+
expect(markdown).not.toContain("Automated verification");
|
|
161
|
+
expect(markdown).not.toContain("Comment:");
|
|
162
|
+
expect(markdown).not.toContain("A removed fact is omitted");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("merges facts result against bundle order", () => {
|
|
166
|
+
const bundle = normalizeFactsBundle({
|
|
167
|
+
stage: "facts",
|
|
168
|
+
facts: [
|
|
169
|
+
{ id: "a", text: "A" },
|
|
170
|
+
{ id: "b", text: "B", recommendedAutomatedVerification: true },
|
|
171
|
+
],
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const result = createFactsResult(bundle, [
|
|
175
|
+
{
|
|
176
|
+
id: "b",
|
|
177
|
+
text: "B edited",
|
|
178
|
+
accepted: true,
|
|
179
|
+
removed: false,
|
|
180
|
+
automatedVerification: false,
|
|
181
|
+
},
|
|
182
|
+
]);
|
|
183
|
+
|
|
184
|
+
expect(result.facts.map((fact) => fact.id)).toEqual(["a", "b"]);
|
|
185
|
+
expect(result.facts[1]).toMatchObject({
|
|
186
|
+
text: "B edited",
|
|
187
|
+
accepted: true,
|
|
188
|
+
automatedVerification: false,
|
|
189
|
+
recommendedAutomatedVerification: true,
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("allows submitted facts to clear existing comments", () => {
|
|
194
|
+
const bundle = normalizeFactsBundle({
|
|
195
|
+
stage: "facts",
|
|
196
|
+
facts: [{ id: "a", text: "A", comment: "Needs detail." }],
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const result = createFactsResult(bundle, [
|
|
200
|
+
{
|
|
201
|
+
id: "a",
|
|
202
|
+
text: "A",
|
|
203
|
+
accepted: true,
|
|
204
|
+
removed: false,
|
|
205
|
+
comment: "",
|
|
206
|
+
automatedVerification: false,
|
|
207
|
+
},
|
|
208
|
+
]);
|
|
209
|
+
|
|
210
|
+
expect(result.facts[0].comment).toBeUndefined();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("rejects blank live fact text", () => {
|
|
214
|
+
const bundle = normalizeFactsBundle({
|
|
215
|
+
stage: "facts",
|
|
216
|
+
facts: [{ id: "a", text: "A" }],
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
expect(() =>
|
|
220
|
+
createFactsResult(bundle, [
|
|
221
|
+
{
|
|
222
|
+
id: "a",
|
|
223
|
+
text: " ",
|
|
224
|
+
accepted: true,
|
|
225
|
+
removed: false,
|
|
226
|
+
automatedVerification: false,
|
|
227
|
+
},
|
|
228
|
+
])
|
|
229
|
+
).toThrow('Fact "a" text cannot be empty');
|
|
230
|
+
});
|
|
231
|
+
});
|