@oh-my-pi/pi-coding-agent 15.5.15 → 15.7.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 (274) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/types/capability/rule-buckets.d.ts +30 -0
  3. package/dist/types/capability/rule.d.ts +7 -0
  4. package/dist/types/cli/classify-install-target.d.ts +0 -10
  5. package/dist/types/cli/completion-gen.d.ts +80 -0
  6. package/dist/types/cli/initial-message.d.ts +1 -1
  7. package/dist/types/cli/tiny-models-cli.d.ts +9 -0
  8. package/dist/types/commands/complete.d.ts +6 -0
  9. package/dist/types/commands/completions.d.ts +13 -0
  10. package/dist/types/commands/setup.d.ts +10 -1
  11. package/dist/types/commands/tiny-models.d.ts +22 -0
  12. package/dist/types/commit/analysis/conventional.d.ts +1 -1
  13. package/dist/types/commit/analysis/summary.d.ts +1 -1
  14. package/dist/types/commit/changelog/generate.d.ts +1 -1
  15. package/dist/types/commit/changelog/index.d.ts +2 -2
  16. package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
  17. package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
  18. package/dist/types/config/model-id-affixes.d.ts +10 -0
  19. package/dist/types/config/settings-schema.d.ts +402 -17
  20. package/dist/types/discovery/builtin-defaults.d.ts +1 -0
  21. package/dist/types/discovery/builtin-rules/index.d.ts +7 -0
  22. package/dist/types/discovery/helpers.d.ts +1 -1
  23. package/dist/types/discovery/index.d.ts +1 -0
  24. package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
  25. package/dist/types/edit/hashline/block-resolver.d.ts +9 -0
  26. package/dist/types/edit/hashline/index.d.ts +1 -0
  27. package/dist/types/eval/js/shared/rewrite-imports.d.ts +16 -1
  28. package/dist/types/eval/py/kernel.d.ts +3 -0
  29. package/dist/types/eval/py/runtime.d.ts +11 -1
  30. package/dist/types/export/html/template.generated.d.ts +1 -1
  31. package/dist/types/internal-urls/agent-protocol.d.ts +2 -1
  32. package/dist/types/internal-urls/artifact-protocol.d.ts +2 -1
  33. package/dist/types/internal-urls/local-protocol.d.ts +2 -1
  34. package/dist/types/internal-urls/memory-protocol.d.ts +2 -1
  35. package/dist/types/internal-urls/omp-protocol.d.ts +2 -1
  36. package/dist/types/internal-urls/router.d.ts +8 -1
  37. package/dist/types/internal-urls/rule-protocol.d.ts +2 -1
  38. package/dist/types/internal-urls/skill-protocol.d.ts +2 -1
  39. package/dist/types/internal-urls/types.d.ts +26 -0
  40. package/dist/types/main.d.ts +1 -0
  41. package/dist/types/memory-backend/index.d.ts +1 -0
  42. package/dist/types/memory-backend/resolve.d.ts +2 -1
  43. package/dist/types/memory-backend/types.d.ts +7 -1
  44. package/dist/types/mnemosyne/backend.d.ts +4 -0
  45. package/dist/types/mnemosyne/config.d.ts +29 -0
  46. package/dist/types/mnemosyne/index.d.ts +3 -0
  47. package/dist/types/mnemosyne/state.d.ts +72 -0
  48. package/dist/types/modes/components/custom-editor.d.ts +2 -3
  49. package/dist/types/modes/components/hook-selector.d.ts +27 -0
  50. package/dist/types/modes/components/index.d.ts +2 -0
  51. package/dist/types/modes/components/segment-track.d.ts +22 -0
  52. package/dist/types/modes/components/status-line/context-thresholds.d.ts +6 -0
  53. package/dist/types/modes/components/tiny-title-download-progress.d.ts +11 -0
  54. package/dist/types/modes/components/welcome.d.ts +22 -0
  55. package/dist/types/modes/controllers/extension-ui-controller.d.ts +4 -1
  56. package/dist/types/modes/gradient-highlight.d.ts +23 -0
  57. package/dist/types/modes/interactive-mode.d.ts +7 -4
  58. package/dist/types/modes/internal-url-autocomplete.d.ts +43 -0
  59. package/dist/types/modes/orchestrate.d.ts +10 -0
  60. package/dist/types/modes/setup-wizard/index.d.ts +16 -0
  61. package/dist/types/modes/setup-wizard/scenes/glyph.d.ts +2 -0
  62. package/dist/types/modes/setup-wizard/scenes/outro.d.ts +2 -0
  63. package/dist/types/modes/setup-wizard/scenes/providers.d.ts +2 -0
  64. package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +19 -0
  65. package/dist/types/modes/setup-wizard/scenes/splash.d.ts +11 -0
  66. package/dist/types/modes/setup-wizard/scenes/theme.d.ts +2 -0
  67. package/dist/types/modes/setup-wizard/scenes/types.d.ts +43 -0
  68. package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +19 -0
  69. package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +14 -0
  70. package/dist/types/modes/theme/defaults/index.d.ts +8406 -8406
  71. package/dist/types/modes/theme/shimmer.d.ts +2 -0
  72. package/dist/types/modes/theme/theme.d.ts +11 -0
  73. package/dist/types/modes/types.d.ts +5 -1
  74. package/dist/types/modes/ultrathink.d.ts +3 -3
  75. package/dist/types/modes/utils/keybinding-matchers.d.ts +5 -0
  76. package/dist/types/sdk.d.ts +3 -0
  77. package/dist/types/session/agent-session.d.ts +33 -0
  78. package/dist/types/system-prompt.d.ts +2 -0
  79. package/dist/types/task/executor.d.ts +2 -0
  80. package/dist/types/task/render.d.ts +5 -1
  81. package/dist/types/tiny/device.d.ts +78 -0
  82. package/dist/types/tiny/dtype.d.ts +85 -0
  83. package/dist/types/tiny/models.d.ts +185 -0
  84. package/dist/types/tiny/text.d.ts +19 -0
  85. package/dist/types/tiny/title-client.d.ts +32 -0
  86. package/dist/types/tiny/title-protocol.d.ts +74 -0
  87. package/dist/types/tiny/worker.d.ts +2 -0
  88. package/dist/types/tools/bash.d.ts +3 -2
  89. package/dist/types/tools/eval.d.ts +1 -1
  90. package/dist/types/tools/index.d.ts +7 -4
  91. package/dist/types/tools/memory-edit.d.ts +40 -0
  92. package/dist/types/tools/{hindsight-recall.d.ts → memory-recall.d.ts} +6 -6
  93. package/dist/types/tools/{hindsight-reflect.d.ts → memory-reflect.d.ts} +6 -6
  94. package/dist/types/tools/memory-render.d.ts +60 -0
  95. package/dist/types/tools/{hindsight-retain.d.ts → memory-retain.d.ts} +6 -6
  96. package/dist/types/tools/todo-write.d.ts +8 -0
  97. package/dist/types/tools/tool-result.d.ts +2 -0
  98. package/dist/types/tui/code-cell.d.ts +2 -0
  99. package/dist/types/tui/output-block.d.ts +17 -0
  100. package/dist/types/utils/title-generator.d.ts +3 -0
  101. package/package.json +18 -14
  102. package/scripts/build-binary.ts +1 -0
  103. package/src/capability/rule-buckets.ts +64 -0
  104. package/src/capability/rule.ts +8 -0
  105. package/src/cli/completion-gen.ts +550 -0
  106. package/src/cli/setup-cli.ts +5 -3
  107. package/src/cli/tiny-models-cli.ts +127 -0
  108. package/src/cli-commands.ts +3 -0
  109. package/src/cli.ts +9 -15
  110. package/src/commands/complete.ts +66 -0
  111. package/src/commands/completions.ts +60 -0
  112. package/src/commands/setup.ts +29 -4
  113. package/src/commands/tiny-models.ts +36 -0
  114. package/src/config/model-equivalence.ts +43 -2
  115. package/src/config/model-id-affixes.ts +64 -0
  116. package/src/config/model-registry.ts +84 -10
  117. package/src/config/settings-schema.ts +275 -15
  118. package/src/discovery/builtin-defaults.ts +39 -0
  119. package/src/discovery/builtin-rules/index.ts +48 -0
  120. package/src/discovery/builtin-rules/rs-box-leak.md +48 -0
  121. package/src/discovery/builtin-rules/rs-future-prelude.md +23 -0
  122. package/src/discovery/builtin-rules/rs-lazylock.md +51 -0
  123. package/src/discovery/builtin-rules/rs-match-ergonomics.md +67 -0
  124. package/src/discovery/builtin-rules/rs-parking-lot.md +44 -0
  125. package/src/discovery/builtin-rules/rs-result-type.md +19 -0
  126. package/src/discovery/builtin-rules/ts-bare-catch.md +38 -0
  127. package/src/discovery/builtin-rules/ts-import-type.md +42 -0
  128. package/src/discovery/builtin-rules/ts-no-any.md +56 -0
  129. package/src/discovery/builtin-rules/ts-no-dynamic-import.md +39 -0
  130. package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
  131. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +50 -0
  132. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +65 -0
  133. package/src/discovery/builtin-rules/ts-set-map.md +28 -0
  134. package/src/discovery/index.ts +1 -0
  135. package/src/edit/hashline/block-resolver.ts +14 -0
  136. package/src/edit/hashline/diff.ts +9 -8
  137. package/src/edit/hashline/execute.ts +2 -1
  138. package/src/edit/hashline/index.ts +1 -0
  139. package/src/eval/__tests__/shared-executors.test.ts +36 -0
  140. package/src/eval/js/shared/local-module-loader.ts +13 -1
  141. package/src/eval/js/shared/rewrite-imports.ts +31 -26
  142. package/src/eval/py/kernel.ts +37 -15
  143. package/src/eval/py/runtime.ts +57 -28
  144. package/src/export/html/template.generated.ts +1 -1
  145. package/src/export/html/template.js +0 -12
  146. package/src/export/ttsr.ts +2 -0
  147. package/src/internal-urls/agent-protocol.ts +18 -1
  148. package/src/internal-urls/artifact-protocol.ts +19 -1
  149. package/src/internal-urls/docs-index.generated.ts +8 -7
  150. package/src/internal-urls/local-protocol.ts +14 -1
  151. package/src/internal-urls/memory-protocol.ts +6 -1
  152. package/src/internal-urls/omp-protocol.ts +5 -1
  153. package/src/internal-urls/router.ts +20 -1
  154. package/src/internal-urls/rule-protocol.ts +8 -1
  155. package/src/internal-urls/skill-protocol.ts +8 -1
  156. package/src/internal-urls/types.ts +27 -0
  157. package/src/lsp/render.ts +1 -1
  158. package/src/main.ts +18 -1
  159. package/src/mcp/oauth-flow.ts +2 -2
  160. package/src/memory-backend/index.ts +1 -0
  161. package/src/memory-backend/resolve.ts +4 -1
  162. package/src/memory-backend/types.ts +8 -1
  163. package/src/mnemosyne/backend.ts +374 -0
  164. package/src/mnemosyne/config.ts +160 -0
  165. package/src/mnemosyne/index.ts +3 -0
  166. package/src/mnemosyne/state.ts +548 -0
  167. package/src/modes/acp/acp-agent.ts +11 -6
  168. package/src/modes/components/agent-dashboard.ts +4 -4
  169. package/src/modes/components/custom-editor.ts +3 -2
  170. package/src/modes/components/diff.ts +2 -2
  171. package/src/modes/components/extensions/extension-list.ts +3 -2
  172. package/src/modes/components/footer.ts +5 -6
  173. package/src/modes/components/history-search.ts +3 -3
  174. package/src/modes/components/hook-selector.ts +92 -8
  175. package/src/modes/components/index.ts +2 -0
  176. package/src/modes/components/mcp-add-wizard.ts +3 -3
  177. package/src/modes/components/model-selector.ts +5 -4
  178. package/src/modes/components/oauth-selector.ts +3 -3
  179. package/src/modes/components/segment-track.ts +52 -0
  180. package/src/modes/components/session-observer-overlay.ts +19 -13
  181. package/src/modes/components/session-selector.ts +3 -3
  182. package/src/modes/components/settings-defs.ts +7 -0
  183. package/src/modes/components/status-line/context-thresholds.ts +11 -0
  184. package/src/modes/components/status-line/segments.ts +2 -2
  185. package/src/modes/components/tiny-title-download-progress.ts +90 -0
  186. package/src/modes/components/tips.txt +13 -0
  187. package/src/modes/components/tool-execution.ts +72 -4
  188. package/src/modes/components/tree-selector.ts +3 -3
  189. package/src/modes/components/user-message-selector.ts +3 -3
  190. package/src/modes/components/welcome.ts +102 -43
  191. package/src/modes/controllers/command-controller.ts +16 -1
  192. package/src/modes/controllers/extension-ui-controller.ts +3 -1
  193. package/src/modes/controllers/input-controller.ts +69 -21
  194. package/src/modes/gradient-highlight.ts +70 -0
  195. package/src/modes/interactive-mode.ts +75 -114
  196. package/src/modes/internal-url-autocomplete.ts +143 -0
  197. package/src/modes/orchestrate.ts +36 -0
  198. package/src/modes/prompt-action-autocomplete.ts +12 -0
  199. package/src/modes/setup-wizard/index.ts +88 -0
  200. package/src/modes/setup-wizard/scenes/glyph.ts +96 -0
  201. package/src/modes/setup-wizard/scenes/outro.ts +35 -0
  202. package/src/modes/setup-wizard/scenes/providers.ts +69 -0
  203. package/src/modes/setup-wizard/scenes/sign-in.ts +193 -0
  204. package/src/modes/setup-wizard/scenes/splash.ts +201 -0
  205. package/src/modes/setup-wizard/scenes/theme.ts +299 -0
  206. package/src/modes/setup-wizard/scenes/types.ts +48 -0
  207. package/src/modes/setup-wizard/scenes/web-search.ts +128 -0
  208. package/src/modes/setup-wizard/wizard-overlay.ts +275 -0
  209. package/src/modes/theme/shimmer.ts +5 -0
  210. package/src/modes/theme/theme.ts +44 -20
  211. package/src/modes/types.ts +6 -1
  212. package/src/modes/ultrathink.ts +9 -53
  213. package/src/modes/utils/keybinding-matchers.ts +11 -0
  214. package/src/prompts/system/memory-consolidation-system.md +8 -0
  215. package/src/prompts/system/memory-extraction-system.md +26 -0
  216. package/src/prompts/{commands/orchestrate.md → system/orchestrate-notice.md} +6 -17
  217. package/src/prompts/system/system-prompt.md +2 -0
  218. package/src/prompts/system/tiny-title-system.md +8 -0
  219. package/src/prompts/tools/memory-edit.md +8 -0
  220. package/src/prompts/tools/read.md +4 -0
  221. package/src/prompts/tools/task.md +4 -7
  222. package/src/sdk.ts +13 -21
  223. package/src/session/agent-session.ts +128 -44
  224. package/src/slash-commands/builtin-registry.ts +18 -1
  225. package/src/system-prompt.ts +4 -0
  226. package/src/task/commands.ts +1 -5
  227. package/src/task/executor.ts +8 -0
  228. package/src/task/index.ts +2 -0
  229. package/src/task/render.ts +69 -26
  230. package/src/tiny/device.ts +117 -0
  231. package/src/tiny/dtype.ts +101 -0
  232. package/src/tiny/models.ts +218 -0
  233. package/src/tiny/text.ts +54 -0
  234. package/src/tiny/title-client.ts +395 -0
  235. package/src/tiny/title-protocol.ts +51 -0
  236. package/src/tiny/worker.ts +587 -0
  237. package/src/tools/bash.ts +74 -29
  238. package/src/tools/browser/tab-worker.ts +1 -1
  239. package/src/tools/eval.ts +9 -4
  240. package/src/tools/index.ts +17 -22
  241. package/src/tools/memory-edit.ts +59 -0
  242. package/src/tools/memory-recall.ts +100 -0
  243. package/src/tools/memory-reflect.ts +88 -0
  244. package/src/tools/memory-render.ts +185 -0
  245. package/src/tools/memory-retain.ts +91 -0
  246. package/src/tools/read.ts +1 -0
  247. package/src/tools/renderers.ts +4 -2
  248. package/src/tools/todo-write.ts +128 -29
  249. package/src/tools/tool-result.ts +8 -0
  250. package/src/tui/code-cell.ts +6 -1
  251. package/src/tui/output-block.ts +199 -38
  252. package/src/utils/title-generator.ts +115 -13
  253. package/dist/types/tools/recipe/index.d.ts +0 -46
  254. package/dist/types/tools/recipe/render.d.ts +0 -36
  255. package/dist/types/tools/recipe/runner.d.ts +0 -60
  256. package/dist/types/tools/recipe/runners/cargo.d.ts +0 -16
  257. package/dist/types/tools/recipe/runners/index.d.ts +0 -2
  258. package/dist/types/tools/recipe/runners/just.d.ts +0 -2
  259. package/dist/types/tools/recipe/runners/make.d.ts +0 -2
  260. package/dist/types/tools/recipe/runners/pkg.d.ts +0 -2
  261. package/dist/types/tools/recipe/runners/task.d.ts +0 -2
  262. package/src/prompts/tools/recipe.md +0 -16
  263. package/src/tools/hindsight-recall.ts +0 -69
  264. package/src/tools/hindsight-reflect.ts +0 -58
  265. package/src/tools/hindsight-retain.ts +0 -57
  266. package/src/tools/recipe/index.ts +0 -81
  267. package/src/tools/recipe/render.ts +0 -19
  268. package/src/tools/recipe/runner.ts +0 -219
  269. package/src/tools/recipe/runners/cargo.ts +0 -131
  270. package/src/tools/recipe/runners/index.ts +0 -8
  271. package/src/tools/recipe/runners/just.ts +0 -73
  272. package/src/tools/recipe/runners/make.ts +0 -101
  273. package/src/tools/recipe/runners/pkg.ts +0 -167
  274. package/src/tools/recipe/runners/task.ts +0 -72
