diff --git a/CLAUDE.md b/CLAUDE.md index 47dc3e3d86..c317064255 120000 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1 +1 @@ -AGENTS.md \ No newline at end of file +AGENTS.md diff --git a/src/agents/cli-runner/helpers.ts b/src/agents/cli-runner/helpers.ts index 7abed5673d..d19885d26e 100644 --- a/src/agents/cli-runner/helpers.ts +++ b/src/agents/cli-runner/helpers.ts @@ -11,6 +11,7 @@ import type { EmbeddedContextFile } from "../pi-embedded-helpers.js"; import { runExec } from "../../process/exec.js"; import { buildTtsSystemPromptHint } from "../../tts/tts.js"; import { resolveDefaultModelForAgent } from "../model-selection.js"; +import { detectRuntimeShell } from "../shell-utils.js"; import { buildSystemPromptParams } from "../system-prompt-params.js"; import { buildAgentSystemPrompt } from "../system-prompt.js"; @@ -226,6 +227,7 @@ export function buildSystemPrompt(params: { node: process.version, model: params.modelDisplay, defaultModel: defaultModelLabel, + shell: detectRuntimeShell(), }, }); const ttsHint = params.config ? buildTtsSystemPromptHint(params.config) : undefined; diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index 8c5a63d32d..280e7d0aba 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -45,6 +45,7 @@ import { resolveSandboxContext } from "../sandbox.js"; import { repairSessionFileIfNeeded } from "../session-file-repair.js"; import { guardSessionManager } from "../session-tool-result-guard-wrapper.js"; import { acquireSessionWriteLock } from "../session-write-lock.js"; +import { detectRuntimeShell } from "../shell-utils.js"; import { applySkillEnvOverrides, applySkillEnvOverridesFromSnapshot, @@ -308,6 +309,7 @@ export async function compactEmbeddedPiSessionDirect( arch: os.arch(), node: process.version, model: `${provider}/${modelId}`, + shell: detectRuntimeShell(), channel: runtimeChannel, capabilities: runtimeCapabilities, channelActions, diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index 2e6c702929..cc95a77cf8 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -49,6 +49,7 @@ import { resolveSandboxRuntimeStatus } from "../../sandbox/runtime-status.js"; import { repairSessionFileIfNeeded } from "../../session-file-repair.js"; import { guardSessionManager } from "../../session-tool-result-guard-wrapper.js"; import { acquireSessionWriteLock } from "../../session-write-lock.js"; +import { detectRuntimeShell } from "../../shell-utils.js"; import { applySkillEnvOverrides, applySkillEnvOverridesFromSnapshot, @@ -331,6 +332,7 @@ export async function runEmbeddedAttempt( node: process.version, model: `${params.provider}/${params.modelId}`, defaultModel: defaultModelLabel, + shell: detectRuntimeShell(), channel: runtimeChannel, capabilities: runtimeCapabilities, channelActions, diff --git a/src/agents/shell-utils.ts b/src/agents/shell-utils.ts index 1a392ba09e..386fd65e69 100644 --- a/src/agents/shell-utils.ts +++ b/src/agents/shell-utils.ts @@ -67,6 +67,63 @@ function resolveShellFromPath(name: string): string | undefined { return undefined; } +function normalizeShellName(value: string): string { + const trimmed = value.trim(); + if (!trimmed) { + return ""; + } + return path + .basename(trimmed) + .replace(/\.(exe|cmd|bat)$/i, "") + .replace(/[^a-zA-Z0-9_-]/g, ""); +} + +export function detectRuntimeShell(): string | undefined { + const overrideShell = process.env.CLAWDBOT_SHELL?.trim(); + if (overrideShell) { + const name = normalizeShellName(overrideShell); + if (name) { + return name; + } + } + + if (process.platform === "win32") { + if (process.env.POWERSHELL_DISTRIBUTION_CHANNEL) { + return "pwsh"; + } + return "powershell"; + } + + const envShell = process.env.SHELL?.trim(); + if (envShell) { + const name = normalizeShellName(envShell); + if (name) { + return name; + } + } + + if (process.env.POWERSHELL_DISTRIBUTION_CHANNEL) { + return "pwsh"; + } + if (process.env.BASH_VERSION) { + return "bash"; + } + if (process.env.ZSH_VERSION) { + return "zsh"; + } + if (process.env.FISH_VERSION) { + return "fish"; + } + if (process.env.KSH_VERSION) { + return "ksh"; + } + if (process.env.NU_VERSION || process.env.NUSHELL_VERSION) { + return "nu"; + } + + return undefined; +} + export function sanitizeBinaryOutput(text: string): string { const scrubbed = text.replace(/[\p{Format}\p{Surrogate}]/gu, ""); if (!scrubbed) { diff --git a/src/agents/system-prompt-params.ts b/src/agents/system-prompt-params.ts index d269253223..e35709009c 100644 --- a/src/agents/system-prompt-params.ts +++ b/src/agents/system-prompt-params.ts @@ -16,6 +16,7 @@ export type RuntimeInfoInput = { node: string; model: string; defaultModel?: string; + shell?: string; channel?: string; capabilities?: string[]; /** Supported message actions for the current channel (e.g., react, edit, unsend) */ diff --git a/src/agents/system-prompt.ts b/src/agents/system-prompt.ts index 8c8682dff6..17ae800c62 100644 --- a/src/agents/system-prompt.ts +++ b/src/agents/system-prompt.ts @@ -190,6 +190,7 @@ export function buildAgentSystemPrompt(params: { node?: string; model?: string; defaultModel?: string; + shell?: string; channel?: string; capabilities?: string[]; repoRoot?: string; @@ -616,6 +617,7 @@ export function buildRuntimeLine( node?: string; model?: string; defaultModel?: string; + shell?: string; repoRoot?: string; }, runtimeChannel?: string, @@ -634,6 +636,7 @@ export function buildRuntimeLine( runtimeInfo?.node ? `node=${runtimeInfo.node}` : "", runtimeInfo?.model ? `model=${runtimeInfo.model}` : "", runtimeInfo?.defaultModel ? `default_model=${runtimeInfo.defaultModel}` : "", + runtimeInfo?.shell ? `shell=${runtimeInfo.shell}` : "", runtimeChannel ? `channel=${runtimeChannel}` : "", runtimeChannel ? `capabilities=${runtimeCapabilities.length > 0 ? runtimeCapabilities.join(",") : "none"}`