dsh-gitbash-shell 0.25.0 → 0.25.1

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 CHANGED
@@ -3,6 +3,26 @@
3
3
  > 倒序排列,新版本条目在最上面。条目格式:`## vX.Y.Z — YYYY-MM-DD` + 类型(feat / fix / docs / chore)+ 要点 + 相关链接。
4
4
  > 纪律见 AGENTS.md「变更记录纪律」:发版前先更新本文件并随版本提交;事故复盘、复现与真机验证记录也记在这里。
5
5
 
6
+ ## v0.25.1 — 2026-09-24
7
+
8
+ **类型**:fix(issue #8 —— 路径方言在 dsh 0.1.7 上**整层静默失效**:工具分发面用错了设置读取器;同一次审计另修侧栏终端还原的同类漏网)
9
+
10
+ > 报障:第三方用户 @youyv 的 https://github.com/KannaKuron/dsh-gitbash-shell/issues/8 —— 在 0.1.7-rc.1 上,`posixPaths` 指示词进了提示词、`DSH_PATH_DIALECT=msys` 也在环境里,但**参数翻译整层不工作**。用户侧的五条最小复现全部命中:文件工具把 `/c/...` 变成 `/c/c/...`、`~` 不展开、`glob`/`grep` 的 `path` 原样丢给原生 `rg`、`bash` 的 `workdir` 不翻译(spawn ENOENT)、工作区内的 `/c/...` 写入被沙箱拒绝(未翻译 ⇒ 落到工作区外)。方向判断也对:"插件翻坏了"与"完全没翻"两种形态混在一起,说明方言层是"半活"的。
11
+
12
+ - **root cause(一行)**:v0.24.4 的 era 适配(`makeLiveReader`)只改了一部分消费点 —— `tools/execute` 这个**唯一承担入参翻译**的 wrapper 仍在调 `readDialectSettings(ctx)`,而它读的是 `settings.get(ns)`;0.1.7 上该方法**已不存在**,于是永远返回 `{ posixPaths: false, … }` 的全关 fallback ⇒ wrapper 里 `if (dialect.posixPaths)` 永不成立,**一个参数都不翻**。同一时刻指示词、shell env、prompt 组装早就在 live reader 上(所以它们看起来"正常") —— 这正是"半活"的来源,也与 issue 里"指示词在、翻译不在"的观察完全一致。
13
+ - 五条症状逐一对应:`read`/`write` 拿到未翻译的 `/c/...`,由 Node 解析成 `<当前盘符>:\c\...`,再经**输出侧**回显器转回 MSYS 形态 ⇒ 用户看到的 `/c/c/...`(输入没翻、输出翻了);`~`/`/tmp` 属挂载表翻译,同样被跳过;`glob`/`grep` 的 `path` 与 `bash` 的 `workdir` 字段同理原样下发。
14
+ - **修法**:wrapper 改用 `liveSettings.dialect()`(与其余消费点同源),并把参数翻译抽成可测的 `translateDispatch(exec, dialect, env)` 导出到 `_internal` —— issue 里那句"现有冒烟只跑纯函数、没跑过一次工具分发的参数翻译"点到了要害:纯函数一直是绿的,**喂给它的接线**没人测。
15
+ - **同一次审计查出的第二处(同类)**:`adoptSidebarShell` 的 dispose 分支用 `s.get(SIDEBAR_NS)` 读当前值再决定是否恢复接管 —— 0.1.7 上没有 `get`,异常直接被 catch 吞掉,**插件卸载时侧栏终端从不还原**。现改走已有的 era-aware `readShell()`。
16
+ - **回归测试(75/75,新增 2 条,其中一条就是 issue 建议的那种)**:
17
+ - `translateDispatch covers every argument face issue #8 reported`:对 issue 的五条症状逐条断言(`/c/...` → `C:/...`;`~` → home 挂载;`/tmp` → TEMP 挂载;`glob`/`grep` 的 `path`;`bash` 的 `workdir`,且 `command` 绝不被改写),外加 glob 拆分、run_code 程序字面量、以及"已原生/相对路径保持对象身份(不触发无谓重渲染)"。
18
+ - `issue #8 root cause stays fixed`:锁定根因本身 —— 断言**旧读取器在 0.1.7 形状的 settings 上返回全关**(这就是它不能给分发面当输入的原因),并断言 `apply()` 的代码里不再出现 `readDialectSettings(ctx)`、侧栏还原不再出现 `s.get(SIDEBAR_NS)`。
19
+ - **验证(真实 dsh 0.1.7-rc.1 宿主上的 A/B,本机 macOS)**:
20
+ - 先核对宿主契约:`packages/core/tools/src/index.ts` 的 `dispatchScheduledExecution` 仍以 `mutableExec` 走 `tools/execute` waterfall,tool 体在 waterfall **之后**读 `exec.arguments`(`tool.execute(exec.arguments, exec)`)—— 所以"就地改写 `exec.arguments`"这条机制在 rc.1 上依然成立,问题只在读取器。
21
+ - A/B 装置:隔离 `DSH_HOME` + 新建 profile + 两份插件 `link:` 安装 + 探针插件**自注册一个回显工具**并通过 `ctx.tools.execute()` 走一次**真实分发**(把工具体收到的参数打出来);由于方言面按 `process.platform === 'win32'` 门控,探针副本只强制打开这一处门 + 把 `gitBash` 能力置 `active: true`,其余全为真代码。
22
+ - **未修复副本**(把 wrapper 换回 `readDialectSettings(ctx)`):工具体收到 `{"file_path":"/c/Users/x/.dsh/f.txt","path":"/c/Users/x/dir","workdir":"/c/Users/x/.dsh"}` 与 `{"file_path":"~/.anonymous-user-id","path":"/tmp/a.txt"}` —— **issue #8 的五条症状在同一台机器上完整复现**。
23
+ - **修复后**:同一装置收到 `C:/Users/x/.dsh/f.txt`、`C:/Users/x/dir`、`C:/Users/x/.dsh`,以及 `~` → home、`/tmp` → TEMP 的挂载翻译。
24
+ - 相关:issue #8 https://github.com/KannaKuron/dsh-gitbash-shell/issues/8 (报障与定位线索来自 @youyv)
25
+
6
26
  ## v0.25.0 — 2026-09-24
7
27
 
8
28
  **类型**:feat(与 dsh-ptc-cordis-preset 去重:issue #7)
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-gitbash-shell",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
4
4
  "main": "./src/index.js",
5
5
  "description": "Windows 全模式 Git Bash:用 Git for Windows bash 替换 PowerShell 执行器,并物化 standard/minimal/code/cordis 的 Git Bash 变体 preset。双 era 适配 dsh <= 0.1.1(内置 id 'code')与 >= 0.1.2(改名 'ptc'),启动探测自动选择,preset id 永不变。联动:dsh-ptc-cordis-preset(两者同装时 PTC 创造模式自动物化为 Git Bash 版)+ dsh-better-sidebar(终端 UI 标签与模型 terminal_* 工具自动切到 Git Bash)。",
6
6
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-gitbash-shell",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
4
4
  "description": "Windows 上让 agent 的每条 shell 命令改经 Git for Windows bash 执行;物化四个内置预设的 Git Bash 变体,统一 MSYS 盘根路径方言,侧栏终端接管可开关。· Run every agent shell command through Git for Windows bash on Windows; materialize Git Bash variants of the built-in presets, unify the MSYS drive-root path dialect, and adopt the sidebar terminal behind a switch.",
5
5
  "type": "module",
6
6
  "repository": {
package/src/index.js CHANGED
@@ -1254,6 +1254,36 @@ const translateEnvCache = new Map()
1254
1254
  * (the default install plus every PATH entry) and accepts a root only when
1255
1255
  * <root>/usr/bin exists, which excludes the WSL bash shim in WindowsApps.
1256
1256
  */
1257
+ /**
1258
+ * One dispatch's path-bearing arguments in the host dialect (pure). The
1259
+ * tools/execute wrapper's decision surface, exported so a test can drive the
1260
+ * whole argument face without a Windows host — the shape issue #8 escaped
1261
+ * through: pure helpers were covered, the wiring that feeds them was not.
1262
+ * @param {{ name?: string, arguments?: object }} exec - the dispatch envelope.
1263
+ * @param {object} dialect - live dialect switches (posixPaths/globSplit/codePaths).
1264
+ * @param {object|null} env - mount table (buildTranslateEnv), null when mounts are off.
1265
+ * @returns {object|undefined} the translated arguments (same reference when untouched).
1266
+ */
1267
+ export function translateDispatch(exec, dialect, env) {
1268
+ if (!exec || !exec.arguments || typeof exec.arguments !== 'object') return exec?.arguments
1269
+ let translated = translatePathArguments(exec.arguments, env)
1270
+ if (exec.name === 'glob' && dialect.globSplit) {
1271
+ const globTranslated = translateGlobArguments(translated, env)
1272
+ if (globTranslated !== translated) translated = globTranslated
1273
+ }
1274
+ // v0.20.0: a run_code program is DATA for a native Node process, so a
1275
+ // '/c/...' literal inside it never reached this layer and landed on the
1276
+ // current drive as '<drive>:\c\...'. The same mount table covers the program
1277
+ // path literals (see rewriteCodePaths; anything unclear leaves the code
1278
+ // untouched).
1279
+ if (exec.name === 'run_code' && dialect.codePaths && typeof translated.code === 'string') {
1280
+ const rewritten = rewriteCodePaths(translated.code, env)
1281
+ const nextCode = programPrelude(env) + rewritten
1282
+ if (nextCode !== translated.code) translated = { ...translated, code: nextCode }
1283
+ }
1284
+ return translated
1285
+ }
1286
+
1257
1287
  export function buildTranslateEnv(configuredBashPath) {
1258
1288
  const cacheKey = typeof configuredBashPath === 'string' && configuredBashPath !== '' ? configuredBashPath : ''
1259
1289
  const cached = translateEnvCache.get(cacheKey)
@@ -1869,13 +1899,13 @@ function adoptSidebarShell(ctx, bashPath, readAdopt) {
1869
1899
  reverted = true
1870
1900
  const s = ctx.get('settings')
1871
1901
  if (!s || typeof s.update !== 'function') return
1872
- let now
1873
- try {
1874
- now = s.get(SIDEBAR_NS)
1875
- } catch {
1876
- return
1877
- }
1878
- if (String(now?.terminalShell ?? '') === bashPath) {
1902
+ // Era-aware read (v0.25.1): `settings.get(ns)` is gone on dsh >= 0.1.7, so
1903
+ // the old call threw straight into the catch and the takeover was NEVER
1904
+ // reverted when this plugin unloaded — same missed-era-read class as
1905
+ // issue #8 (readShell above carries both branches).
1906
+ const now = readShell()
1907
+ if (now === null) return
1908
+ if (String(now) === bashPath) {
1879
1909
  s.update(SIDEBAR_NS, { terminalShell: previous }).catch(() => {})
1880
1910
  }
1881
1911
  },
@@ -2331,27 +2361,21 @@ export async function apply(ctx, config = {}) {
2331
2361
  let echo = false
2332
2362
  let echoEnv = null
2333
2363
  try {
2334
- const dialect = readDialectSettings(ctx)
2364
+ // The LIVE, era-aware reader (v0.25.1, issue #8). The legacy
2365
+ // `readDialectSettings(ctx)` reads `settings.get(ns)`, which does not
2366
+ // exist on dsh >= 0.1.7 — it returned the all-off fallback, so this
2367
+ // wrapper translated NOTHING while the directive and the shell env
2368
+ // (both already on the live reader) kept working: `/c/...` reached the
2369
+ // file tools untranslated, `~` was never expanded, `glob.path` and
2370
+ // `bash.workdir` went through raw, and an in-workspace `/c/...` write
2371
+ // fell outside the sandbox. One missed call site, five symptoms.
2372
+ const dialect = liveSettings.dialect()
2335
2373
  if (dialect.posixPaths) {
2336
2374
  echo = true
2337
2375
  const env = dialect.virtualMounts ? buildTranslateEnv(dialect.bashPath) : null
2338
2376
  echoEnv = env
2339
2377
  if (exec && exec.arguments && typeof exec.arguments === 'object') {
2340
- let translated = translatePathArguments(exec.arguments, env)
2341
- if (exec.name === 'glob' && dialect.globSplit) {
2342
- const globTranslated = translateGlobArguments(translated, env)
2343
- if (globTranslated !== translated) translated = globTranslated
2344
- }
2345
- // v0.20.0: a run_code program is DATA for a native Node process, so
2346
- // a '/c/...' literal inside it never reached this layer and landed
2347
- // on the current drive as '<drive>:\\c\\...'. The same mount table
2348
- // now covers the program path literals (see rewriteCodePaths;
2349
- // anything unclear leaves the code untouched).
2350
- if (exec.name === 'run_code' && dialect.codePaths && typeof translated.code === 'string') {
2351
- const rewritten = rewriteCodePaths(translated.code, env)
2352
- const nextCode = programPrelude(env) + rewritten
2353
- if (nextCode !== translated.code) translated = { ...translated, code: nextCode }
2354
- }
2378
+ const translated = translateDispatch(exec, dialect, env)
2355
2379
  if (translated !== exec.arguments) exec.arguments = translated
2356
2380
  }
2357
2381
  }
@@ -2572,4 +2596,4 @@ export async function apply(ctx, config = {}) {
2572
2596
  }
2573
2597
 
2574
2598
  // Test surface: pure helpers, no Cordis context required.
2575
- export const _internal = { PRESET_IDS, PEER_COVERED_PRESET_ID, PEER_CAPABILITY, effectivePresetIds, detectPeerCoverage, readSuppressPeerCordis, translateMsysPath, translatePathArguments, rewriteCodePaths, scanCodeLiterals, programPrelude, translateGlobArguments, buildTranslateEnv, rewriteErrorContent, rewriteErrorMessage, rewriteFailureMessage, msysEcho, driveToMsys, pathEcho, ERROR_CONTENT_TOOLS, readPosixPaths, readAdoptSidebar, readDialectSettings, windowsToMsys, rewriteResultPaths, adoptSidebarShell, MARKER_FILE, classify, materialize, cleanupOnDispose, firstUserRoot, hashTree, skillsHashes, syncDecision, installRegisterShim, baseForRoster, detectBase, pickComposition, personaEraForText, detectPersonaEra, injectPresentRow, hostHasToolPresent, detectPresentSupport, injectPluginManagerRow, hostHasPluginManagerTools, rowFormOf, rowFormsOf, alignEngineRow, alignRalphRow, ROW_SOURCE }
2599
+ export const _internal = { PRESET_IDS, PEER_COVERED_PRESET_ID, PEER_CAPABILITY, effectivePresetIds, detectPeerCoverage, readSuppressPeerCordis, translateDispatch, translateMsysPath, translatePathArguments, rewriteCodePaths, scanCodeLiterals, programPrelude, translateGlobArguments, buildTranslateEnv, rewriteErrorContent, rewriteErrorMessage, rewriteFailureMessage, msysEcho, driveToMsys, pathEcho, ERROR_CONTENT_TOOLS, readPosixPaths, readAdoptSidebar, readDialectSettings, windowsToMsys, rewriteResultPaths, adoptSidebarShell, MARKER_FILE, classify, materialize, cleanupOnDispose, firstUserRoot, hashTree, skillsHashes, syncDecision, installRegisterShim, baseForRoster, detectBase, pickComposition, personaEraForText, detectPersonaEra, injectPresentRow, hostHasToolPresent, detectPresentSupport, injectPluginManagerRow, hostHasPluginManagerTools, rowFormOf, rowFormsOf, alignEngineRow, alignRalphRow, ROW_SOURCE }