@@ -1,69 +0,0 @@
1
- import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
- import { logger, untilAborted } from "@oh-my-pi/pi-utils";
3
- import * as z from "zod/v4";
4
- import { formatCurrentTime, formatMemories } from "../hindsight/content";
5
- import recallDescription from "../prompts/tools/recall.md" with { type: "text" };
6
- import type { ToolSession } from ".";
7
-
8
- const hindsightRecallSchema = z.object({
9
- query: z.string().describe("natural language search query"),
10
- });
11
-
12
- export type HindsightRecallParams = z.infer<typeof hindsightRecallSchema>;
13
-
14
- export class HindsightRecallTool implements AgentTool<typeof hindsightRecallSchema> {
15
- readonly name = "recall";
16
- readonly approval = "read" as const;
17
- readonly label = "Recall";
18
- readonly description = recallDescription;
19
- readonly parameters = hindsightRecallSchema;
20
- readonly strict = true;
21
- readonly loadMode = "discoverable";
22
- readonly summary = "Search hindsight memory for relevant prior context";
23
-
24
- constructor(private readonly session: ToolSession) {}
25
-
26
- static createIf(session: ToolSession): HindsightRecallTool | null {
27
- if (session.settings.get("memory.backend") !== "hindsight") return null;
28
- return new HindsightRecallTool(session);
29
- }
30
-
31
- async execute(_id: string, params: HindsightRecallParams, signal?: AbortSignal): Promise<AgentToolResult> {
32
- return untilAborted(signal, async () => {
33
- const state = this.session.getHindsightSessionState?.();
34
- if (!state) {
35
- throw new Error("Hindsight backend is not initialised for this session.");
36
- }
37
-
38
- try {
39
- const response = await state.client.recall(state.bankId, params.query, {
40
- budget: state.config.recallBudget,
41
- maxTokens: state.config.recallMaxTokens,
42
- types: state.config.recallTypes.length > 0 ? state.config.recallTypes : undefined,
43
- tags: state.recallTags,
44
- tagsMatch: state.recallTagsMatch,
45
- });
46
- const results = response.results ?? [];
47
- if (results.length === 0) {
48
- return {
49
- content: [{ type: "text", text: "No relevant memories found." }],
50
- details: {},
51
- };
52
- }
53
- const formatted = formatMemories(results);
54
- return {
55
- content: [
56
- {
57
- type: "text",
58
- text: `Found ${results.length} relevant memories (as of ${formatCurrentTime()} UTC):\n\n${formatted}`,
59
- },
60
- ],
61
- details: {},
62
- };
63
- } catch (err) {
64
- logger.warn("recall failed", { bankId: state.bankId, error: String(err) });
65
- throw err instanceof Error ? err : new Error(String(err));
66
- }
67
- });
68
- }
69
- }
@@ -1,58 +0,0 @@
1
- import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
- import { logger, untilAborted } from "@oh-my-pi/pi-utils";
3
- import * as z from "zod/v4";
4
- import { ensureBankMission } from "../hindsight/bank";
5
- import reflectDescription from "../prompts/tools/reflect.md" with { type: "text" };
6
- import type { ToolSession } from ".";
7
-
8
- const hindsightReflectSchema = z.object({
9
- query: z.string().describe("question to answer"),
10
- context: z.string().describe("optional context").optional(),
11
- });
12
-
13
- export type HindsightReflectParams = z.infer<typeof hindsightReflectSchema>;
14
-
15
- export class HindsightReflectTool implements AgentTool<typeof hindsightReflectSchema> {
16
- readonly name = "reflect";
17
- readonly approval = "read" as const;
18
- readonly label = "Reflect";
19
- readonly description = reflectDescription;
20
- readonly parameters = hindsightReflectSchema;
21
- readonly strict = true;
22
- readonly loadMode = "discoverable";
23
- readonly summary = "Reflect on recent work and write hindsight memory";
24
-
25
- constructor(private readonly session: ToolSession) {}
26
-
27
- static createIf(session: ToolSession): HindsightReflectTool | null {
28
- if (session.settings.get("memory.backend") !== "hindsight") return null;
29
- return new HindsightReflectTool(session);
30
- }
31
-
32
- async execute(_id: string, params: HindsightReflectParams, signal?: AbortSignal): Promise<AgentToolResult> {
33
- return untilAborted(signal, async () => {
34
- const state = this.session.getHindsightSessionState?.();
35
- if (!state) {
36
- throw new Error("Hindsight backend is not initialised for this session.");
37
- }
38
-
39
- try {
40
- await ensureBankMission(state.client, state.bankId, state.config, state.missionsSet);
41
- const response = await state.client.reflect(state.bankId, params.query, {
42
- context: params.context,
43
- budget: state.config.recallBudget,
44
- tags: state.recallTags,
45
- tagsMatch: state.recallTagsMatch,
46
- });
47
- const text = response.text?.trim() || "No relevant information found to reflect on.";
48
- return {
49
- content: [{ type: "text", text }],
50
- details: {},
51
- };
52
- } catch (err) {
53
- logger.warn("reflect failed", { bankId: state.bankId, error: String(err) });
54
- throw err instanceof Error ? err : new Error(String(err));
55
- }
56
- });
57
- }
58
- }
@@ -1,57 +0,0 @@
1
- import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
- import * as z from "zod/v4";
3
- import retainDescription from "../prompts/tools/retain.md" with { type: "text" };
4
- import type { ToolSession } from ".";
5
-
6
- const hindsightRetainSchema = z.object({
7
- items: z
8
- .array(
9
- z.object({
10
- content: z.string().describe("information to remember"),
11
- context: z.string().describe("source context").optional(),
12
- }),
13
- )
14
- .min(1)
15
- .describe("memories to retain"),
16
- });
17
-
18
- export type HindsightRetainParams = z.infer<typeof hindsightRetainSchema>;
19
- export class HindsightRetainTool implements AgentTool<typeof hindsightRetainSchema> {
20
- readonly name = "retain";
21
- readonly approval = "read" as const;
22
- readonly label = "Retain";
23
- readonly description = retainDescription;
24
- readonly parameters = hindsightRetainSchema;
25
- readonly strict = true;
26
- readonly loadMode = "discoverable";
27
- readonly summary = "Store important facts in hindsight memory";
28
-
29
- constructor(private readonly session: ToolSession) {}
30
-
31
- static createIf(session: ToolSession): HindsightRetainTool | null {
32
- if (session.settings.get("memory.backend") !== "hindsight") return null;
33
- return new HindsightRetainTool(session);
34
- }
35
-
36
- async execute(_id: string, params: HindsightRetainParams): Promise<AgentToolResult> {
37
- const state = this.session.getHindsightSessionState?.();
38
- if (!state) {
39
- throw new Error("Hindsight backend is not initialised for this session.");
40
- }
41
-
42
- // Push every item onto the session-owned queue and return immediately.
43
- // The queue flushes either when it reaches its batch threshold or when
44
- // its debounce timer fires. If the eventual batch fails, the queue
45
- // surfaces a UI-only warning notice — the LLM is not informed.
46
- for (const item of params.items) {
47
- state.enqueueRetain(item.content, item.context);
48
- }
49
-
50
- const count = params.items.length;
51
- const noun = count === 1 ? "memory" : "memories";
52
- return {
53
- content: [{ type: "text", text: `${count} ${noun} queued.` }],
54
- details: { count },
55
- };
56
- }
57
- }
@@ -1,81 +0,0 @@
1
- import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
- import type { Component } from "@oh-my-pi/pi-tui";
3
- import { prompt } from "@oh-my-pi/pi-utils";
4
- import * as z from "zod/v4";
5
- import type { RenderResultOptions } from "../../extensibility/custom-tools/types";
6
- import type { Theme } from "../../modes/theme/theme";
7
- import recipeDescription from "../../prompts/tools/recipe.md" with { type: "text" };
8
- import type { ToolSession } from "..";
9
- import { type BashRenderContext, BashTool, type BashToolDetails } from "../bash";
10
- import { createRecipeToolRenderer, type RecipeRenderArgs } from "./render";
11
- import { buildPromptModel, type DetectedRunner, resolveCommand } from "./runner";
12
- import { RUNNERS } from "./runners";
13
-
14
- const recipeSchema = z
15
- .object({
16
- op: z.string().describe('task name and args, e.g. "test" or "build --release"'),
17
- })
18
- .strict();
19
- type RecipeParams = z.infer<typeof recipeSchema>;
20
-
21
- type RecipeRenderResult = {
22
- content: Array<{ type: string; text?: string }>;
23
- details?: BashToolDetails;
24
- isError?: boolean;
25
- };
26
-
27
- export class RecipeTool implements AgentTool<typeof recipeSchema, BashToolDetails, Theme> {
28
- readonly name = "recipe";
29
- readonly label = "Run";
30
- readonly approval = "exec" as const;
31
- readonly description: string;
32
- readonly parameters = recipeSchema;
33
- readonly strict = true;
34
- readonly concurrency = "exclusive";
35
- readonly loadMode = "discoverable";
36
- readonly summary = "Execute a saved bash recipe (multi-step shell command preset)";
37
- readonly mergeCallAndResult = true;
38
- readonly inline = true;
39
- readonly renderCall: (args: RecipeRenderArgs, options: RenderResultOptions, uiTheme: Theme) => Component;
40
- readonly renderResult: (
41
- result: RecipeRenderResult,
42
- options: RenderResultOptions & { renderContext?: BashRenderContext },
43
- uiTheme: Theme,
44
- args?: RecipeRenderArgs,
45
- ) => Component;
46
-
47
- readonly #bash: BashTool;
48
- readonly #runners: DetectedRunner[];
49
-
50
- constructor(session: ToolSession, runners: DetectedRunner[]) {
51
- this.#runners = runners;
52
- this.#bash = new BashTool(session);
53
- this.description = prompt.render(recipeDescription, buildPromptModel(runners));
54
- const renderer = createRecipeToolRenderer(runners);
55
- this.renderCall = renderer.renderCall;
56
- this.renderResult = renderer.renderResult;
57
- }
58
-
59
- static async createIf(session: ToolSession): Promise<RecipeTool | null> {
60
- if (!session.settings.get("recipe.enabled")) return null;
61
- const detected = (await Promise.all(RUNNERS.map(runner => runner.detect(session.cwd)))).filter(
62
- (runner): runner is DetectedRunner => runner !== null && runner.tasks.length > 0,
63
- );
64
- if (detected.length === 0) return null;
65
- return new RecipeTool(session, detected);
66
- }
67
-
68
- async execute(
69
- toolCallId: string,
70
- { op }: RecipeParams,
71
- signal?: AbortSignal,
72
- onUpdate?: AgentToolUpdateCallback<BashToolDetails>,
73
- ctx?: AgentToolContext,
74
- ): Promise<AgentToolResult<BashToolDetails>> {
75
- const { command, cwd } = resolveCommand(op, this.#runners);
76
- return await this.#bash.execute(toolCallId, { command, cwd }, signal, onUpdate, ctx);
77
- }
78
- }
79
-
80
- export * from "./runner";
81
- export { tasksFromCargoMetadata } from "./runners/cargo";
@@ -1,19 +0,0 @@
1
- import { createShellRenderer } from "../bash";
2
- import type { DetectedRunner } from "./runner";
3
- import { commandFromOp, cwdFromOp, titleFromOp } from "./runner";
4
-
5
- export interface RecipeRenderArgs {
6
- op?: string;
7
- __partialJson?: string;
8
- [key: string]: unknown;
9
- }
10
-
11
- export function createRecipeToolRenderer(runners: DetectedRunner[]) {
12
- return createShellRenderer<RecipeRenderArgs>({
13
- resolveTitle: args => titleFromOp(args?.op, runners),
14
- resolveCommand: args => commandFromOp(args?.op, runners),
15
- resolveCwd: args => cwdFromOp(args?.op, runners),
16
- });
17
- }
18
-
19
- export const recipeToolRenderer = createRecipeToolRenderer([]);
@@ -1,219 +0,0 @@
1
- import { ToolError } from "../tool-errors";
2
-
3
- export interface RunnerTask {
4
- name: string;
5
- doc?: string;
6
- /** Parameter names only; used for the `name foo bar` signature line in the description. */
7
- parameters: string[];
8
- /** Override for this specific task, e.g. `cargo run --package crate --bin`. */
9
- commandPrefix?: string;
10
- /** Token passed to the runner command; defaults to `name`. Used when display names are namespaced. */
11
- commandName?: string;
12
- /** Working directory for the task, relative to the session cwd; absent means the runner's root cwd. */
13
- cwd?: string;
14
- }
15
-
16
- export interface DetectedRunner {
17
- id: string;
18
- label: string;
19
- /** Resolved shell prefix, e.g. "just" or "bun run" or "make". */
20
- commandPrefix: string;
21
- tasks: RunnerTask[];
22
- }
23
-
24
- export interface TaskRunner {
25
- id: string;
26
- label: string;
27
- /**
28
- * Probe `cwd` for the manifest, the binary, and the task list.
29
- * Returns null when this runner does not apply.
30
- */
31
- detect(cwd: string): Promise<DetectedRunner | null>;
32
- }
33
-
34
- interface ParsedOp {
35
- head: string;
36
- tail: string;
37
- }
38
-
39
- interface PromptTaskModel {
40
- name: string;
41
- paramSig?: string;
42
- command?: string;
43
- doc?: string;
44
- cwd?: string;
45
- }
46
-
47
- const PROMPT_TASK_LIMIT = 20;
48
-
49
- interface PromptRunnerModel {
50
- id: string;
51
- label: string;
52
- commandPrefix: string;
53
- tasks: PromptTaskModel[];
54
- hiddenTaskCount?: number;
55
- }
56
-
57
- export interface RecipePromptModel {
58
- [key: string]: unknown;
59
- hasMultipleRunners: boolean;
60
- ambiguityExampleRunner?: string;
61
- ambiguityExampleTask?: string;
62
- runners: PromptRunnerModel[];
63
- }
64
-
65
- function parseOp(op: string): ParsedOp {
66
- const trimmedStart = op.trimStart();
67
- if (trimmedStart.length === 0) return { head: "", tail: "" };
68
- const match = /^(\S+)(?:\s+([\s\S]*))?$/u.exec(trimmedStart);
69
- return { head: match?.[1] ?? "", tail: match?.[2] ?? "" };
70
- }
71
-
72
- function findRunnerById(id: string, runners: DetectedRunner[]): DetectedRunner | undefined {
73
- return runners.find(runner => runner.id === id);
74
- }
75
-
76
- function hasTask(runner: DetectedRunner, taskName: string): boolean {
77
- return runner.tasks.some(task => task.name === taskName);
78
- }
79
-
80
- function findMatchingRunners(taskName: string, runners: DetectedRunner[]): DetectedRunner[] {
81
- return runners.filter(runner => hasTask(runner, taskName));
82
- }
83
-
84
- function formatAvailableTasks(runners: DetectedRunner[]): string {
85
- return runners
86
- .map(runner => {
87
- const names = runner.tasks.map(task => task.name).join(", ");
88
- return `- ${runner.id}: ${names || "(no tasks)"}`;
89
- })
90
- .join("\n");
91
- }
92
-
93
- function formatRunnerIds(runners: DetectedRunner[]): string {
94
- return runners.map(runner => runner.id).join(", ");
95
- }
96
-
97
- function buildCommand(commandPrefix: string, taskName: string, tail: string): string {
98
- return [commandPrefix, taskName, tail]
99
- .filter(part => part.trim().length > 0)
100
- .join(" ")
101
- .trim();
102
- }
103
-
104
- function resolveRunnerAndTask(
105
- op: string,
106
- runners: DetectedRunner[],
107
- ): { runner: DetectedRunner; task: RunnerTask; tail: string } {
108
- const { head, tail } = parseOp(op);
109
- if (!head) {
110
- throw new ToolError(`recipe op is empty. Available tasks:\n${formatAvailableTasks(runners)}`);
111
- }
112
-
113
- const colonIndex = head.indexOf(":");
114
- if (colonIndex > 0) {
115
- const maybeRunnerId = head.slice(0, colonIndex);
116
- const explicitRunner = findRunnerById(maybeRunnerId, runners);
117
- if (explicitRunner) {
118
- const taskName = head.slice(colonIndex + 1);
119
- const explicitTask = explicitRunner.tasks.find(task => task.name === taskName);
120
- if (!taskName || !explicitTask) {
121
- throw new ToolError(
122
- `Task \`${taskName || "(empty)"}\` not found in runner \`${explicitRunner.id}\`. Available tasks:\n${formatAvailableTasks(runners)}`,
123
- );
124
- }
125
- return { runner: explicitRunner, task: explicitTask, tail };
126
- }
127
- }
128
-
129
- const matches = findMatchingRunners(head, runners);
130
- if (matches.length === 1) {
131
- return { runner: matches[0]!, task: matches[0]!.tasks.find(task => task.name === head)!, tail };
132
- }
133
- if (matches.length > 1) {
134
- const ids = matches.map(runner => runner.id).join(", ");
135
- throw new ToolError(
136
- `Task \`${head}\` exists in multiple runners (${ids}). Use \`<runner-id>:<task>\`, for example \`${matches[0]!.id}:${head}\`. Available tasks:\n${formatAvailableTasks(runners)}`,
137
- );
138
- }
139
-
140
- throw new ToolError(
141
- `No runner task named \`${head}\`. Use one of the available runner ids (${formatRunnerIds(runners)}) as a prefix when needed, e.g. \`pkg:${head}\`. Available tasks:\n${formatAvailableTasks(runners)}`,
142
- );
143
- }
144
-
145
- export interface ResolvedTask {
146
- command: string;
147
- cwd?: string;
148
- }
149
-
150
- export function resolveCommand(op: string, runners: DetectedRunner[]): ResolvedTask {
151
- const { runner, task, tail } = resolveRunnerAndTask(op, runners);
152
- const command = buildCommand(task.commandPrefix ?? runner.commandPrefix, task.commandName ?? task.name, tail);
153
- return task.cwd ? { command, cwd: task.cwd } : { command };
154
- }
155
-
156
- export function resolveTaskFromOp(op: string | undefined, runners: DetectedRunner[]): ResolvedTask | undefined {
157
- if (!op) return undefined;
158
- try {
159
- return resolveCommand(op, runners);
160
- } catch {
161
- return undefined;
162
- }
163
- }
164
-
165
- export function commandFromOp(op: string | undefined, runners: DetectedRunner[]): string | undefined {
166
- return resolveTaskFromOp(op, runners)?.command;
167
- }
168
-
169
- export function cwdFromOp(op: string | undefined, runners: DetectedRunner[]): string | undefined {
170
- return resolveTaskFromOp(op, runners)?.cwd;
171
- }
172
-
173
- export function titleFromOp(op: string | undefined, runners: DetectedRunner[]): string {
174
- if (!op) return "Run";
175
- const { head } = parseOp(op);
176
- if (!head) return "Run";
177
- const colonIndex = head.indexOf(":");
178
- if (colonIndex > 0) {
179
- const runner = findRunnerById(head.slice(0, colonIndex), runners);
180
- return runner?.label ?? "Run";
181
- }
182
- const matches = findMatchingRunners(head, runners);
183
- return matches.length === 1 ? matches[0]!.label : "Run";
184
- }
185
-
186
- function findAmbiguityExample(runners: DetectedRunner[]): { runner: string; task: string } | undefined {
187
- const seen = new Map<string, string>();
188
- for (const runner of runners) {
189
- for (const task of runner.tasks) {
190
- const previousRunner = seen.get(task.name);
191
- if (previousRunner) return { runner: previousRunner, task: task.name };
192
- seen.set(task.name, runner.id);
193
- }
194
- }
195
- const firstRunner = runners[0];
196
- const firstTask = firstRunner?.tasks[0];
197
- return firstRunner && firstTask ? { runner: firstRunner.id, task: firstTask.name } : undefined;
198
- }
199
-
200
- export function buildPromptModel(runners: DetectedRunner[]): RecipePromptModel {
201
- const ambiguityExample = findAmbiguityExample(runners);
202
- return {
203
- hasMultipleRunners: runners.length > 1,
204
- ambiguityExampleRunner: ambiguityExample?.runner,
205
- ambiguityExampleTask: ambiguityExample?.task,
206
- runners: runners.map(runner => ({
207
- id: runner.id,
208
- label: runner.label,
209
- commandPrefix: runner.commandPrefix,
210
- tasks: runner.tasks.slice(0, PROMPT_TASK_LIMIT).map(task => ({
211
- name: task.name,
212
- paramSig: task.parameters.length > 0 ? task.parameters.join(" ") : undefined,
213
- command: buildCommand(task.commandPrefix ?? runner.commandPrefix, task.commandName ?? task.name, ""),
214
- doc: task.doc,
215
- cwd: task.cwd,
216
- })),
217
- })),
218
- };
219
- }
@@ -1,131 +0,0 @@
1
- import * as fs from "node:fs/promises";
2
- import * as path from "node:path";
3
- import { $which, isEnoent, logger } from "@oh-my-pi/pi-utils";
4
- import type { DetectedRunner, RunnerTask, TaskRunner } from "../runner";
5
-
6
- export interface CargoMetadataTarget {
7
- kind?: string[];
8
- name?: string;
9
- }
10
-
11
- export interface CargoMetadataPackage {
12
- id?: string;
13
- name?: string;
14
- targets?: CargoMetadataTarget[];
15
- }
16
-
17
- export interface CargoMetadata {
18
- packages?: CargoMetadataPackage[];
19
- workspace_members?: string[];
20
- }
21
-
22
- type CargoTargetKind = "bin" | "example" | "test";
23
-
24
- async function hasCargoManifest(cwd: string): Promise<boolean> {
25
- try {
26
- const stat = await fs.stat(path.join(cwd, "Cargo.toml"));
27
- return stat.isFile();
28
- } catch (err) {
29
- if (isEnoent(err)) return false;
30
- throw err;
31
- }
32
- }
33
-
34
- function shellQuote(value: string): string {
35
- return `'${value.replaceAll("'", `'\\''`)}'`;
36
- }
37
-
38
- function cargoTargetKind(target: CargoMetadataTarget): CargoTargetKind | undefined {
39
- if (target.kind?.includes("bin")) return "bin";
40
- if (target.kind?.includes("example")) return "example";
41
- if (target.kind?.includes("test")) return "test";
42
- return undefined;
43
- }
44
-
45
- function commandPrefixForTarget(packageName: string, kind: CargoTargetKind): string {
46
- const packageFlag = `--package ${shellQuote(packageName)}`;
47
- switch (kind) {
48
- case "bin":
49
- return `cargo run ${packageFlag} --bin`;
50
- case "example":
51
- return `cargo run ${packageFlag} --example`;
52
- case "test":
53
- return `cargo test ${packageFlag} --test`;
54
- }
55
- }
56
-
57
- function taskNameForTarget(
58
- packageName: string,
59
- kind: CargoTargetKind,
60
- targetName: string,
61
- isWorkspace: boolean,
62
- ): string {
63
- const category = kind === "bin" ? "bin" : kind;
64
- return isWorkspace ? `${packageName}/${category}/${targetName}` : `${category}/${targetName}`;
65
- }
66
-
67
- export function tasksFromCargoMetadata(metadata: CargoMetadata): RunnerTask[] {
68
- const workspaceMembers = new Set(metadata.workspace_members ?? []);
69
- const workspacePackages = (metadata.packages ?? []).filter(pkg => pkg.id && workspaceMembers.has(pkg.id));
70
- const packages = workspacePackages.length > 0 ? workspacePackages : (metadata.packages ?? []);
71
- const isWorkspace = packages.length > 1;
72
- const tasks: RunnerTask[] = [];
73
- const seen = new Set<string>();
74
-
75
- for (const pkg of packages) {
76
- if (!pkg.name) continue;
77
- for (const target of pkg.targets ?? []) {
78
- if (!target.name) continue;
79
- const kind = cargoTargetKind(target);
80
- if (!kind) continue;
81
- const name = taskNameForTarget(pkg.name, kind, target.name, isWorkspace);
82
- if (seen.has(name)) continue;
83
- seen.add(name);
84
- tasks.push({
85
- name,
86
- doc: `${pkg.name} ${kind} target ${target.name}`,
87
- parameters: [],
88
- commandPrefix: commandPrefixForTarget(pkg.name, kind),
89
- commandName: shellQuote(target.name),
90
- });
91
- }
92
- }
93
-
94
- return tasks;
95
- }
96
-
97
- async function readCargoMetadata(cwd: string): Promise<CargoMetadata | null> {
98
- try {
99
- const proc = Bun.spawn(["cargo", "metadata", "--no-deps", "--format-version=1"], {
100
- cwd,
101
- stdin: "ignore",
102
- stdout: "pipe",
103
- stderr: "pipe",
104
- });
105
- const [stdout, exit] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
106
- if (exit !== 0) return null;
107
- return JSON.parse(stdout) as CargoMetadata;
108
- } catch (err) {
109
- logger.debug("cargo metadata failed", { error: err instanceof Error ? err.message : String(err) });
110
- return null;
111
- }
112
- }
113
-
114
- export const cargoRunner: TaskRunner = {
115
- id: "cargo",
116
- label: "Cargo",
117
- async detect(cwd: string): Promise<DetectedRunner | null> {
118
- try {
119
- if (!$which("cargo")) return null;
120
- if (!(await hasCargoManifest(cwd))) return null;
121
- const metadata = await readCargoMetadata(cwd);
122
- if (!metadata) return null;
123
- const tasks = tasksFromCargoMetadata(metadata);
124
- if (tasks.length === 0) return null;
125
- return { id: "cargo", label: "Cargo", commandPrefix: "cargo", tasks };
126
- } catch (err) {
127
- logger.debug("cargo runner probe failed", { error: err instanceof Error ? err.message : String(err) });
128
- return null;
129
- }
130
- },
131
- };
@@ -1,8 +0,0 @@
1
- import type { TaskRunner } from "../runner";
2
- import { cargoRunner } from "./cargo";
3
- import { justRunner } from "./just";
4
- import { makeRunner } from "./make";
5
- import { pkgRunner } from "./pkg";
6
- import { taskRunner } from "./task";
7
-
8
- export const RUNNERS: TaskRunner[] = [justRunner, pkgRunner, cargoRunner, makeRunner, taskRunner];