@oh-my-pi/pi-coding-agent 13.7.5 → 13.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [13.7.6] - 2026-03-04
6
+ ### Added
7
+
8
+ - Exported `dedupeParseErrors` utility function to deduplicate parse error messages while preserving order
9
+
10
+ ### Fixed
11
+
12
+ - Reduced duplicate parse error messages when multiple patterns fail on the same file
13
+ - Normalized parse error output in ast-grep to remove pattern-specific prefixes and show only file-level errors
14
+
5
15
  ## [13.7.4] - 2026-03-04
6
16
  ### Added
7
17
  - Added `fetch.useKagiSummarizer` setting to toggle Kagi Universal Summarizer usage in the fetch tool.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "13.7.5",
4
+ "version": "13.7.6",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/can1357/oh-my-pi",
7
7
  "author": "Can Boluk",
@@ -41,12 +41,12 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@mozilla/readability": "^0.6",
44
- "@oh-my-pi/omp-stats": "13.7.5",
45
- "@oh-my-pi/pi-agent-core": "13.7.5",
46
- "@oh-my-pi/pi-ai": "13.7.5",
47
- "@oh-my-pi/pi-natives": "13.7.5",
48
- "@oh-my-pi/pi-tui": "13.7.5",
49
- "@oh-my-pi/pi-utils": "13.7.5",
44
+ "@oh-my-pi/omp-stats": "13.7.6",
45
+ "@oh-my-pi/pi-agent-core": "13.7.6",
46
+ "@oh-my-pi/pi-ai": "13.7.6",
47
+ "@oh-my-pi/pi-natives": "13.7.6",
48
+ "@oh-my-pi/pi-tui": "13.7.6",
49
+ "@oh-my-pi/pi-utils": "13.7.6",
50
50
  "@sinclair/typebox": "^0.34",
51
51
  "@xterm/headless": "^6.0",
52
52
  "ajv": "^8.18",
@@ -16,6 +16,7 @@ import type { ToolSession } from ".";
16
16
  import type { OutputMeta } from "./output-meta";
17
17
  import { hasGlobPathChars, parseSearchPath, resolveToCwd } from "./path-utils";
18
18
  import {
19
+ dedupeParseErrors,
19
20
  formatCount,
20
21
  formatEmptyMessage,
21
22
  formatErrorMessage,
@@ -140,6 +141,7 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
140
141
  signal,
141
142
  });
142
143
 
144
+ const dedupedParseErrors = dedupeParseErrors(result.parseErrors);
143
145
  const formatPath = (filePath: string): string => {
144
146
  const cleanPath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
145
147
  if (isDirectory) {
@@ -178,15 +180,15 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
178
180
  filesSearched: result.filesSearched,
179
181
  applied: result.applied,
180
182
  limitReached: result.limitReached,
181
- parseErrors: result.parseErrors,
183
+ parseErrors: dedupedParseErrors,
182
184
  scopePath,
183
185
  files: fileList,
184
186
  fileReplacements: [],
185
187
  };
186
188
 
187
189
  if (result.totalReplacements === 0) {
188
- const parseMessage = result.parseErrors?.length
189
- ? `\n${formatParseErrors(result.parseErrors).join("\n")}`
190
+ const parseMessage = dedupedParseErrors.length
191
+ ? `\n${formatParseErrors(dedupedParseErrors).join("\n")}`
190
192
  : "";
191
193
  return toolResult(baseDetails).text(`No replacements made${parseMessage}`).done();
192
194
  }
@@ -258,8 +260,8 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
258
260
  if (result.limitReached) {
259
261
  outputLines.push("", "Limit reached; narrow path or increase limit.");
260
262
  }
261
- if (result.parseErrors?.length) {
262
- outputLines.push("", ...formatParseErrors(result.parseErrors));
263
+ if (dedupedParseErrors.length) {
264
+ outputLines.push("", ...formatParseErrors(dedupedParseErrors));
263
265
  }
264
266
 
265
267
  // Register pending action so `resolve` can apply or discard these previewed changes
@@ -281,13 +283,14 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
281
283
  maxFiles,
282
284
  failOnParseError: false,
283
285
  });
