@oh-my-pi/pi-coding-agent 15.10.11 → 15.11.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.
- package/CHANGELOG.md +103 -2
- package/dist/cli.js +5790 -5731
- package/dist/types/async/index.d.ts +0 -1
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/gallery-fixtures/types.d.ts +5 -0
- package/dist/types/cli-commands.d.ts +12 -0
- package/dist/types/commands/launch.d.ts +4 -0
- package/dist/types/config/api-key-resolver.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/model-registry.d.ts +1 -0
- package/dist/types/config/model-resolver.d.ts +18 -0
- package/dist/types/config/settings-schema.d.ts +85 -34
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/edit/hashline/noop-loop-guard.d.ts +72 -0
- package/dist/types/eval/py/executor.d.ts +5 -0
- package/dist/types/eval/py/kernel.d.ts +6 -1
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/runner.d.ts +3 -2
- package/dist/types/extensibility/extensions/types.d.ts +3 -0
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/internal-urls/history-protocol.d.ts +14 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/irc/bus.d.ts +66 -0
- package/dist/types/memory-backend/index.d.ts +1 -0
- package/dist/types/memory-backend/runtime.d.ts +4 -0
- package/dist/types/memory-backend/types.d.ts +66 -1
- package/dist/types/modes/components/agent-hub.d.ts +30 -0
- package/dist/types/modes/components/compaction-summary-message.d.ts +10 -4
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/ttsr-notification.d.ts +5 -1
- package/dist/types/modes/components/welcome.d.ts +3 -9
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/index.d.ts +3 -3
- package/dist/types/modes/interactive-mode.d.ts +10 -4
- package/dist/types/modes/oauth-manual-input.d.ts +7 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +39 -2
- package/dist/types/modes/rpc/rpc-mode.d.ts +31 -2
- package/dist/types/modes/rpc/rpc-subagents.d.ts +24 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +75 -1
- package/dist/types/modes/setup-wizard/index.d.ts +5 -1
- package/dist/types/modes/setup-wizard/lazy.d.ts +2 -0
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +5 -2
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -1
- package/dist/types/registry/agent-lifecycle.d.ts +51 -0
- package/dist/types/registry/agent-registry.d.ts +16 -5
- package/dist/types/secrets/index.d.ts +1 -1
- package/dist/types/secrets/obfuscator.d.ts +8 -2
- package/dist/types/session/agent-session.d.ts +49 -32
- package/dist/types/session/messages.d.ts +2 -4
- package/dist/types/session/session-history-format.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +21 -3
- package/dist/types/session/streaming-output.d.ts +46 -0
- package/dist/types/slash-commands/acp-builtins.d.ts +16 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -0
- package/dist/types/slash-commands/types.d.ts +1 -1
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +12 -2
- package/dist/types/task/index.d.ts +13 -6
- package/dist/types/task/output-manager.d.ts +0 -7
- package/dist/types/task/repair-args.d.ts +8 -7
- package/dist/types/task/types.d.ts +63 -51
- package/dist/types/thinking.d.ts +4 -0
- package/dist/types/tiny/title-client.d.ts +11 -0
- package/dist/types/tiny/title-protocol.d.ts +1 -0
- package/dist/types/tools/browser/tab-worker.d.ts +3 -1
- package/dist/types/tools/find.d.ts +0 -11
- package/dist/types/tools/grouped-file-output.d.ts +0 -49
- package/dist/types/tools/index.d.ts +7 -3
- package/dist/types/tools/irc.d.ts +76 -38
- package/dist/types/tools/job.d.ts +7 -1
- package/dist/types/utils/git.d.ts +15 -2
- package/dist/types/utils/title-generator.d.ts +3 -2
- package/examples/extensions/with-deps/package.json +1 -0
- package/package.json +11 -10
- package/scripts/bundle-dist.ts +28 -19
- package/src/async/index.ts +0 -1
- package/src/auto-thinking/classifier.ts +1 -0
- package/src/cli/args.ts +3 -0
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/gallery-fixtures/agentic.ts +230 -115
- package/src/cli/gallery-fixtures/types.ts +5 -0
- package/src/cli-commands.ts +29 -0
- package/src/cli.ts +28 -15
- package/src/commands/launch.ts +4 -0
- package/src/commit/agentic/tools/analyze-file.ts +38 -19
- package/src/commit/model-selection.ts +3 -2
- package/src/config/api-key-resolver.ts +8 -6
- package/src/config/keybindings.ts +6 -1
- package/src/config/model-registry.ts +97 -30
- package/src/config/model-resolver.ts +60 -0
- package/src/config/settings-schema.ts +99 -55
- package/src/config/settings.ts +68 -3
- package/src/edit/hashline/execute.ts +39 -2
- package/src/edit/hashline/noop-loop-guard.ts +99 -0
- package/src/eval/__tests__/agent-bridge.test.ts +5 -3
- package/src/eval/agent-bridge.ts +3 -16
- package/src/eval/completion-bridge.ts +1 -0
- package/src/eval/js/shared/prelude.txt +1 -1
- package/src/eval/py/executor.ts +29 -7
- package/src/eval/py/index.ts +6 -1
- package/src/eval/py/kernel.ts +31 -11
- package/src/eval/py/prelude.py +5 -6
- package/src/eval/py/runtime.ts +37 -0
- package/src/exec/bash-executor.ts +82 -3
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +38 -13
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/get-commands-handler.ts +2 -1
- package/src/extensibility/extensions/runner.ts +6 -1
- package/src/extensibility/extensions/types.ts +3 -0
- package/src/extensibility/shared-events.ts +2 -2
- package/src/hindsight/bank.ts +17 -2
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/internal-urls/history-protocol.ts +113 -0
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/router.ts +3 -1
- package/src/internal-urls/types.ts +1 -1
- package/src/irc/bus.ts +292 -0
- package/src/main.ts +26 -66
- package/src/memories/index.ts +2 -0
- package/src/memory-backend/index.ts +1 -0
- package/src/memory-backend/local-backend.ts +9 -0
- package/src/memory-backend/off-backend.ts +9 -0
- package/src/memory-backend/runtime.ts +66 -0
- package/src/memory-backend/types.ts +81 -1
- package/src/mnemopi/backend.ts +151 -4
- package/src/modes/acp/acp-agent.ts +119 -11
- package/src/modes/components/{session-observer-overlay.ts → agent-hub.ts} +586 -367
- package/src/modes/components/assistant-message.ts +19 -21
- package/src/modes/components/compaction-summary-message.ts +68 -32
- package/src/modes/components/custom-editor.ts +10 -0
- package/src/modes/components/footer.ts +3 -1
- package/src/modes/components/status-line/component.ts +118 -34
- package/src/modes/components/tool-execution.ts +31 -1
- package/src/modes/components/ttsr-notification.ts +72 -30
- package/src/modes/components/welcome.ts +9 -33
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +65 -0
- package/src/modes/controllers/extension-ui-controller.ts +8 -8
- package/src/modes/controllers/input-controller.ts +19 -2
- package/src/modes/controllers/mcp-command-controller.ts +38 -3
- package/src/modes/controllers/selector-controller.ts +21 -17
- package/src/modes/index.ts +3 -21
- package/src/modes/interactive-mode.ts +47 -22
- package/src/modes/oauth-manual-input.ts +30 -3
- package/src/modes/rpc/rpc-client.ts +154 -3
- package/src/modes/rpc/rpc-mode.ts +97 -12
- package/src/modes/rpc/rpc-subagents.ts +265 -0
- package/src/modes/rpc/rpc-types.ts +81 -1
- package/src/modes/setup-wizard/index.ts +12 -2
- package/src/modes/setup-wizard/lazy.ts +16 -0
- package/src/modes/theme/theme.ts +18 -5
- package/src/modes/types.ts +5 -5
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +51 -49
- package/src/prompts/system/irc-incoming.md +3 -4
- package/src/prompts/system/orchestrate-notice.md +2 -2
- package/src/prompts/system/subagent-system-prompt.md +0 -5
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/system/workflow-notice.md +2 -2
- package/src/prompts/tools/eval.md +3 -3
- package/src/prompts/tools/irc.md +29 -19
- package/src/prompts/tools/read.md +2 -2
- package/src/prompts/tools/task-summary.md +5 -16
- package/src/prompts/tools/task.md +38 -29
- package/src/registry/agent-lifecycle.ts +218 -0
- package/src/registry/agent-registry.ts +16 -5
- package/src/sdk.ts +37 -10
- package/src/secrets/index.ts +8 -1
- package/src/secrets/obfuscator.ts +39 -18
- package/src/session/agent-session.ts +422 -291
- package/src/session/messages.ts +11 -78
- package/src/session/session-history-format.ts +246 -0
- package/src/session/session-manager.ts +59 -5
- package/src/session/streaming-output.ts +226 -10
- package/src/slash-commands/acp-builtins.ts +24 -0
- package/src/slash-commands/builtin-registry.ts +20 -0
- package/src/slash-commands/types.ts +1 -1
- package/src/system-prompt.ts +14 -0
- package/src/task/executor.ts +851 -461
- package/src/task/index.ts +721 -796
- package/src/task/output-manager.ts +0 -11
- package/src/task/render.ts +148 -63
- package/src/task/repair-args.ts +21 -9
- package/src/task/types.ts +82 -66
- package/src/thinking.ts +7 -0
- package/src/tiny/title-client.ts +34 -5
- package/src/tiny/title-protocol.ts +1 -1
- package/src/tiny/worker.ts +6 -4
- package/src/tools/ask.ts +4 -2
- package/src/tools/bash.ts +61 -10
- package/src/tools/browser/tab-worker.ts +26 -7
- package/src/tools/browser.ts +28 -1
- package/src/tools/find.ts +2 -27
- package/src/tools/grouped-file-output.ts +1 -118
- package/src/tools/image-gen.ts +11 -4
- package/src/tools/index.ts +17 -13
- package/src/tools/inspect-image.ts +1 -0
- package/src/tools/irc.ts +596 -171
- package/src/tools/job.ts +41 -7
- package/src/tools/read.ts +57 -1
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resolve.ts +4 -1
- package/src/utils/commit-message-generator.ts +1 -0
- package/src/utils/git.ts +267 -13
- package/src/utils/title-generator.ts +24 -5
- package/dist/types/async/support.d.ts +0 -2
- package/dist/types/modes/components/session-observer-overlay.d.ts +0 -11
- package/dist/types/task/simple-mode.d.ts +0 -8
- package/src/async/support.ts +0 -5
- package/src/task/simple-mode.ts +0 -27
package/src/eval/py/kernel.ts
CHANGED
|
@@ -17,7 +17,13 @@ import { Settings } from "../../config/settings";
|
|
|
17
17
|
import { type KernelDisplayOutput, renderKernelDisplay } from "./display";
|
|
18
18
|
import { PYTHON_PRELUDE } from "./prelude";
|
|
19
19
|
import RUNNER_SCRIPT from "./runner.py" with { type: "text" };
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
enumeratePythonRuntimes,
|
|
22
|
+
filterEnv,
|
|
23
|
+
type PythonRuntime,
|
|
24
|
+
resolveExplicitPythonRuntime,
|
|
25
|
+
resolvePythonRuntime,
|
|
26
|
+
} from "./runtime";
|
|
21
27
|
import { hostHasInheritableConsole, shouldHideKernelWindow } from "./spawn-options";
|
|
22
28
|
|
|
23
29
|
export type { KernelDisplayOutput, PythonStatusEvent } from "./display";
|
|
@@ -96,6 +102,11 @@ interface KernelLifecycleOptions {
|
|
|
96
102
|
interface KernelStartOptions extends KernelLifecycleOptions {
|
|
97
103
|
cwd: string;
|
|
98
104
|
env?: Record<string, string | undefined>;
|
|
105
|
+
/**
|
|
106
|
+
* Explicit interpreter path (`python.interpreter` from the session's
|
|
107
|
+
* settings). When set, runtime discovery is skipped entirely.
|
|
108
|
+
*/
|
|
109
|
+
interpreter?: string;
|
|
99
110
|
}
|
|
100
111
|
|
|
101
112
|
interface KernelShutdownOptions {
|
|
@@ -129,20 +140,24 @@ function throwIfAborted(signal: AbortSignal | undefined, fallbackReason: string)
|
|
|
129
140
|
throw createAbortError("AbortError", typeof reason === "string" ? reason : fallbackReason);
|
|
130
141
|
}
|
|
131
142
|
|
|
132
|
-
// Cache successful probes per resolved cwd: every cell
|
|
133
|
-
// two — backend.isAvailable + ensureKernelAvailable)
|
|
134
|
-
// when the kernel is already hot. Failures are not
|
|
135
|
-
// Python mid-session is picked up on the next attempt.
|
|
143
|
+
// Cache successful probes per resolved cwd + explicit interpreter: every cell
|
|
144
|
+
// otherwise pays one (or two — backend.isAvailable + ensureKernelAvailable)
|
|
145
|
+
// interpreter spawns even when the kernel is already hot. Failures are not
|
|
146
|
+
// cached so installing a Python mid-session is picked up on the next attempt.
|
|
136
147
|
const availabilityCache = new Map<string, Promise<PythonKernelAvailability>>();
|
|
137
148
|
|
|
138
|
-
export async function checkPythonKernelAvailability(
|
|
149
|
+
export async function checkPythonKernelAvailability(
|
|
150
|
+
cwd: string,
|
|
151
|
+
interpreter?: string,
|
|
152
|
+
): Promise<PythonKernelAvailability> {
|
|
139
153
|
if (isBunTestRuntime() || $flag("PI_PYTHON_SKIP_CHECK")) {
|
|
140
154
|
return { ok: true };
|
|
141
155
|
}
|
|
142
|
-
const
|
|
156
|
+
const resolvedCwd = path.resolve(cwd);
|
|
157
|
+
const key = `${resolvedCwd}\0${interpreter ?? ""}`;
|
|
143
158
|
const cached = availabilityCache.get(key);
|
|
144
159
|
if (cached) return await cached;
|
|
145
|
-
const probe = probePythonKernelAvailability(
|
|
160
|
+
const probe = probePythonKernelAvailability(resolvedCwd, interpreter);
|
|
146
161
|
availabilityCache.set(key, probe);
|
|
147
162
|
const result = await probe;
|
|
148
163
|
if (!result.ok && availabilityCache.get(key) === probe) {
|
|
@@ -151,12 +166,14 @@ export async function checkPythonKernelAvailability(cwd: string): Promise<Python
|
|
|
151
166
|
return result;
|
|
152
167
|
}
|
|
153
168
|
|
|
154
|
-
async function probePythonKernelAvailability(cwd: string): Promise<PythonKernelAvailability> {
|
|
169
|
+
async function probePythonKernelAvailability(cwd: string, interpreter?: string): Promise<PythonKernelAvailability> {
|
|
155
170
|
try {
|
|
156
171
|
const settings = await Settings.init();
|
|
157
172
|
const { env } = settings.getShellConfig();
|
|
158
173
|
const baseEnv = filterEnv(env);
|
|
159
|
-
const runtimes =
|
|
174
|
+
const runtimes = interpreter
|
|
175
|
+
? [resolveExplicitPythonRuntime(interpreter, cwd, baseEnv)]
|
|
176
|
+
: enumeratePythonRuntimes(cwd, baseEnv);
|
|
160
177
|
if (runtimes.length === 0) {
|
|
161
178
|
return { ok: false, reason: "Python executable not found on PATH" };
|
|
162
179
|
}
|
|
@@ -239,6 +256,7 @@ export class PythonKernel {
|
|
|
239
256
|
"PythonKernel.start:availabilityCheck",
|
|
240
257
|
checkPythonKernelAvailability,
|
|
241
258
|
options.cwd,
|
|
259
|
+
options.interpreter,
|
|
242
260
|
);
|
|
243
261
|
if (!availability.ok) {
|
|
244
262
|
throw new Error(availability.reason ?? "Python kernel unavailable");
|
|
@@ -251,7 +269,9 @@ export class PythonKernel {
|
|
|
251
269
|
let runtime = availability.runtime;
|
|
252
270
|
if (!runtime) {
|
|
253
271
|
const { env: shellEnv } = (await Settings.init()).getShellConfig();
|
|
254
|
-
runtime =
|
|
272
|
+
runtime = options.interpreter
|
|
273
|
+
? resolveExplicitPythonRuntime(options.interpreter, options.cwd, filterEnv(shellEnv))
|
|
274
|
+
: resolvePythonRuntime(options.cwd, filterEnv(shellEnv));
|
|
255
275
|
}
|
|
256
276
|
const spawnEnv: Record<string, string> = {};
|
|
257
277
|
for (const [key, value] of Object.entries(runtime.env)) {
|
package/src/eval/py/prelude.py
CHANGED
|
@@ -519,21 +519,20 @@ if "__omp_prelude_loaded__" not in globals():
|
|
|
519
519
|
text = res.get("text") if isinstance(res, dict) else res
|
|
520
520
|
return json.loads(text) if schema is not None else text
|
|
521
521
|
|
|
522
|
-
def agent(prompt, *, agent_type="task", model=None,
|
|
522
|
+
def agent(prompt, *, agent_type="task", model=None, label=None, schema=None):
|
|
523
523
|
"""Run a subagent and return its final output.
|
|
524
524
|
|
|
525
525
|
`agent_type` selects the subagent definition (default "task"). Pass
|
|
526
|
-
`model` to override that agent's model, `
|
|
527
|
-
|
|
528
|
-
|
|
526
|
+
`model` to override that agent's model, `label` for the output artifact
|
|
527
|
+
id, and `schema` to request structured JSON output; when `schema` is
|
|
528
|
+
supplied the parsed object is returned. Share background by writing a
|
|
529
|
+
local:// file and referencing it in the prompt.
|
|
529
530
|
"""
|
|
530
531
|
args = {"prompt": prompt}
|
|
531
532
|
if agent_type is not None:
|
|
532
533
|
args["agentType"] = agent_type
|
|
533
534
|
if model is not None:
|
|
534
535
|
args["model"] = model
|
|
535
|
-
if context is not None:
|
|
536
|
-
args["context"] = context
|
|
537
536
|
if label is not None:
|
|
538
537
|
args["label"] = label
|
|
539
538
|
if schema is not None:
|
package/src/eval/py/runtime.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* for both the shared gateway and local kernel spawning.
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
|
+
import * as os from "node:os";
|
|
8
9
|
import * as path from "node:path";
|
|
9
10
|
import { $env, $which, getPythonEnvDir } from "@oh-my-pi/pi-utils";
|
|
10
11
|
|
|
@@ -182,6 +183,42 @@ function venvBinDir(venvPath: string): string {
|
|
|
182
183
|
return process.platform === "win32" ? path.join(venvPath, "Scripts") : path.join(venvPath, "bin");
|
|
183
184
|
}
|
|
184
185
|
|
|
186
|
+
function detectExplicitVenv(pythonPath: string): { venvPath: string; binDir: string } | undefined {
|
|
187
|
+
const binDir = path.dirname(pythonPath);
|
|
188
|
+
const venvPath = path.dirname(binDir);
|
|
189
|
+
if (fs.existsSync(path.join(venvPath, "pyvenv.cfg"))) {
|
|
190
|
+
return { venvPath, binDir };
|
|
191
|
+
}
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Resolve an explicitly configured interpreter (`python.interpreter`) into a
|
|
197
|
+
* runtime, bypassing discovery. Does not probe or validate the executable —
|
|
198
|
+
* callers must check it actually runs. `~` expands to the home directory and
|
|
199
|
+
* relative paths resolve against `cwd`. When the interpreter sits inside a
|
|
200
|
+
* virtualenv (a `pyvenv.cfg` above its bin dir), the venv activation env is
|
|
201
|
+
* applied so subprocesses and `pip` resolve consistently.
|
|
202
|
+
*/
|
|
203
|
+
export function resolveExplicitPythonRuntime(
|
|
204
|
+
interpreter: string,
|
|
205
|
+
cwd: string,
|
|
206
|
+
baseEnv: Record<string, string | undefined>,
|
|
207
|
+
): PythonRuntime {
|
|
208
|
+
const expanded =
|
|
209
|
+
interpreter === "~"
|
|
210
|
+
? os.homedir()
|
|
211
|
+
: interpreter.startsWith("~/")
|
|
212
|
+
? path.join(os.homedir(), interpreter.slice(2))
|
|
213
|
+
: interpreter;
|
|
214
|
+
const pythonPath = path.isAbsolute(expanded) ? expanded : path.resolve(cwd, expanded);
|
|
215
|
+
const venv = detectExplicitVenv(pythonPath);
|
|
216
|
+
if (venv) {
|
|
217
|
+
return { pythonPath, env: applyVenvEnv(baseEnv, venv.venvPath, venv.binDir), venvPath: venv.venvPath };
|
|
218
|
+
}
|
|
219
|
+
return { pythonPath, env: { ...baseEnv } };
|
|
220
|
+
}
|
|
221
|
+
|
|
185
222
|
/**
|
|
186
223
|
* Enumerate candidate Python runtimes in priority order: an active/project venv,
|
|
187
224
|
* the managed `~/.omp/python-env`, then the system interpreter on PATH. Every
|
|
@@ -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
|
|
104
|
-
const
|
|
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 =
|
|
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({
|