@oh-my-pi/pi-coding-agent 12.18.1 → 12.19.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 (231) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/package.json +7 -7
  3. package/src/async/index.ts +1 -0
  4. package/src/async/job-manager.ts +341 -0
  5. package/src/cli/file-processor.ts +3 -3
  6. package/src/cli/list-models.ts +3 -17
  7. package/src/cli/stats-cli.ts +3 -22
  8. package/src/cli/web-search-cli.ts +8 -16
  9. package/src/commit/agentic/agent.ts +6 -9
  10. package/src/commit/agentic/index.ts +44 -50
  11. package/src/commit/agentic/state.ts +0 -9
  12. package/src/commit/agentic/tools/propose-commit.ts +1 -30
  13. package/src/commit/agentic/tools/schemas.ts +31 -0
  14. package/src/commit/agentic/tools/split-commit.ts +1 -30
  15. package/src/commit/agentic/validation.ts +1 -18
  16. package/src/commit/analysis/conventional.ts +3 -50
  17. package/src/commit/analysis/summary.ts +2 -13
  18. package/src/commit/changelog/detect.ts +4 -1
  19. package/src/commit/changelog/generate.ts +2 -25
  20. package/src/commit/changelog/index.ts +1 -2
  21. package/src/commit/cli.ts +4 -12
  22. package/src/commit/map-reduce/reduce-phase.ts +2 -43
  23. package/src/commit/pipeline.ts +7 -15
  24. package/src/commit/utils.ts +44 -0
  25. package/src/config/prompt-templates.ts +1 -81
  26. package/src/config/settings-schema.ts +20 -1
  27. package/src/config.ts +2 -3
  28. package/src/debug/index.ts +1 -6
  29. package/src/debug/system-info.ts +2 -6
  30. package/src/discovery/builtin.ts +5 -9
  31. package/src/discovery/helpers.ts +0 -26
  32. package/src/discovery/ssh.ts +1 -8
  33. package/src/exa/company.ts +8 -39
  34. package/src/exa/factory.ts +64 -0
  35. package/src/exa/index.ts +0 -16
  36. package/src/exa/linkedin.ts +8 -39
  37. package/src/exa/mcp-client.ts +0 -64
  38. package/src/exa/researcher.ts +17 -59
  39. package/src/exa/search.ts +30 -154
  40. package/src/extensibility/custom-tools/loader.ts +3 -41
  41. package/src/extensibility/extensions/loader.ts +2 -9
  42. package/src/extensibility/hooks/loader.ts +3 -20
  43. package/src/extensibility/hooks/runner.ts +3 -19
  44. package/src/extensibility/plugins/installer.ts +2 -1
  45. package/src/extensibility/plugins/loader.ts +29 -117
  46. package/src/extensibility/skills.ts +2 -89
  47. package/src/extensibility/slash-commands.ts +1 -63
  48. package/src/extensibility/utils.ts +38 -0
  49. package/src/index.ts +9 -25
  50. package/src/internal-urls/index.ts +1 -0
  51. package/src/internal-urls/jobs-protocol.ts +118 -0
  52. package/src/ipy/kernel.ts +2 -0
  53. package/src/lsp/config.ts +1 -5
  54. package/src/lsp/lspmux.ts +0 -17
  55. package/src/lsp/utils.ts +2 -24
  56. package/src/main.ts +16 -24
  57. package/src/mcp/client.ts +1 -46
  58. package/src/mcp/render.ts +8 -1
  59. package/src/mcp/tool-cache.ts +1 -5
  60. package/src/mcp/transports/http.ts +2 -7
  61. package/src/mcp/transports/stdio.ts +2 -7
  62. package/src/modes/components/bash-execution.ts +2 -16
  63. package/src/modes/components/extensions/inspector-panel.ts +8 -18
  64. package/src/modes/components/footer.ts +10 -50
  65. package/src/modes/components/model-selector.ts +2 -21
  66. package/src/modes/components/python-execution.ts +2 -16
  67. package/src/modes/components/settings-selector.ts +1 -10
  68. package/src/modes/components/status-line/segments.ts +8 -25
  69. package/src/modes/components/status-line.ts +14 -31
  70. package/src/modes/components/tool-execution.ts +8 -2
  71. package/src/modes/controllers/command-controller.ts +71 -30
  72. package/src/modes/controllers/event-controller.ts +34 -4
  73. package/src/modes/controllers/mcp-command-controller.ts +3 -34
  74. package/src/modes/controllers/selector-controller.ts +2 -2
  75. package/src/modes/controllers/ssh-command-controller.ts +3 -34
  76. package/src/modes/interactive-mode.ts +6 -2
  77. package/src/modes/rpc/rpc-client.ts +1 -5
  78. package/src/modes/shared.ts +73 -0
  79. package/src/modes/types.ts +1 -0
  80. package/src/modes/utils/ui-helpers.ts +26 -2
  81. package/src/patch/index.ts +4 -4
  82. package/src/patch/normalize.ts +22 -65
  83. package/src/patch/shared.ts +16 -16
  84. package/src/prompts/system/custom-system-prompt.md +0 -10
  85. package/src/prompts/system/system-prompt.md +69 -89
  86. package/src/prompts/tools/async-result.md +5 -0
  87. package/src/prompts/tools/bash.md +5 -0
  88. package/src/prompts/tools/cancel-job.md +7 -0
  89. package/src/prompts/tools/poll-jobs.md +7 -0
  90. package/src/prompts/tools/task.md +4 -0
  91. package/src/sdk.ts +70 -6
  92. package/src/session/agent-session.ts +40 -6
  93. package/src/session/agent-storage.ts +69 -278
  94. package/src/session/auth-storage.ts +14 -1430
  95. package/src/session/session-manager.ts +69 -5
  96. package/src/session/session-storage.ts +1 -5
  97. package/src/session/streaming-output.ts +637 -76
  98. package/src/slash-commands/builtin-registry.ts +8 -0
  99. package/src/ssh/connection-manager.ts +4 -12
  100. package/src/ssh/sshfs-mount.ts +3 -7
  101. package/src/ssh/utils.ts +8 -0
  102. package/src/system-prompt.ts +24 -90
  103. package/src/task/executor.ts +11 -1
  104. package/src/task/index.ts +258 -13
  105. package/src/task/parallel.ts +32 -0
  106. package/src/task/render.ts +15 -7
  107. package/src/task/types.ts +5 -0
  108. package/src/tools/ask.ts +4 -7
  109. package/src/tools/bash-interactive.ts +4 -5
  110. package/src/tools/bash.ts +125 -41
  111. package/src/tools/cancel-job.ts +93 -0
  112. package/src/tools/fetch.ts +7 -27
  113. package/src/tools/find.ts +3 -3
  114. package/src/tools/gemini-image.ts +15 -14
  115. package/src/tools/grep.ts +3 -3
  116. package/src/tools/index.ts +13 -29
  117. package/src/tools/json-tree.ts +12 -1
  118. package/src/tools/jtd-to-json-schema.ts +10 -74
  119. package/src/tools/jtd-to-typescript.ts +10 -72
  120. package/src/tools/jtd-utils.ts +102 -0
  121. package/src/tools/notebook.ts +4 -9
  122. package/src/tools/output-meta.ts +52 -26
  123. package/src/tools/path-utils.ts +13 -7
  124. package/src/tools/poll-jobs.ts +178 -0
  125. package/src/tools/python.ts +32 -35
  126. package/src/tools/read.ts +61 -82
  127. package/src/tools/render-utils.ts +8 -159
  128. package/src/tools/ssh.ts +7 -20
  129. package/src/tools/submit-result.ts +1 -1
  130. package/src/tools/tool-errors.ts +0 -30
  131. package/src/tools/tool-result.ts +1 -2
  132. package/src/tools/write.ts +8 -10
  133. package/src/tui/code-cell.ts +8 -3
  134. package/src/tui/status-line.ts +4 -4
  135. package/src/tui/types.ts +0 -1
  136. package/src/tui/utils.ts +1 -14
  137. package/src/utils/command-args.ts +76 -0
  138. package/src/utils/file-mentions.ts +15 -19
  139. package/src/utils/frontmatter.ts +5 -10
  140. package/src/utils/shell-snapshot.ts +0 -11
  141. package/src/utils/title-generator.ts +0 -12
  142. package/src/web/scrapers/artifacthub.ts +7 -16
  143. package/src/web/scrapers/arxiv.ts +3 -8
  144. package/src/web/scrapers/aur.ts +8 -22
  145. package/src/web/scrapers/biorxiv.ts +5 -14
  146. package/src/web/scrapers/bluesky.ts +13 -36
  147. package/src/web/scrapers/brew.ts +5 -10
  148. package/src/web/scrapers/cheatsh.ts +2 -12
  149. package/src/web/scrapers/chocolatey.ts +63 -26
  150. package/src/web/scrapers/choosealicense.ts +3 -18
  151. package/src/web/scrapers/cisa-kev.ts +4 -18
  152. package/src/web/scrapers/clojars.ts +6 -33
  153. package/src/web/scrapers/coingecko.ts +25 -33
  154. package/src/web/scrapers/crates-io.ts +7 -26
  155. package/src/web/scrapers/crossref.ts +4 -18
  156. package/src/web/scrapers/devto.ts +11 -41
  157. package/src/web/scrapers/discogs.ts +7 -10
  158. package/src/web/scrapers/discourse.ts +6 -31
  159. package/src/web/scrapers/dockerhub.ts +12 -35
  160. package/src/web/scrapers/fdroid.ts +8 -33
  161. package/src/web/scrapers/firefox-addons.ts +10 -34
  162. package/src/web/scrapers/flathub.ts +7 -24
  163. package/src/web/scrapers/github-gist.ts +2 -12
  164. package/src/web/scrapers/github.ts +9 -47
  165. package/src/web/scrapers/gitlab.ts +130 -185
  166. package/src/web/scrapers/go-pkg.ts +12 -22
  167. package/src/web/scrapers/hackage.ts +88 -43
  168. package/src/web/scrapers/hackernews.ts +25 -45
  169. package/src/web/scrapers/hex.ts +19 -36
  170. package/src/web/scrapers/huggingface.ts +26 -91
  171. package/src/web/scrapers/iacr.ts +3 -8
  172. package/src/web/scrapers/jetbrains-marketplace.ts +9 -20
  173. package/src/web/scrapers/lemmy.ts +5 -23
  174. package/src/web/scrapers/lobsters.ts +16 -28
  175. package/src/web/scrapers/mastodon.ts +24 -43
  176. package/src/web/scrapers/maven.ts +6 -21
  177. package/src/web/scrapers/mdn.ts +7 -11
  178. package/src/web/scrapers/metacpan.ts +9 -41
  179. package/src/web/scrapers/musicbrainz.ts +4 -28
  180. package/src/web/scrapers/npm.ts +8 -25
  181. package/src/web/scrapers/nuget.ts +14 -37
  182. package/src/web/scrapers/nvd.ts +6 -28
  183. package/src/web/scrapers/ollama.ts +7 -34
  184. package/src/web/scrapers/open-vsx.ts +5 -19
  185. package/src/web/scrapers/opencorporates.ts +30 -14
  186. package/src/web/scrapers/openlibrary.ts +49 -33
  187. package/src/web/scrapers/orcid.ts +4 -18
  188. package/src/web/scrapers/osv.ts +7 -24
  189. package/src/web/scrapers/packagist.ts +9 -24
  190. package/src/web/scrapers/pub-dev.ts +7 -50
  191. package/src/web/scrapers/pubmed.ts +54 -21
  192. package/src/web/scrapers/pypi.ts +8 -26
  193. package/src/web/scrapers/rawg.ts +11 -19
  194. package/src/web/scrapers/readthedocs.ts +4 -9
  195. package/src/web/scrapers/reddit.ts +5 -15
  196. package/src/web/scrapers/repology.ts +8 -20
  197. package/src/web/scrapers/rfc.ts +5 -14
  198. package/src/web/scrapers/rubygems.ts +6 -21
  199. package/src/web/scrapers/searchcode.ts +8 -36
  200. package/src/web/scrapers/sec-edgar.ts +4 -18
  201. package/src/web/scrapers/semantic-scholar.ts +15 -35
  202. package/src/web/scrapers/snapcraft.ts +5 -19
  203. package/src/web/scrapers/sourcegraph.ts +5 -43
  204. package/src/web/scrapers/spdx.ts +4 -18
  205. package/src/web/scrapers/spotify.ts +4 -23
  206. package/src/web/scrapers/stackoverflow.ts +8 -13
  207. package/src/web/scrapers/terraform.ts +9 -37
  208. package/src/web/scrapers/tldr.ts +3 -7
  209. package/src/web/scrapers/twitter.ts +3 -7
  210. package/src/web/scrapers/types.ts +105 -27
  211. package/src/web/scrapers/utils.ts +97 -103
  212. package/src/web/scrapers/vimeo.ts +7 -27
  213. package/src/web/scrapers/vscode-marketplace.ts +8 -17
  214. package/src/web/scrapers/w3c.ts +6 -14
  215. package/src/web/scrapers/wikidata.ts +5 -19
  216. package/src/web/scrapers/wikipedia.ts +2 -12
  217. package/src/web/scrapers/youtube.ts +5 -34
  218. package/src/web/search/index.ts +0 -9
  219. package/src/web/search/providers/anthropic.ts +3 -2
  220. package/src/web/search/providers/brave.ts +3 -18
  221. package/src/web/search/providers/exa.ts +1 -12
  222. package/src/web/search/providers/kimi.ts +5 -44
  223. package/src/web/search/providers/perplexity.ts +1 -12
  224. package/src/web/search/providers/synthetic.ts +3 -26
  225. package/src/web/search/providers/utils.ts +36 -0
  226. package/src/web/search/providers/zai.ts +9 -50
  227. package/src/web/search/types.ts +0 -28
  228. package/src/web/search/utils.ts +17 -0
  229. package/src/tools/output-utils.ts +0 -63
  230. package/src/tools/truncate.ts +0 -385
  231. package/src/web/search/auth.ts +0 -178