286
+ const dedupedApplyParseErrors = dedupeParseErrors(applyResult.parseErrors);
284
287
  const appliedDetails: AstEditToolDetails = {
285
288
  totalReplacements: applyResult.totalReplacements,
286
289
  filesTouched: applyResult.filesTouched,
287
290
  filesSearched: applyResult.filesSearched,
288
291
  applied: applyResult.applied,
289
292
  limitReached: applyResult.limitReached,
290
- parseErrors: applyResult.parseErrors,
293
+ parseErrors: dedupedApplyParseErrors,
291
294
  scopePath,
292
295
  files: fileList,
293
296
  fileReplacements,
@@ -16,6 +16,7 @@ import type { ToolSession } from ".";
16
16
  import type { OutputMeta } from "./output-meta";
17
17
  import { hasGlobPathChars, parseSearchPath, resolveToCwd } from "./path-utils";
18
18
  import {
19
+ dedupeParseErrors,
19
20
  formatCount,
20
21
  formatEmptyMessage,
21
22
  formatErrorMessage,
@@ -133,6 +134,11 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
133
134
  signal,
134
135
  });
135
136
 
137
+ const normalizedParseErrors = (result.parseErrors ?? []).map(error => {
138
+ const parseError = error.match(/^.+: (.+: parse error \(syntax tree contains error nodes\))$/);
139
+ return parseError?.[1] ?? error;
140
+ });
141
+ const dedupedParseErrors = dedupeParseErrors(normalizedParseErrors);
136
142
  const formatPath = (filePath: string): string => {
137
143
  const cleanPath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
138
144
  if (isDirectory) {
@@ -165,15 +171,15 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
165
171
  fileCount: result.filesWithMatches,
166
172
  filesSearched: result.filesSearched,
167
173
  limitReached: result.limitReached,
168
- parseErrors: result.parseErrors,
174
+ parseErrors: dedupedParseErrors,
169
175
  scopePath,
170
176
  files: fileList,
171
177
  fileMatches: [],
172
178
  };
173
179
 
174
180
  if (result.matches.length === 0) {
175
- const parseMessage = result.parseErrors?.length
176
- ? `\n${formatParseErrors(result.parseErrors).join("\n")}`
181
+ const parseMessage = dedupedParseErrors.length
182
+ ? `\n${formatParseErrors(dedupedParseErrors).join("\n")}`
177
183
  : "";
178
184
  return toolResult(baseDetails).text(`No matches found${parseMessage}`).done();
179
185
  }
@@ -253,8 +259,8 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
253
259
  if (result.limitReached) {
254
260
  outputLines.push("", "Result limit reached; narrow path pattern or increase limit.");
255
261
  }
256
- if (result.parseErrors?.length) {
257
- outputLines.push("", ...formatParseErrors(result.parseErrors));
262
+ if (dedupedParseErrors.length) {
263
+ outputLines.push("", ...formatParseErrors(dedupedParseErrors));
258
264
  }
259
265
 
260
266
  return toolResult(details).text(outputLines.join("\n")).done();
@@ -533,10 +533,25 @@ export function wrapBrackets(text: string, theme: Theme): string {
533
533
 
534
534
  export const PARSE_ERRORS_LIMIT = 20;
535
535
 
536
+ export function dedupeParseErrors(errors: string[] | undefined): string[] {
537
+ if (!errors || errors.length === 0) return [];
538
+ const seen = new Set<string>();
539
+ const deduped: string[] = [];
540
+ for (const error of errors) {
541
+ if (seen.has(error)) continue;
542
+ seen.add(error);
543
+ deduped.push(error);
544
+ }
545
+ return deduped;
546
+ }
547
+
536
548
  export function formatParseErrors(errors: string[]): string[] {
537
- if (errors.length === 0) return [];
538
- const capped = errors.slice(0, PARSE_ERRORS_LIMIT);
549
+ const deduped = dedupeParseErrors(errors);
550
+ if (deduped.length === 0) return [];
551
+ const capped = deduped.slice(0, PARSE_ERRORS_LIMIT);
539
552
  const header =
540
- errors.length > PARSE_ERRORS_LIMIT ? `Parse issues (${PARSE_ERRORS_LIMIT} / ${errors.length}):` : "Parse issues:";
553
+ deduped.length > PARSE_ERRORS_LIMIT
554
+ ? `Parse issues (${PARSE_ERRORS_LIMIT} / ${deduped.length}):`
555
+ : "Parse issues:";
541
556
  return [header, ...capped.map(err => `- ${err}`)];
542
557
  }