@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.
Files changed (337) hide show
  1. package/CHANGELOG.md +1629 -0
  2. package/README.md +1041 -0
  3. package/docs/compaction.md +403 -0
  4. package/docs/config-usage.md +113 -0
  5. package/docs/custom-tools.md +541 -0
  6. package/docs/extension-loading.md +1004 -0
  7. package/docs/hooks.md +867 -0
  8. package/docs/rpc.md +1040 -0
  9. package/docs/sdk.md +994 -0
  10. package/docs/session-tree-plan.md +441 -0
  11. package/docs/session.md +240 -0
  12. package/docs/skills.md +290 -0
  13. package/docs/theme.md +670 -0
  14. package/docs/tree.md +197 -0
  15. package/docs/tui.md +341 -0
  16. package/examples/README.md +21 -0
  17. package/examples/custom-tools/README.md +124 -0
  18. package/examples/custom-tools/hello/index.ts +20 -0
  19. package/examples/custom-tools/question/index.ts +84 -0
  20. package/examples/custom-tools/subagent/README.md +172 -0
  21. package/examples/custom-tools/subagent/agents/planner.md +37 -0
  22. package/examples/custom-tools/subagent/agents/scout.md +50 -0
  23. package/examples/custom-tools/subagent/agents/worker.md +24 -0
  24. package/examples/custom-tools/subagent/agents.ts +156 -0
  25. package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
  26. package/examples/custom-tools/subagent/commands/implement.md +10 -0
  27. package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
  28. package/examples/custom-tools/subagent/index.ts +1002 -0
  29. package/examples/custom-tools/todo/index.ts +212 -0
  30. package/examples/hooks/README.md +56 -0
  31. package/examples/hooks/auto-commit-on-exit.ts +49 -0
  32. package/examples/hooks/confirm-destructive.ts +59 -0
  33. package/examples/hooks/custom-compaction.ts +116 -0
  34. package/examples/hooks/dirty-repo-guard.ts +52 -0
  35. package/examples/hooks/file-trigger.ts +41 -0
  36. package/examples/hooks/git-checkpoint.ts +53 -0
  37. package/examples/hooks/handoff.ts +150 -0
  38. package/examples/hooks/permission-gate.ts +34 -0
  39. package/examples/hooks/protected-paths.ts +30 -0
  40. package/examples/hooks/qna.ts +119 -0
  41. package/examples/hooks/snake.ts +343 -0
  42. package/examples/hooks/status-line.ts +40 -0
  43. package/examples/sdk/01-minimal.ts +22 -0
  44. package/examples/sdk/02-custom-model.ts +49 -0
  45. package/examples/sdk/03-custom-prompt.ts +44 -0
  46. package/examples/sdk/04-skills.ts +44 -0
  47. package/examples/sdk/05-tools.ts +90 -0
  48. package/examples/sdk/06-hooks.ts +61 -0
  49. package/examples/sdk/07-context-files.ts +36 -0
  50. package/examples/sdk/08-slash-commands.ts +42 -0
  51. package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
  52. package/examples/sdk/10-settings.ts +38 -0
  53. package/examples/sdk/11-sessions.ts +48 -0
  54. package/examples/sdk/12-full-control.ts +95 -0
  55. package/examples/sdk/README.md +154 -0
  56. package/package.json +89 -0
  57. package/src/bun-imports.d.ts +16 -0
  58. package/src/capability/context-file.ts +40 -0
  59. package/src/capability/extension.ts +48 -0
  60. package/src/capability/hook.ts +40 -0
  61. package/src/capability/index.ts +616 -0
  62. package/src/capability/instruction.ts +37 -0
  63. package/src/capability/mcp.ts +52 -0
  64. package/src/capability/prompt.ts +35 -0
  65. package/src/capability/rule.ts +56 -0
  66. package/src/capability/settings.ts +35 -0
  67. package/src/capability/skill.ts +49 -0
  68. package/src/capability/slash-command.ts +40 -0
  69. package/src/capability/system-prompt.ts +35 -0
  70. package/src/capability/tool.ts +38 -0
  71. package/src/capability/types.ts +166 -0
  72. package/src/cli/args.ts +259 -0
  73. package/src/cli/file-processor.ts +121 -0
  74. package/src/cli/list-models.ts +104 -0
  75. package/src/cli/plugin-cli.ts +661 -0
  76. package/src/cli/session-picker.ts +41 -0
  77. package/src/cli/update-cli.ts +274 -0
  78. package/src/cli.ts +10 -0
  79. package/src/config.ts +391 -0
  80. package/src/core/agent-session.ts +2178 -0
  81. package/src/core/auth-storage.ts +258 -0
  82. package/src/core/bash-executor.ts +197 -0
  83. package/src/core/compaction/branch-summarization.ts +315 -0
  84. package/src/core/compaction/compaction.ts +664 -0
  85. package/src/core/compaction/index.ts +7 -0
  86. package/src/core/compaction/utils.ts +153 -0
  87. package/src/core/custom-commands/bundled/review/index.ts +156 -0
  88. package/src/core/custom-commands/index.ts +15 -0
  89. package/src/core/custom-commands/loader.ts +226 -0
  90. package/src/core/custom-commands/types.ts +112 -0
  91. package/src/core/custom-tools/index.ts +22 -0
  92. package/src/core/custom-tools/loader.ts +248 -0
  93. package/src/core/custom-tools/types.ts +185 -0
  94. package/src/core/custom-tools/wrapper.ts +29 -0
  95. package/src/core/exec.ts +139 -0
  96. package/src/core/export-html/index.ts +159 -0
  97. package/src/core/export-html/template.css +774 -0
  98. package/src/core/export-html/template.generated.ts +2 -0
  99. package/src/core/export-html/template.html +45 -0
  100. package/src/core/export-html/template.js +1185 -0
  101. package/src/core/export-html/template.macro.ts +24 -0
  102. package/src/core/file-mentions.ts +54 -0
  103. package/src/core/hooks/index.ts +16 -0
  104. package/src/core/hooks/loader.ts +288 -0
  105. package/src/core/hooks/runner.ts +434 -0
  106. package/src/core/hooks/tool-wrapper.ts +98 -0
  107. package/src/core/hooks/types.ts +770 -0
  108. package/src/core/index.ts +53 -0
  109. package/src/core/logger.ts +112 -0
  110. package/src/core/mcp/client.ts +185 -0
  111. package/src/core/mcp/config.ts +248 -0
  112. package/src/core/mcp/index.ts +45 -0
  113. package/src/core/mcp/loader.ts +99 -0
  114. package/src/core/mcp/manager.ts +235 -0
  115. package/src/core/mcp/tool-bridge.ts +156 -0
  116. package/src/core/mcp/transports/http.ts +316 -0
  117. package/src/core/mcp/transports/index.ts +6 -0
  118. package/src/core/mcp/transports/stdio.ts +252 -0
  119. package/src/core/mcp/types.ts +228 -0
  120. package/src/core/messages.ts +211 -0
  121. package/src/core/model-registry.ts +334 -0
  122. package/src/core/model-resolver.ts +494 -0
  123. package/src/core/plugins/doctor.ts +67 -0
  124. package/src/core/plugins/index.ts +38 -0
  125. package/src/core/plugins/installer.ts +189 -0
  126. package/src/core/plugins/loader.ts +339 -0
  127. package/src/core/plugins/manager.ts +672 -0
  128. package/src/core/plugins/parser.ts +105 -0
  129. package/src/core/plugins/paths.ts +37 -0
  130. package/src/core/plugins/types.ts +190 -0
  131. package/src/core/sdk.ts +900 -0
  132. package/src/core/session-manager.ts +1837 -0
  133. package/src/core/settings-manager.ts +860 -0
  134. package/src/core/skills.ts +352 -0
  135. package/src/core/slash-commands.ts +132 -0
  136. package/src/core/system-prompt.ts +442 -0
  137. package/src/core/timings.ts +25 -0
  138. package/src/core/title-generator.ts +110 -0
  139. package/src/core/tools/ask.ts +193 -0
  140. package/src/core/tools/bash-interceptor.ts +120 -0
  141. package/src/core/tools/bash.ts +91 -0
  142. package/src/core/tools/context.ts +32 -0
  143. package/src/core/tools/edit-diff.ts +487 -0
  144. package/src/core/tools/edit.ts +140 -0
  145. package/src/core/tools/exa/company.ts +59 -0
  146. package/src/core/tools/exa/index.ts +63 -0
  147. package/src/core/tools/exa/linkedin.ts +59 -0
  148. package/src/core/tools/exa/mcp-client.ts +368 -0
  149. package/src/core/tools/exa/render.ts +200 -0
  150. package/src/core/tools/exa/researcher.ts +90 -0
  151. package/src/core/tools/exa/search.ts +338 -0
  152. package/src/core/tools/exa/types.ts +167 -0
  153. package/src/core/tools/exa/websets.ts +248 -0
  154. package/src/core/tools/find.ts +244 -0
  155. package/src/core/tools/grep.ts +584 -0
  156. package/src/core/tools/index.ts +283 -0
  157. package/src/core/tools/ls.ts +142 -0
  158. package/src/core/tools/lsp/client.ts +767 -0
  159. package/src/core/tools/lsp/clients/biome-client.ts +207 -0
  160. package/src/core/tools/lsp/clients/index.ts +49 -0
  161. package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
  162. package/src/core/tools/lsp/config.ts +845 -0
  163. package/src/core/tools/lsp/edits.ts +110 -0
  164. package/src/core/tools/lsp/index.ts +1364 -0
  165. package/src/core/tools/lsp/render.ts +560 -0
  166. package/src/core/tools/lsp/rust-analyzer.ts +145 -0
  167. package/src/core/tools/lsp/types.ts +495 -0
  168. package/src/core/tools/lsp/utils.ts +526 -0
  169. package/src/core/tools/notebook.ts +182 -0
  170. package/src/core/tools/output.ts +198 -0
  171. package/src/core/tools/path-utils.ts +61 -0
  172. package/src/core/tools/read.ts +507 -0
  173. package/src/core/tools/renderers.ts +820 -0
  174. package/src/core/tools/review.ts +275 -0
  175. package/src/core/tools/rulebook.ts +124 -0
  176. package/src/core/tools/task/agents.ts +158 -0
  177. package/src/core/tools/task/artifacts.ts +114 -0
  178. package/src/core/tools/task/commands.ts +157 -0
  179. package/src/core/tools/task/discovery.ts +217 -0
  180. package/src/core/tools/task/executor.ts +531 -0
  181. package/src/core/tools/task/index.ts +548 -0
  182. package/src/core/tools/task/model-resolver.ts +176 -0
  183. package/src/core/tools/task/parallel.ts +38 -0
  184. package/src/core/tools/task/render.ts +502 -0
  185. package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
  186. package/src/core/tools/task/types.ts +142 -0
  187. package/src/core/tools/truncate.ts +265 -0
  188. package/src/core/tools/web-fetch.ts +2511 -0
  189. package/src/core/tools/web-search/auth.ts +199 -0
  190. package/src/core/tools/web-search/index.ts +583 -0
  191. package/src/core/tools/web-search/providers/anthropic.ts +198 -0
  192. package/src/core/tools/web-search/providers/exa.ts +196 -0
  193. package/src/core/tools/web-search/providers/perplexity.ts +195 -0
  194. package/src/core/tools/web-search/render.ts +372 -0
  195. package/src/core/tools/web-search/types.ts +180 -0
  196. package/src/core/tools/write.ts +63 -0
  197. package/src/core/ttsr.ts +211 -0
  198. package/src/core/utils.ts +187 -0
  199. package/src/discovery/agents-md.ts +75 -0
  200. package/src/discovery/builtin.ts +647 -0
  201. package/src/discovery/claude.ts +623 -0
  202. package/src/discovery/cline.ts +104 -0
  203. package/src/discovery/codex.ts +571 -0
  204. package/src/discovery/cursor.ts +266 -0
  205. package/src/discovery/gemini.ts +368 -0
  206. package/src/discovery/github.ts +120 -0
  207. package/src/discovery/helpers.test.ts +127 -0
  208. package/src/discovery/helpers.ts +249 -0
  209. package/src/discovery/index.ts +84 -0
  210. package/src/discovery/mcp-json.ts +127 -0
  211. package/src/discovery/vscode.ts +99 -0
  212. package/src/discovery/windsurf.ts +219 -0
  213. package/src/index.ts +192 -0
  214. package/src/main.ts +507 -0
  215. package/src/migrations.ts +156 -0
  216. package/src/modes/cleanup.ts +23 -0
  217. package/src/modes/index.ts +48 -0
  218. package/src/modes/interactive/components/armin.ts +382 -0
  219. package/src/modes/interactive/components/assistant-message.ts +86 -0
  220. package/src/modes/interactive/components/bash-execution.ts +199 -0
  221. package/src/modes/interactive/components/bordered-loader.ts +41 -0
  222. package/src/modes/interactive/components/branch-summary-message.ts +42 -0
  223. package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
  224. package/src/modes/interactive/components/custom-editor.ts +122 -0
  225. package/src/modes/interactive/components/diff.ts +147 -0
  226. package/src/modes/interactive/components/dynamic-border.ts +25 -0
  227. package/src/modes/interactive/components/extensions/extension-dashboard.ts +296 -0
  228. package/src/modes/interactive/components/extensions/extension-list.ts +479 -0
  229. package/src/modes/interactive/components/extensions/index.ts +9 -0
  230. package/src/modes/interactive/components/extensions/inspector-panel.ts +313 -0
  231. package/src/modes/interactive/components/extensions/state-manager.ts +558 -0
  232. package/src/modes/interactive/components/extensions/types.ts +191 -0
  233. package/src/modes/interactive/components/hook-editor.ts +117 -0
  234. package/src/modes/interactive/components/hook-input.ts +64 -0
  235. package/src/modes/interactive/components/hook-message.ts +96 -0
  236. package/src/modes/interactive/components/hook-selector.ts +91 -0
  237. package/src/modes/interactive/components/model-selector.ts +560 -0
  238. package/src/modes/interactive/components/oauth-selector.ts +136 -0
  239. package/src/modes/interactive/components/plugin-settings.ts +481 -0
  240. package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
  241. package/src/modes/interactive/components/session-selector.ts +220 -0
  242. package/src/modes/interactive/components/settings-defs.ts +597 -0
  243. package/src/modes/interactive/components/settings-selector.ts +545 -0
  244. package/src/modes/interactive/components/show-images-selector.ts +45 -0
  245. package/src/modes/interactive/components/status-line/index.ts +4 -0
  246. package/src/modes/interactive/components/status-line/presets.ts +94 -0
  247. package/src/modes/interactive/components/status-line/segments.ts +350 -0
  248. package/src/modes/interactive/components/status-line/separators.ts +55 -0
  249. package/src/modes/interactive/components/status-line/types.ts +81 -0
  250. package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
  251. package/src/modes/interactive/components/status-line.ts +384 -0
  252. package/src/modes/interactive/components/theme-selector.ts +62 -0
  253. package/src/modes/interactive/components/thinking-selector.ts +64 -0
  254. package/src/modes/interactive/components/tool-execution.ts +946 -0
  255. package/src/modes/interactive/components/tree-selector.ts +877 -0
  256. package/src/modes/interactive/components/ttsr-notification.ts +82 -0
  257. package/src/modes/interactive/components/user-message-selector.ts +159 -0
  258. package/src/modes/interactive/components/user-message.ts +18 -0
  259. package/src/modes/interactive/components/visual-truncate.ts +50 -0
  260. package/src/modes/interactive/components/welcome.ts +228 -0
  261. package/src/modes/interactive/interactive-mode.ts +2669 -0
  262. package/src/modes/interactive/theme/dark.json +102 -0
  263. package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
  264. package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
  265. package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
  266. package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
  267. package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
  268. package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
  269. package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
  270. package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
  271. package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
  272. package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
  273. package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
  274. package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
  275. package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
  276. package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
  277. package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
  278. package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
  279. package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
  280. package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
  281. package/src/modes/interactive/theme/defaults/index.ts +67 -0
  282. package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
  283. package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
  284. package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
  285. package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
  286. package/src/modes/interactive/theme/defaults/light-github.json +114 -0
  287. package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
  288. package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
  289. package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
  290. package/src/modes/interactive/theme/defaults/light-one.json +105 -0
  291. package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
  292. package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
  293. package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
  294. package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
  295. package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
  296. package/src/modes/interactive/theme/light.json +99 -0
  297. package/src/modes/interactive/theme/theme-schema.json +424 -0
  298. package/src/modes/interactive/theme/theme.ts +2211 -0
  299. package/src/modes/print-mode.ts +163 -0
  300. package/src/modes/rpc/rpc-client.ts +527 -0
  301. package/src/modes/rpc/rpc-mode.ts +494 -0
  302. package/src/modes/rpc/rpc-types.ts +203 -0
  303. package/src/prompts/architect-plan.md +10 -0
  304. package/src/prompts/branch-summary-preamble.md +3 -0
  305. package/src/prompts/branch-summary.md +28 -0
  306. package/src/prompts/browser.md +71 -0
  307. package/src/prompts/compaction-summary.md +34 -0
  308. package/src/prompts/compaction-turn-prefix.md +16 -0
  309. package/src/prompts/compaction-update-summary.md +41 -0
  310. package/src/prompts/explore.md +82 -0
  311. package/src/prompts/implement-with-critic.md +11 -0
  312. package/src/prompts/implement.md +11 -0
  313. package/src/prompts/init.md +30 -0
  314. package/src/prompts/plan.md +54 -0
  315. package/src/prompts/reviewer.md +81 -0
  316. package/src/prompts/summarization-system.md +3 -0
  317. package/src/prompts/system-prompt.md +27 -0
  318. package/src/prompts/task.md +56 -0
  319. package/src/prompts/title-system.md +8 -0
  320. package/src/prompts/tools/ask.md +24 -0
  321. package/src/prompts/tools/bash.md +23 -0
  322. package/src/prompts/tools/edit.md +9 -0
  323. package/src/prompts/tools/find.md +6 -0
  324. package/src/prompts/tools/grep.md +12 -0
  325. package/src/prompts/tools/lsp.md +14 -0
  326. package/src/prompts/tools/output.md +23 -0
  327. package/src/prompts/tools/read.md +25 -0
  328. package/src/prompts/tools/web-fetch.md +8 -0
  329. package/src/prompts/tools/web-search.md +10 -0
  330. package/src/prompts/tools/write.md +10 -0
  331. package/src/utils/changelog.ts +99 -0
  332. package/src/utils/clipboard.ts +265 -0
  333. package/src/utils/fuzzy.ts +108 -0
  334. package/src/utils/mime.ts +30 -0
  335. package/src/utils/shell-snapshot.ts +218 -0
  336. package/src/utils/shell.ts +364 -0
  337. package/src/utils/tools-manager.ts +265 -0
