@oh-my-pi/pi-coding-agent 3.21.0 → 3.25.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 (71) hide show
  1. package/CHANGELOG.md +55 -1
  2. package/docs/sdk.md +47 -50
  3. package/examples/custom-tools/README.md +0 -15
  4. package/examples/hooks/custom-compaction.ts +1 -3
  5. package/examples/sdk/README.md +6 -10
  6. package/package.json +5 -5
  7. package/src/cli/args.ts +9 -6
  8. package/src/core/agent-session.ts +3 -3
  9. package/src/core/custom-commands/bundled/wt/index.ts +3 -0
  10. package/src/core/custom-tools/wrapper.ts +0 -1
  11. package/src/core/extensions/index.ts +1 -6
  12. package/src/core/extensions/wrapper.ts +0 -7
  13. package/src/core/file-mentions.ts +5 -8
  14. package/src/core/sdk.ts +48 -111
  15. package/src/core/session-manager.ts +7 -0
  16. package/src/core/system-prompt.ts +22 -33
  17. package/src/core/tools/ask.ts +14 -7
  18. package/src/core/tools/bash-interceptor.ts +4 -4
  19. package/src/core/tools/bash.ts +19 -9
  20. package/src/core/tools/complete.ts +131 -0
  21. package/src/core/tools/context.ts +7 -0
  22. package/src/core/tools/edit.ts +8 -15
  23. package/src/core/tools/exa/render.ts +4 -16
  24. package/src/core/tools/find.ts +7 -18
  25. package/src/core/tools/git.ts +13 -3
  26. package/src/core/tools/grep.ts +7 -18
  27. package/src/core/tools/index.test.ts +188 -0
  28. package/src/core/tools/index.ts +106 -236
  29. package/src/core/tools/jtd-to-json-schema.ts +274 -0
  30. package/src/core/tools/ls.ts +4 -9
  31. package/src/core/tools/lsp/index.ts +32 -29
  32. package/src/core/tools/lsp/render.ts +7 -28
  33. package/src/core/tools/notebook.ts +3 -5
  34. package/src/core/tools/output.ts +130 -31
  35. package/src/core/tools/read.ts +8 -19
  36. package/src/core/tools/review.ts +0 -18
  37. package/src/core/tools/rulebook.ts +8 -2
  38. package/src/core/tools/task/agents.ts +28 -7
  39. package/src/core/tools/task/artifacts.ts +6 -9
  40. package/src/core/tools/task/discovery.ts +0 -6
  41. package/src/core/tools/task/executor.ts +306 -257
  42. package/src/core/tools/task/index.ts +65 -235
  43. package/src/core/tools/task/name-generator.ts +247 -0
  44. package/src/core/tools/task/render.ts +158 -19
  45. package/src/core/tools/task/types.ts +13 -11
  46. package/src/core/tools/task/worker-protocol.ts +18 -0
  47. package/src/core/tools/task/worker.ts +270 -0
  48. package/src/core/tools/web-fetch.ts +4 -36
  49. package/src/core/tools/web-search/index.ts +2 -1
  50. package/src/core/tools/web-search/render.ts +1 -4
  51. package/src/core/tools/write.ts +7 -15
  52. package/src/discovery/helpers.test.ts +1 -1
  53. package/src/index.ts +5 -16
  54. package/src/main.ts +4 -4
  55. package/src/modes/interactive/theme/theme.ts +4 -4
  56. package/src/prompts/task.md +14 -57
  57. package/src/prompts/tools/output.md +4 -3
  58. package/src/prompts/tools/task.md +70 -0
  59. package/examples/custom-tools/question/index.ts +0 -84
  60. package/examples/custom-tools/subagent/README.md +0 -172
  61. package/examples/custom-tools/subagent/agents/planner.md +0 -37
  62. package/examples/custom-tools/subagent/agents/scout.md +0 -50
  63. package/examples/custom-tools/subagent/agents/worker.md +0 -24
  64. package/examples/custom-tools/subagent/agents.ts +0 -156
  65. package/examples/custom-tools/subagent/commands/implement-and-review.md +0 -10
  66. package/examples/custom-tools/subagent/commands/implement.md +0 -10
  67. package/examples/custom-tools/subagent/commands/scout-and-plan.md +0 -9
  68. package/examples/custom-tools/subagent/index.ts +0 -1002
  69. package/examples/sdk/05-tools.ts +0 -94
  70. package/examples/sdk/12-full-control.ts +0 -95
  71. package/src/prompts/browser.md +0 -71
