@oh-my-pi/pi-coding-agent 15.10.11 → 15.10.12

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 (121) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/cli.js +5349 -5328
  3. package/dist/types/cli/args.d.ts +1 -0
  4. package/dist/types/cli-commands.d.ts +12 -0
  5. package/dist/types/commands/launch.d.ts +4 -0
  6. package/dist/types/config/api-key-resolver.d.ts +3 -0
  7. package/dist/types/config/model-registry.d.ts +1 -0
  8. package/dist/types/config/model-resolver.d.ts +18 -0
  9. package/dist/types/config/settings-schema.d.ts +29 -1
  10. package/dist/types/config/settings.d.ts +7 -0
  11. package/dist/types/edit/hashline/noop-loop-guard.d.ts +72 -0
  12. package/dist/types/eval/py/executor.d.ts +5 -0
  13. package/dist/types/eval/py/kernel.d.ts +6 -1
  14. package/dist/types/eval/py/runtime.d.ts +9 -0
  15. package/dist/types/exec/bash-executor.d.ts +2 -0
  16. package/dist/types/extensibility/extensions/runner.d.ts +3 -2
  17. package/dist/types/extensibility/extensions/types.d.ts +3 -0
  18. package/dist/types/memory-backend/index.d.ts +1 -0
  19. package/dist/types/memory-backend/runtime.d.ts +4 -0
  20. package/dist/types/memory-backend/types.d.ts +66 -1
  21. package/dist/types/modes/index.d.ts +3 -3
  22. package/dist/types/modes/interactive-mode.d.ts +7 -2
  23. package/dist/types/modes/oauth-manual-input.d.ts +7 -0
  24. package/dist/types/modes/rpc/rpc-client.d.ts +39 -2
  25. package/dist/types/modes/rpc/rpc-mode.d.ts +31 -2
  26. package/dist/types/modes/rpc/rpc-subagents.d.ts +24 -0
  27. package/dist/types/modes/rpc/rpc-types.d.ts +75 -1
  28. package/dist/types/modes/setup-wizard/index.d.ts +5 -1
  29. package/dist/types/modes/setup-wizard/lazy.d.ts +2 -0
  30. package/dist/types/modes/types.d.ts +2 -0
  31. package/dist/types/secrets/index.d.ts +1 -1
  32. package/dist/types/secrets/obfuscator.d.ts +8 -2
  33. package/dist/types/session/agent-session.d.ts +14 -2
  34. package/dist/types/session/streaming-output.d.ts +23 -0
  35. package/dist/types/slash-commands/acp-builtins.d.ts +16 -0
  36. package/dist/types/slash-commands/builtin-registry.d.ts +1 -0
  37. package/dist/types/slash-commands/types.d.ts +1 -1
  38. package/dist/types/system-prompt.d.ts +2 -0
  39. package/dist/types/task/executor.d.ts +1 -0
  40. package/dist/types/task/index.d.ts +2 -2
  41. package/dist/types/task/types.d.ts +8 -0
  42. package/dist/types/thinking.d.ts +4 -0
  43. package/dist/types/tiny/title-client.d.ts +11 -0
  44. package/dist/types/tiny/title-protocol.d.ts +1 -0
  45. package/dist/types/tools/index.d.ts +6 -0
  46. package/dist/types/utils/git.d.ts +15 -2
  47. package/dist/types/utils/title-generator.d.ts +3 -2
  48. package/package.json +10 -10
  49. package/src/auto-thinking/classifier.ts +1 -0
  50. package/src/cli/args.ts +3 -0
  51. package/src/cli-commands.ts +29 -0
  52. package/src/cli.ts +8 -9
  53. package/src/commands/launch.ts +4 -0
  54. package/src/commit/model-selection.ts +3 -2
  55. package/src/config/api-key-resolver.ts +8 -6
  56. package/src/config/model-registry.ts +97 -30
  57. package/src/config/model-resolver.ts +60 -0
  58. package/src/config/settings-schema.ts +43 -15
  59. package/src/config/settings.ts +61 -3
  60. package/src/edit/hashline/execute.ts +39 -2
  61. package/src/edit/hashline/noop-loop-guard.ts +99 -0
  62. package/src/eval/completion-bridge.ts +1 -0
  63. package/src/eval/py/executor.ts +29 -7
  64. package/src/eval/py/index.ts +6 -1
  65. package/src/eval/py/kernel.ts +31 -11
  66. package/src/eval/py/runtime.ts +37 -0
  67. package/src/exec/bash-executor.ts +82 -3
  68. package/src/extensibility/extensions/get-commands-handler.ts +2 -1
  69. package/src/extensibility/extensions/runner.ts +6 -1
  70. package/src/extensibility/extensions/types.ts +3 -0
  71. package/src/hindsight/bank.ts +17 -2
  72. package/src/internal-urls/docs-index.generated.ts +3 -3
  73. package/src/main.ts +18 -6
  74. package/src/memories/index.ts +2 -0
  75. package/src/memory-backend/index.ts +1 -0
  76. package/src/memory-backend/local-backend.ts +9 -0
  77. package/src/memory-backend/off-backend.ts +9 -0
  78. package/src/memory-backend/runtime.ts +66 -0
  79. package/src/memory-backend/types.ts +81 -1
  80. package/src/mnemopi/backend.ts +151 -4
  81. package/src/modes/acp/acp-agent.ts +119 -11
  82. package/src/modes/components/assistant-message.ts +19 -21
  83. package/src/modes/components/footer.ts +3 -1
  84. package/src/modes/components/status-line/component.ts +118 -34
  85. package/src/modes/controllers/command-controller.ts +1 -1
  86. package/src/modes/controllers/input-controller.ts +1 -0
  87. package/src/modes/controllers/mcp-command-controller.ts +38 -3
  88. package/src/modes/index.ts +3 -21
  89. package/src/modes/interactive-mode.ts +39 -9
  90. package/src/modes/oauth-manual-input.ts +30 -3
  91. package/src/modes/rpc/rpc-client.ts +154 -3
  92. package/src/modes/rpc/rpc-mode.ts +97 -12
  93. package/src/modes/rpc/rpc-subagents.ts +265 -0
  94. package/src/modes/rpc/rpc-types.ts +81 -1
  95. package/src/modes/setup-wizard/index.ts +12 -2
  96. package/src/modes/setup-wizard/lazy.ts +16 -0
  97. package/src/modes/types.ts +2 -0
  98. package/src/sdk.ts +8 -1
  99. package/src/secrets/index.ts +8 -1
  100. package/src/secrets/obfuscator.ts +39 -18
  101. package/src/session/agent-session.ts +179 -54
  102. package/src/session/streaming-output.ts +166 -10
  103. package/src/slash-commands/acp-builtins.ts +24 -0
  104. package/src/slash-commands/builtin-registry.ts +20 -0
  105. package/src/slash-commands/types.ts +1 -1
  106. package/src/system-prompt.ts +14 -0
  107. package/src/task/executor.ts +13 -12
  108. package/src/task/index.ts +9 -8
  109. package/src/task/render.ts +18 -3
  110. package/src/task/types.ts +9 -0
  111. package/src/thinking.ts +7 -0
  112. package/src/tiny/title-client.ts +34 -5
  113. package/src/tiny/title-protocol.ts +1 -1
  114. package/src/tiny/worker.ts +6 -4
  115. package/src/tools/bash.ts +46 -5
  116. package/src/tools/image-gen.ts +11 -4
  117. package/src/tools/index.ts +13 -1
  118. package/src/tools/inspect-image.ts +1 -0
  119. package/src/utils/commit-message-generator.ts +1 -0
  120. package/src/utils/git.ts +267 -13
  121. package/src/utils/title-generator.ts +24 -5