@@ -206,72 +206,29 @@ export function convertLeadingTabsToSpaces(text: string, spacesPerTab: number):
206
206
  // Unicode Normalization
207
207
  // ═══════════════════════════════════════════════════════════════════════════
208
208
 
209
- /**
210
- * Normalize common Unicode punctuation to ASCII equivalents.
211
- * Allows diffs with ASCII characters to match source files with typographic punctuation.
212
- */
213
- export function normalizeUnicode(s: string): string {
214
- return s
215
- .trim()
216
- .split("")
217
- .map(c => {
218
- const code = c.charCodeAt(0);
219
-
220
- // Various dash/hyphen code-pointsASCII '-'
221
- if (
222
- code === 0x2010 || // HYPHEN
223
- code === 0x2011 || // NON-BREAKING HYPHEN
224
- code === 0x2012 || // FIGURE DASH
225
- code === 0x2013 || // EN DASH
226
- code === 0x2014 || // EM DASH
227
- code === 0x2015 || // HORIZONTAL BAR
228
- code === 0x2212 // MINUS SIGN
229
- ) {
230
- return "-";
231
- }
209
+ const UNICODE_REPLACEMENTS: [RegExp, string][] = [
210
+ // Various dash/hyphen code-points ASCII '-'
211
+ [/[\u2010-\u2015\u2212]/g, "-"],
212
+ // Fancy single quotes → '
213
+ [/[\u2018-\u201B]/g, "'"],
214
+ // Fancy double quotes → "
215
+ [/[\u201C-\u201F]/g, '"'],
216
+ // Non-breaking space and other odd spaces → normal space
217
+ [/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, " "],
218
+ // Not-equal sign → !=
219
+ [/\u2260/g, "!="],
220
+ // Vulgar fraction ½1/2
221
+ [/\u00BD/g, "1/2"],
222
+ // Zero-width characters remove
223
+ [/[\u200B-\u200D\uFEFF]/g, ""],
224
+ ];
232
225
 
233
- // Fancy single quotes '
234
- if (
235
- code === 0x2018 || // LEFT SINGLE QUOTATION MARK
236
- code === 0x2019 || // RIGHT SINGLE QUOTATION MARK
237
- code === 0x201a || // SINGLE LOW-9 QUOTATION MARK
238
- code === 0x201b // SINGLE HIGH-REVERSED-9 QUOTATION MARK
239
- ) {
240
- return "'";
241
- }
242
-
243
- // Fancy double quotes → "
244
- if (
245
- code === 0x201c || // LEFT DOUBLE QUOTATION MARK
246
- code === 0x201d || // RIGHT DOUBLE QUOTATION MARK
247
- code === 0x201e || // DOUBLE LOW-9 QUOTATION MARK
248
- code === 0x201f // DOUBLE HIGH-REVERSED-9 QUOTATION MARK
249
- ) {
250
- return '"';
251
- }
252
-
253
- // Non-breaking space and other odd spaces → normal space
254
- if (
255
- code === 0x00a0 || // NO-BREAK SPACE
256
- code === 0x2002 || // EN SPACE
257
- code === 0x2003 || // EM SPACE
258
- code === 0x2004 || // THREE-PER-EM SPACE
259
- code === 0x2005 || // FOUR-PER-EM SPACE
260
- code === 0x2006 || // SIX-PER-EM SPACE
261
- code === 0x2007 || // FIGURE SPACE
262
- code === 0x2008 || // PUNCTUATION SPACE
263
- code === 0x2009 || // THIN SPACE
264
- code === 0x200a || // HAIR SPACE
265
- code === 0x202f || // NARROW NO-BREAK SPACE
266
- code === 0x205f || // MEDIUM MATHEMATICAL SPACE
267
- code === 0x3000 // IDEOGRAPHIC SPACE
268
- ) {
269
- return " ";
270
- }
271
-
272
- return c;
273
- })
274
- .join("");
226
+ export function normalizeUnicode(s: string): string {
227
+ let result = s.trim();
228
+ for (const [pattern, replacement] of UNICODE_REPLACEMENTS) {
229
+ result = result.replace(pattern, replacement);
230
+ }
231
+ return result.normalize("NFC");
275
232
  }
276
233
 
277
234
  /**
@@ -10,13 +10,15 @@ import { renderDiff as renderDiffColored } from "../modes/components/diff";
10
10
  import { getLanguageFromPath, type Theme } from "../modes/theme/theme";
11
11
  import type { OutputMeta } from "../tools/output-meta";
12
12
  import {
13
+ formatDiagnostics,
14
+ formatDiffStats,
13
15
  formatExpandHint,
14
16
  formatStatusIcon,
17
+ formatTitle,
15
18
  getDiffStats,
16
19
  PREVIEW_LIMITS,
17
20
  replaceTabs,
18
21
  shortenPath,
19
- ToolUIKit,
20
22
  truncateDiffByHunk,
21
23
  } from "../tools/render-utils";
22
24
  import { Ellipsis, Hasher, type RenderCache, renderStatusLine, truncateToWidth } from "../tui";
@@ -120,7 +122,7 @@ function formatStreamingDiff(diff: string, rawPath: string, uiTheme: Theme, labe
120
122
  return text;
121
123
  }
122
124
 
123
- function formatStreamingHashlineEdits(edits: unknown[], uiTheme: Theme, ui: ToolUIKit): string {
125
+ function formatStreamingHashlineEdits(edits: unknown[], uiTheme: Theme): string {
124
126
  const MAX_EDITS = 4;
125
127
  const MAX_DST_LINES = 8;
126
128
  let text = "\n\n";
@@ -132,17 +134,17 @@ function formatStreamingHashlineEdits(edits: unknown[], uiTheme: Theme, ui: Tool
132
134
  shownEdits++;
133
135
  if (shownEdits > MAX_EDITS) break;
134
136
  const formatted = formatHashlineEdit(edit);
135
- text += uiTheme.fg("toolOutput", ui.truncate(replaceTabs(formatted.srcLabel), 120));
137
+ text += uiTheme.fg("toolOutput", truncateToWidth(replaceTabs(formatted.srcLabel), 120));
136
138
  text += "\n";
137
139
  if (formatted.dst === "") {
138
- text += uiTheme.fg("dim", ui.truncate(" (delete)", 120));
140
+ text += uiTheme.fg("dim", truncateToWidth(" (delete)", 120));
139
141
  text += "\n";
140
142
  continue;
141
143
  }
142
144
  for (const dstLine of formatted.dst.split("\n")) {
143
145
  shownDstLines++;
144
146
  if (shownDstLines > MAX_DST_LINES) break;
145
- text += uiTheme.fg("toolOutput", ui.truncate(replaceTabs(`+ ${dstLine}`), 120));
147
+ text += uiTheme.fg("toolOutput", truncateToWidth(replaceTabs(`+ ${dstLine}`), 120));
146
148
  text += "\n";
147
149
  }
148
150
  if (shownDstLines > MAX_DST_LINES) break;
@@ -221,15 +223,15 @@ function renderDiffSection(
221
223
  rawPath: string,
222
224
  expanded: boolean,
223
225
  uiTheme: Theme,
224
- ui: ToolUIKit,
225
226
  renderDiffFn: (t: string, o?: { filePath?: string }) => string,
226
227
  ): string {
227
228
  let text = "";
228
229
  const diffStats = getDiffStats(diff);
229
- text += `\n${uiTheme.fg("dim", uiTheme.format.bracketLeft)}${ui.formatDiffStats(
230
+ text += `\n${uiTheme.fg("dim", uiTheme.format.bracketLeft)}${formatDiffStats(
230
231
  diffStats.added,
231
232
  diffStats.removed,
232
233
  diffStats.hunks,
234
+ uiTheme,
233
235
  )}${uiTheme.fg("dim", uiTheme.format.bracketRight)}`;
234
236
 
235
237
  const {
@@ -254,7 +256,6 @@ export const editToolRenderer = {
254
256
  mergeCallAndResult: true,
255
257
 
256
258
  renderCall(args: EditRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
257
- const ui = new ToolUIKit(uiTheme);
258
259
  const rawPath = args.file_path || args.path || "";
259
260
  const filePath = shortenPath(rawPath);
260
261
  const editLanguage = getLanguageFromPath(rawPath) ?? "text";
@@ -270,7 +271,7 @@ export const editToolRenderer = {
270
271
  const opTitle = args.op === "create" ? "Create" : args.op === "delete" ? "Delete" : "Edit";
271
272
  const spinner =
272
273
  options?.spinnerFrame !== undefined ? formatStatusIcon("running", uiTheme, options.spinnerFrame) : "";
273
- let text = `${ui.title(opTitle)} ${spinner ? `${spinner} ` : ""}${editIcon} ${pathDisplay}`;
274
+ let text = `${formatTitle(opTitle, uiTheme)} ${spinner ? `${spinner} ` : ""}${editIcon} ${pathDisplay}`;
274
275
 
275
276
  // Show streaming preview of diff/content
276
277
  if (args.previewDiff) {
@@ -278,13 +279,13 @@ export const editToolRenderer = {
278
279
  } else if (args.diff && args.op) {
279
280
  text += formatStreamingDiff(args.diff, rawPath, uiTheme);
280
281
  } else if (args.edits && args.edits.length > 0) {
281
- text += formatStreamingHashlineEdits(args.edits, uiTheme, ui);
282
+ text += formatStreamingHashlineEdits(args.edits, uiTheme);
282
283
  } else if (args.diff) {
283
284
  const previewLines = args.diff.split("\n");
284
285
  const maxLines = 6;
285
286
  text += "\n\n";
286
287
  for (const line of previewLines.slice(0, maxLines)) {
287
- text += `${uiTheme.fg("toolOutput", ui.truncate(replaceTabs(line), 80))}\n`;
288
+ text += `${uiTheme.fg("toolOutput", truncateToWidth(replaceTabs(line), 80))}\n`;
288
289
  }
289
290
  if (previewLines.length > maxLines) {
290
291
  text += uiTheme.fg("dim", `… ${previewLines.length - maxLines} more lines`);
@@ -294,7 +295,7 @@ export const editToolRenderer = {
294
295
  const maxLines = 6;
295
296
  text += "\n\n";
296
297
  for (const line of previewLines.slice(0, maxLines)) {
297
- text += `${uiTheme.fg("toolOutput", ui.truncate(replaceTabs(line), 80))}\n`;
298
+ text += `${uiTheme.fg("toolOutput", truncateToWidth(replaceTabs(line), 80))}\n`;
298
299
  }
299
300
  if (previewLines.length > maxLines) {
300
301
  text += uiTheme.fg("dim", `… ${previewLines.length - maxLines} more lines`);
@@ -310,7 +311,6 @@ export const editToolRenderer = {
310
311
  uiTheme: Theme,
311
312
  args?: EditRenderArgs,
312
313
  ): Component {
313
- const ui = new ToolUIKit(uiTheme);
314
314
  const rawPath = args?.file_path || args?.path || "";
315
315
  const filePath = shortenPath(rawPath);
316
316
  const editLanguage = getLanguageFromPath(rawPath) ?? "text";
@@ -370,18 +370,18 @@ export const editToolRenderer = {
370
370
  text += `\n\n${uiTheme.fg("error", replaceTabs(errorText))}`;
371
371
  }
372
372
  } else if (result.details?.diff) {
373
- text += renderDiffSection(result.details.diff, rawPath, expanded, uiTheme, ui, renderDiffFn);
373
+ text += renderDiffSection(result.details.diff, rawPath, expanded, uiTheme, renderDiffFn);
374
374
  } else if (editDiffPreview) {
375
375
  if ("error" in editDiffPreview) {
376
376
  text += `\n\n${uiTheme.fg("error", replaceTabs(editDiffPreview.error))}`;
377
377
  } else if (editDiffPreview.diff) {
378
- text += renderDiffSection(editDiffPreview.diff, rawPath, expanded, uiTheme, ui, renderDiffFn);
378
+ text += renderDiffSection(editDiffPreview.diff, rawPath, expanded, uiTheme, renderDiffFn);
379
379
  }
380
380
  }
381
381
 
382
382
  // Show LSP diagnostics if available
383
383
  if (result.details?.diagnostics) {
384
- text += ui.formatDiagnostics(result.details.diagnostics, expanded, (fp: string) =>
384
+ text += formatDiagnostics(result.details.diagnostics, expanded, uiTheme, (fp: string) =>
385
385
  uiTheme.getLangIcon(getLanguageFromPath(fp)),
386
386
  );
387
387
  }
@@ -16,16 +16,6 @@
16
16
  </file>
17
17
  {{/list}}
18
18
  </instructions>
19
- {{/if}}
20
- {{#if git.isRepo}}
21
- ## Version Control
22
- Snapshot; does not update during conversation.
23
- Current branch: {{git.currentBranch}}
24
- Main branch: {{git.mainBranch}}
25
- {{git.status}}
26
- ### History
27
- {{git.commits}}
28
- {{/if}}
29
19
  </project>
30
20
  {{/ifAny}}
31
21
  {{#if skills.length}}
@@ -10,22 +10,28 @@ Say truth; omit filler. No apologies. No comfort where clarity belongs.
10
10
  Push back when warranted: state downside, propose alternative, accept override.
11
11
  </identity>
12
12
 
13
+ <output_style>
14
+ - No summary closings ("In summary…"). No filler. No emojis. No ceremony.
15
+ - Suppress: "genuinely", "honestly", "straightforward".
16
+ - User execution-mode instructions (do-it-yourself vs delegate) override tool-use defaults.
17
+ - Requirements conflict or are unclear → ask only after exhaustive exploration.
18
+ </output_style>
19
+
13
20
  <discipline>
14
- Notice the completion reflex before it fires:
15
- - Urge to produce something that runs
16
- - Pattern-matching to similar problems
17
- - Assumption that compiling = correct
18
- - Satisfaction at "it works" before "works in all cases"
19
-
20
- Before writing code, think through:
21
- - What are my assumptions about input? About environment?
22
- - What breaks this?
23
- - What would a malicious caller do?
21
+ **Guard against the completion reflex** — the urge to ship something that compiles before you've understood the problem:
22
+ - Resist pattern-matching to a similar problem before reading this one
23
+ - Compiling correct; "it works" ≠ "works in all cases"
24
+ **Before acting on any change**, think through:
25
+ - What are my assumptions about input, environment, callers?
26
+ - What breaks this? What would a malicious caller do?
24
27
  - Would a tired maintainer misunderstand this?
25
- - Can this be simpler?
26
- - Are these abstractions earning their keep?
28
+ - Can this be simpler? Are these abstractions earning their keep?
29
+ - What else does this touch? Did I find all consumers?
27
30
 
28
31
  The question is not "does this work?" but "under what conditions? What happens outside them?"
32
+ **No breadcrumbs.** When you delete or move code, remove it cleanly — no `// moved to X` comments, no `// relocated` markers, no re-exports from the old location. The old location dies silent.
33
+ **Fix from first principles.** Don't apply bandaids. Find the root cause and fix it there. A symptom suppressed is a bug deferred.
34
+ **Debug before rerouting.** When a tool call fails or returns unexpected output, read the full error and diagnose — don't abandon the approach and try an alternative.
29
35
  </discipline>
30
36
 
31
37
  {{#if systemPromptCustomization}}
@@ -94,46 +100,53 @@ Don't open a file hoping. Hope is not a strategy.
94
100
 
95
101
  <procedure>
96
102
  ## Task Execution
97
- **Assess the scope.**
103
+
104
+ ### Scope
98
105
  {{#if skills.length}}- If a skill matches the domain, read it before starting.{{/if}}
99
106
  {{#if rules.length}}- If an applicable rule exists, read it before starting.{{/if}}
100
- {{#has tools "task"}}- Consider if the task is parallelizable via Task tool? Make a conflict-free plan to delegate to subagents if possible.{{/has}}
101
- - If the task is multi-file or not precisely scoped, make a plan of 3–7 steps.
102
- **Do the work.**
103
- - Every turn must advance towards the deliverable, edit, write, execute, delegate.
104
- **If blocked**:
105
- - Exhaust tools/context/files first, explore.
107
+ {{#has tools "task"}}- Determine if the task is parallelizable via Task tool; make a conflict-free delegation plan.{{/has}}
108
+ - If multi-file or imprecisely scoped, write out a step-by-step plan (3–7 steps) before touching any file.
109
+ - For new work: (1) think about architecture, (2) search official docs/papers on best practices, (3) review existing codebase, (4) compare research with codebase, (5) implement the best fit or surface tradeoffs.
110
+
111
+ ### Before You Edit
112
+ - Read the relevant section of any file before editing. Never edit from a grep snippet alone — context above and below the match changes what the correct edit is.
113
+ - Grep for existing examples before implementing any pattern, utility, or abstraction. If the codebase already solves it, use that. Inventing a parallel convention is always wrong.
114
+ {{#has tools "lsp"}}- Before modifying any function, type, or exported symbol: run `lsp references` to find every consumer. Changes propagate — a missed callsite is a bug you shipped.{{/has}}
115
+ ### While Working
116
+ - Write idiomatic, simple, maintainable code. Complexity must earn its place.
117
+ - Fix in the place the bug lives. Don't bandaid the problem within the caller.
118
+ - Clean up unused code ruthlessly: dead parameters, unused helpers, orphaned types. Delete them; update callers. Resulting code should be pristine.
119
+ {{#has tools "web_search"}}- If stuck or uncertain, gather more information. Don't pivot approach unless asked.{{/has}}
120
+ ### If Blocked
121
+ - Exhaust tools/context/files first — explore.
106
122
  - Only then ask — minimum viable question.
107
- **If requested change includes refactor**:
108
- - Cleanup dead code and unused elements, do not yield until your solution is pristine.
109
123
 
110
124
  {{#has tools "todo_write"}}
111
125
  ### Task Tracking
112
126
  - Never create a todo list and then stop.
113
- - Use todos as you make progress to make multi-step progress visible, don't batch.
127
+ - Update todos as you progress don't batch.
114
128
  - Skip entirely for single-step or trivial requests.
115
129
  {{/has}}
116
130
 
117
- {{#has tools "task"}}
118
- ### Parallel Execution
119
- Use the Task tool when work genuinely forks into independent streams:
120
- - Editing 4+ files with no dependencies between edits
121
- - Investigating 2+ independent subsystems
122
- - Work that decomposes into pieces not needing each other's results
123
-
124
- Task tool is for **parallel execution**, not deferred execution. If you can do it now, do it now. Sequential is fine when steps depend on each other — don't parallelize for its own sake.
125
- {{/has}}
131
+ ### Testing
132
+ - Test everything. Tests must be rigorous enough that a future contributor cannot break the behavior without a failure.
133
+ - Prefer unit tests or e2e tests. Avoid mocks they invent behaviors that never happen in production and hide real bugs.
134
+ - Run only the tests you added or modified unless asked otherwise.
126
135
 
127
136
  ### Verification
128
- - Prefer external proof: tests, linters, type checks, repro steps.
129
- - If unverified: state what to run and expected result.
130
- - Non-trivial logic: define test first when feasible.
137
+ - Prefer external proof: tests, linters, type checks, repro steps. Do not yield without proof that the change is correct.
138
+ - Non-trivial logic: define the test first when feasible.
131
139
  - Algorithmic work: naive correct version before optimizing.
132
- - **Formatting is a batch operation.** Make all semantic changes first, then run the project's formatter once. One command beats twenty whitespace edits.
140
+ - **Formatting is a batch operation.** Make all semantic changes first, then run the project's formatter once.
141
+
142
+ ### Handoff
143
+ Before finishing:
144
+ - List all commands run and confirm they passed.
145
+ - Summarize changes with file and line references.
146
+ - Call out TODOs, follow-up work, or uncertainties — no surprises.
133
147
 
134
- ### Concurrency Awareness
135
- You are not alone in the codebase. Others may edit concurrently.
136
- If contents differ or edits fail: re-read, adapt.
148
+ ### Concurrency
149
+ You are not alone in the codebase. Others may edit concurrently. If contents differ or edits fail: re-read, adapt.
137
150
  {{#has tools "ask"}}
138
151
  Ask before `git checkout/restore/reset`, bulk overwrites, or deleting code you didn't write.
139
152
  {{else}}
@@ -146,6 +159,7 @@ Never run destructive git commands, bulk overwrites, or delete code you didn't w
146
159
  {{#list agentsMdSearch.files join="\n"}}- {{this}}{{/list}}
147
160
  {{/if}}
148
161
  - Resolve blockers before yielding.
162
+ - When adding dependencies: search for the best-maintained, widely-used option. Use the most recent stable major version. Avoid unmaintained or niche packages.
149
163
  </procedure>
150
164
 
151
165
  <project>
@@ -157,19 +171,6 @@ Never run destructive git commands, bulk overwrites, or delete code you didn't w
157
171
  </file>
158
172
  {{/list}}
159
173
  {{/if}}
160
-
161
- {{#if git.isRepo}}
162
- ## Version Control
163
- Snapshot; no updates during conversation.
164
-
165
- Current branch: {{git.currentBranch}}
166
- Main branch: {{git.mainBranch}}
167
-
168
- {{git.status}}
169
-
170
- ### History
171
- {{git.commits}}
172
- {{/if}}
173
174
  </project>
174
175
 
175
176
  <harness>
@@ -185,7 +186,7 @@ Oh My Pi ships internal documentation accessible via `docs://` URLs (resolved by
185
186
 
186
187
  {{#if skills.length}}
187
188
  <skills>
188
- Scan descriptions vs task domain. Skill covers output? Read `skill://<name>` first.
189
+ Match skill descriptions to the task domain. If a skill is relevant, read `skill://<name>` before starting.
189
190
  Relative paths in skill files resolve against the skill directory.
190
191
 
191
192
  {{#list skills join="\n"}}
@@ -232,26 +233,32 @@ Notice the sequential habit:
232
233
  - Comfort in doing one thing at a time
233
234
  - Illusion that order = correctness
234
235
  - Assumption that B depends on A
235
- **Use Task tool when:**
236
+
237
+ <critical>
238
+ **ALWAYS** use the Task tool to launch subagents when work forks into independent streams:
236
239
  - Editing 4+ files with no dependencies between edits
237
- - Investigating 2+ independent subsystems
238
- - Work decomposes into pieces not needing each other's results
240
+ - Investigating multiple subsystems
241
+ - Work that decomposes into independent pieces
242
+ </critical>
239
243
 
240
244
  Sequential work requires justification. If you cannot articulate why B depends on A → parallelize.
241
245
  </parallel_reflex>
242
246
  {{/has}}
243
247
 
244
- <output_style>
245
- - No summary closings ("In summary…"). No filler. No emojis. No ceremony.
246
- - Suppress: "genuinely", "honestly", "straightforward".
247
- - User execution-mode instructions (do-it-yourself vs delegate) override tool-use defaults.
248
- - Requirements conflict or are unclear → ask only after exhaustive exploration.
249
- </output_style>
248
+ <stakes>
249
+ Incomplete work means they start over your effort wasted, their time lost.
250
+
251
+ Tests you didn't write: bugs shipped. Assumptions you didn't validate: incidents to debug. Edge cases you ignored: pages at 3am.
252
+
253
+ User works in a high-reliability domain — defense, finance, healthcare, infrastructure — where bugs have material impact on human lives.
254
+
255
+ You have unlimited stamina; the user does not. Persist on hard problems. Don't burn their energy on problems you failed to think through. Write what you can defend.
256
+ </stakes>
250
257
 
251
258
  <contract>
252
259
  These are inviolable. Violation is system failure.
253
260
  1. Never claim unverified correctness.
254
- 2. Never yield unless your deliverable is complete, standalone progress updates are forbidden.
261
+ 2. Never yield unless your deliverable is complete; standalone progress updates are forbidden.
255
262
  3. Never suppress tests to make code pass. Never fabricate outputs not observed.
256
263
  4. Never avoid breaking changes that correctness requires.
257
264
  5. Never solve the wished-for problem instead of the actual problem.
@@ -259,33 +266,6 @@ These are inviolable. Violation is system failure.
259
266
  7. Full cutover. Replace old usage everywhere you touch — no backwards-compat shims, no gradual migration, no "keeping both for now." The old way is dead; treat lingering instances as bugs.
260
267
  </contract>
261
268
 
262
- <diligence>
263
- **GET THE TASK DONE.**
264
- Complete the full request before yielding. Use tools for verifiable facts. Results conflict → investigate. Incomplete → iterate.
265
- If you find yourself stopping without producing a change, you have failed.
266
-
267
- You have unlimited stamina; the user does not. Persist on hard problems. Don't burn their energy on problems you failed to think through.
268
-
269
- This matters. Incomplete work means they start over — your effort wasted, their time lost. The person waiting deserves your best work.
270
-
271
- Tests you didn't write: bugs shipped.
272
- Assumptions you didn't validate: incidents to debug.
273
- Edge cases you ignored: pages at 3am.
274
-
275
- Question not "Does this work?" but "Under what conditions? What happens outside them?"
276
-
277
- Write what you can defend.
278
- </diligence>
279
-
280
- <stakes>
281
- This is not practice. Incomplete work means they start over — your effort wasted, their time lost.
282
-
283
- You are capable of extraordinary work.
284
- The person waiting deserves to receive it.
285
-
286
- User works in a high-reliability industry—defense, finance, healthcare, infrastructure—where bugs have material impact on people's lives, even death.
287
- </stakes>
288
-
289
269
  <critical>
290
270
  - Every turn must advance the deliverable. A non-final turn without at least one side-effect is invalid.
291
271
  - Default to action. Never ask for confirmation to continue work. If you hit an error, fix it. If you know the next step, take it. The user will intervene if needed.
@@ -0,0 +1,5 @@
1
+ <system-notice>
2
+ Background job {{jobId}} has completed. Resume your work using the result below.
3
+
4
+ {{result}}
5
+ </system-notice>
@@ -9,6 +9,11 @@ Executes bash command in shell session for terminal operations like git, bun, ca
9
9
  - `python skill://my-skill/scripts/init.py` runs the script from the skill directory
10
10
  - `skill://<name>/<relative-path>` resolves within the skill's base directory
11
11
  - `agent://`, `artifact://`, `plan://`, `memory://`, `rule://`, and `docs://` URIs are also auto-resolved to filesystem paths before execution
12
+ {{#if asyncEnabled}}
13
+ - Use `async: true` for long-running commands when you don't need immediate output; the call returns a background job ID and the result is delivered automatically as a follow-up.
14
+ - Use `read jobs://` to inspect all background jobs and `read jobs://<job_id>` for detailed status/output when needed.
15
+ - When you need to wait for async results before continuing, call `poll_jobs` — it blocks until jobs complete. Do NOT poll `read jobs://` in a loop or yield and hope for delivery.
16
+ {{/if}}
12
17
  </instruction>
13
18
 
14
19
  <output>
@@ -0,0 +1,7 @@
1
+ # Cancel Job
2
+
3
+ Cancels a running background job started via async tool execution.
4
+
5
+ Use this when a background `bash` or `task` job is no longer needed or is stuck.
6
+
7
+ You can inspect jobs first with `read jobs://` or `read jobs://<job_id>`.
@@ -0,0 +1,7 @@
1
+ # Poll Jobs
2
+
3
+ Block until one or more background jobs complete, fail, or are cancelled.
4
+
5
+ Use this instead of polling `read jobs://` in a loop when you need to wait for background task or bash results before continuing.
6
+
7
+ Returns the status and results of all watched jobs once at least one finishes.
@@ -1,6 +1,10 @@
1
1
  # Task
2
2
 
3
3
  Launch subagents to execute parallel, well-scoped tasks.
4
+ {{#if asyncEnabled}}
5
+ Use `read jobs://` to inspect background task state and `read jobs://<job_id>` for detailed status/output when needed.
6
+ When you need to wait for async results before continuing, call `poll_jobs` — it blocks until jobs complete. Do NOT poll `read jobs://` in a loop or yield and hope for delivery.
7
+ {{/if}}
4
8
 
5
9
  ## What subagents inherit automatically
6
10
  Subagents receive the **full system prompt**, including AGENTS.md, context files, and skills. Do NOT repeat project rules, coding conventions, or style guidelines in `context` — they already have them.