@oh-my-pi/pi-coding-agent 14.6.2 → 14.6.3

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 (58) hide show
  1. package/CHANGELOG.md +71 -2
  2. package/README.md +21 -0
  3. package/package.json +23 -7
  4. package/src/cli/grievances-cli.ts +89 -4
  5. package/src/commands/grievances.ts +33 -7
  6. package/src/config/prompt-templates.ts +14 -7
  7. package/src/config/settings-schema.ts +585 -100
  8. package/src/config/settings.ts +42 -0
  9. package/src/discovery/helpers.ts +13 -6
  10. package/src/edit/index.ts +3 -3
  11. package/src/edit/line-hash.ts +73 -25
  12. package/src/edit/modes/hashline.lark +10 -3
  13. package/src/edit/modes/hashline.ts +104 -38
  14. package/src/edit/renderer.ts +3 -3
  15. package/src/hindsight/backend.ts +444 -0
  16. package/src/hindsight/bank.ts +131 -0
  17. package/src/hindsight/client.ts +445 -0
  18. package/src/hindsight/config.ts +165 -0
  19. package/src/hindsight/content.ts +205 -0
  20. package/src/hindsight/index.ts +6 -0
  21. package/src/hindsight/retain-queue.ts +166 -0
  22. package/src/hindsight/transcript.ts +71 -0
  23. package/src/main.ts +7 -10
  24. package/src/memories/index.ts +1 -1
  25. package/src/memory-backend/index.ts +4 -0
  26. package/src/memory-backend/local-backend.ts +30 -0
  27. package/src/memory-backend/off-backend.ts +16 -0
  28. package/src/memory-backend/resolve.ts +24 -0
  29. package/src/memory-backend/types.ts +69 -0
  30. package/src/modes/components/settings-defs.ts +50 -451
  31. package/src/modes/components/settings-selector.ts +2 -2
  32. package/src/modes/components/status-line/presets.ts +1 -1
  33. package/src/modes/controllers/command-controller.ts +6 -5
  34. package/src/modes/controllers/event-controller.ts +12 -0
  35. package/src/modes/controllers/selector-controller.ts +3 -12
  36. package/src/modes/theme/theme.ts +4 -0
  37. package/src/prompts/tools/github.md +3 -0
  38. package/src/prompts/tools/hashline.md +20 -16
  39. package/src/prompts/tools/read.md +10 -6
  40. package/src/prompts/tools/recall.md +5 -0
  41. package/src/prompts/tools/reflect.md +5 -0
  42. package/src/prompts/tools/retain.md +5 -0
  43. package/src/prompts/tools/search.md +1 -1
  44. package/src/sdk.ts +12 -9
  45. package/src/session/agent-session.ts +75 -3
  46. package/src/slash-commands/builtin-registry.ts +2 -12
  47. package/src/tools/ast-edit.ts +14 -5
  48. package/src/tools/ast-grep.ts +12 -3
  49. package/src/tools/find.ts +47 -7
  50. package/src/tools/gh-renderer.ts +10 -1
  51. package/src/tools/gh.ts +233 -5
  52. package/src/tools/hindsight-recall.ts +70 -0
  53. package/src/tools/hindsight-reflect.ts +57 -0
  54. package/src/tools/hindsight-retain.ts +63 -0
  55. package/src/tools/index.ts +17 -0
  56. package/src/tools/path-utils.ts +55 -0
  57. package/src/tools/read.ts +1 -1
  58. package/src/tools/search.ts +45 -8
@@ -22,6 +22,7 @@ import {
22
22
  hasGlobPathChars,
23
23
  normalizePathLikeInput,
24
24
  parseSearchPath,
25
+ partitionExistingPaths,
25
26
  resolveExplicitSearchPaths,
26
27
  resolveToCwd,
27
28
  } from "./path-utils";
@@ -68,6 +69,10 @@ export interface SearchToolDetails {
68
69
  * `result.text` lines but uses a `│` gutter and `*` to mark match lines (vs space for
69
70
  * context). The TUI uses this directly so it never parses model-facing hashline anchors. */
70
71
  displayContent?: string;
72
+ /** User-supplied paths whose base directory was missing on disk. The tool
73
+ * skipped these and continued with the surviving entries; surfaced as a
74
+ * non-fatal warning in the renderer and in the model-facing text. */
75
+ missingPaths?: string[];
71
76
  }
72
77
 
73
78
  type SearchParams = Static<typeof searchSchema>;
@@ -82,7 +87,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
82
87
  constructor(private readonly session: ToolSession) {
83
88
  const displayMode = resolveFileDisplayMode(session);
84
89
  this.description = prompt.render(searchDescription, {
85
- IS_HASHLINE_MODE: displayMode.hashLines,
90
+ IS_HL_MODE: displayMode.hashLines,
86
91
  IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
87
92
  });
88
93
  }
@@ -140,13 +145,26 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
140
145
  }
141
146
  resolvedPathInputs.push(resource.sourcePath);
142
147
  }