@@ -6,6 +6,7 @@
6
6
  import * as fs from "node:fs/promises";
7
7
  import { ExponentialYield } from "@oh-my-pi/pi-agent-core/utils/yield";
8
8
  import { executeShell, type MinimizerOptions, Shell, type ShellRunResult } from "@oh-my-pi/pi-natives";
9
+ import { isExecutable, type ShellConfig } from "@oh-my-pi/pi-utils/procmgr";
9
10
  import { Settings, type ShellMinimizerSettings } from "../config/settings";
10
11
  import { OutputSink } from "../session/streaming-output";
11
12
  import { resolveOutputMaxColumns, resolveOutputSinkHeadBytes } from "../tools/output-meta";
@@ -22,6 +23,8 @@ export interface BashExecutorOptions {
22
23
  sessionKey?: string;
23
24
  /** Additional environment variables to inject */
24
25
  env?: Record<string, string>;
26
+ /** Run through the configured user shell instead of brush parsing directly. */
27
+ useUserShell?: boolean;
25
28
  /** Artifact path/id for full output storage */
26
29
  artifactPath?: string;
27
30
  artifactId?: string;
@@ -95,13 +98,86 @@ export function buildMinimizerOptions(group: ShellMinimizerSettings): MinimizerO
95
98
  only: group.only.length > 0 ? group.only : undefined,
96
99
  except: group.except.length > 0 ? group.except : undefined,
97
100
  maxCaptureBytes: group.maxCaptureBytes,
101
+ sourceOutlineLevel: group.sourceOutlineLevel === "default" ? undefined : group.sourceOutlineLevel,
102
+ legacyFilters: group.legacyFilters,
103
+ };
104
+ }
105
+
106
+ function shellBasename(shell: string): string {
107
+ return shell.replace(/\\/g, "/").split("/").pop()?.toLowerCase() ?? "";
108
+ }
109
+
110
+ function isBashShell(shell: string): boolean {
111
+ const basename = shellBasename(shell);
112
+ return basename.includes("bash");
113
+ }
114
+
115
+ function needsInteractiveShellArg(shell: string): boolean {
116
+ const basename = shellBasename(shell);
117
+ return basename.includes("zsh");
118
+ }
119
+
120
+ function supportsAutoUserShell(shell: string): boolean {
121
+ const basename = shellBasename(shell);
122
+ return basename.includes("bash") || basename.includes("zsh") || basename.includes("fish");
123
+ }
124
+
125
+ function hasInteractiveShellArg(args: string[]): boolean {
126
+ return args.some(arg => arg === "--interactive" || /^-[^-]*i/.test(arg));
127
+ }
128
+
129
+ function ensureInteractiveShellArgs(shell: string, args: string[]): string[] {
130
+ if (!needsInteractiveShellArg(shell) || hasInteractiveShellArg(args)) return args;
131
+
132
+ const commandIndex = args.findIndex(arg => arg === "-c" || arg === "--command");
133
+ if (commandIndex !== -1) {
134
+ return [...args.slice(0, commandIndex), "-i", ...args.slice(commandIndex)];
135
+ }
136
+
137
+ const compactCommandIndex = args.findIndex(arg => /^-[^-]*c[^-]*$/.test(arg));
138
+ if (compactCommandIndex !== -1) {
139
+ return args.map((arg, index) => (index === compactCommandIndex ? arg.replace("c", "ic") : arg));
140
+ }
141
+
142
+ return [...args, "-i"];
143
+ }
144
+
145
+ function quoteShellArg(value: string): string {
146
+ return `'${value.replace(/'/g, "'\\''")}'`;
147
+ }
148
+
149
+ function buildUserShellCommand(shell: string, args: string[], command: string): string {
150
+ return [shell, ...ensureInteractiveShellArgs(shell, args), command].map(quoteShellArg).join(" ");
151
+ }
152
+
153
+ function resolveUserShellConfig(settings: Settings, baseConfig: ShellConfig): ShellConfig {
154
+ const customShellPath = settings.get("shellPath");
155
+ const envShell = Bun.env.SHELL;
156
+ if (customShellPath || process.platform === "win32" || !envShell || envShell === baseConfig.shell) {
157
+ return baseConfig;
158
+ }
159
+ if (!supportsAutoUserShell(envShell) || !isExecutable(envShell)) {
160
+ return baseConfig;
161
+ }
162
+
163
+ return {
164
+ ...baseConfig,
165
+ shell: envShell,
166
+ env: {
167
+ ...baseConfig.env,
168
+ SHELL: envShell,
169
+ },
98
170
  };
99
171
  }