@@ -0,0 +1,548 @@
1
+ /**
2
+ * Task tool - Delegate tasks to specialized agents.
3
+ *
4
+ * Discovers agent definitions from:
5
+ * - Bundled agents (shipped with omp-coding-agent)
6
+ * - ~/.omp/agent/agents/*.md (user-level)
7
+ * - .omp/agents/*.md (project-level)
8
+ *
9
+ * Supports:
10
+ * - Single agent execution
11
+ * - Parallel execution with concurrency limits
12
+ * - Progress tracking via JSON events
13
+ * - Session artifacts for debugging
14
+ */
15
+
16
+ import type { AgentTool } from "@oh-my-pi/pi-agent-core";
17
+ import type { Usage } from "@oh-my-pi/pi-ai";
18
+ import type { Theme } from "../../../modes/interactive/theme/theme";
19
+ import { cleanupTempDir, createTempArtifactsDir, getArtifactsDir } from "./artifacts";
20
+ import { discoverAgents, getAgent } from "./discovery";
21
+ import { runSubprocess } from "./executor";
22
+ import { mapWithConcurrencyLimit } from "./parallel";
23
+ import { formatDuration, renderCall, renderResult } from "./render";
24
+ import {
25
+ type AgentProgress,
26
+ MAX_AGENTS_IN_DESCRIPTION,
27
+ MAX_CONCURRENCY,
28
+ MAX_PARALLEL_TASKS,
29
+ OMP_BLOCKED_AGENT_ENV,
30
+ OMP_NO_SUBAGENTS_ENV,
31
+ OMP_SPAWNS_ENV,
32
+ type TaskToolDetails,
33
+ taskSchema,
34
+ } from "./types";
35
+
36
+ // Import review tools for side effects (registers subprocess tool handlers)
37
+ import "../review";
38
+
39
+ /** Format byte count for display */
40
+ function formatBytes(bytes: number): string {
41
+ if (bytes < 1024) return `${bytes}B`;
42
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}K`;
43
+ return `${(bytes / (1024 * 1024)).toFixed(1)}M`;
44
+ }
45
+
46
+ function createUsageTotals(): Usage {
47
+ return {
48
+ input: 0,
49
+ output: 0,
50
+ cacheRead: 0,
51
+ cacheWrite: 0,
52
+ totalTokens: 0,
53
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
54
+ };
55
+ }
56
+
57
+ function addUsageTotals(target: Usage, usage: Partial<Usage>): void {
58
+ const input = usage.input ?? 0;
59
+ const output = usage.output ?? 0;
60
+ const cacheRead = usage.cacheRead ?? 0;
61
+ const cacheWrite = usage.cacheWrite ?? 0;
62
+ const totalTokens = usage.totalTokens ?? input + output + cacheRead + cacheWrite;
63
+ const cost =
64
+ usage.cost ??
65
+ ({
66
+ input: 0,
67
+ output: 0,
68
+ cacheRead: 0,
69
+ cacheWrite: 0,
70
+ total: 0,
71
+ } satisfies Usage["cost"]);
72
+
73
+ target.input += input;
74
+ target.output += output;
75
+ target.cacheRead += cacheRead;
76
+ target.cacheWrite += cacheWrite;
77
+ target.totalTokens += totalTokens;
78
+ target.cost.input += cost.input;
79
+ target.cost.output += cost.output;
80
+ target.cost.cacheRead += cost.cacheRead;
81
+ target.cost.cacheWrite += cost.cacheWrite;
82
+ target.cost.total += cost.total;
83
+ }
84
+
85
+ function parseSubagentUsage(events: string[] | undefined): Usage | undefined {
86
+ if (!events || events.length === 0) return undefined;
87
+
88
+ const totals = createUsageTotals();
89
+ let hasUsage = false;
90
+
91
+ for (const line of events) {
92
+ let event: unknown;
93
+ try {
94
+ event = JSON.parse(line);
95
+ } catch {
96
+ continue;
97
+ }
98
+
99
+ if (!event || typeof event !== "object") continue;
100
+ const record = event as Record<string, unknown>;
101
+ if (record.type !== "message_end") continue;
102
+
103
+ const message = record.message;
104
+ if (!message || typeof message !== "object") continue;
105
+ const msgRecord = message as Record<string, unknown>;
106
+ if (msgRecord.role !== "assistant") continue;
107
+ if (msgRecord.stopReason === "aborted" || msgRecord.stopReason === "error") continue;
108
+
109
+ const usage = msgRecord.usage;
110
+ if (!usage || typeof usage !== "object") continue;
111
+
112
+ addUsageTotals(totals, usage as Partial<Usage>);
113
+ hasUsage = true;
114
+ }
115
+
116
+ return hasUsage ? totals : undefined;
117
+ }
118
+
119
+ /** Session context interface */
120
+ interface SessionContext {
121
+ getSessionFile: () => string | null;
122
+ }
123
+
124
+ /** Task tool options */
125
+ interface TaskToolOptions {
126
+ /** Set of available tool names (for cross-tool awareness) */
127
+ availableTools?: Set<string>;
128
+ }
129
+
130
+ // Re-export types and utilities
131
+ export { loadBundledAgents as BUNDLED_AGENTS } from "./agents";
132
+ export { discoverCommands, expandCommand, getCommand } from "./commands";
133
+ export { discoverAgents, getAgent } from "./discovery";
134
+ export type { AgentDefinition, AgentProgress, SingleResult, TaskParams, TaskToolDetails } from "./types";
135
+ export { taskSchema } from "./types";
136
+
137
+ /**
138
+ * Build dynamic tool description listing available agents.
139
+ */
140
+ function buildDescription(cwd: string): string {
141
+ const { agents } = discoverAgents(cwd);
142
+
143
+ const lines: string[] = [];
144
+
145
+ lines.push("Launch a new agent to handle complex, multi-step tasks autonomously.");
146
+ lines.push("");
147
+ lines.push(
148
+ "The Task tool launches specialized agents (subprocesses) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.",
149
+ );
150
+ lines.push("");
151
+ lines.push("Available agent types and the tools they have access to:");
152
+
153
+ for (const agent of agents.slice(0, MAX_AGENTS_IN_DESCRIPTION)) {
154
+ const tools = agent.tools?.join(", ") || "All tools";
155
+ lines.push(`- ${agent.name}: ${agent.description} (Tools: ${tools})`);
156
+ }
157
+ if (agents.length > MAX_AGENTS_IN_DESCRIPTION) {
158
+ lines.push(` ...and ${agents.length - MAX_AGENTS_IN_DESCRIPTION} more agents`);
159
+ }
160
+
161
+ lines.push("");
162
+ lines.push("When NOT to use the Task tool:");
163
+ lines.push(
164
+ "- If you want to read a specific file path, use the Read or Glob tool instead of the Task tool, to find the match more quickly",
165
+ );
166
+ lines.push(
167
+ '- If you are searching for a specific class definition like "class Foo", use the Glob tool instead, to find the match more quickly',
168
+ );
169
+ lines.push(
170
+ "- If you are searching for code within a specific file or set of 2-3 files, use the Read tool instead of the Task tool, to find the match more quickly",
171
+ );
172
+ lines.push("- Other tasks that are not related to the agent descriptions above");
173
+ lines.push("");
174
+ lines.push("");
175
+ lines.push("Usage notes:");
176
+ lines.push("- Always include a short description of the task in the task parameter");
177
+ lines.push("- Launch multiple agents concurrently whenever possible, to maximize performance");
178
+ lines.push(
179
+ "- When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.",
180
+ );
181
+ lines.push(
182
+ "- Each agent invocation is stateless. You will not be able to send additional messages to the agent, nor will the agent be able to communicate with you outside of its final report. Therefore, your task should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.",
183
+ );
184
+ lines.push(
185
+ "- IMPORTANT: Agent results are intermediate data, not task completions. Use the agent's findings to continue executing the user's request. Do not treat agent reports as 'task complete' signals - they provide context for you to perform the actual work.",
186
+ );
187
+ lines.push("- The agent's outputs should generally be trusted");
188
+ lines.push(
189
+ "- Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent",
190
+ );
191
+ lines.push(
192
+ "- If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.",
193
+ );
194
+ lines.push("");
195
+ lines.push("Parameters:");
196
+ lines.push(
197
+ `- tasks: Array of {agent, task, model?} - tasks to run in parallel (max ${MAX_PARALLEL_TASKS}, ${MAX_CONCURRENCY} concurrent)`,
198
+ );
199
+ lines.push(
200
+ ' - model: (optional) Override the agent\'s default model with fuzzy matching (e.g., "sonnet", "codex", "5.2"). Supports comma-separated fallbacks: "gpt, opus" tries gpt first, then opus. Use "default" for omp\'s default model',
201
+ );
202
+ lines.push(
203
+ "- context: (optional) Shared context string prepended to all task prompts - use this to avoid repeating instructions",
204
+ );
205
+ lines.push("");
206
+ lines.push("Results are always written to {tempdir}/omp-task-{runId}/task_{agent}_{index}.md");
207
+ lines.push("");
208
+ lines.push("Example usage:");
209
+ lines.push("");
210
+ lines.push("<example_agent_descriptions>");
211
+ lines.push('"code-reviewer": use this agent after you are done writing a significant piece of code');
212
+ lines.push('"explore": use this agent for fast codebase exploration and research');
213
+ lines.push("</example_agent_descriptions>");
214
+ lines.push("");
215
+ lines.push("<example>");
216
+ lines.push('user: "Please write a function that checks if a number is prime"');
217
+ lines.push("assistant: Sure let me write a function that checks if a number is prime");
218
+ lines.push("assistant: I'm going to use the Write tool to write the following code:");
219
+ lines.push("<code>");
220
+ lines.push("function isPrime(n) {");
221
+ lines.push(" if (n <= 1) return false");
222
+ lines.push(" for (let i = 2; i * i <= n; i++) {");
223
+ lines.push(" if (n % i === 0) return false");
224
+ lines.push(" }");
225
+ lines.push(" return true");
226
+ lines.push("}");
227
+ lines.push("</code>");
228
+ lines.push("<commentary>");
229
+ lines.push(
230
+ "Since a significant piece of code was written and the task was completed, now use the code-reviewer agent to review the code",
231
+ );
232
+ lines.push("</commentary>");
233
+ lines.push("assistant: Now let me use the code-reviewer agent to review the code");
234
+ lines.push(
235
+ 'assistant: Uses the Task tool: { tasks: [{ agent: "code-reviewer", task: "Review the isPrime function" }] }',
236
+ );
237
+ lines.push("</example>");
238
+ lines.push("");
239
+ lines.push("<example>");
240
+ lines.push('user: "Find all TODO comments in the codebase"');
241
+ lines.push("assistant: I'll use multiple explore agents to search different directories in parallel");
242
+ lines.push("assistant: Uses the Task tool:");
243
+ lines.push("{");
244
+ lines.push(' "context": "Find all TODO comments. Return file:line:content format.",');
245
+ lines.push(' "tasks": [');
246
+ lines.push(' { "agent": "explore", "task": "Search in src/" },');
247
+ lines.push(' { "agent": "explore", "task": "Search in lib/" },');
248
+ lines.push(' { "agent": "explore", "task": "Search in tests/" }');
249
+ lines.push(" ]");
250
+ lines.push("}");
251
+ lines.push("Results → {tempdir}/omp-task-{runId}/task_explore_*.md");
252
+ lines.push("</example>");
253
+
254
+ return lines.join("\n");
255
+ }
256
+
257
+ /**
258
+ * Create the task tool configured for a specific working directory.
259
+ */
260
+ export function createTaskTool(
261
+ cwd: string,
262
+ sessionContext?: SessionContext,
263
+ options?: TaskToolOptions,
264
+ ): AgentTool<typeof taskSchema, TaskToolDetails, Theme> {
265
+ const hasOutputTool = options?.availableTools?.has("output") ?? false;
266
+ // Check if subagents are completely inhibited (legacy recursion prevention)
267
+ if (process.env[OMP_NO_SUBAGENTS_ENV]) {
268
+ return {
269
+ name: "task",
270
+ label: "Task",
271
+ description: "Sub-agents disabled (recursion prevention)",
272
+ parameters: taskSchema,
273
+ execute: async () => ({
274
+ content: [{ type: "text", text: "Sub-agents are disabled for this agent (recursion prevention)." }],
275
+ details: {
276
+ projectAgentsDir: null,
277
+ results: [],
278
+ totalDurationMs: 0,
279
+ },
280
+ }),
281
+ };
282
+ }
283
+
284
+ // Check for same-agent blocking (allows other agent types)
285
+ const blockedAgent = process.env[OMP_BLOCKED_AGENT_ENV];
286
+
287
+ return {
288
+ name: "task",
289
+ label: "Task",
290
+ description: buildDescription(cwd),
291
+ parameters: taskSchema,
292
+ renderCall,
293
+ renderResult,
294
+ execute: async (_toolCallId, params, signal, onUpdate) => {
295
+ const startTime = Date.now();
296
+ const { agents, projectAgentsDir } = discoverAgents(cwd);
297
+ const context = params.context;
298
+
299
+ // Handle empty or missing tasks
300
+ if (!params.tasks || params.tasks.length === 0) {
301
+ const available = agents.map((a) => a.name).join(", ") || "none";
302
+ return {
303
+ content: [
304
+ {
305
+ type: "text",
306
+ text: `No tasks provided. Use: { tasks: [{agent, task}, ...] }\nAvailable agents: ${available}`,
307
+ },
308
+ ],
309
+ details: {
310
+ projectAgentsDir,
311
+ results: [],
312
+ totalDurationMs: 0,
313
+ },
314
+ };
315
+ }
316
+
317
+ // Validate task count
318
+ if (params.tasks.length > MAX_PARALLEL_TASKS) {
319
+ return {
320
+ content: [
321
+ {
322
+ type: "text",
323
+ text: `Too many tasks (${params.tasks.length}). Max is ${MAX_PARALLEL_TASKS}.`,
324
+ },
325
+ ],
326
+ details: {
327
+ projectAgentsDir,
328
+ results: [],
329
+ totalDurationMs: 0,
330
+ },
331
+ };
332
+ }
333
+
334
+ // Derive artifacts directory
335
+ const sessionFile = sessionContext?.getSessionFile() ?? null;
336
+ const artifactsDir = sessionFile ? getArtifactsDir(sessionFile) : null;
337
+ const tempArtifactsDir = artifactsDir ? null : createTempArtifactsDir();
338
+ const effectiveArtifactsDir = artifactsDir || tempArtifactsDir!;
339
+
340
+ // Initialize progress tracking
341
+ const progressMap = new Map<number, AgentProgress>();
342
+
343
+ // Update callback
344
+ const emitProgress = () => {
345
+ const progress = Array.from(progressMap.values()).sort((a, b) => a.index - b.index);
346
+ onUpdate?.({
347
+ content: [{ type: "text", text: `Running ${params.tasks.length} agents...` }],
348
+ details: {
349
+ projectAgentsDir,
350
+ results: [],
351
+ totalDurationMs: Date.now() - startTime,
352
+ progress,
353
+ },
354
+ });
355
+ };
356
+
357
+ try {
358
+ let tasks = params.tasks;
359
+ let skippedSelfRecursion = 0;
360
+
361
+ // Filter out blocked agent (self-recursion prevention)
362
+ if (blockedAgent) {
363
+ const blockedTasks = tasks.filter((t) => t.agent === blockedAgent);
364
+ tasks = tasks.filter((t) => t.agent !== blockedAgent);
365
+ skippedSelfRecursion = blockedTasks.length;
366
+
367
+ if (skippedSelfRecursion > 0 && tasks.length === 0) {
368
+ return {
369
+ content: [
370
+ {
371
+ type: "text",
372
+ text: `Cannot spawn ${blockedAgent} agent from within itself (recursion prevention). Use a different agent type.`,
373
+ },
374
+ ],
375
+ details: {
376
+ projectAgentsDir,
377
+ results: [],
378
+ totalDurationMs: Date.now() - startTime,
379
+ },
380
+ };
381
+ }
382
+ }
383
+
384
+ // Validate all agents exist
385
+ for (const task of tasks) {
386
+ if (!getAgent(agents, task.agent)) {
387
+ const available = agents.map((a) => a.name).join(", ");
388
+ return {
389
+ content: [{ type: "text", text: `Unknown agent: ${task.agent}. Available: ${available}` }],
390
+ details: {
391
+ projectAgentsDir,
392
+ results: [],
393
+ totalDurationMs: Date.now() - startTime,
394
+ },
395
+ };
396
+ }
397
+ }
398
+
399
+ // Check spawn restrictions from parent
400
+ const parentSpawns = process.env[OMP_SPAWNS_ENV];
401
+ const isSpawnAllowed = (agentName: string): boolean => {
402
+ if (parentSpawns === undefined) return true; // Root = allow all
403
+ if (parentSpawns === "") return false; // Empty = deny all
404
+ if (parentSpawns === "*") return true; // Wildcard = allow all
405
+ const allowed = new Set(parentSpawns.split(",").map((s) => s.trim()));
406
+ return allowed.has(agentName);
407
+ };
408
+
409
+ for (const task of tasks) {
410
+ if (!isSpawnAllowed(task.agent)) {
411
+ const allowed = parentSpawns === "" ? "none (spawns disabled for this agent)" : parentSpawns;
412
+ return {
413
+ content: [{ type: "text", text: `Cannot spawn '${task.agent}'. Allowed: ${allowed}` }],
414
+ details: {
415
+ projectAgentsDir,
416
+ results: [],
417
+ totalDurationMs: Date.now() - startTime,
418
+ },
419
+ };
420
+ }
421
+ }
422
+
423
+ // Initialize progress for all tasks
424
+ for (let i = 0; i < tasks.length; i++) {
425
+ const agentCfg = getAgent(agents, tasks[i].agent);
426
+ progressMap.set(i, {
427
+ index: i,
428
+ agent: tasks[i].agent,
429
+ agentSource: agentCfg?.source ?? "user",
430
+ status: "pending",
431
+ task: tasks[i].task,
432
+ recentTools: [],
433
+ recentOutput: [],
434
+ toolCount: 0,
435
+ tokens: 0,
436
+ durationMs: 0,
437
+ modelOverride: tasks[i].model,
438
+ });
439
+ }
440
+ emitProgress();
441
+
442
+ // Build full prompts with context prepended
443
+ const tasksWithContext = tasks.map((t) => ({
444
+ agent: t.agent,
445
+ task: context ? `${context}\n\n${t.task}` : t.task,
446
+ model: t.model,
447
+ }));
448
+
449
+ // Execute in parallel with concurrency limit
450
+ const results = await mapWithConcurrencyLimit(tasksWithContext, MAX_CONCURRENCY, async (task, index) => {
451
+ const agent = getAgent(agents, task.agent)!;
452
+ return runSubprocess({
453
+ cwd,
454
+ agent,
455
+ task: task.task,
456
+ index,
457
+ context: undefined, // Already prepended above
458
+ modelOverride: task.model,
459
+ sessionFile,
460
+ persistArtifacts: !!artifactsDir,
461
+ artifactsDir: effectiveArtifactsDir,
462
+ signal,
463
+ onProgress: (progress) => {
464
+ progressMap.set(index, structuredClone(progress));
465
+ emitProgress();
466
+ },
467
+ });
468
+ });
469
+
470
+ const aggregatedUsage = createUsageTotals();
471
+ let hasAggregatedUsage = false;
472
+ const resultsWithUsage = results.map((result) => {
473
+ const usage = parseSubagentUsage(result.jsonlEvents);
474
+ if (usage) {
475
+ addUsageTotals(aggregatedUsage, usage);
476
+ hasAggregatedUsage = true;
477
+ return { ...result, usage };
478
+ }
479
+ return result;
480
+ });
481
+
482
+ // Collect output paths (artifacts already written by executor in real-time)
483
+ const outputPaths: string[] = [];
484
+ for (const result of resultsWithUsage) {
485
+ if (result.artifactPaths) {
486
+ outputPaths.push(result.artifactPaths.outputPath);
487
+ }
488
+ }
489
+
490
+ // Build final output - match plugin format
491
+ const successCount = resultsWithUsage.filter((r) => r.exitCode === 0).length;
492
+ const totalDuration = Date.now() - startTime;
493
+
494
+ const summaries = resultsWithUsage.map((r) => {
495
+ const status = r.exitCode === 0 ? "completed" : `failed (exit ${r.exitCode})`;
496
+ const output = r.output.trim() || r.stderr.trim() || "(no output)";
497
+ const preview = output.split("\n").slice(0, 5).join("\n");
498
+ // Include output metadata and ID; include path only if Output tool unavailable (for Read fallback)
499
+ const outputId = `${r.agent}_${r.index}`;
500
+ const meta = r.outputMeta
501
+ ? ` [${r.outputMeta.lineCount} lines, ${formatBytes(r.outputMeta.charCount)}]`
502
+ : "";
503
+ const pathInfo = !hasOutputTool && r.artifactPaths?.outputPath ? ` (${r.artifactPaths.outputPath})` : "";
504
+ return `[${r.agent}] ${status}${meta} ${outputId}${pathInfo}\n${preview}`;
505
+ });
506
+
507
+ const skippedNote =
508
+ skippedSelfRecursion > 0
509
+ ? ` (${skippedSelfRecursion} ${blockedAgent} task${skippedSelfRecursion > 1 ? "s" : ""} skipped - self-recursion blocked)`
510
+ : "";
511
+ const summary = `${successCount}/${resultsWithUsage.length} succeeded${skippedNote} [${formatDuration(totalDuration)}]\n\n${summaries.join("\n\n---\n\n")}`;
512
+
513
+ // Cleanup temp directory if used
514
+ if (tempArtifactsDir) {
515
+ await cleanupTempDir(tempArtifactsDir);
516
+ }
517
+
518
+ return {
519
+ content: [{ type: "text", text: summary }],
520
+ details: {
521
+ projectAgentsDir,
522
+ results: resultsWithUsage,
523
+ totalDurationMs: totalDuration,
524
+ usage: hasAggregatedUsage ? aggregatedUsage : undefined,
525
+ outputPaths,
526
+ },
527
+ };
528
+ } catch (err) {
529
+ // Cleanup temp directory on error
530
+ if (tempArtifactsDir) {
531
+ await cleanupTempDir(tempArtifactsDir);
532
+ }
533
+
534
+ return {
535
+ content: [{ type: "text", text: `Task execution failed: ${err}` }],
536
+ details: {
537
+ projectAgentsDir,
538
+ results: [],
539
+ totalDurationMs: Date.now() - startTime,
540
+ },
541
+ };
542
+ }
543
+ },
544
+ };
545
+ }
546
+
547
+ // Default task tool using process.cwd()
548
+ export const taskTool = createTaskTool(process.cwd());