@@ -12,6 +12,7 @@ import { formatDimensionNote, resizeImage } from "../../utils/image-resize";
12
12
  import { detectSupportedImageMimeTypeFromFile } from "../../utils/mime";
13
13
  import { ensureTool } from "../../utils/tools-manager";
14
14
  import type { RenderResultOptions } from "../custom-tools/types";
15
+ import type { ToolSession } from "../sdk";
15
16
  import { untilAborted } from "../utils";
16
17
  import { createLsTool } from "./ls";
17
18
  import { resolveReadPath, resolveToCwd } from "./path-utils";
@@ -340,14 +341,9 @@ export interface ReadToolDetails {
340
341
  redirectedTo?: "ls";
341
342
  }
342
343
 
343
- export interface ReadToolOptions {
344
- /** Whether to auto-resize images to 2000x2000 max. Default: true */
345
- autoResizeImages?: boolean;
346
- }
347
-
348
- export function createReadTool(cwd: string, options?: ReadToolOptions): AgentTool<typeof readSchema> {
349
- const autoResizeImages = options?.autoResizeImages ?? true;
350
- const lsTool = createLsTool(cwd);
344
+ export function createReadTool(session: ToolSession): AgentTool<typeof readSchema> {
345
+ const autoResizeImages = session.settings?.getImageAutoResize() ?? true;
346
+ const lsTool = createLsTool(session);
351
347
  return {
352
348
  name: "read",
353
349
  label: "Read",
@@ -358,7 +354,7 @@ export function createReadTool(cwd: string, options?: ReadToolOptions): AgentToo
358
354
  { path: readPath, offset, limit }: { path: string; offset?: number; limit?: number },
359
355
  signal?: AbortSignal,
360
356
  ) => {
361
- const absolutePath = resolveReadPath(readPath, cwd);
357
+ const absolutePath = resolveReadPath(readPath, session.cwd);
362
358
 
363
359
  return untilAborted(signal, async () => {
364
360
  let isDirectory = false;
@@ -378,14 +374,12 @@ export function createReadTool(cwd: string, options?: ReadToolOptions): AgentToo
378
374
  }
379
375
  } catch (error) {
380
376
  if (isNotFoundError(error)) {
381
- const suggestions = await findReadPathSuggestions(readPath, cwd);
377
+ const suggestions = await findReadPathSuggestions(readPath, session.cwd);
382
378
  let message = `File not found: ${readPath}`;
383
379
 
384
380
  if (suggestions?.suggestions.length) {
385
381
  const scopeLabel = suggestions.scopeLabel ? ` in ${suggestions.scopeLabel}` : "";
386
- message += `\n\nClosest matches${scopeLabel}:\n${suggestions.suggestions
387
- .map((match) => `- ${match}`)
388
- .join("\n")}`;
382
+ message += `\n\nClosest matches${scopeLabel}:\n${suggestions.suggestions.map((match) => `- ${match}`).join("\n")}`;
389
383
  if (suggestions.truncated) {
390
384
  message += `\n[Search truncated to first ${MAX_FUZZY_CANDIDATES} paths. Refine the path if the match isn't listed.]`;
391
385
  }
@@ -462,9 +456,7 @@ export function createReadTool(cwd: string, options?: ReadToolOptions): AgentToo
462
456
  let outputText = truncation.content;
463
457
 
464
458
  if (truncation.truncated) {
465
- outputText += `\n\n[Document converted via markitdown. Output truncated to ${formatSize(
466
- DEFAULT_MAX_BYTES,
467
- )}]`;
459
+ outputText += `\n\n[Document converted via markitdown. Output truncated to ${formatSize(DEFAULT_MAX_BYTES)}]`;
468
460
  details = { truncation };
469
461
  }
470
462
 
@@ -562,9 +554,6 @@ export function createReadTool(cwd: string, options?: ReadToolOptions): AgentToo
562
554
  };
563
555
  }
564
556
 
565
- /** Default read tool using process.cwd() - for backwards compatibility */
566
- export const readTool = createReadTool(process.cwd());
567
-
568
557
  // =============================================================================
569
558
  // TUI Renderer
570
559
  // =============================================================================
@@ -19,13 +19,6 @@ const PRIORITY_LABELS: Record<number, string> = {
19
19
  3: "P3",
20
20
  };
21
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
22
  // report_finding schema
30
23
  const ReportFindingParams = Type.Object({
31
24
  title: Type.String({
@@ -62,8 +55,6 @@ export const reportFindingTool: AgentTool<typeof ReportFindingParams, ReportFind
62
55
  label: "Report Finding",
63
56
  description: "Report a code review finding. Use this for each issue found. Call submit_review when done.",
64
57
  parameters: ReportFindingParams,
65
- hidden: true,
66
-
67
58
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
68
59
  const { title, body, priority, confidence, file_path, line_start, line_end } = params;
69
60
  const location = `${file_path}:${line_start}${line_end !== line_start ? `-${line_end}` : ""}`;
@@ -142,7 +133,6 @@ export const submitReviewTool: AgentTool<typeof SubmitReviewParams, SubmitReview
142
133
  label: "Submit Review",
143
134
  description: "Submit the final review verdict. Call this after all findings have been reported.",
144
135
  parameters: SubmitReviewParams,
145
- hidden: true,
146
136
 
147
137
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
148
138
  const { overall_correctness, explanation, confidence } = params;
@@ -206,14 +196,6 @@ export const submitReviewTool: AgentTool<typeof SubmitReviewParams, SubmitReview
206
196
  },
207
197
  };
208
198
 
209
- export function createReportFindingTool(): AgentTool<typeof ReportFindingParams, ReportFindingDetails, Theme> {
210
- return reportFindingTool;
211
- }
212
-
213
- export function createSubmitReviewTool(): AgentTool<typeof SubmitReviewParams, SubmitReviewDetails, Theme> {
214
- return submitReviewTool;
215
- }
216
-
217
199
  // Re-export types for external use
218
200
  export type { ReportFindingDetails, SubmitReviewDetails };
219
201
 
@@ -9,6 +9,7 @@
9
9
  import type { AgentTool } from "@oh-my-pi/pi-agent-core";
10
10
  import { Type } from "@sinclair/typebox";
11
11
  import type { Rule } from "../../capability/rule";
12
+ import type { ToolSession } from "./index";
12
13
 
13
14
  export interface RulebookToolDetails {
14
15
  type: "rulebook";
@@ -23,9 +24,14 @@ const rulebookSchema = Type.Object({
23
24
 
24
25
  /**
25
26
  * Create a rulebook tool with access to discovered rules.
26
- * @param rules - Array of discovered rules (non-TTSR rules with descriptions)
27
+ * Returns null if no rules available.
27
28
  */
28
- export function createRulebookTool(rules: Rule[]): AgentTool<typeof rulebookSchema> {
29
+ export function createRulebookTool(session: ToolSession): AgentTool<typeof rulebookSchema> | null {
30
+ const rules = session.rulebookRules;
31
+ if (!rules || rules.length === 0) {
32
+ return null;
33
+ }
34
+
29
35
  // Build lookup map for O(1) access
30
36
  const ruleMap = new Map<string, Rule>();
31
37
  for (const rule of rules) {
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  // Embed agent markdown files at build time
8
- import browserMd from "../../../prompts/browser.md" with { type: "text" };
9
8
  import exploreMd from "../../../prompts/explore.md" with { type: "text" };
10
9
  import planMd from "../../../prompts/plan.md" with { type: "text" };
11
10
  import reviewerMd from "../../../prompts/reviewer.md" with { type: "text" };
@@ -13,11 +12,37 @@ import taskMd from "../../../prompts/task.md" with { type: "text" };
13
12
  import type { AgentDefinition, AgentSource } from "./types";
14
13
 
15
14
  const EMBEDDED_AGENTS: { name: string; content: string }[] = [
16
- { name: "browser.md", content: browserMd },
17
15
  { name: "explore.md", content: exploreMd },
18
16
  { name: "plan.md", content: planMd },
19
17
  { name: "reviewer.md", content: reviewerMd },
20
- { name: "task.md", content: taskMd },
18
+ {
19
+ name: "task.md",
20
+ content: `---
21
+ name: task
22
+ description: General-purpose subagent with full capabilities for delegated multi-step tasks
23
+ spawns: explore
24
+ model: default
25
+ ---
26
+ ${taskMd}`,
27
+ },
28
+ {
29
+ name: "quick_task.md",
30
+ content: `---
31
+ name: quick_task
32
+ description: Quick task for fast execution
33
+ model: pi/smol
34
+ ---
35
+ ${taskMd}`,
36
+ },
37
+ {
38
+ name: "deep_task.md",
39
+ content: `---
40
+ name: deep_task
41
+ description: Deep task for comprehensive reasoning
42
+ model: pi/slow
43
+ ---
44
+ ${taskMd}`,
45
+ },
21
46
  ];
22
47
 
23
48
  /**
@@ -88,16 +113,12 @@ function parseAgent(fileName: string, content: string, source: AgentSource): Age
88
113
  spawns = "*";
89
114
  }
90
115
 
91
- const recursive =
92
- frontmatter.recursive === undefined ? false : frontmatter.recursive === "true" || frontmatter.recursive === "1";
93
-
94
116
  return {
95
117
  name: frontmatter.name,
96
118
  description: frontmatter.description,
97
119
  tools: tools && tools.length > 0 ? tools : undefined,
98
120
  spawns,
99
121
  model: frontmatter.model,
100
- recursive,
101
122
  systemPrompt: body,
102
123
  source,
103
124
  filePath: `embedded:${fileName}`,
@@ -38,14 +38,12 @@ export function ensureArtifactsDir(dir: string): void {
38
38
  */
39
39
  export function getArtifactPaths(
40
40
  dir: string,
41
- agentName: string,
42
- index: number,
41
+ taskId: string,
43
42
  ): { inputPath: string; outputPath: string; jsonlPath: string } {
44
- const base = `${agentName}_${index}`;
45
43
  return {
46
- inputPath: path.join(dir, `${base}.in.md`),
47
- outputPath: path.join(dir, `${base}.out.md`),
48
- jsonlPath: path.join(dir, `${base}.jsonl`),
44
+ inputPath: path.join(dir, `${taskId}.in.md`),
45
+ outputPath: path.join(dir, `${taskId}.out.md`),
46
+ jsonlPath: path.join(dir, `${taskId}.jsonl`),
49
47
  };
50
48
  }
51
49
 
@@ -54,15 +52,14 @@ export function getArtifactPaths(
54
52
  */
55
53
  export async function writeArtifacts(
56
54
  dir: string,
57
- agentName: string,
58
- index: number,
55
+ taskId: string,
59
56
  input: string,
60
57
  output: string,
61
58
  jsonlEvents?: string[],
62
59
  ): Promise<{ inputPath: string; outputPath: string; jsonlPath?: string }> {
63
60
  ensureArtifactsDir(dir);
64
61
 
65
- const paths = getArtifactPaths(dir, agentName, index);
62
+ const paths = getArtifactPaths(dir, taskId);
66
63
 
67
64
  // Write input
68
65
  await fs.promises.writeFile(paths.inputPath, input, "utf-8");
@@ -126,18 +126,12 @@ function loadAgentsFromDir(dir: string, source: AgentSource): AgentDefinition[]
126
126
  spawns = "*";
127
127
  }
128
128
 
129
- const recursive =
130
- frontmatter.recursive === undefined
131
- ? undefined
132
- : frontmatter.recursive === "true" || frontmatter.recursive === "1";
133
-
134
129
  agents.push({
135
130
  name: frontmatter.name,
136
131
  description: frontmatter.description,
137
132
  tools: tools && tools.length > 0 ? tools : undefined,
138
133
  spawns,
139
134
  model: frontmatter.model,
140
- recursive,
141
135
  systemPrompt: body,
142
136
  source,
143
137
  filePath,