@oh-my-pi/pi-coding-agent 13.18.0 → 13.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 (64) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/package.json +7 -11
  3. package/src/autoresearch/git.ts +25 -30
  4. package/src/autoresearch/tools/log-experiment.ts +61 -74
  5. package/src/commit/agentic/agent.ts +0 -3
  6. package/src/commit/agentic/index.ts +19 -22
  7. package/src/commit/agentic/tools/git-file-diff.ts +3 -6
  8. package/src/commit/agentic/tools/git-hunk.ts +3 -3
  9. package/src/commit/agentic/tools/git-overview.ts +6 -9
  10. package/src/commit/agentic/tools/index.ts +6 -8
  11. package/src/commit/agentic/tools/propose-commit.ts +4 -7
  12. package/src/commit/agentic/tools/recent-commits.ts +3 -3
  13. package/src/commit/agentic/tools/split-commit.ts +4 -4
  14. package/src/commit/changelog/index.ts +5 -9
  15. package/src/commit/pipeline.ts +10 -12
  16. package/src/config/keybindings.ts +7 -6
  17. package/src/config/settings-schema.ts +44 -0
  18. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +4 -16
  19. package/src/extensibility/custom-commands/bundled/review/index.ts +43 -41
  20. package/src/extensibility/custom-tools/types.ts +1 -1
  21. package/src/extensibility/extensions/types.ts +3 -1
  22. package/src/extensibility/hooks/types.ts +1 -1
  23. package/src/extensibility/plugins/marketplace/fetcher.ts +2 -57
  24. package/src/extensibility/plugins/marketplace/source-resolver.ts +4 -4
  25. package/src/index.ts +1 -0
  26. package/src/main.ts +24 -2
  27. package/src/modes/components/footer.ts +9 -29
  28. package/src/modes/components/hook-editor.ts +3 -3
  29. package/src/modes/components/hook-selector.ts +6 -1
  30. package/src/modes/components/session-observer-overlay.ts +472 -0
  31. package/src/modes/components/settings-defs.ts +19 -0
  32. package/src/modes/components/status-line.ts +15 -61
  33. package/src/modes/controllers/command-controller.ts +1 -0
  34. package/src/modes/controllers/event-controller.ts +59 -2
  35. package/src/modes/controllers/extension-ui-controller.ts +1 -0
  36. package/src/modes/controllers/input-controller.ts +3 -0
  37. package/src/modes/controllers/selector-controller.ts +26 -0
  38. package/src/modes/interactive-mode.ts +195 -43
  39. package/src/modes/session-observer-registry.ts +146 -0
  40. package/src/modes/shared.ts +0 -42
  41. package/src/modes/types.ts +2 -0
  42. package/src/modes/utils/keybinding-matchers.ts +9 -0
  43. package/src/prompts/system/custom-system-prompt.md +5 -0
  44. package/src/prompts/system/system-prompt.md +6 -0
  45. package/src/sdk.ts +28 -13
  46. package/src/secrets/index.ts +1 -1
  47. package/src/secrets/obfuscator.ts +24 -16
  48. package/src/session/agent-session.ts +75 -30
  49. package/src/session/session-manager.ts +15 -5
  50. package/src/system-prompt.ts +4 -0
  51. package/src/task/executor.ts +28 -0
  52. package/src/task/index.ts +88 -78
  53. package/src/task/types.ts +25 -0
  54. package/src/task/worktree.ts +127 -145
  55. package/src/tools/exit-plan-mode.ts +1 -0
  56. package/src/tools/gh.ts +120 -297
  57. package/src/tools/read.ts +13 -79
  58. package/src/utils/external-editor.ts +11 -5
  59. package/src/utils/git.ts +1400 -0
  60. package/src/web/search/render.ts +6 -4
  61. package/src/commit/git/errors.ts +0 -9
  62. package/src/commit/git/index.ts +0 -210
  63. package/src/commit/git/operations.ts +0 -54
  64. package/src/tools/gh-cli.ts +0 -125
package/src/tools/read.ts CHANGED
@@ -470,6 +470,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
470
470
  sourceUrl?: string;
471
471
  sourceInternal?: string;
472
472
  entityLabel: string;
473
+ ignoreResultLimits?: boolean;
473
474
  },
474
475
  ): AgentToolResult<ReadToolDetails> {
475
476
  const displayMode = resolveFileDisplayMode(this.session);
@@ -478,6 +479,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
478
479
  const totalLines = allLines.length;
479
480
  const startLine = offset ? Math.max(0, offset - 1) : 0;
480
481
  const startLineDisplay = startLine + 1;
482
+ const ignoreResultLimits = options.ignoreResultLimits ?? false;
481
483
 
482
484
  const resultBuilder = toolResult(details);
483
485
  if (options.sourcePath) {
@@ -502,10 +504,11 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
502
504
  .done();
503
505
  }
504
506
 
505
- const endLine = limit !== undefined ? Math.min(startLine + limit, allLines.length) : allLines.length;
507
+ const endLine =
508
+ limit !== undefined && !ignoreResultLimits ? Math.min(startLine + limit, allLines.length) : allLines.length;
506
509
  const selectedContent = allLines.slice(startLine, endLine).join("\n");
507
- const userLimitedLines = limit !== undefined ? endLine - startLine : undefined;
508
- const truncation = truncateHead(selectedContent);
510
+ const userLimitedLines = limit !== undefined && !ignoreResultLimits ? endLine - startLine : undefined;
511
+ const truncation = ignoreResultLimits ? noTruncResult(selectedContent) : truncateHead(selectedContent);
509
512
 
510
513
  const shouldAddHashLines = displayMode.hashLines;
511
514
  const shouldAddLineNumbers = shouldAddHashLines ? false : displayMode.lineNumbers;
@@ -1011,82 +1014,13 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
1011
1014
  return toolResult(details).text(resource.content).sourceInternal(url).done();
1012
1015
  }
