context-mode 2.0.0-next.6 → 2.0.0-next.8

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.
@@ -11,5 +11,5 @@
11
11
  }
12
12
  },
13
13
  "name": "context-mode",
14
- "version": "2.0.0-next.6"
14
+ "version": "2.0.0-next.8"
15
15
  }
package/run.mjs CHANGED
@@ -236,7 +236,7 @@ var EDIT_TOOLS = Object.freeze(["Write", "Edit"]);
236
236
  var EDIT_PATH_KEYS = Object.freeze(["file_path", "path"]);
237
237
  var STATS_COLUMNS = Object.freeze(["Scope", "Produced", "Returned", "Saved", "Reduction %"]);
238
238
  var PURGE_SCOPES = Object.freeze(["session", "source", "project"]);
239
- var DOCTOR_CHECK_ORDER = Object.freeze(["D1", "D2", "D3", "D4", "D5", "D5b", "D6", "D7", "D8", "D9", "D10"]);
239
+ var DOCTOR_CHECK_ORDER = Object.freeze(["D1", "D2", "D3", "D4", "D5", "D5b", "D6", "D7", "D8", "D9", "D10", "D11"]);
240
240
  var DOCTOR_LABEL = Object.freeze({
241
241
  D1: "version",
242
242
  D2: "node",
@@ -248,7 +248,8 @@ var DOCTOR_LABEL = Object.freeze({
248
248
  D7: "project and worktree",
249
249
  D8: "context.db",
250
250
  D9: "index",
251
- D10: "enforcement mode"
251
+ D10: "enforcement mode",
252
+ D11: "shell"
252
253
  });
253
254
  var REPORT_TRUNCATED_LINE = `(report truncated at ${REPORT_CAP} B; run ctx_doctor again after the first fix)`;
254
255
  var D2_NODE_FIX = `install Node ${NODE_MIN} or later`;
@@ -271,6 +272,9 @@ var GIT_HEAD_SHA_ARGS = Object.freeze(["rev-parse", "HEAD"]);
271
272
  var UPGRADE_GIT_CHECKOUT_AGENTS = Object.freeze(["pi", "opencode"]);
272
273
  var GIT_NO_PROMPT_ENV = Object.freeze({ GIT_TERMINAL_PROMPT: "0" });
273
274
  var GIT_NO_CRLF_ENV = Object.freeze({ GIT_CONFIG_COUNT: "1", GIT_CONFIG_KEY_0: "core.autocrlf", GIT_CONFIG_VALUE_0: "false" });
275
+ var GIT_BIN = "git";
276
+ var WIN_SHELL_LANGS = Object.freeze(["shell", "bash", "sh"]);
277
+ var GIT_BASH_RELATIVE = Object.freeze([["..", "bin", "bash.exe"], ["..", "..", "bin", "bash.exe"]]);
274
278
  var OPS_SPLIT_FILES = Object.freeze([
275
279
  "doctor-env.ts",
276
280
  "doctor-config.ts",
@@ -287,10 +291,10 @@ var OPS_SPLIT_FILES = Object.freeze([
287
291
  var HOOK_HOST_FILE = Object.freeze({
288
292
  "claude-code": { root: "plugin", file: ["hooks", "claude-code.json"], key: ["hooks"], shape: "nested", timeoutKey: "timeout", matcher: true },
289
293
  codex: { root: "home", file: [".codex", "hooks.json"], key: ["hooks"], shape: "nested", timeoutKey: "timeout", matcher: true },
290
- gemini: { root: "home", file: [".gemini", "settings.json"], key: ["hooks"], shape: "nested", timeoutKey: "timeout", matcher: false },
291
- qwen: { root: "home", file: [".qwen", "settings.json"], key: ["hooks"], shape: "nested", timeoutKey: "timeout", matcher: true },
294
+ gemini: { root: "plugin", file: ["hooks", "hooks.json"], key: ["hooks"], shape: "nested", timeoutKey: "timeout", matcher: false },
295
+ qwen: { root: "plugin", file: ["hooks", "qwen.json"], key: ["hooks"], shape: "nested", timeoutKey: "timeout", matcher: true },
292
296
  "copilot-cli": { root: "home", file: [".copilot", "hooks", "context-mode.json"], key: ["hooks"], shape: "flat", timeoutKey: "timeoutSec", matcher: false },
293
- vscode: { root: "project", file: [".github", "hooks", "context-mode.json"], key: ["hooks"], shape: "flat", timeoutKey: "timeout", matcher: false },
297
+ vscode: { root: "home", file: [".copilot", "hooks", "context-mode-vscode.json"], key: ["hooks"], shape: "flat", timeoutKey: "timeout", matcher: false },
294
298
  kiro: { root: "home", file: [".kiro", "hooks", "context-mode.json"], key: ["hooks"], shape: "kiro", timeoutKey: "timeout", matcher: false },
295
299
  zcode: { root: "home", file: [".zcode", "cli", "config.json"], key: ["hooks", "events"], shape: "zcode", timeoutKey: "timeoutMs", matcher: true },
296
300
  pi: null,
@@ -324,12 +328,16 @@ var V1_SCRIPT_NAMES = Object.freeze([
324
328
  ]);
325
329
  var V1_CODEX_HOOKS_DIR = Object.freeze(["hooks", "codex"]);
326
330
  var V1_CACHE_HEAL_EXTS = Object.freeze([".mjs", ".sh"]);
331
+ var VSCODE_USER_DIR_SEGMENTS = Object.freeze(["Code", "User"]);
332
+ var VSCODE_MCP_FILE = "mcp.json";
327
333
  var KIRO_AGENT_FILE = Object.freeze(["agents", "default.json"]);
328
334
  var KIRO_STEERING_FILE = Object.freeze(["steering", "KIRO.md"]);
329
335
  var OPENCODE_CONFIG_FILES = Object.freeze(["opencode.jsonc", "opencode.json"]);
330
336
  var V1_DATA_ROOT_ENTRIES = Object.freeze(["content", "platform.json"]);
331
337
  var PI_ROLE = "custom";
332
338
  var V1_PATH_SEPS = Object.freeze(["/", "\\"]);
339
+ var HOST_SEP_TOKEN = "${/}";
340
+ var HOOK_SCRIPT_SEPS = Object.freeze([...V1_PATH_SEPS, HOST_SEP_TOKEN]);
333
341
  var V1_REASON = Object.freeze({
334
342
  hook: "old context-mode hook entry: command contains context-mode and an old script name or context-mode hook",
335
343
  cacheHealHook: "old context-mode cache-heal SessionStart hook entry",
@@ -356,9 +364,11 @@ var OPENCODE_TOOL_PREFIX = SERVER_KEY + "_";
356
364
  var ZCODE_MCP_FEATURE_KEY = Object.freeze(["features", "mcp"]);
357
365
  var CODEX_OMIT_TOOLS_FROM_FIELD = "omit_tools_from";
358
366
  var CODEX_OMIT_DEFERRED = Object.freeze(["deferred"]);
367
+ var CODEX_APPROVAL_MODE_FIELD = "default_tools_approval_mode";
368
+ var CODEX_APPROVAL_APPROVE = "approve";
359
369
  var OWN_MCP_ENTRY = Object.freeze({
360
- codex: { file: [".codex", "config.toml"], key: ["mcp_servers", SERVER_KEY], fields: { [CODEX_OMIT_TOOLS_FROM_FIELD]: CODEX_OMIT_DEFERRED } },
361
- vscode: { file: [".vscode", "mcp.json"], key: ["servers", SERVER_KEY] },
370
+ codex: { file: [".codex", "config.toml"], key: ["mcp_servers", SERVER_KEY], fields: { [CODEX_OMIT_TOOLS_FROM_FIELD]: CODEX_OMIT_DEFERRED, [CODEX_APPROVAL_MODE_FIELD]: CODEX_APPROVAL_APPROVE } },
371
+ vscode: { file: [VSCODE_MCP_FILE], key: ["servers", SERVER_KEY] },
362
372
  kiro: { file: [".kiro", "settings", "mcp.json"], key: ["mcpServers", SERVER_KEY] },
363
373
  zcode: {
364
374
  file: [".zcode", "cli", "config.json"],
@@ -732,8 +742,14 @@ var VSCODE_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostTool
732
742
  var MCP_PREFIX2 = "mcp_";
733
743
  var MCP_SEP2 = "_";
734
744
  var SESSION_START_NEW = "new";
735
- var SHELL_TOOL_NAME = "run_in_terminal";
736
- var NATIVE_TO_CANONICAL4 = { [SHELL_TOOL_NAME]: "Bash" };
745
+ var SHELL_TOOL_NAMES = Object.freeze(["run_in_terminal", "runTerminalCommand", "bash", "powershell"]);
746
+ var READ_TOOL_NAMES = Object.freeze(["read_file", "view"]);
747
+ var WEBFETCH_TOOL_NAMES = Object.freeze(["fetch_webpage", "web_fetch"]);
748
+ var NATIVE_TO_CANONICAL4 = Object.fromEntries([
749
+ ...SHELL_TOOL_NAMES.map((n) => [n, "Bash"]),
750
+ ...READ_TOOL_NAMES.map((n) => [n, "Read"]),
751
+ ...WEBFETCH_TOOL_NAMES.map((n) => [n, "WebFetch"])
752
+ ]);
737
753
  function canonicalName4(name) {
738
754
  if (name.startsWith(TOOL_NAME_PREFIX6)) return name.slice(TOOL_NAME_PREFIX6.length);
739
755
  if (Object.hasOwn(NATIVE_TO_CANONICAL4, name)) return NATIVE_TO_CANONICAL4[name];
@@ -743,14 +759,88 @@ function canonicalName4(name) {
743
759
  if (sep3 <= 0 || sep3 === rest.length - 1) return name;
744
760
  return "mcp:" + rest.slice(0, sep3) + ":" + rest.slice(sep3 + MCP_SEP2.length);
745
761
  }
762
+ var isNum = (v) => typeof v === "number" && Number.isFinite(v);
763
+ function policyInput(name, canonical, input) {
764
+ if (canonical === "Read" && READ_TOOL_NAMES.includes(name)) {
765
+ const out = { ...input };
766
+ if (name === "read_file") {
767
+ if (typeof input.filePath === "string") out.file_path = input.filePath;
768
+ if (isNum(input.startLine)) out.offset = input.startLine;
769
+ if (isNum(input.startLine) && isNum(input.endLine) && input.endLine >= input.startLine) out.limit = input.endLine - input.startLine + 1;
770
+ } else if (Array.isArray(input.view_range) && isNum(input.view_range[0])) {
771
+ out.offset = input.view_range[0];
772
+ const end = input.view_range[1];
773
+ if (isNum(end) && end >= input.view_range[0]) out.limit = end - input.view_range[0] + 1;
774
+ }
775
+ return out;
776
+ }
777
+ if (canonical === "WebFetch" && name === "fetch_webpage" && Array.isArray(input.urls) && typeof input.urls[0] === "string") {
778
+ return { ...input, url: input.urls[0] };
779
+ }
780
+ return input;
781
+ }
746
782
  function readResult4(resp) {
747
783
  const text = resp === void 0 ? "" : typeof resp === "string" ? resp : JSON.stringify(resp);
748
784
  const isError = typeof resp === "object" && resp !== null && resp.is_error === true;
749
785
  return { isError, bytes: Buffer.byteLength(text), text };
750
786
  }
787
+ var CAMEL_EVENTS = {
788
+ sessionStart: "SessionStart",
789
+ userPromptSubmitted: "UserPromptSubmit",
790
+ preToolUse: "PreToolUse",
791
+ postToolUse: "PostToolUse",
792
+ preCompact: "PreCompact"
793
+ };
794
+ var EVENT_BY_KEY2 = [
795
+ ["toolResult", "postToolUse"],
796
+ ["toolName", "preToolUse"],
797
+ ["prompt", "userPromptSubmitted"],
798
+ ["source", "sessionStart"],
799
+ ["trigger", "preCompact"]
800
+ ];
801
+ var SESSION_SOURCES = { new: "startup", startup: "startup", resume: "resume" };
802
+ var RESULT_SUCCESS2 = "success";
803
+ function isRecord2(v) {
804
+ return typeof v === "object" && v !== null && !Array.isArray(v);
805
+ }
806
+ function readArgs2(v) {
807
+ if (isRecord2(v)) return v;
808
+ if (typeof v !== "string") return {};
809
+ try {
810
+ const parsed = JSON.parse(v);
811
+ return isRecord2(parsed) ? parsed : {};
812
+ } catch {
813
+ return {};
814
+ }
815
+ }
816
+ function readCamelResult(v) {
817
+ const r = isRecord2(v) ? v : {};
818
+ const text = typeof r.textResultForLlm === "string" ? r.textResultForLlm : "";
819
+ return { isError: typeof r.resultType === "string" && r.resultType !== RESULT_SUCCESS2, bytes: Buffer.byteLength(text), text };
820
+ }
821
+ function decodeCamel(p, name) {
822
+ const event = typeof name === "string" ? CAMEL_EVENTS[name] ?? VSCODE_EVENTS.find((e) => e === name) : void 0;
823
+ if (event === void 0) throw new Error("vscode: unknown hook event " + String(name));
824
+ const ev = { event, agent: "vscode", sessionId: String(p.sessionId ?? ""), cwd: String(p.cwd ?? "") };
825
+ if (event === "SessionStart" && typeof p.source === "string" && SESSION_SOURCES[p.source] !== void 0) ev.source = SESSION_SOURCES[p.source];
826
+ if (event === "UserPromptSubmit" && typeof p.prompt === "string") ev.prompt = p.prompt;
827
+ if (event === "PreToolUse" || event === "PostToolUse") {
828
+ const toolName = String(p.toolName ?? "");
829
+ const canonical = canonicalName4(toolName);
830
+ ev.tool = { name: toolName, canonicalName: canonical, input: policyInput(toolName, canonical, readArgs2(p.toolArgs)) };
831
+ }
832
+ if (event === "PostToolUse") ev.result = readCamelResult(p.toolResult);
833
+ return ev;
834
+ }
751
835
  function decode6(raw) {
752
836
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new Error("vscode: hook payload is not a JSON object");
753
837
  const p = raw;
838
+ if (p.hookEventName !== void 0) return decodeCamel(p, p.hookEventName);
839
+ if (p.hook_event_name === void 0) {
840
+ const byKey = EVENT_BY_KEY2.find(([key]) => Object.hasOwn(p, key))?.[1];
841
+ if (byKey === void 0) throw new Error("vscode: missing required field hook_event_name");
842
+ return decodeCamel(p, byKey);
843
+ }
754
844
  const event = VSCODE_EVENTS.find((e) => e === p.hook_event_name);
755
845
  if (event === void 0) throw new Error("vscode: unknown hook event " + String(p.hook_event_name));
756
846
  const ev = { event, agent: "vscode", sessionId: String(p.session_id ?? ""), cwd: String(p.cwd ?? "") };
@@ -758,8 +848,9 @@ function decode6(raw) {
758
848
  if (event === "UserPromptSubmit" && typeof p.prompt === "string") ev.prompt = p.prompt;
759
849
  if (event === "PreToolUse" || event === "PostToolUse") {
760
850
  const name = String(p.tool_name ?? "");
761
- const input = typeof p.tool_input === "object" && p.tool_input !== null && !Array.isArray(p.tool_input) ? p.tool_input : {};
762
- ev.tool = { name, canonicalName: canonicalName4(name), input };
851
+ const input = isRecord2(p.tool_input) ? p.tool_input : {};
852
+ const canonical = canonicalName4(name);
853
+ ev.tool = { name, canonicalName: canonical, input: policyInput(name, canonical, input) };
763
854
  }
764
855
  if (event === "PostToolUse") ev.result = readResult4(p.tool_response);
765
856
  return ev;
@@ -777,12 +868,14 @@ var KIRO_EVENTS = {
777
868
  PromptSubmit: "UserPromptSubmit",
778
869
  userPromptSubmit: "UserPromptSubmit",
779
870
  PreToolUse: "PreToolUse",
780
- PostToolUse: "PostToolUse"
871
+ preToolUse: "PreToolUse",
872
+ PostToolUse: "PostToolUse",
873
+ postToolUse: "PostToolUse"
781
874
  };
782
875
  var PROMPT_ENV = "USER_PROMPT";
783
876
  var MCP_PREFIX3 = "mcp_";
784
- var SHELL_TOOL_NAME2 = "execute_bash";
785
- var NATIVE_TO_CANONICAL5 = { [SHELL_TOOL_NAME2]: "Bash" };
877
+ var SHELL_TOOL_NAMES2 = Object.freeze(["execute_bash", "execute_pwsh", "shell"]);
878
+ var NATIVE_TO_CANONICAL5 = Object.fromEntries(SHELL_TOOL_NAMES2.map((n) => [n, "Bash"]));
786
879
  var MCP_SEP3 = "_";
787
880
  var KIRO_SOURCES = { startup: "startup", resume: "resume", clear: "clear", compact: "compact" };
788
881
  function canonicalName5(name) {
@@ -923,8 +1016,8 @@ var OPENCODE_EVENTS = {
923
1016
  compaction: "PreCompact",
924
1017
  "step-finish": "Usage"
925
1018
  };
926
- var SHELL_TOOL_NAME3 = "bash";
927
- var NATIVE_TO_CANONICAL7 = { webfetch: "WebFetch", [SHELL_TOOL_NAME3]: "Bash" };
1019
+ var SHELL_TOOL_NAME = "bash";
1020
+ var NATIVE_TO_CANONICAL7 = { webfetch: "WebFetch", [SHELL_TOOL_NAME]: "Bash" };
928
1021
  function toCanonicalTool(name) {
929
1022
  if (name.startsWith(OWN_WIRE_PREFIX)) return name.slice(OWN_WIRE_PREFIX.length);
930
1023
  return Object.prototype.hasOwnProperty.call(NATIVE_TO_CANONICAL7, name) ? NATIVE_TO_CANONICAL7[name] : name;
@@ -1044,8 +1137,8 @@ var ADAPTERS = Object.freeze([
1044
1137
  hookTimeoutMs: HOOK_TIMEOUT_MS,
1045
1138
  denyChannel: "deny-reason",
1046
1139
  hookChannel: { kind: "package", path: "hooks/qwen.json" },
1047
- skillChannel: { kind: "home-dir", path: ".qwen/skills" },
1048
- // ADR-0020
1140
+ skillChannel: { kind: "package", path: "skills" },
1141
+ // ADR-0020, D-HQ-SKILLS-3: qwen loads <extension>/skills, shared with gemini
1049
1142
  decode: decode4,
1050
1143
  encode: encode4
1051
1144
  },
@@ -1073,7 +1166,8 @@ var ADAPTERS = Object.freeze([
1073
1166
  projectSource: "arg:workspaceFolder",
1074
1167
  toolNamePrefix: TOOL_NAME_PREFIX6,
1075
1168
  // PreCompact sets the pending flag (ADR-0009 §6, DEVIATIONS D-M3-A4-1).
1076
- nativeTools: { shell: [SHELL_TOOL_NAME] },
1169
+ nativeTools: { shell: [...SHELL_TOOL_NAMES], read: [...READ_TOOL_NAMES], webfetch: [...WEBFETCH_TOOL_NAMES] },
1170
+ // D-HF-VSCODE-PROJECT-1, D-N7-VSCODE-TOOLS-1
1077
1171
  execReadOnlyHint: false,
1078
1172
  eventMap: { ...CLAUDE_EVENTS, PreCompact: "PreCompact" },
1079
1173
  compactDelivery: "pending-flag",
@@ -1081,8 +1175,8 @@ var ADAPTERS = Object.freeze([
1081
1175
  hookTimeoutMs: HOOK_TIMEOUT_MS,
1082
1176
  denyChannel: "deny-reason",
1083
1177
  hookChannel: { kind: "home-file", path: ".copilot/hooks/context-mode-vscode.json" },
1084
- skillChannel: { kind: "home-dir", path: ".copilot/skills" },
1085
- // ADR-0020
1178
+ skillChannel: { kind: "home-dir", path: ".agents/skills" },
1179
+ // ADR-0020, D-HQ-COPILOT-NAMES-3: Copilot CLI owns ~/.copilot/skills
1086
1180
  decode: decode6,
1087
1181
  encode: encode6
1088
1182
  },
@@ -1091,9 +1185,9 @@ var ADAPTERS = Object.freeze([
1091
1185
  installMode: "mcp-entry",
1092
1186
  projectSource: "arg:--agent+cwd",
1093
1187
  toolNamePrefix: TOOL_NAME_PREFIX7,
1094
- nativeTools: { shell: [SHELL_TOOL_NAME2] },
1188
+ nativeTools: { shell: [...SHELL_TOOL_NAMES2] },
1095
1189
  execReadOnlyHint: false,
1096
- // D-HF-KIRO-SHELL-1
1190
+ // D-HF-KIRO-SHELL-1, D-N7-KIRO-1
1097
1191
  eventMap: { SessionStart: "SessionStart", UserPromptSubmit: "UserPromptSubmit", PreToolUse: "PreToolUse", PostToolUse: "PostToolUse" },
1098
1192
  compactDelivery: "none",
1099
1193
  compactSummary: "none",
@@ -1106,7 +1200,7 @@ var ADAPTERS = Object.freeze([
1106
1200
  encode: encode7
1107
1201
  },
1108
1202
  {
1109
- // toolNamePrefix and nativeTools unverified until T-ZC (DECISIONS-r9 R3, R4: Claude names).
1203
+ // toolNamePrefix is the documented ZCode name mcp__<server>__<tool> (D-N7-ZCODE-NAMES-1); nativeTools are Claude names.
1110
1204
  id: "zcode",
1111
1205
  installMode: "hooks+mcp",
1112
1206
  projectSource: "spawn-cwd",
@@ -1148,7 +1242,7 @@ var ADAPTERS = Object.freeze([
1148
1242
  installMode: "in-process",
1149
1243
  projectSource: "spawn-cwd",
1150
1244
  toolNamePrefix: TOOL_NAME_PREFIX10,
1151
- nativeTools: { shell: [SHELL_TOOL_NAME3] },
1245
+ nativeTools: { shell: [SHELL_TOOL_NAME] },
1152
1246
  execReadOnlyHint: false,
1153
1247
  // D-HF-OPENCODE-SHELL-1
1154
1248
  // Usage: step-finish cost row (DEVIATIONS D-M5-arch-2).
@@ -1186,6 +1280,8 @@ var ERROR_TEXT = Object.freeze({
1186
1280
  NO_PROJECT: "Project source <kind> is missing. Fix: run ctx_doctor.",
1187
1281
  UNKNOWN_FIELD: 'Unknown field "<f>". Fix: use "queries".',
1188
1282
  TIMEOUT: "Timed out after <ms> ms. Fix: pass background: true.",
1283
+ NO_GIT_BASH: "Git Bash not found. Fix: install Git for Windows.",
1284
+ // D-VT-WINBASH-1
1189
1285
  NO_RUNTIME: "No <lang> runtime on PATH. Fix: install it or pick another language.",
1190
1286
  FETCH_FAILED: "Fetch failed: <code>. Fix: check the URL or proxy.",
1191
1287
  SSRF: "Blocked private address <host>. Fix: use a public URL.",
@@ -4190,22 +4286,25 @@ function parseShell(cmd) {
4190
4286
  import { join as join2 } from "node:path";
4191
4287
 
4192
4288
  // src/core/project/config-dir.ts
4193
- import { join } from "node:path";
4289
+ import { join, posix, win32 } from "node:path";
4194
4290
  var { mkdirSync: mkdirSync2, realpathSync } = process.getBuiltinModule("node:fs");
4195
- function homeDirOf(env) {
4196
- const home = env.HOME;
4197
- if (home !== void 0 && home !== "") return home;
4198
- const profile = env.USERPROFILE;
4199
- if (profile !== void 0 && profile !== "") return profile;
4200
- return null;
4201
- }
4202
- function homeOf(env) {
4203
- const home = homeDirOf(env);
4291
+ function homeDirOf(env, platform = process.platform) {
4292
+ const set = (v) => v !== void 0 && v !== "";
4293
+ if (platform === "win32") {
4294
+ if (set(env.USERPROFILE)) return env.USERPROFILE;
4295
+ if (set(env.HOMEDRIVE) && set(env.HOMEPATH)) return env.HOMEDRIVE + env.HOMEPATH;
4296
+ return set(env.HOME) ? env.HOME : null;
4297
+ }
4298
+ if (set(env.HOME)) return env.HOME;
4299
+ return set(env.USERPROFILE) ? env.USERPROFILE : null;
4300
+ }
4301
+ function requireHomeDir(env, platform = process.platform) {
4302
+ const home = homeDirOf(env, platform);
4204
4303
  if (home !== null) return home;
4205
- throw new CtxError("NO_HOME", { name: process.platform === "win32" ? "USERPROFILE" : "HOME" });
4304
+ throw new CtxError("NO_HOME", { name: platform === "win32" ? "USERPROFILE" : "HOME" });
4206
4305
  }
4207
4306
  function resolveConfigDir(env) {
4208
- const dir = join(homeOf(env), DATA_ROOT.slice(1));
4307
+ const dir = join(requireHomeDir(env), DATA_ROOT.slice(1));
4209
4308
  mkdirSync2(dir, { recursive: true });
4210
4309
  return realpathSync.native(dir);
4211
4310
  }
@@ -4947,7 +5046,7 @@ function firstLine(file) {
4947
5046
  }
4948
5047
  function git(dir, args) {
4949
5048
  const { spawnSync } = process.getBuiltinModule("node:child_process");
4950
- const r = spawnSync("git", args, { cwd: dir, shell: false, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
5049
+ const r = spawnSync("git", args, { cwd: dir, shell: false, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
4951
5050
  if (r.error !== void 0 || r.status !== 0) throw new CtxError("NO_GIT_DIR", { path: dir });
4952
5051
  return r.stdout.split("\n").filter((l) => l !== "");
4953
5052
  }
@@ -5022,6 +5121,14 @@ function buildChildEnv(parent, extra) {
5022
5121
  // src/core/sandbox/runtimes.ts
5023
5122
  import { accessSync, constants as fsConstants, statSync as statSync3 } from "node:fs";
5024
5123
  import path from "node:path";
5124
+
5125
+ // src/core/sandbox/path-entries.ts
5126
+ function pathEntries(value, platform = process.platform, delimiter = platform === "win32" ? ";" : ":") {
5127
+ const win = platform === "win32";
5128
+ return value.split(delimiter).map((e) => win && e.length >= 2 && e.startsWith('"') && e.endsWith('"') ? e.slice(1, -1) : e).filter(Boolean);
5129
+ }
5130
+
5131
+ // src/core/sandbox/runtimes.ts
5025
5132
  var PATHEXT_SEPARATOR = ";";
5026
5133
  var WIN_STARTABLE_EXT = Object.freeze([".exe", ".com"]);
5027
5134
  function isWin() {
@@ -5031,7 +5138,7 @@ function candidates(lang) {
5031
5138
  const win = isWin();
5032
5139
  switch (lang) {
5033
5140
  case "shell":
5034
- return win ? ["sh.exe", "bash.exe"] : ["sh"];
5141
+ return win ? [] : ["sh"];
5035
5142
  case "powershell":
5036
5143
  return ["pwsh", "pwsh.exe", "powershell.exe"];
5037
5144
  case "javascript":
@@ -5062,7 +5169,8 @@ function isRunnable(full) {
5062
5169
  }
5063
5170
  }
5064
5171
  function findRuntime(lang, env = process.env) {
5065
- const dirs = (env.PATH ?? "").split(path.delimiter).filter((d) => d !== "");
5172
+ if (isWin() && WIN_SHELL_LANGS.includes(lang)) return findGitBash(env);
5173
+ const dirs = pathEntries(env.PATH ?? "", process.platform, path.delimiter);
5066
5174
  const pathExt = isWin() ? (env.PATHEXT ?? PATHEXT_DEFAULT).split(PATHEXT_SEPARATOR).filter((e) => e !== "") : [];
5067
5175
  const names = candidates(lang);
5068
5176
  let shim = null;
@@ -5078,6 +5186,18 @@ function findRuntime(lang, env = process.env) {
5078
5186
  }
5079
5187
  return shim;
5080
5188
  }
5189
+ function findGitBash(env) {
5190
+ const git2 = findRuntime(GIT_BIN, env);
5191
+ if (git2 === null) return null;
5192
+ for (const rel of GIT_BASH_RELATIVE) {
5193
+ const full = path.resolve(path.dirname(git2), ...rel);
5194
+ if (isRunnable(full)) return full;
5195
+ }
5196
+ return null;
5197
+ }
5198
+ function noRuntimeError(lang) {
5199
+ return isWin() && WIN_SHELL_LANGS.includes(lang) ? new CtxError("NO_GIT_BASH") : new CtxError("NO_RUNTIME", { lang });
5200
+ }
5081
5201
 
5082
5202
  // src/core/sandbox/spawn.ts
5083
5203
  import { spawn } from "node:child_process";
@@ -5584,7 +5704,7 @@ async function runWrapperMain(argv, env) {
5584
5704
  if (d.kind === "deny" && userPolicyDeny(d.reason)) return exit(RUN_FAIL_EXIT, "", d.reason + "\n");
5585
5705
  try {
5586
5706
  const runtime = findRuntime(SHELL);
5587
- if (runtime === null) throw new CtxError("NO_RUNTIME", { lang: SHELL });
5707
+ if (runtime === null) throw noRuntimeError(SHELL);
5588
5708
  const project = projectOf(argv, cwd);
5589
5709
  const r = await runProcess([runtime, "-c", command], {
5590
5710
  cwd,