100
172
 
101
173
  export async function executeBash(command: string, options?: BashExecutorOptions): Promise<BashResult> {
102
174
  const settings = await Settings.init();
103
- const { shell, env: shellEnv, prefix } = settings.getShellConfig();
104
- const snapshotPath = shell.includes("bash") ? await getOrCreateSnapshot(shell, shellEnv) : null;
175
+ const baseShellConfig = settings.getShellConfig();
176
+ const shellConfig =
177
+ options?.useUserShell === true ? resolveUserShellConfig(settings, baseShellConfig) : baseShellConfig;
178
+ const { shell, args, env: shellEnv, prefix } = shellConfig;
179
+ const bashShell = isBashShell(shell);
180
+ const snapshotPath = bashShell ? await getOrCreateSnapshot(shell, shellEnv) : null;
105
181
 
106
182
  const minimizer = buildMinimizerOptions(settings.getGroup("shellMinimizer"));
107
183
 
@@ -110,7 +186,10 @@ export async function executeBash(command: string, options?: BashExecutorOptions
110
186
 
111
187
  // Apply command prefix if configured
112
188
  const prefixedCommand = prefix ? `${prefix} ${command}` : command;
113
- const finalCommand = prefixedCommand;
189
+ const finalCommand =
190
+ options?.useUserShell === true && !bashShell
191
+ ? buildUserShellCommand(shell, args, prefixedCommand)
192
+ : prefixedCommand;
114
193
 
115
194
  // Create output sink for truncation and artifact handling
116
195
  const sink = new OutputSink({
@@ -15,6 +15,7 @@
15
15
  * themselves. Each frontend (interactive-mode, ACP) prepends its own builtins.
16
16
  */
17
17
  import type { SkillsSettings } from "../../config/settings";
18
+ import { BUILTIN_SLASH_COMMAND_RESERVED_NAMES } from "../../slash-commands/builtin-registry";
18
19
  import type { CustomCommandSource, LoadedCustomCommand } from "../custom-commands";
19
20
  import { getSkillSlashCommandName, type Skill } from "../skills";
20
21
  import type { SlashCommandInfo, SlashCommandLocation } from "../slash-commands";
@@ -32,7 +33,7 @@ export function getSessionSlashCommands(session: CommandsCapableSession): SlashC
32
33
 
33
34
  const runner = session.extensionRunner;
34
35
  if (runner) {
35
- for (const cmd of runner.getRegisteredCommands()) {
36
+ for (const cmd of runner.getRegisteredCommands(BUILTIN_SLASH_COMMAND_RESERVED_NAMES)) {
36
37
  out.push({
37
38
  name: cmd.name,
38
39
  description: cmd.description,
@@ -6,6 +6,7 @@ import type { CredentialDisabledEvent, ImageContent, Model, ProviderResponseMeta
6
6
  import type { KeyId } from "@oh-my-pi/pi-tui";
7
7
  import { logger } from "@oh-my-pi/pi-utils";
8
8
  import type { ModelRegistry } from "../../config/model-registry";
9
+ import type { MemoryRuntimeContext } from "../../memory-backend";
9
10
  import { type Theme, theme } from "../../modes/theme/theme";
10
11
  import type { SessionManager } from "../../session/session-manager";
11
12
  import type {
@@ -187,6 +188,7 @@ export class ExtensionRunner {
187
188
  #switchSessionHandler: SwitchSessionHandler = async () => ({ cancelled: false });
188
189
  #reloadHandler: () => Promise<void> = async () => {};
189
190
  #shutdownHandler: ShutdownHandler = () => {};
191
+ #getMemoryFn?: () => MemoryRuntimeContext | undefined;
190
192
  #commandDiagnostics: Array<{ type: string; message: string; path: string }> = [];
191
193
  #initialized = false;
192
194
  /**
@@ -204,8 +206,10 @@ export class ExtensionRunner {
204
206
  private readonly cwd: string,
205
207
  private readonly sessionManager: SessionManager,
206
208
  private readonly modelRegistry: ModelRegistry,
209
+ getMemory?: () => MemoryRuntimeContext | undefined,
207
210
  ) {
208
211
  this.#uiContext = noOpUIContext;
212
+ this.#getMemoryFn = getMemory;
209
213
  }
210
214
 
211
215
  initialize(
@@ -427,7 +431,7 @@ export class ExtensionRunner {
427
431
  return this.extensions.flatMap(ext => ext.assistantThinkingRenderers);
428
432
  }
429
433
 
430
- getRegisteredCommands(reserved?: Set<string>): RegisteredCommand[] {
434
+ getRegisteredCommands(reserved?: ReadonlySet<string>): RegisteredCommand[] {
431
435
  this.#commandDiagnostics = [];
432
436
 
433
437
  const commands = new Map<string, RegisteredCommand>();
@@ -480,6 +484,7 @@ export class ExtensionRunner {
480
484
  hasPendingMessages: () => this.#hasPendingMessagesFn(),
481
485
  shutdown: () => this.#shutdownHandler(),
482
486
  getSystemPrompt: () => this.#getSystemPromptFn(),
487
+ memory: this.#getMemoryFn?.(),
483
488
  };
484
489
  }
485
490
 
@@ -40,6 +40,7 @@ import type { EditToolDetails } from "../../edit";
40
40
  import type { PythonResult } from "../../eval/py/executor";
41
41
  import type { BashResult } from "../../exec/bash-executor";
42
42
  import type { ExecOptions, ExecResult } from "../../exec/exec";
43
+ import type { MemoryRuntimeContext } from "../../memory-backend";
43
44
  import type { CustomEditor } from "../../modes/components/custom-editor";
44
45
  import type { Theme } from "../../modes/theme/theme";
45
46
  import type { CustomMessage } from "../../session/messages";
@@ -319,6 +320,8 @@ export interface ExtensionContext {
319
320
  shutdown(): void;
320
321
  /** Get the current effective system prompt. */
321
322
  getSystemPrompt(): string[];
323
+ /** Structured memory runtime for status/search/save across the configured backend. */
324
+ memory?: MemoryRuntimeContext;
322
325
  }
323
326
 
324
327
  /**
@@ -22,6 +22,7 @@
22
22
 
23
23
  import * as path from "node:path";
24
24
  import { logger } from "@oh-my-pi/pi-utils";
25
+ import * as git from "../utils/git";
25
26
  import type { HindsightApi } from "./client";
26
27
  import type { HindsightConfig } from "./config";
27
28
 
@@ -53,10 +54,24 @@ function baseBankId(config: HindsightConfig): string {
53
54
  return prefix ? `${prefix}-${base}` : base;
54
55
  }
55
56
 
56
- /** Best-effort project label from a working-directory path. */
57
+ /**
58
+ * Best-effort project label from a working-directory path.
59
+ *
60
+ * When `directory` lives inside a git repository we resolve the primary
61
+ * checkout root (or the shared common dir for bare-repo worktrees) via
62
+ * {@link git.repo.primaryRootSync} and basename that, so every linked
63
+ * worktree of one repo shares the same `project:<name>` tag.
64
+ * Outside a repo (or when resolution fails), fall back to the cwd basename.
65
+ *
66
+ * Sync only: this runs on the hot path of `computeBankScope`, which is
67
+ * exposed as a sync API to callers like `backend.ts` and must stay sync.
68
+ * `git.repo.primaryRootSync` walks `.git`/`commondir` with sync file reads —
69
+ * no subprocess — so the cost is one or two `stat`s and a small `readFile`.
70
+ */
57
71
  function projectLabel(directory: string): string {
58
72
  if (!directory) return UNKNOWN_PROJECT;
59
- return path.basename(directory) || UNKNOWN_PROJECT;
73
+ const primary = git.repo.primaryRootSync(directory);
74
+ return path.basename(primary ?? directory) || UNKNOWN_PROJECT;
60
75
  }
61
76
 
62
77
  /**