1013
1016
 
1014
- // Apply pagination similar to file reading.
1015
- const allLines = resource.content.split("\n");
1016
- const totalLines = allLines.length;
1017
-
1018
- const startLine = offset ? Math.max(0, offset - 1) : 0;
1019
- const startLineDisplay = startLine + 1;
1020
-
1021
- if (startLine >= allLines.length) {
1022
- const suggestion =
1023
- allLines.length === 0
1024
- ? "The resource is empty."
1025
- : `Use offset=1 to read from the start, or offset=${allLines.length} to read the last line.`;
1026
- return toolResult<ReadToolDetails>(details)
1027
- .text(`Offset ${offset} is beyond end of resource (${allLines.length} lines total). ${suggestion}`)
1028
- .done();
1029
- }
1030
-
1031
- const ignoreLimits = scheme === "skill";
1032
- let selectedContent: string;
1033
- let userLimitedLines: number | undefined;
1034
- if (limit !== undefined && !ignoreLimits) {
1035
- const endLine = Math.min(startLine + limit, allLines.length);
1036
- selectedContent = allLines.slice(startLine, endLine).join("\n");
1037
- userLimitedLines = endLine - startLine;
1038
- } else {
1039
- selectedContent = allLines.slice(startLine).join("\n");
1040
- }
1041
-
1042
- const truncation: TruncationResult = ignoreLimits
1043
- ? noTruncResult(selectedContent)
1044
- : truncateHead(selectedContent);
1045
-
1046
- let outputText: string;
1047
- let truncationInfo:
1048
- | { result: TruncationResult; options: { direction: "head"; startLine?: number; totalFileLines?: number } }
1049
- | undefined;
1050
-
1051
- if (truncation.firstLineExceedsLimit) {
1052
- const firstLine = allLines[startLine] ?? "";
1053
- const firstLineBytes = Buffer.byteLength(firstLine, "utf-8");
1054
- const snippet = truncateHeadBytes(firstLine, DEFAULT_MAX_BYTES);
1055
-
1056
- outputText = snippet.text;
1057
- if (snippet.text.length === 0) {
1058
- outputText = `[Line ${startLineDisplay} is ${formatBytes(
1059
- firstLineBytes,
1060
- )}, exceeds ${formatBytes(DEFAULT_MAX_BYTES)} limit. Unable to display a valid UTF-8 snippet.]`;
1061
- }
1062
- details.truncation = truncation;
1063
- truncationInfo = {
1064
- result: truncation,
1065
- options: { direction: "head", startLine: startLineDisplay, totalFileLines: totalLines },
1066
- };
1067
- } else if (truncation.truncated) {
1068
- outputText = truncation.content;
1069
- details.truncation = truncation;
1070
- truncationInfo = {
1071
- result: truncation,
1072
- options: { direction: "head", startLine: startLineDisplay, totalFileLines: totalLines },
1073
- };
1074
- } else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {
1075
- const remaining = allLines.length - (startLine + userLimitedLines);
1076
- const nextOffset = startLine + userLimitedLines + 1;
1077
-
1078
- outputText = truncation.content;
1079
- outputText += `\n\n[${remaining} more lines in resource. Use offset=${nextOffset} to continue]`;
1080
- details.truncation = truncation;
1081
- } else {
1082
- outputText = truncation.content;
1083
- }
1084
-
1085
- const resultBuilder = toolResult(details).text(outputText).sourceInternal(url);
1086
- if (truncationInfo) {
1087
- resultBuilder.truncation(truncationInfo.result, truncationInfo.options);
1088
- }
1089
- return resultBuilder.done();
1017
+ return this.#buildInMemoryTextResult(resource.content, offset, limit, {
1018
+ details,
1019
+ sourcePath: resource.sourcePath,
1020
+ sourceInternal: url,
1021
+ entityLabel: "resource",
1022
+ ignoreResultLimits: scheme === "skill",
1023
+ });
1090
1024
  }
1091
1025
 
1092
1026
  /** Read directory contents as a formatted listing */
@@ -17,6 +17,8 @@ export interface OpenInEditorOptions {
17
17
  extension?: string;
18
18
  /** Custom stdio configuration (default: all "inherit"). */
19
19
  stdio?: [number | "inherit", number | "inherit", number | "inherit"];
20
+ /** Keep the file's trailing newline instead of trimming it from the returned text. */
21
+ trimTrailingNewline?: boolean;
20
22
  }
21
23
 
22
24
  /**
@@ -40,13 +42,17 @@ export async function openInEditor(
40
42
  const stdio = options?.stdio ?? ["inherit", "inherit", "inherit"];
41
43
 
42
44
  const child = spawn(editor, [...editorArgs, tmpFile], { stdio, shell: process.platform === "win32" });
43
- const exitCode = await new Promise<number>((resolve, reject) => {
44
- child.once("exit", (code, signal) => resolve(code ?? (signal ? -1 : 0)));
45
- child.once("error", error => reject(error));
46
- });
45
+ const { promise, reject, resolve } = Promise.withResolvers<number>();
46
+ child.once("exit", (code, signal) => resolve(code ?? (signal ? -1 : 0)));
47
+ child.once("error", error => reject(error));
48
+ const exitCode = await promise;
47
49
 
48
50
  if (exitCode === 0) {
49
- return (await Bun.file(tmpFile).text()).replace(/\n$/, "");
51
+ const text = await Bun.file(tmpFile).text();
52
+ if (options?.trimTrailingNewline === false) {
53
+ return text;
54
+ }
55
+ return text.replace(/\n$/, "");
50
56
  }
51
57
  return null;
52
58
  } finally {