@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,318 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
6
|
+
|
|
7
|
+
export type PhaseName = "planning" | "executing" | "reviewing";
|
|
8
|
+
export type RuntimePhase = PhaseName | "idle";
|
|
9
|
+
|
|
10
|
+
export interface PhaseModelRef {
|
|
11
|
+
provider: string;
|
|
12
|
+
id: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Config values loaded from JSON can intentionally clear inherited values.
|
|
17
|
+
*
|
|
18
|
+
* - `null` clears a value from a parent config.
|
|
19
|
+
* - `[]` clears active tools.
|
|
20
|
+
* - `""` clears string values.
|
|
21
|
+
*/
|
|
22
|
+
export interface PhaseProfile {
|
|
23
|
+
model?: PhaseModelRef | null;
|
|
24
|
+
thinking?: ThinkingLevel | null;
|
|
25
|
+
activeTools?: string[] | null;
|
|
26
|
+
statusLabel?: string | null;
|
|
27
|
+
systemPrompt?: string | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface PlannotatorConfig {
|
|
31
|
+
defaults?: PhaseProfile | null;
|
|
32
|
+
phases?: Partial<Record<PhaseName, PhaseProfile | null>>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface LoadedPlannotatorConfig {
|
|
36
|
+
config: PlannotatorConfig;
|
|
37
|
+
warnings: string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ResolvedPhaseProfile {
|
|
41
|
+
model?: PhaseModelRef;
|
|
42
|
+
thinking?: ThinkingLevel;
|
|
43
|
+
activeTools?: string[];
|
|
44
|
+
statusLabel?: string;
|
|
45
|
+
systemPrompt?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface PromptVariables {
|
|
49
|
+
planFilePath: string;
|
|
50
|
+
todoList: string;
|
|
51
|
+
completedCount: number;
|
|
52
|
+
totalCount: number;
|
|
53
|
+
remainingCount: number;
|
|
54
|
+
phase: RuntimePhase;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface PromptRenderResult {
|
|
58
|
+
text: string;
|
|
59
|
+
unknownVariables: string[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const INTERNAL_CONFIG_PATH = join(dirname(fileURLToPath(import.meta.url)), "plannotator.json");
|
|
63
|
+
const PHASES: PhaseName[] = ["planning", "executing", "reviewing"];
|
|
64
|
+
const THINKING_LEVELS = new Set<string>(["minimal", "low", "medium", "high", "xhigh"]);
|
|
65
|
+
|
|
66
|
+
function getAgentConfigDir(): string {
|
|
67
|
+
const envDir = process.env.PI_CODING_AGENT_DIR;
|
|
68
|
+
if (envDir) return envDir;
|
|
69
|
+
return join(process.env.HOME || process.env.USERPROFILE || homedir(), ".pi", "agent");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
73
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function readJsonFile(path: string): { data?: unknown; error?: string } {
|
|
77
|
+
if (!existsSync(path)) return {};
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
return { data: JSON.parse(readFileSync(path, "utf-8")) };
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return { error: `Failed to parse ${path}: ${error instanceof Error ? error.message : String(error)}` };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function normalizeModel(value: unknown): PhaseModelRef | null | undefined {
|
|
87
|
+
if (value === null) return null;
|
|
88
|
+
if (!isRecord(value)) return undefined;
|
|
89
|
+
|
|
90
|
+
const provider = typeof value.provider === "string" ? value.provider.trim() : "";
|
|
91
|
+
const id = typeof value.id === "string" ? value.id.trim() : "";
|
|
92
|
+
if (!provider || !id) return undefined;
|
|
93
|
+
return { provider, id };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function normalizeThinking(value: unknown): ThinkingLevel | null | undefined {
|
|
97
|
+
if (value === null) return null;
|
|
98
|
+
if (typeof value !== "string") return undefined;
|
|
99
|
+
const trimmed = value.trim();
|
|
100
|
+
if (!trimmed) return null;
|
|
101
|
+
|
|
102
|
+
return THINKING_LEVELS.has(trimmed as ThinkingLevel) ? (trimmed as ThinkingLevel) : undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function normalizeTools(value: unknown): string[] | null | undefined {
|
|
106
|
+
if (value === null) return null;
|
|
107
|
+
if (!Array.isArray(value)) return undefined;
|
|
108
|
+
if (value.length === 0) return [];
|
|
109
|
+
|
|
110
|
+
const tools = value.filter((tool): tool is string => typeof tool === "string" && tool.trim().length > 0);
|
|
111
|
+
return tools.length > 0 ? tools : undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function normalizeLabel(value: unknown): string | null | undefined {
|
|
115
|
+
if (value === null) return null;
|
|
116
|
+
if (typeof value !== "string") return undefined;
|
|
117
|
+
const trimmed = value.trim();
|
|
118
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function normalizePrompt(value: unknown): string | null | undefined {
|
|
122
|
+
if (value === null) return null;
|
|
123
|
+
if (typeof value !== "string") return undefined;
|
|
124
|
+
return value.length > 0 ? value : null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function normalizeProfile(raw: unknown): PhaseProfile | null | undefined {
|
|
128
|
+
if (raw === null) return null;
|
|
129
|
+
if (!isRecord(raw)) return undefined;
|
|
130
|
+
|
|
131
|
+
const profile: PhaseProfile = {};
|
|
132
|
+
|
|
133
|
+
if ("model" in raw) profile.model = normalizeModel(raw.model);
|
|
134
|
+
if ("thinking" in raw) profile.thinking = normalizeThinking(raw.thinking);
|
|
135
|
+
if ("thinkingLevel" in raw && profile.thinking === undefined) profile.thinking = normalizeThinking(raw.thinkingLevel);
|
|
136
|
+
if ("activeTools" in raw) profile.activeTools = normalizeTools(raw.activeTools);
|
|
137
|
+
if ("statusLabel" in raw) profile.statusLabel = normalizeLabel(raw.statusLabel);
|
|
138
|
+
if ("systemPrompt" in raw) profile.systemPrompt = normalizePrompt(raw.systemPrompt);
|
|
139
|
+
|
|
140
|
+
return profile;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function cloneProfile(profile: PhaseProfile | null | undefined): PhaseProfile | null | undefined {
|
|
144
|
+
if (profile === null || profile === undefined) return profile;
|
|
145
|
+
return { ...profile, activeTools: profile.activeTools ? [...profile.activeTools] : profile.activeTools };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function mergeProfile(base: PhaseProfile | null | undefined, override: PhaseProfile | null | undefined): PhaseProfile | null | undefined {
|
|
149
|
+
if (override === null) return null;
|
|
150
|
+
if (override === undefined) return cloneProfile(base);
|
|
151
|
+
if (base === null || base === undefined) return cloneProfile(override);
|
|
152
|
+
|
|
153
|
+
const merged: PhaseProfile = {
|
|
154
|
+
model: override.model !== undefined ? override.model : base.model,
|
|
155
|
+
thinking: override.thinking !== undefined ? override.thinking : base.thinking,
|
|
156
|
+
activeTools: override.activeTools !== undefined ? override.activeTools : base.activeTools,
|
|
157
|
+
statusLabel: override.statusLabel !== undefined ? override.statusLabel : base.statusLabel,
|
|
158
|
+
systemPrompt: override.systemPrompt !== undefined ? override.systemPrompt : base.systemPrompt,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return merged;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function mergeConfig(base: PlannotatorConfig, override: PlannotatorConfig): PlannotatorConfig {
|
|
165
|
+
const phases: Partial<Record<PhaseName, PhaseProfile | null>> = {};
|
|
166
|
+
for (const phase of PHASES) {
|
|
167
|
+
const merged = mergeProfile(base.phases?.[phase], override.phases?.[phase]);
|
|
168
|
+
if (merged !== undefined) phases[phase] = merged;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
defaults: mergeProfile(base.defaults, override.defaults),
|
|
173
|
+
phases: Object.keys(phases).length > 0 ? phases : undefined,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function loadConfigSource(path: string): { config: PlannotatorConfig; warning?: string } {
|
|
178
|
+
const parsed = readJsonFile(path);
|
|
179
|
+
if (parsed.error) {
|
|
180
|
+
return { config: {}, warning: parsed.error };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const raw = parsed.data;
|
|
184
|
+
if (!isRecord(raw)) return { config: {} };
|
|
185
|
+
|
|
186
|
+
const config: PlannotatorConfig = {};
|
|
187
|
+
if ("defaults" in raw) config.defaults = normalizeProfile(raw.defaults);
|
|
188
|
+
|
|
189
|
+
if ("phases" in raw && isRecord(raw.phases)) {
|
|
190
|
+
const phases: Partial<Record<PhaseName, PhaseProfile | null>> = {};
|
|
191
|
+
for (const phase of PHASES) {
|
|
192
|
+
const normalized = normalizeProfile(raw.phases[phase]);
|
|
193
|
+
if (normalized !== undefined) phases[phase] = normalized;
|
|
194
|
+
}
|
|
195
|
+
if (Object.keys(phases).length > 0) config.phases = phases;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return { config };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function loadPlannotatorConfig(cwd: string): LoadedPlannotatorConfig {
|
|
202
|
+
const warnings: string[] = [];
|
|
203
|
+
|
|
204
|
+
const internal = loadConfigSource(INTERNAL_CONFIG_PATH);
|
|
205
|
+
if (internal.warning) warnings.push(internal.warning);
|
|
206
|
+
|
|
207
|
+
const globalPath = join(getAgentConfigDir(), "plannotator.json");
|
|
208
|
+
const globalConfig = loadConfigSource(globalPath);
|
|
209
|
+
if (globalConfig.warning) warnings.push(globalConfig.warning);
|
|
210
|
+
|
|
211
|
+
const projectPath = join(cwd, ".pi", "plannotator.json");
|
|
212
|
+
const projectConfig = loadConfigSource(projectPath);
|
|
213
|
+
if (projectConfig.warning) warnings.push(projectConfig.warning);
|
|
214
|
+
|
|
215
|
+
const merged = mergeConfig(mergeConfig(internal.config, globalConfig.config), projectConfig.config);
|
|
216
|
+
return { config: merged, warnings };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function resolvePhaseProfile(config: PlannotatorConfig, phase: PhaseName): ResolvedPhaseProfile {
|
|
220
|
+
const defaults = config.defaults ?? {};
|
|
221
|
+
const phaseConfig = config.phases?.[phase] ?? {};
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
model: resolveModel(defaults.model, phaseConfig.model),
|
|
225
|
+
thinking: resolveThinking(defaults.thinking, phaseConfig.thinking),
|
|
226
|
+
activeTools: resolveTools(defaults.activeTools, phaseConfig.activeTools),
|
|
227
|
+
statusLabel: resolveString(defaults.statusLabel, phaseConfig.statusLabel),
|
|
228
|
+
systemPrompt: resolveString(defaults.systemPrompt, phaseConfig.systemPrompt),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function resolveModel(base: PhaseModelRef | null | undefined, override: PhaseModelRef | null | undefined): PhaseModelRef | undefined {
|
|
233
|
+
if (override !== undefined) {
|
|
234
|
+
return override ?? undefined;
|
|
235
|
+
}
|
|
236
|
+
return base ?? undefined;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function resolveThinking(base: ThinkingLevel | null | undefined, override: ThinkingLevel | null | undefined): ThinkingLevel | undefined {
|
|
240
|
+
if (override !== undefined) {
|
|
241
|
+
return override ?? undefined;
|
|
242
|
+
}
|
|
243
|
+
return base ?? undefined;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function resolveTools(base: string[] | null | undefined, override: string[] | null | undefined): string[] | undefined {
|
|
247
|
+
if (override !== undefined) {
|
|
248
|
+
if (override === null) return [];
|
|
249
|
+
return [...override];
|
|
250
|
+
}
|
|
251
|
+
if (base === null) return [];
|
|
252
|
+
return base ? [...base] : undefined;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function resolveString(base: string | null | undefined, override: string | null | undefined): string | undefined {
|
|
256
|
+
if (override !== undefined) {
|
|
257
|
+
if (override === null || override === "") return undefined;
|
|
258
|
+
return override;
|
|
259
|
+
}
|
|
260
|
+
return base ?? undefined;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function buildPromptVariables(options: {
|
|
264
|
+
planFilePath: string;
|
|
265
|
+
phase: RuntimePhase;
|
|
266
|
+
totalCount: number;
|
|
267
|
+
completedCount: number;
|
|
268
|
+
remainingCount?: number;
|
|
269
|
+
todoList?: string;
|
|
270
|
+
}): PromptVariables {
|
|
271
|
+
const totalCount = options.totalCount;
|
|
272
|
+
const completedCount = options.completedCount;
|
|
273
|
+
const remainingCount = options.remainingCount ?? Math.max(totalCount - completedCount, 0);
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
planFilePath: options.planFilePath,
|
|
277
|
+
todoList: options.todoList ?? "",
|
|
278
|
+
completedCount,
|
|
279
|
+
totalCount,
|
|
280
|
+
remainingCount,
|
|
281
|
+
phase: options.phase,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function renderTemplate(template: string, vars: PromptVariables): PromptRenderResult {
|
|
286
|
+
const unknownVariables = new Set<string>();
|
|
287
|
+
const text = template.replace(/\$\{([a-zA-Z0-9_]+)\}/g, (_match, key: string) => {
|
|
288
|
+
if (key in vars) {
|
|
289
|
+
const value = vars[key as keyof PromptVariables];
|
|
290
|
+
return value === undefined || value === null ? "" : String(value);
|
|
291
|
+
}
|
|
292
|
+
unknownVariables.add(key);
|
|
293
|
+
return "";
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
return { text, unknownVariables: [...unknownVariables] };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function formatTodoList(items: Array<{ step: number; text: string; completed: boolean }>): {
|
|
300
|
+
todoList: string;
|
|
301
|
+
completedCount: number;
|
|
302
|
+
totalCount: number;
|
|
303
|
+
remainingCount: number;
|
|
304
|
+
} {
|
|
305
|
+
const totalCount = items.length;
|
|
306
|
+
const completedCount = items.filter((item) => item.completed).length;
|
|
307
|
+
const remainingItems = items.filter((item) => !item.completed);
|
|
308
|
+
const todoList = remainingItems.length
|
|
309
|
+
? remainingItems.map((item) => `- [ ] ${item.step}. ${item.text}`).join("\n")
|
|
310
|
+
: "";
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
todoList,
|
|
314
|
+
completedCount,
|
|
315
|
+
totalCount,
|
|
316
|
+
remainingCount: remainingItems.length,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
type SendUserMessageContent = Parameters<ExtensionAPI["sendUserMessage"]>[0];
|
|
4
|
+
type SendUserMessageOptions = Parameters<ExtensionAPI["sendUserMessage"]>[1];
|
|
5
|
+
type NotificationType = "info" | "warning" | "error";
|
|
6
|
+
|
|
7
|
+
type CurrentPiSession = {
|
|
8
|
+
token: symbol;
|
|
9
|
+
sendUserMessage: (content: SendUserMessageContent, options?: SendUserMessageOptions) => void;
|
|
10
|
+
notify?: (message: string, type?: NotificationType) => void;
|
|
11
|
+
identity?: PiSessionIdentity;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type CurrentPiSessionStore = {
|
|
15
|
+
current?: CurrentPiSession;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type PlannotatorGlobal = typeof globalThis & {
|
|
19
|
+
__plannotatorCurrentPiSession?: CurrentPiSessionStore;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type CurrentPiSessionRegistration = {
|
|
23
|
+
token: symbol;
|
|
24
|
+
update: (ctx: ExtensionContext) => void;
|
|
25
|
+
clear: () => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type PiSessionIdentity = {
|
|
29
|
+
sessionId?: string;
|
|
30
|
+
sessionFile?: string;
|
|
31
|
+
sessionName?: string;
|
|
32
|
+
cwd?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const globalStore = globalThis as PlannotatorGlobal;
|
|
36
|
+
|
|
37
|
+
function getStore(): CurrentPiSessionStore {
|
|
38
|
+
globalStore.__plannotatorCurrentPiSession ??= {};
|
|
39
|
+
return globalStore.__plannotatorCurrentPiSession;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getErrorMessage(err: unknown): string {
|
|
43
|
+
return err instanceof Error ? err.message : String(err);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getPiSessionIdentity(ctx: ExtensionContext): PiSessionIdentity {
|
|
47
|
+
return {
|
|
48
|
+
sessionId: ctx.sessionManager.getSessionId(),
|
|
49
|
+
sessionFile: ctx.sessionManager.getSessionFile(),
|
|
50
|
+
sessionName: ctx.sessionManager.getSessionName(),
|
|
51
|
+
cwd: ctx.cwd,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isDifferentSession(origin: PiSessionIdentity, current: PiSessionIdentity | undefined): boolean {
|
|
56
|
+
if (!current) return false;
|
|
57
|
+
if (origin.sessionId && current.sessionId) return origin.sessionId !== current.sessionId;
|
|
58
|
+
if (origin.sessionFile && current.sessionFile) return origin.sessionFile !== current.sessionFile;
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function setCurrentPiSession(token: symbol, pi: ExtensionAPI, ctx?: ExtensionContext): void {
|
|
63
|
+
const current: CurrentPiSession = {
|
|
64
|
+
token,
|
|
65
|
+
sendUserMessage: (content, options) => {
|
|
66
|
+
pi.sendUserMessage(content, options);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
if (ctx) {
|
|
70
|
+
current.notify = (message, type = "info") => {
|
|
71
|
+
ctx.ui.notify(message, type);
|
|
72
|
+
};
|
|
73
|
+
current.identity = getPiSessionIdentity(ctx);
|
|
74
|
+
}
|
|
75
|
+
getStore().current = current;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function registerCurrentPiSession(pi: ExtensionAPI): CurrentPiSessionRegistration {
|
|
79
|
+
const token = Symbol("plannotator-current-pi-session");
|
|
80
|
+
setCurrentPiSession(token, pi);
|
|
81
|
+
return {
|
|
82
|
+
token,
|
|
83
|
+
update: (ctx) => {
|
|
84
|
+
setCurrentPiSession(token, pi, ctx);
|
|
85
|
+
},
|
|
86
|
+
clear: () => {
|
|
87
|
+
const store = getStore();
|
|
88
|
+
if (store.current?.token === token) {
|
|
89
|
+
store.current = undefined;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function notifyCurrentPiSession(
|
|
96
|
+
message: string,
|
|
97
|
+
type: NotificationType = "info",
|
|
98
|
+
origin?: PiSessionIdentity,
|
|
99
|
+
): boolean {
|
|
100
|
+
const current = getStore().current;
|
|
101
|
+
if (!current?.notify) return false;
|
|
102
|
+
if (origin && !isDifferentSession(origin, current.identity)) return false;
|
|
103
|
+
try {
|
|
104
|
+
current.notify(message, type);
|
|
105
|
+
return true;
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error(`Plannotator current-session notification failed: ${getErrorMessage(err)}`);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function isCurrentPiSessionDifferentFrom(origin: PiSessionIdentity): boolean {
|
|
113
|
+
return isDifferentSession(origin, getStore().current?.identity);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getCurrentPiSessionLabel(): string {
|
|
117
|
+
const identity = getStore().current?.identity;
|
|
118
|
+
if (!identity) return "unknown";
|
|
119
|
+
return identity.sessionName || identity.sessionFile || identity.sessionId || "current active Pi session";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function withCurrentPiSessionFallbackHeader(content: SendUserMessageContent): SendUserMessageContent {
|
|
123
|
+
if (typeof content !== "string") return content;
|
|
124
|
+
return `This Plannotator feedback was submitted from a browser tab opened before Pi switched sessions. It is being delivered to ${getCurrentPiSessionLabel()} because the original Pi session is no longer active.
|
|
125
|
+
|
|
126
|
+
${content}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function sendUserMessageToCurrentPiSession(
|
|
130
|
+
content: SendUserMessageContent,
|
|
131
|
+
options?: SendUserMessageOptions,
|
|
132
|
+
origin?: PiSessionIdentity,
|
|
133
|
+
): { ok: true } | { ok: false; reason: "no-current" | "same-session" | "send-failed"; error: unknown } {
|
|
134
|
+
const current = getStore().current;
|
|
135
|
+
if (!current) {
|
|
136
|
+
return { ok: false, reason: "no-current", error: new Error("No active Pi session is available.") };
|
|
137
|
+
}
|
|
138
|
+
if (origin && !isDifferentSession(origin, current.identity)) {
|
|
139
|
+
return { ok: false, reason: "same-session", error: new Error("No different active Pi session is available.") };
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
current.sendUserMessage(content, options);
|
|
143
|
+
return { ok: true };
|
|
144
|
+
} catch (err) {
|
|
145
|
+
return { ok: false, reason: "send-failed", error: err };
|
|
146
|
+
}
|
|
147
|
+
}
|