@oh-my-pi/pi-coding-agent 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/CHANGELOG.md +1629 -0
- package/README.md +1041 -0
- package/docs/compaction.md +403 -0
- package/docs/config-usage.md +113 -0
- package/docs/custom-tools.md +541 -0
- package/docs/extension-loading.md +1004 -0
- package/docs/hooks.md +867 -0
- package/docs/rpc.md +1040 -0
- package/docs/sdk.md +994 -0
- package/docs/session-tree-plan.md +441 -0
- package/docs/session.md +240 -0
- package/docs/skills.md +290 -0
- package/docs/theme.md +670 -0
- package/docs/tree.md +197 -0
- package/docs/tui.md +341 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +124 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/question/index.ts +84 -0
- package/examples/custom-tools/subagent/README.md +172 -0
- package/examples/custom-tools/subagent/agents/planner.md +37 -0
- package/examples/custom-tools/subagent/agents/scout.md +50 -0
- package/examples/custom-tools/subagent/agents/worker.md +24 -0
- package/examples/custom-tools/subagent/agents.ts +156 -0
- package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
- package/examples/custom-tools/subagent/commands/implement.md +10 -0
- package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
- package/examples/custom-tools/subagent/index.ts +1002 -0
- package/examples/custom-tools/todo/index.ts +212 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +49 -0
- package/examples/hooks/confirm-destructive.ts +59 -0
- package/examples/hooks/custom-compaction.ts +116 -0
- package/examples/hooks/dirty-repo-guard.ts +52 -0
- package/examples/hooks/file-trigger.ts +41 -0
- package/examples/hooks/git-checkpoint.ts +53 -0
- package/examples/hooks/handoff.ts +150 -0
- package/examples/hooks/permission-gate.ts +34 -0
- package/examples/hooks/protected-paths.ts +30 -0
- package/examples/hooks/qna.ts +119 -0
- package/examples/hooks/snake.ts +343 -0
- package/examples/hooks/status-line.ts +40 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +44 -0
- package/examples/sdk/04-skills.ts +44 -0
- package/examples/sdk/05-tools.ts +90 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +36 -0
- package/examples/sdk/08-slash-commands.ts +42 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
- package/examples/sdk/10-settings.ts +38 -0
- package/examples/sdk/11-sessions.ts +48 -0
- package/examples/sdk/12-full-control.ts +95 -0
- package/examples/sdk/README.md +154 -0
- package/package.json +89 -0
- package/src/bun-imports.d.ts +16 -0
- package/src/capability/context-file.ts +40 -0
- package/src/capability/extension.ts +48 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +616 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +52 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule.ts +56 -0
- package/src/capability/settings.ts +35 -0
- package/src/capability/skill.ts +49 -0
- package/src/capability/slash-command.ts +40 -0
- package/src/capability/system-prompt.ts +35 -0
- package/src/capability/tool.ts +38 -0
- package/src/capability/types.ts +166 -0
- package/src/cli/args.ts +259 -0
- package/src/cli/file-processor.ts +121 -0
- package/src/cli/list-models.ts +104 -0
- package/src/cli/plugin-cli.ts +661 -0
- package/src/cli/session-picker.ts +41 -0
- package/src/cli/update-cli.ts +274 -0
- package/src/cli.ts +10 -0
- package/src/config.ts +391 -0
- package/src/core/agent-session.ts +2178 -0
- package/src/core/auth-storage.ts +258 -0
- package/src/core/bash-executor.ts +197 -0
- package/src/core/compaction/branch-summarization.ts +315 -0
- package/src/core/compaction/compaction.ts +664 -0
- package/src/core/compaction/index.ts +7 -0
- package/src/core/compaction/utils.ts +153 -0
- package/src/core/custom-commands/bundled/review/index.ts +156 -0
- package/src/core/custom-commands/index.ts +15 -0
- package/src/core/custom-commands/loader.ts +226 -0
- package/src/core/custom-commands/types.ts +112 -0
- package/src/core/custom-tools/index.ts +22 -0
- package/src/core/custom-tools/loader.ts +248 -0
- package/src/core/custom-tools/types.ts +185 -0
- package/src/core/custom-tools/wrapper.ts +29 -0
- package/src/core/exec.ts +139 -0
- package/src/core/export-html/index.ts +159 -0
- package/src/core/export-html/template.css +774 -0
- package/src/core/export-html/template.generated.ts +2 -0
- package/src/core/export-html/template.html +45 -0
- package/src/core/export-html/template.js +1185 -0
- package/src/core/export-html/template.macro.ts +24 -0
- package/src/core/file-mentions.ts +54 -0
- package/src/core/hooks/index.ts +16 -0
- package/src/core/hooks/loader.ts +288 -0
- package/src/core/hooks/runner.ts +434 -0
- package/src/core/hooks/tool-wrapper.ts +98 -0
- package/src/core/hooks/types.ts +770 -0
- package/src/core/index.ts +53 -0
- package/src/core/logger.ts +112 -0
- package/src/core/mcp/client.ts +185 -0
- package/src/core/mcp/config.ts +248 -0
- package/src/core/mcp/index.ts +45 -0
- package/src/core/mcp/loader.ts +99 -0
- package/src/core/mcp/manager.ts +235 -0
- package/src/core/mcp/tool-bridge.ts +156 -0
- package/src/core/mcp/transports/http.ts +316 -0
- package/src/core/mcp/transports/index.ts +6 -0
- package/src/core/mcp/transports/stdio.ts +252 -0
- package/src/core/mcp/types.ts +228 -0
- package/src/core/messages.ts +211 -0
- package/src/core/model-registry.ts +334 -0
- package/src/core/model-resolver.ts +494 -0
- package/src/core/plugins/doctor.ts +67 -0
- package/src/core/plugins/index.ts +38 -0
- package/src/core/plugins/installer.ts +189 -0
- package/src/core/plugins/loader.ts +339 -0
- package/src/core/plugins/manager.ts +672 -0
- package/src/core/plugins/parser.ts +105 -0
- package/src/core/plugins/paths.ts +37 -0
- package/src/core/plugins/types.ts +190 -0
- package/src/core/sdk.ts +900 -0
- package/src/core/session-manager.ts +1837 -0
- package/src/core/settings-manager.ts +860 -0
- package/src/core/skills.ts +352 -0
- package/src/core/slash-commands.ts +132 -0
- package/src/core/system-prompt.ts +442 -0
- package/src/core/timings.ts +25 -0
- package/src/core/title-generator.ts +110 -0
- package/src/core/tools/ask.ts +193 -0
- package/src/core/tools/bash-interceptor.ts +120 -0
- package/src/core/tools/bash.ts +91 -0
- package/src/core/tools/context.ts +32 -0
- package/src/core/tools/edit-diff.ts +487 -0
- package/src/core/tools/edit.ts +140 -0
- package/src/core/tools/exa/company.ts +59 -0
- package/src/core/tools/exa/index.ts +63 -0
- package/src/core/tools/exa/linkedin.ts +59 -0
- package/src/core/tools/exa/mcp-client.ts +368 -0
- package/src/core/tools/exa/render.ts +200 -0
- package/src/core/tools/exa/researcher.ts +90 -0
- package/src/core/tools/exa/search.ts +338 -0
- package/src/core/tools/exa/types.ts +167 -0
- package/src/core/tools/exa/websets.ts +248 -0
- package/src/core/tools/find.ts +244 -0
- package/src/core/tools/grep.ts +584 -0
- package/src/core/tools/index.ts +283 -0
- package/src/core/tools/ls.ts +142 -0
- package/src/core/tools/lsp/client.ts +767 -0
- package/src/core/tools/lsp/clients/biome-client.ts +207 -0
- package/src/core/tools/lsp/clients/index.ts +49 -0
- package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
- package/src/core/tools/lsp/config.ts +845 -0
- package/src/core/tools/lsp/edits.ts +110 -0
- package/src/core/tools/lsp/index.ts +1364 -0
- package/src/core/tools/lsp/render.ts +560 -0
- package/src/core/tools/lsp/rust-analyzer.ts +145 -0
- package/src/core/tools/lsp/types.ts +495 -0
- package/src/core/tools/lsp/utils.ts +526 -0
- package/src/core/tools/notebook.ts +182 -0
- package/src/core/tools/output.ts +198 -0
- package/src/core/tools/path-utils.ts +61 -0
- package/src/core/tools/read.ts +507 -0
- package/src/core/tools/renderers.ts +820 -0
- package/src/core/tools/review.ts +275 -0
- package/src/core/tools/rulebook.ts +124 -0
- package/src/core/tools/task/agents.ts +158 -0
- package/src/core/tools/task/artifacts.ts +114 -0
- package/src/core/tools/task/commands.ts +157 -0
- package/src/core/tools/task/discovery.ts +217 -0
- package/src/core/tools/task/executor.ts +531 -0
- package/src/core/tools/task/index.ts +548 -0
- package/src/core/tools/task/model-resolver.ts +176 -0
- package/src/core/tools/task/parallel.ts +38 -0
- package/src/core/tools/task/render.ts +502 -0
- package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
- package/src/core/tools/task/types.ts +142 -0
- package/src/core/tools/truncate.ts +265 -0
- package/src/core/tools/web-fetch.ts +2511 -0
- package/src/core/tools/web-search/auth.ts +199 -0
- package/src/core/tools/web-search/index.ts +583 -0
- package/src/core/tools/web-search/providers/anthropic.ts +198 -0
- package/src/core/tools/web-search/providers/exa.ts +196 -0
- package/src/core/tools/web-search/providers/perplexity.ts +195 -0
- package/src/core/tools/web-search/render.ts +372 -0
- package/src/core/tools/web-search/types.ts +180 -0
- package/src/core/tools/write.ts +63 -0
- package/src/core/ttsr.ts +211 -0
- package/src/core/utils.ts +187 -0
- package/src/discovery/agents-md.ts +75 -0
- package/src/discovery/builtin.ts +647 -0
- package/src/discovery/claude.ts +623 -0
- package/src/discovery/cline.ts +104 -0
- package/src/discovery/codex.ts +571 -0
- package/src/discovery/cursor.ts +266 -0
- package/src/discovery/gemini.ts +368 -0
- package/src/discovery/github.ts +120 -0
- package/src/discovery/helpers.test.ts +127 -0
- package/src/discovery/helpers.ts +249 -0
- package/src/discovery/index.ts +84 -0
- package/src/discovery/mcp-json.ts +127 -0
- package/src/discovery/vscode.ts +99 -0
- package/src/discovery/windsurf.ts +219 -0
- package/src/index.ts +192 -0
- package/src/main.ts +507 -0
- package/src/migrations.ts +156 -0
- package/src/modes/cleanup.ts +23 -0
- package/src/modes/index.ts +48 -0
- package/src/modes/interactive/components/armin.ts +382 -0
- package/src/modes/interactive/components/assistant-message.ts +86 -0
- package/src/modes/interactive/components/bash-execution.ts +199 -0
- package/src/modes/interactive/components/bordered-loader.ts +41 -0
- package/src/modes/interactive/components/branch-summary-message.ts +42 -0
- package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
- package/src/modes/interactive/components/custom-editor.ts +122 -0
- package/src/modes/interactive/components/diff.ts +147 -0
- package/src/modes/interactive/components/dynamic-border.ts +25 -0
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +296 -0
- package/src/modes/interactive/components/extensions/extension-list.ts +479 -0
- package/src/modes/interactive/components/extensions/index.ts +9 -0
- package/src/modes/interactive/components/extensions/inspector-panel.ts +313 -0
- package/src/modes/interactive/components/extensions/state-manager.ts +558 -0
- package/src/modes/interactive/components/extensions/types.ts +191 -0
- package/src/modes/interactive/components/hook-editor.ts +117 -0
- package/src/modes/interactive/components/hook-input.ts +64 -0
- package/src/modes/interactive/components/hook-message.ts +96 -0
- package/src/modes/interactive/components/hook-selector.ts +91 -0
- package/src/modes/interactive/components/model-selector.ts +560 -0
- package/src/modes/interactive/components/oauth-selector.ts +136 -0
- package/src/modes/interactive/components/plugin-settings.ts +481 -0
- package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
- package/src/modes/interactive/components/session-selector.ts +220 -0
- package/src/modes/interactive/components/settings-defs.ts +597 -0
- package/src/modes/interactive/components/settings-selector.ts +545 -0
- package/src/modes/interactive/components/show-images-selector.ts +45 -0
- package/src/modes/interactive/components/status-line/index.ts +4 -0
- package/src/modes/interactive/components/status-line/presets.ts +94 -0
- package/src/modes/interactive/components/status-line/segments.ts +350 -0
- package/src/modes/interactive/components/status-line/separators.ts +55 -0
- package/src/modes/interactive/components/status-line/types.ts +81 -0
- package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
- package/src/modes/interactive/components/status-line.ts +384 -0
- package/src/modes/interactive/components/theme-selector.ts +62 -0
- package/src/modes/interactive/components/thinking-selector.ts +64 -0
- package/src/modes/interactive/components/tool-execution.ts +946 -0
- package/src/modes/interactive/components/tree-selector.ts +877 -0
- package/src/modes/interactive/components/ttsr-notification.ts +82 -0
- package/src/modes/interactive/components/user-message-selector.ts +159 -0
- package/src/modes/interactive/components/user-message.ts +18 -0
- package/src/modes/interactive/components/visual-truncate.ts +50 -0
- package/src/modes/interactive/components/welcome.ts +228 -0
- package/src/modes/interactive/interactive-mode.ts +2669 -0
- package/src/modes/interactive/theme/dark.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
- package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
- package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
- package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
- package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
- package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
- package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
- package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
- package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
- package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
- package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
- package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
- package/src/modes/interactive/theme/defaults/index.ts +67 -0
- package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
- package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
- package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
- package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
- package/src/modes/interactive/theme/defaults/light-github.json +114 -0
- package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
- package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
- package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
- package/src/modes/interactive/theme/defaults/light-one.json +105 -0
- package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
- package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
- package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
- package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
- package/src/modes/interactive/theme/light.json +99 -0
- package/src/modes/interactive/theme/theme-schema.json +424 -0
- package/src/modes/interactive/theme/theme.ts +2211 -0
- package/src/modes/print-mode.ts +163 -0
- package/src/modes/rpc/rpc-client.ts +527 -0
- package/src/modes/rpc/rpc-mode.ts +494 -0
- package/src/modes/rpc/rpc-types.ts +203 -0
- package/src/prompts/architect-plan.md +10 -0
- package/src/prompts/branch-summary-preamble.md +3 -0
- package/src/prompts/branch-summary.md +28 -0
- package/src/prompts/browser.md +71 -0
- package/src/prompts/compaction-summary.md +34 -0
- package/src/prompts/compaction-turn-prefix.md +16 -0
- package/src/prompts/compaction-update-summary.md +41 -0
- package/src/prompts/explore.md +82 -0
- package/src/prompts/implement-with-critic.md +11 -0
- package/src/prompts/implement.md +11 -0
- package/src/prompts/init.md +30 -0
- package/src/prompts/plan.md +54 -0
- package/src/prompts/reviewer.md +81 -0
- package/src/prompts/summarization-system.md +3 -0
- package/src/prompts/system-prompt.md +27 -0
- package/src/prompts/task.md +56 -0
- package/src/prompts/title-system.md +8 -0
- package/src/prompts/tools/ask.md +24 -0
- package/src/prompts/tools/bash.md +23 -0
- package/src/prompts/tools/edit.md +9 -0
- package/src/prompts/tools/find.md +6 -0
- package/src/prompts/tools/grep.md +12 -0
- package/src/prompts/tools/lsp.md +14 -0
- package/src/prompts/tools/output.md +23 -0
- package/src/prompts/tools/read.md +25 -0
- package/src/prompts/tools/web-fetch.md +8 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +10 -0
- package/src/utils/changelog.ts +99 -0
- package/src/utils/clipboard.ts +265 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/mime.ts +30 -0
- package/src/utils/shell-snapshot.ts +218 -0
- package/src/utils/shell.ts +364 -0
- package/src/utils/tools-manager.ts +265 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review tools - report_finding and submit_review
|
|
3
|
+
*
|
|
4
|
+
* Used by the reviewer agent to report findings in a structured way.
|
|
5
|
+
* Both tools are hidden by default - only enabled when explicitly listed in agent's tools.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
9
|
+
import type { Component } from "@oh-my-pi/pi-tui";
|
|
10
|
+
import { Container, Spacer, Text } from "@oh-my-pi/pi-tui";
|
|
11
|
+
import { Type } from "@sinclair/typebox";
|
|
12
|
+
import type { Theme } from "../../modes/interactive/theme/theme";
|
|
13
|
+
import { theme } from "../../modes/interactive/theme/theme";
|
|
14
|
+
|
|
15
|
+
const PRIORITY_LABELS: Record<number, string> = {
|
|
16
|
+
0: "P0",
|
|
17
|
+
1: "P1",
|
|
18
|
+
2: "P2",
|
|
19
|
+
3: "P3",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const _PRIORITY_DESCRIPTIONS: Record<number, string> = {
|
|
23
|
+
0: "Drop everything to fix. Blocking release, operations, or major usage.",
|
|
24
|
+
1: "Urgent. Should be addressed in the next cycle.",
|
|
25
|
+
2: "Normal. To be fixed eventually.",
|
|
26
|
+
3: "Low. Nice to have.",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// report_finding schema
|
|
30
|
+
const ReportFindingParams = Type.Object({
|
|
31
|
+
title: Type.String({
|
|
32
|
+
description: "≤80 chars, imperative, prefixed with [P0-P3]. E.g., '[P1] Un-padding slices along wrong dimension'",
|
|
33
|
+
}),
|
|
34
|
+
body: Type.String({
|
|
35
|
+
description: "Markdown explaining why this is a problem. One paragraph max.",
|
|
36
|
+
}),
|
|
37
|
+
priority: Type.Union([Type.Literal(0), Type.Literal(1), Type.Literal(2), Type.Literal(3)], {
|
|
38
|
+
description: "0=P0 (critical), 1=P1 (urgent), 2=P2 (normal), 3=P3 (low)",
|
|
39
|
+
}),
|
|
40
|
+
confidence: Type.Number({
|
|
41
|
+
minimum: 0,
|
|
42
|
+
maximum: 1,
|
|
43
|
+
description: "Confidence score 0.0-1.0",
|
|
44
|
+
}),
|
|
45
|
+
file_path: Type.String({ description: "Absolute path to the file" }),
|
|
46
|
+
line_start: Type.Number({ description: "Start line of the issue" }),
|
|
47
|
+
line_end: Type.Number({ description: "End line of the issue" }),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
interface ReportFindingDetails {
|
|
51
|
+
title: string;
|
|
52
|
+
body: string;
|
|
53
|
+
priority: number;
|
|
54
|
+
confidence: number;
|
|
55
|
+
file_path: string;
|
|
56
|
+
line_start: number;
|
|
57
|
+
line_end: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const reportFindingTool: AgentTool<typeof ReportFindingParams, ReportFindingDetails, Theme> = {
|
|
61
|
+
name: "report_finding",
|
|
62
|
+
label: "Report Finding",
|
|
63
|
+
description: "Report a code review finding. Use this for each issue found. Call submit_review when done.",
|
|
64
|
+
parameters: ReportFindingParams,
|
|
65
|
+
hidden: true,
|
|
66
|
+
|
|
67
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
68
|
+
const { title, body, priority, confidence, file_path, line_start, line_end } = params;
|
|
69
|
+
const location = `${file_path}:${line_start}${line_end !== line_start ? `-${line_end}` : ""}`;
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
content: [
|
|
73
|
+
{
|
|
74
|
+
type: "text",
|
|
75
|
+
text: `Finding recorded: ${PRIORITY_LABELS[priority]} ${title}\nLocation: ${location}\nConfidence: ${(confidence * 100).toFixed(0)}%`,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
details: { title, body, priority, confidence, file_path, line_start, line_end },
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
renderCall(args, theme): Component {
|
|
83
|
+
const priority = PRIORITY_LABELS[args.priority as number] ?? "P?";
|
|
84
|
+
const color = args.priority === 0 ? "error" : args.priority === 1 ? "warning" : "muted";
|
|
85
|
+
const titleText = String(args.title).replace(/^\[P\d\]\s*/, "");
|
|
86
|
+
return new Text(
|
|
87
|
+
`${theme.fg("toolTitle", theme.bold("report_finding "))}${theme.fg(color, `[${priority}]`)} ${theme.fg("dim", titleText)}`,
|
|
88
|
+
0,
|
|
89
|
+
0,
|
|
90
|
+
);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
renderResult(result, _options, theme): Component {
|
|
94
|
+
const { details } = result;
|
|
95
|
+
if (!details) {
|
|
96
|
+
const text = result.content[0];
|
|
97
|
+
return new Text(text?.type === "text" ? text.text : "", 0, 0);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const priority = PRIORITY_LABELS[details.priority] ?? "P?";
|
|
101
|
+
const color = details.priority === 0 ? "error" : details.priority === 1 ? "warning" : "muted";
|
|
102
|
+
const location = `${details.file_path}:${details.line_start}${details.line_end !== details.line_start ? `-${details.line_end}` : ""}`;
|
|
103
|
+
|
|
104
|
+
return new Text(
|
|
105
|
+
`${theme.fg("success", theme.status.success)} ${theme.fg(color, `[${priority}]`)} ${theme.fg("dim", location)}`,
|
|
106
|
+
0,
|
|
107
|
+
0,
|
|
108
|
+
);
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// submit_review schema
|
|
113
|
+
const SubmitReviewParams = Type.Object({
|
|
114
|
+
overall_correctness: Type.Union([Type.Literal("correct"), Type.Literal("incorrect")], {
|
|
115
|
+
description: "Whether the patch is correct (no bugs, tests won't break)",
|
|
116
|
+
}),
|
|
117
|
+
explanation: Type.String({
|
|
118
|
+
description: "1-3 sentence explanation justifying the verdict",
|
|
119
|
+
}),
|
|
120
|
+
confidence: Type.Number({
|
|
121
|
+
minimum: 0,
|
|
122
|
+
maximum: 1,
|
|
123
|
+
description: "Overall confidence score 0.0-1.0",
|
|
124
|
+
}),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
interface SubmitReviewDetails {
|
|
128
|
+
overall_correctness: "correct" | "incorrect";
|
|
129
|
+
explanation: string;
|
|
130
|
+
confidence: number;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const submitReviewTool: AgentTool<typeof SubmitReviewParams, SubmitReviewDetails, Theme> = {
|
|
134
|
+
name: "submit_review",
|
|
135
|
+
label: "Submit Review",
|
|
136
|
+
description: "Submit the final review verdict. Call this after all findings have been reported.",
|
|
137
|
+
parameters: SubmitReviewParams,
|
|
138
|
+
hidden: true,
|
|
139
|
+
|
|
140
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
141
|
+
const { overall_correctness, explanation, confidence } = params;
|
|
142
|
+
|
|
143
|
+
let summary = `## Review Summary\n\n`;
|
|
144
|
+
summary += `**Verdict:** ${overall_correctness === "correct" ? `${theme.status.success} Patch is correct` : `${theme.status.error} Patch is incorrect`}\n`;
|
|
145
|
+
summary += `**Confidence:** ${(confidence * 100).toFixed(0)}%\n\n`;
|
|
146
|
+
summary += explanation;
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
content: [{ type: "text", text: summary }],
|
|
150
|
+
details: { overall_correctness, explanation, confidence },
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
renderCall(args, theme): Component {
|
|
155
|
+
const verdict = args.overall_correctness === "correct" ? "correct" : "incorrect";
|
|
156
|
+
const color = args.overall_correctness === "correct" ? "success" : "error";
|
|
157
|
+
return new Text(
|
|
158
|
+
`${theme.fg("toolTitle", theme.bold("submit_review "))}${theme.fg(color, verdict)} ${theme.fg("dim", `(${((args.confidence as number) * 100).toFixed(0)}%)`)}`,
|
|
159
|
+
0,
|
|
160
|
+
0,
|
|
161
|
+
);
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
renderResult(result, { expanded }, theme): Component {
|
|
165
|
+
const { details } = result;
|
|
166
|
+
if (!details) {
|
|
167
|
+
const text = result.content[0];
|
|
168
|
+
return new Text(text?.type === "text" ? text.text : "", 0, 0);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const container = new Container();
|
|
172
|
+
const verdictColor = details.overall_correctness === "correct" ? "success" : "error";
|
|
173
|
+
const verdictIcon = details.overall_correctness === "correct" ? theme.status.success : theme.status.error;
|
|
174
|
+
|
|
175
|
+
container.addChild(
|
|
176
|
+
new Text(
|
|
177
|
+
`${theme.fg(verdictColor, verdictIcon)} Patch is ${theme.fg(verdictColor, details.overall_correctness)} ${theme.fg("dim", `(${(details.confidence * 100).toFixed(0)}% confidence)`)}`,
|
|
178
|
+
0,
|
|
179
|
+
0,
|
|
180
|
+
),
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
if (expanded) {
|
|
184
|
+
container.addChild(new Spacer(1));
|
|
185
|
+
container.addChild(new Text(theme.fg("dim", details.explanation), 0, 0));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return container;
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export function createReportFindingTool(): AgentTool<typeof ReportFindingParams, ReportFindingDetails, Theme> {
|
|
193
|
+
return reportFindingTool;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function createSubmitReviewTool(): AgentTool<typeof SubmitReviewParams, SubmitReviewDetails, Theme> {
|
|
197
|
+
return submitReviewTool;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Re-export types for external use
|
|
201
|
+
export type { ReportFindingDetails, SubmitReviewDetails };
|
|
202
|
+
|
|
203
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
204
|
+
// Subprocess tool handlers - registered for extraction/rendering in task tool
|
|
205
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
206
|
+
|
|
207
|
+
import path from "node:path";
|
|
208
|
+
import { subprocessToolRegistry } from "./task/subprocess-tool-registry";
|
|
209
|
+
|
|
210
|
+
// Register report_finding handler
|
|
211
|
+
subprocessToolRegistry.register<ReportFindingDetails>("report_finding", {
|
|
212
|
+
extractData: (event) => event.result?.details as ReportFindingDetails | undefined,
|
|
213
|
+
|
|
214
|
+
renderInline: (data, theme) => {
|
|
215
|
+
const priority = PRIORITY_LABELS[data.priority] ?? "P?";
|
|
216
|
+
const color = data.priority === 0 ? "error" : data.priority === 1 ? "warning" : "muted";
|
|
217
|
+
const titleText = data.title.replace(/^\[P\d\]\s*/, "");
|
|
218
|
+
const loc = `${path.basename(data.file_path)}:${data.line_start}`;
|
|
219
|
+
return new Text(`${theme.fg(color, `[${priority}]`)} ${titleText} ${theme.fg("dim", loc)}`, 0, 0);
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
renderFinal: (allData, theme, expanded) => {
|
|
223
|
+
const container = new Container();
|
|
224
|
+
const displayCount = expanded ? allData.length : Math.min(3, allData.length);
|
|
225
|
+
|
|
226
|
+
for (let i = 0; i < displayCount; i++) {
|
|
227
|
+
const data = allData[i];
|
|
228
|
+
const priority = PRIORITY_LABELS[data.priority] ?? "P?";
|
|
229
|
+
const color = data.priority === 0 ? "error" : data.priority === 1 ? "warning" : "muted";
|
|
230
|
+
const titleText = data.title.replace(/^\[P\d\]\s*/, "");
|
|
231
|
+
const loc = `${path.basename(data.file_path)}:${data.line_start}`;
|
|
232
|
+
|
|
233
|
+
container.addChild(
|
|
234
|
+
new Text(` ${theme.fg(color, `[${priority}]`)} ${titleText} ${theme.fg("dim", loc)}`, 0, 0),
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
if (expanded && data.body) {
|
|
238
|
+
container.addChild(new Text(` ${theme.fg("dim", data.body)}`, 0, 0));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (allData.length > displayCount) {
|
|
243
|
+
container.addChild(
|
|
244
|
+
new Text(
|
|
245
|
+
theme.fg("dim", ` ${theme.format.ellipsis} ${allData.length - displayCount} more findings`),
|
|
246
|
+
0,
|
|
247
|
+
0,
|
|
248
|
+
),
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return container;
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Register submit_review handler
|
|
257
|
+
subprocessToolRegistry.register<SubmitReviewDetails>("submit_review", {
|
|
258
|
+
extractData: (event) => event.result?.details as SubmitReviewDetails | undefined,
|
|
259
|
+
|
|
260
|
+
// Terminate subprocess after review is submitted
|
|
261
|
+
shouldTerminate: () => true,
|
|
262
|
+
|
|
263
|
+
renderInline: (data, theme) => {
|
|
264
|
+
const verdictColor = data.overall_correctness === "correct" ? "success" : "error";
|
|
265
|
+
const verdictIcon = data.overall_correctness === "correct" ? theme.status.success : theme.status.error;
|
|
266
|
+
return new Text(
|
|
267
|
+
`${theme.fg(verdictColor, verdictIcon)} Review: ${theme.fg(verdictColor, data.overall_correctness)} (${(data.confidence * 100).toFixed(0)}%)`,
|
|
268
|
+
0,
|
|
269
|
+
0,
|
|
270
|
+
);
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
// Note: renderFinal is NOT used for submit_review - we use the combined
|
|
274
|
+
// renderReviewResult in render.ts to show verdict + findings together
|
|
275
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rulebook Tool
|
|
3
|
+
*
|
|
4
|
+
* Allows the agent to fetch full content of rules that have descriptions.
|
|
5
|
+
* Rules are listed in the system prompt with name + description; this tool
|
|
6
|
+
* retrieves the complete rule content on demand.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
10
|
+
import { Type } from "@sinclair/typebox";
|
|
11
|
+
import type { Rule } from "../../capability/rule";
|
|
12
|
+
|
|
13
|
+
export interface RulebookToolDetails {
|
|
14
|
+
type: "rulebook";
|
|
15
|
+
ruleName: string;
|
|
16
|
+
found: boolean;
|
|
17
|
+
content?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const rulebookSchema = Type.Object({
|
|
21
|
+
name: Type.String({ description: "The name of the rule to fetch" }),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create a rulebook tool with access to discovered rules.
|
|
26
|
+
* @param rules - Array of discovered rules (non-TTSR rules with descriptions)
|
|
27
|
+
*/
|
|
28
|
+
export function createRulebookTool(rules: Rule[]): AgentTool<typeof rulebookSchema> {
|
|
29
|
+
// Build lookup map for O(1) access
|
|
30
|
+
const ruleMap = new Map<string, Rule>();
|
|
31
|
+
for (const rule of rules) {
|
|
32
|
+
ruleMap.set(rule.name, rule);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const ruleNames = rules.map((r) => r.name);
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
name: "rulebook",
|
|
39
|
+
label: "Rulebook",
|
|
40
|
+
description: `Fetch the full content of a project rule by name. Use this when a rule listed in <available_rules> is relevant to your current task. Available: ${ruleNames.join(", ") || "(none)"}`,
|
|
41
|
+
parameters: rulebookSchema,
|
|
42
|
+
execute: async (_toolCallId: string, { name }: { name: string }) => {
|
|
43
|
+
const rule = ruleMap.get(name);
|
|
44
|
+
|
|
45
|
+
if (!rule) {
|
|
46
|
+
const available = ruleNames.join(", ");
|
|
47
|
+
return {
|
|
48
|
+
content: [{ type: "text", text: `Rule "${name}" not found. Available rules: ${available || "(none)"}` }],
|
|
49
|
+
details: {
|
|
50
|
+
type: "rulebook",
|
|
51
|
+
ruleName: name,
|
|
52
|
+
found: false,
|
|
53
|
+
} satisfies RulebookToolDetails,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: "text", text: `# Rule: ${rule.name}\n\n${rule.content}` }],
|
|
59
|
+
details: {
|
|
60
|
+
type: "rulebook",
|
|
61
|
+
ruleName: name,
|
|
62
|
+
found: true,
|
|
63
|
+
content: rule.content,
|
|
64
|
+
} satisfies RulebookToolDetails,
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Filter rules to only those suitable for the rulebook (have descriptions, no TTSR trigger).
|
|
72
|
+
*/
|
|
73
|
+
export function filterRulebookRules(rules: Rule[]): Rule[] {
|
|
74
|
+
return rules.filter((rule) => {
|
|
75
|
+
// Exclude TTSR rules (handled separately by streaming)
|
|
76
|
+
if (rule.ttsrTrigger) return false;
|
|
77
|
+
// Exclude always-apply rules (already in context)
|
|
78
|
+
if (rule.alwaysApply) return false;
|
|
79
|
+
// Must have a description for agent to know when to fetch
|
|
80
|
+
if (!rule.description) return false;
|
|
81
|
+
return true;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Format rules for inclusion in the system prompt.
|
|
87
|
+
* Lists rule names and descriptions so the agent knows what's available.
|
|
88
|
+
*/
|
|
89
|
+
export function formatRulesForPrompt(rules: Rule[]): string {
|
|
90
|
+
if (rules.length === 0) {
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const lines = [
|
|
95
|
+
"\n\n## Available Rules",
|
|
96
|
+
"",
|
|
97
|
+
"The following project rules are available. Use the `rulebook` tool to fetch a rule's full content when it's relevant to your task.",
|
|
98
|
+
"",
|
|
99
|
+
"<available_rules>",
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
for (const rule of rules) {
|
|
103
|
+
lines.push(" <rule>");
|
|
104
|
+
lines.push(` <name>${escapeXml(rule.name)}</name>`);
|
|
105
|
+
lines.push(` <description>${escapeXml(rule.description || "")}</description>`);
|
|
106
|
+
if (rule.globs && rule.globs.length > 0) {
|
|
107
|
+
lines.push(` <globs>${escapeXml(rule.globs.join(", "))}</globs>`);
|
|
108
|
+
}
|
|
109
|
+
lines.push(" </rule>");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
lines.push("</available_rules>");
|
|
113
|
+
|
|
114
|
+
return lines.join("\n");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function escapeXml(str: string): string {
|
|
118
|
+
return str
|
|
119
|
+
.replace(/&/g, "&")
|
|
120
|
+
.replace(/</g, "<")
|
|
121
|
+
.replace(/>/g, ">")
|
|
122
|
+
.replace(/"/g, """)
|
|
123
|
+
.replace(/'/g, "'");
|
|
124
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled agent definitions.
|
|
3
|
+
*
|
|
4
|
+
* Agents are embedded at build time via Bun's import with { type: "text" }.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Embed agent markdown files at build time
|
|
8
|
+
import browserMd from "../../../prompts/browser.md" with { type: "text" };
|
|
9
|
+
import exploreMd from "../../../prompts/explore.md" with { type: "text" };
|
|
10
|
+
import planMd from "../../../prompts/plan.md" with { type: "text" };
|
|
11
|
+
import reviewerMd from "../../../prompts/reviewer.md" with { type: "text" };
|
|
12
|
+
import taskMd from "../../../prompts/task.md" with { type: "text" };
|
|
13
|
+
import type { AgentDefinition, AgentSource } from "./types";
|
|
14
|
+
|
|
15
|
+
const EMBEDDED_AGENTS: { name: string; content: string }[] = [
|
|
16
|
+
{ name: "browser.md", content: browserMd },
|
|
17
|
+
{ name: "explore.md", content: exploreMd },
|
|
18
|
+
{ name: "plan.md", content: planMd },
|
|
19
|
+
{ name: "reviewer.md", content: reviewerMd },
|
|
20
|
+
{ name: "task.md", content: taskMd },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parse YAML frontmatter from markdown content.
|
|
25
|
+
*/
|
|
26
|
+
function parseFrontmatter(content: string): { frontmatter: Record<string, string>; body: string } {
|
|
27
|
+
const frontmatter: Record<string, string> = {};
|
|
28
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
29
|
+
|
|
30
|
+
if (!normalized.startsWith("---")) {
|
|
31
|
+
return { frontmatter, body: normalized };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const endIndex = normalized.indexOf("\n---", 3);
|
|
35
|
+
if (endIndex === -1) {
|
|
36
|
+
return { frontmatter, body: normalized };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const frontmatterBlock = normalized.slice(4, endIndex);
|
|
40
|
+
const body = normalized.slice(endIndex + 4).trim();
|
|
41
|
+
|
|
42
|
+
for (const line of frontmatterBlock.split("\n")) {
|
|
43
|
+
const match = line.match(/^([\w-]+):\s*(.*)$/);
|
|
44
|
+
if (match) {
|
|
45
|
+
let value = match[2].trim();
|
|
46
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
47
|
+
value = value.slice(1, -1);
|
|
48
|
+
}
|
|
49
|
+
frontmatter[match[1]] = value;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { frontmatter, body };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Parse an agent from embedded content.
|
|
58
|
+
*/
|
|
59
|
+
function parseAgent(fileName: string, content: string, source: AgentSource): AgentDefinition | null {
|
|
60
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
61
|
+
|
|
62
|
+
if (!frontmatter.name || !frontmatter.description) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const tools = frontmatter.tools
|
|
67
|
+
?.split(",")
|
|
68
|
+
.map((t) => t.trim())
|
|
69
|
+
.filter(Boolean);
|
|
70
|
+
|
|
71
|
+
// Parse spawns field
|
|
72
|
+
let spawns: string[] | "*" | undefined;
|
|
73
|
+
if (frontmatter.spawns !== undefined) {
|
|
74
|
+
const spawnsRaw = frontmatter.spawns.trim();
|
|
75
|
+
if (spawnsRaw === "*") {
|
|
76
|
+
spawns = "*";
|
|
77
|
+
} else if (spawnsRaw) {
|
|
78
|
+
spawns = spawnsRaw
|
|
79
|
+
.split(",")
|
|
80
|
+
.map((s) => s.trim())
|
|
81
|
+
.filter(Boolean);
|
|
82
|
+
if (spawns.length === 0) spawns = undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Backward compat: infer spawns: "*" when tools includes "task"
|
|
87
|
+
if (spawns === undefined && tools?.includes("task")) {
|
|
88
|
+
spawns = "*";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const recursive =
|
|
92
|
+
frontmatter.recursive === undefined ? false : frontmatter.recursive === "true" || frontmatter.recursive === "1";
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
name: frontmatter.name,
|
|
96
|
+
description: frontmatter.description,
|
|
97
|
+
tools: tools && tools.length > 0 ? tools : undefined,
|
|
98
|
+
spawns,
|
|
99
|
+
model: frontmatter.model,
|
|
100
|
+
recursive,
|
|
101
|
+
systemPrompt: body,
|
|
102
|
+
source,
|
|
103
|
+
filePath: `embedded:${fileName}`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Cache for bundled agents */
|
|
108
|
+
let bundledAgentsCache: AgentDefinition[] | null = null;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Load all bundled agents from embedded content.
|
|
112
|
+
* Results are cached after first load.
|
|
113
|
+
*/
|
|
114
|
+
export function loadBundledAgents(): AgentDefinition[] {
|
|
115
|
+
if (bundledAgentsCache !== null) {
|
|
116
|
+
return bundledAgentsCache;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const agents: AgentDefinition[] = [];
|
|
120
|
+
|
|
121
|
+
for (const { name, content } of EMBEDDED_AGENTS) {
|
|
122
|
+
const agent = parseAgent(name, content, "bundled");
|
|
123
|
+
if (agent) {
|
|
124
|
+
agents.push(agent);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
bundledAgentsCache = agents;
|
|
129
|
+
return agents;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get a bundled agent by name.
|
|
134
|
+
*/
|
|
135
|
+
export function getBundledAgent(name: string): AgentDefinition | undefined {
|
|
136
|
+
return loadBundledAgents().find((a) => a.name === name);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get all bundled agents as a map keyed by name.
|
|
141
|
+
*/
|
|
142
|
+
export function getBundledAgentsMap(): Map<string, AgentDefinition> {
|
|
143
|
+
const map = new Map<string, AgentDefinition>();
|
|
144
|
+
for (const agent of loadBundledAgents()) {
|
|
145
|
+
map.set(agent.name, agent);
|
|
146
|
+
}
|
|
147
|
+
return map;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Clear the bundled agents cache (for testing).
|
|
152
|
+
*/
|
|
153
|
+
export function clearBundledAgentsCache(): void {
|
|
154
|
+
bundledAgentsCache = null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Re-export for backward compatibility
|
|
158
|
+
export const BUNDLED_AGENTS = loadBundledAgents;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session artifacts for subagent outputs.
|
|
3
|
+
*
|
|
4
|
+
* When a session exists, writes agent outputs to a sibling directory.
|
|
5
|
+
* Otherwise uses temp files that are cleaned up after execution.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import * as os from "node:os";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Derive artifacts directory from session file path.
|
|
14
|
+
*
|
|
15
|
+
* /path/to/sessions/project/2026-01-01T14-28-11-636Z_uuid.jsonl
|
|
16
|
+
* → /path/to/sessions/project/2026-01-01T14-28-11-636Z_uuid/
|
|
17
|
+
*/
|
|
18
|
+
export function getArtifactsDir(sessionFile: string | null): string | null {
|
|
19
|
+
if (!sessionFile) return null;
|
|
20
|
+
// Strip .jsonl extension to get directory path
|
|
21
|
+
if (sessionFile.endsWith(".jsonl")) {
|
|
22
|
+
return sessionFile.slice(0, -6);
|
|
23
|
+
}
|
|
24
|
+
return sessionFile;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Ensure artifacts directory exists.
|
|
29
|
+
*/
|
|
30
|
+
export function ensureArtifactsDir(dir: string): void {
|
|
31
|
+
if (!fs.existsSync(dir)) {
|
|
32
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Generate artifact file paths for an agent run.
|
|
38
|
+
*/
|
|
39
|
+
export function getArtifactPaths(
|
|
40
|
+
dir: string,
|
|
41
|
+
agentName: string,
|
|
42
|
+
index: number,
|
|
43
|
+
): { inputPath: string; outputPath: string; jsonlPath: string } {
|
|
44
|
+
const base = `${agentName}_${index}`;
|
|
45
|
+
return {
|
|
46
|
+
inputPath: path.join(dir, `${base}.in.md`),
|
|
47
|
+
outputPath: path.join(dir, `${base}.out.md`),
|
|
48
|
+
jsonlPath: path.join(dir, `${base}.jsonl`),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Write artifacts for an agent run.
|
|
54
|
+
*/
|
|
55
|
+
export async function writeArtifacts(
|
|
56
|
+
dir: string,
|
|
57
|
+
agentName: string,
|
|
58
|
+
index: number,
|
|
59
|
+
input: string,
|
|
60
|
+
output: string,
|
|
61
|
+
jsonlEvents?: string[],
|
|
62
|
+
): Promise<{ inputPath: string; outputPath: string; jsonlPath?: string }> {
|
|
63
|
+
ensureArtifactsDir(dir);
|
|
64
|
+
|
|
65
|
+
const paths = getArtifactPaths(dir, agentName, index);
|
|
66
|
+
|
|
67
|
+
// Write input
|
|
68
|
+
await fs.promises.writeFile(paths.inputPath, input, "utf-8");
|
|
69
|
+
|
|
70
|
+
// Write output
|
|
71
|
+
await fs.promises.writeFile(paths.outputPath, output, "utf-8");
|
|
72
|
+
|
|
73
|
+
// Write JSONL if events provided
|
|
74
|
+
if (jsonlEvents && jsonlEvents.length > 0) {
|
|
75
|
+
await fs.promises.writeFile(paths.jsonlPath, jsonlEvents.join("\n"), "utf-8");
|
|
76
|
+
return paths;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { inputPath: paths.inputPath, outputPath: paths.outputPath };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Create a temporary artifacts directory.
|
|
84
|
+
*/
|
|
85
|
+
export function createTempArtifactsDir(runId?: string): string {
|
|
86
|
+
const id = runId || `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
87
|
+
const dir = path.join(os.tmpdir(), `omp-task-${id}`);
|
|
88
|
+
ensureArtifactsDir(dir);
|
|
89
|
+
return dir;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Clean up temporary artifacts.
|
|
94
|
+
*/
|
|
95
|
+
export async function cleanupTempArtifacts(paths: string[]): Promise<void> {
|
|
96
|
+
for (const p of paths) {
|
|
97
|
+
try {
|
|
98
|
+
await fs.promises.unlink(p);
|
|
99
|
+
} catch {
|
|
100
|
+
// Ignore cleanup errors
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Clean up a temporary directory and its contents.
|
|
107
|
+
*/
|
|
108
|
+
export async function cleanupTempDir(dir: string): Promise<void> {
|
|
109
|
+
try {
|
|
110
|
+
await fs.promises.rm(dir, { recursive: true, force: true });
|
|
111
|
+
} catch {
|
|
112
|
+
// Ignore cleanup errors
|
|
113
|
+
}
|
|
114
|
+
}
|