143
- if (resolvedPathInputs.length === 1) {
144
- const parsedPath = parseSearchPath(resolvedPathInputs[0] ?? ".");
148
+ // Tolerate missing entries in a multi-path call: skip ones whose base
149
+ // directory is gone, and only error if every entry is missing. Single
150
+ // missing path keeps the original ENOENT semantics.
151
+ let missingPaths: string[] = [];
152
+ let effectivePaths = resolvedPathInputs;
153
+ if (resolvedPathInputs.length > 1) {
154
+ const partition = await partitionExistingPaths(resolvedPathInputs, this.session.cwd, parseSearchPath);
155
+ if (partition.valid.length === 0) {
156
+ throw new ToolError(`Path not found: ${partition.missing.join(", ")}`);
157
+ }
158
+ effectivePaths = partition.valid;
159
+ missingPaths = partition.missing;
160
+ }
161
+ if (effectivePaths.length === 1) {
162
+ const parsedPath = parseSearchPath(effectivePaths[0] ?? ".");
145
163
  searchPath = resolveToCwd(parsedPath.basePath, this.session.cwd);
146
164
  globFilter = parsedPath.glob;
147
165
  scopePath = formatScopePath(searchPath);
148
166
  } else {
149
- const multiSearchPath = await resolveExplicitSearchPaths(resolvedPathInputs, this.session.cwd, globFilter);
167
+ const multiSearchPath = await resolveExplicitSearchPaths(effectivePaths, this.session.cwd, globFilter);
150
168
  if (!multiSearchPath) {
151
169
  throw new ToolError("`paths` must contain at least one path or glob");
152
170
  }
@@ -285,6 +303,8 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
285
303
  const limitMessage = `Result limit reached; narrow paths or use skip=${nextSkip}.`;
286
304
  const { record: recordFile, list: fileList } = createFileRecorder();
287
305
  const fileMatchCounts = new Map<string, number>();
306
+ const missingPathsNote =
307
+ missingPaths.length > 0 ? `Skipped missing paths: ${missingPaths.join(", ")}` : undefined;
288
308
  if (selectedMatches.length === 0) {
289
309
  const details: SearchToolDetails = {
290
310
  scopePath,
@@ -292,8 +312,10 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
292
312
  fileCount: 0,
293
313
  files: [],
294
314
  truncated: false,
315
+ missingPaths: missingPaths.length > 0 ? missingPaths : undefined,
295
316
  };
296
- return toolResult(details).text("No matches found").done();
317
+ const text = missingPathsNote ? `No matches found\n${missingPathsNote}` : "No matches found";
318
+ return toolResult(details).text(text).done();
297
319
  }
298
320
  const outputLines: string[] = [];
299
321
  let linesTruncated = false;
@@ -365,6 +387,9 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
365
387
  if (matchLimitReached || result.limitReached) {
366
388
  outputLines.push("", limitMessage);
367
389
  }
390
+ if (missingPathsNote) {
391
+ outputLines.push("", missingPathsNote);
392
+ }
368
393
  const rawOutput = outputLines.join("\n");
369
394
  const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
370
395
  const output = truncation.content;
@@ -382,6 +407,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
382
407
  matchLimitReached: matchLimitReached ? effectiveLimit : undefined,
383
408
  resultLimitReached: result.limitReached ? internalLimit : undefined,
384
409
  displayContent: displayLines.join("\n"),
410
+ missingPaths: missingPaths.length > 0 ? missingPaths : undefined,
385
411
  };
386
412
  if (truncation.truncated) details.truncation = truncation;
387
413
  if (linesTruncated) details.linesTruncated = true;
@@ -487,12 +513,20 @@ export const searchToolRenderer = {
487
513
  details?.truncated || truncation || limits?.matchLimit || limits?.resultLimit || limits?.columnTruncated,
488
514
  );
489
515
 
516
+ const missingPathsList = details?.missingPaths ?? [];
517
+ const missingNote =
518
+ missingPathsList.length > 0
519
+ ? uiTheme.fg("warning", `skipped missing: ${missingPathsList.join(", ")}`)
520
+ : undefined;
521
+
490
522
  if (matchCount === 0) {
491
523
  const header = renderStatusLine(
492
524
  { icon: "warning", title: "Search", description: args?.pattern, meta: ["0 matches"] },
493
525
  uiTheme,
494
526
  );
495
- return new Text([header, formatEmptyMessage("No matches found", uiTheme)].join("\n"), 0, 0);
527
+ const lines = [header, formatEmptyMessage("No matches found", uiTheme)];
528
+ if (missingNote) lines.push(missingNote);
529
+ return new Text(lines.join("\n"), 0, 0);
496
530
  }
497
531
 
498
532
  const summaryParts = [formatCount("match", matchCount), formatCount("file", fileCount)];
@@ -538,8 +572,11 @@ export const searchToolRenderer = {
538
572
  if (limits?.columnTruncated) truncationReasons.push(`line length ${limits.columnTruncated.maxColumn}`);
539
573
  if (truncation?.artifactId) truncationReasons.push(formatFullOutputReference(truncation.artifactId));
540
574
 
541
- const extraLines =
542
- truncationReasons.length > 0 ? [uiTheme.fg("warning", `truncated: ${truncationReasons.join(", ")}`)] : [];
575
+ const extraLines: string[] = [];
576
+ if (truncationReasons.length > 0) {
577
+ extraLines.push(uiTheme.fg("warning", `truncated: ${truncationReasons.join(", ")}`));
578
+ }
579
+ if (missingNote) extraLines.push(missingNote);
543
580
 
544
581
  let cached: RenderCache | undefined;
545
582
  return {