dsh-gitbash-shell 0.16.1 → 0.17.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/README.md CHANGED
@@ -157,10 +157,18 @@ PTC/创造及用户自建模式一律覆盖。
157
157
  - **提示词源头替换**:组装期(`system-prompt/assemble`)把官方提示词 sections / contexts /
158
158
  variables 里的 Windows 绝对路径**原位**改写成 `/c/...`——不增删任何内容、不动工具 schema;
159
159
  - **一句话指示**:经 `systemPrompt.context`(order 126)注入全局指示——shell 是 Git for
160
- Windows bash,路径用 MSYS 盘根,所有工具都直接接受这种写法;
161
- - **参数翻译**:`tools/execute` 上把文件工具的路径参数(`file_path` / `path` / `workdir`)由
160
+ Windows bash,路径用 MSYS 盘根,所有工具都直接接受这种写法——含 `~`、`/tmp`、`/dev/null`、
161
+ `/usr` 等 bash 原生习惯(v0.17.0 起与 Git Bash 挂载表一致地解析);
162
+ - **参数翻译**:`tools/execute` 上把工具的路径参数(`file_path` / `path` / `workdir`,含 present
163
+ 的嵌套 `files[].path`)由 `/c/...` 翻回 `C:/...` 交给 Node 文件工具;bash 的 `command` 字段
164
+ 不动——那是 Git Bash 母语;**bash 虚拟路径按 Git Bash 挂载表解析(v0.17.0)**:`/tmp` → 用户
165
+ TEMP、`/dev/null` → Windows NUL 空设备、`~` → 家目录、`/usr` `/bin` `/etc` 等 → Git 安装根,
166
+ 与 bash 写入/读取同一物理位置;glob 的绝对 pattern(`/c/.../*.md`)自动拆成 `path` + 相对
167
+ `pattern`(原先静默匹配空);
162
168
  `/c/...` 翻回 `C:/...` 交给 Node 文件工具;bash 的 `command` 字段不动——那是 Git Bash 母语;
163
- - **结果回流**:成功结果里的路径元数据(`read`/`write`/`edit` 的 `path`、`glob` 的 `paths[]`、
169
+ - **结果回流**:成功结果里的路径元数据(`read`/`read_image`/`write`/`edit` 的 `path`、`glob` 的
170
+ `paths[]`、`grep` 的 `matches[].path`、`present` 的 `files[].path`)改写回 MSYS 形式,其中落在
171
+ 用户 TEMP 下的路径回显为 `/tmp/...`(与 bash 的 `$TMP` 一致);文件内容与错误结果不动;
164
172
  `grep` 的 `matches[].path`)改写回 MSYS 形式;文件内容与错误结果不动;
165
173
  - **运行时事实**:向官方 `dsh-shell-env` 注册表贡献 `DSH_PATH_DIALECT=msys`,模型可在执行时
166
174
  核验(随开关实时生效)。
package/README_EN.md CHANGED
@@ -99,11 +99,20 @@ While it is on (Windows only), `posixPaths` gates all of the following:
99
99
  /c/... form — nothing is added or removed, and tool schemas stay untouched;
100
100
  - **One-sentence directive**: a global directive via `systemPrompt.context` (order 126) — the
101
101
  shell is Git for Windows bash, paths use MSYS drive roots, and every tool accepts that form
102
+ directly — including the bash-native habits (`~`, `/tmp`, `/dev/null`, `/usr`), which resolve
103
+ exactly as bash itself resolves them (v0.17.0, mirroring the Git Bash mount table);
102
104
  directly;
103
- - **Argument translation**: on `tools/execute` the file tools' path arguments (`file_path` /
105
+ - **Argument translation**: on `tools/execute` the tools' path arguments (`file_path` / `path` /
106
+ `workdir`, plus present's nested `files[].path`) are translated from /c/... back to C:/... for
107
+ the Node-backed file tools; a bash command's `command` field is left alone — that is Git Bash's
108
+ native form; **bash virtual paths resolve through the Git Bash mount table (v0.17.0)**: /tmp is
109
+ the user TEMP dir, /dev/null the Windows NUL device, ~ the home directory, and /usr /bin /etc
110
+ live under the Git install root — the same physical locations bash itself reads and writes; an
111
+ absolute glob pattern (/c/.../*.md) is split into `path` + a relative `pattern` (it used to
112
+ silently match nothing);
104
113
  `path` / `workdir`) are translated from /c/... back to C:/... for the Node-backed file tools;
105
114
  a bash command's `command` field is left alone — that is Git Bash's native form;
106
- - **Result round-trip**: path metadata in successful results (`path` of read/write/edit,
115
+ - **Result round-trip**: path metadata in successful results (`path` of read/read_image/write/edit,
107
116
  `paths[]` of glob, `matches[].path` of grep) flows back in the MSYS form; file contents and
108
117
  error results are untouched;
109
118
  - **Runtime fact**: `DSH_PATH_DIALECT=msys` is contributed to the official `dsh-shell-env`
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-gitbash-shell",
3
- "version": "0.16.1",
3
+ "version": "0.17.0",
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.16.1",
3
+ "version": "0.17.0",
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/client.js CHANGED
@@ -62,7 +62,7 @@ window.__ModuleLoader__.load({
62
62
  "switch.on": "启用",
63
63
  "switch.off": "停用",
64
64
  "error": "写入失败",
65
- "hint": "启用后:所有工具(bash 命令与 workdir、read/write/edit/read_image/glob/grep 的路径参数)统一使用 MSYS 盘根 POSIX 路径(/c/Users/...);文件工具的路径参数由宿主自动翻译,模型无感。停用后恢复 dsh 原生行为(文件工具用 Windows 路径)。bash 始终是 Git Bash,不受此开关影响。",
65
+ "hint": "启用后:所有工具(bash 命令与 workdir、read/write/edit/read_image/glob/grep 与 present 的路径参数)统一使用 MSYS 盘根 POSIX 路径(/c/Users/...);bash 的原生习惯——~ 家目录、/tmp 临时目录、/dev/null、/usr 等——在所有工具中同样有效(与 Git Bash 挂载表一致),路径参数由宿主自动翻译,模型无感。停用后恢复 dsh 原生行为(文件工具用 Windows 路径)。bash 始终是 Git Bash,不受此开关影响。",
66
66
  "adopt.label": "接管侧栏终端",
67
67
  "adopt.hint": "开启时把 dsh-better-sidebar 的终端 shell 写成 Git Bash(默认开启);关闭后恢复接管前的值,已手动改过的终端设置不会被碰。模型侧的 bash 工具不受此开关影响——那由插件本体提供。",
68
68
  };
@@ -76,7 +76,7 @@ window.__ModuleLoader__.load({
76
76
  "switch.on": "Enable",
77
77
  "switch.off": "Disable",
78
78
  "error": "write failed",
79
- "hint": "Enabled: every tool (bash commands and workdir, read/write/edit/read_image/glob/grep path arguments) uses MSYS drive-root POSIX paths (/c/Users/...); the host translates path fields for the file tools automatically. Disabled restores dsh-native behavior (Windows paths for file tools). Bash is always Git Bash while the plugin is installed; this switch only governs the cross-tool path dialect.",
79
+ "hint": "Enabled: every tool (bash commands and workdir, read/write/edit/read_image/glob/grep and present path arguments) uses MSYS drive-root POSIX paths (/c/Users/...); bash-native habits — the ~ home shorthand, /tmp, /dev/null, /usr — resolve in every tool exactly as bash itself resolves them, and the host translates the path fields automatically. Disabled restores dsh-native behavior (Windows paths for file tools). Bash is always Git Bash while the plugin is installed; this switch only governs the cross-tool path dialect.",
80
80
  "adopt.label": "Adopt sidebar terminal",
81
81
  "adopt.hint": "On (default) writes Git Bash into dsh-better-sidebar's terminal shell; turning it off restores the pre-adoption value and never touches a manually chosen one. The model-side bash tool is unaffected — the plugin itself provides that.",
82
82
  };
@@ -100,7 +100,7 @@ window.__ModuleLoader__.load({
100
100
  "switch.on": "تفعيل",
101
101
  "switch.off": "تعطيل",
102
102
  "error": "فشل الكتابة",
103
- "hint": "عند التفعيل: تستخدم كل الأدوات (أوامر bash و workdir، ومعاملات المسار في read/write/edit/read_image/glob/grep) مسارات POSIX من جذر قرص MSYS (/c/Users/...)؛ ويتولّى المضيف ترجمة حقول المسار لأدوات الملفات تلقائيًا. عند التعطيل يعود السلوك الأصلي لـ dsh (مسارات Windows لأدوات الملفات). يبقى bash دائمًا Git Bash ما دامت الإضافة مثبَّتة؛ هذا المفتاح لا يحكم سوى لهجة المسارات بين الأدوات.",
103
+ "hint": "عند التفعيل: تستخدم كل الأدوات (أوامر bash و workdir، ومعاملات المسار في read/write/edit/read_image/glob/grep و present) مسارات POSIX من جذر قرص MSYS (/c/Users/...)؛ وعادات bash الأصلية — ~ للمجلد الرئيسي و /tmp و /dev/null و /usr — تعمل في كل أداة تمامًا كما تحلّها bash نفسها، ويتولّى المضيف ترجمة حقول المسار تلقائيًا. عند التعطيل يعود السلوك الأصلي لـ dsh (مسارات Windows لأدوات الملفات). يبقى bash دائمًا Git Bash ما دامت الإضافة مثبَّتة؛ هذا المفتاح لا يحكم سوى لهجة المسارات بين الأدوات.",
104
104
  "adopt.label": "تبنّي طرفية الشريط الجانبي",
105
105
  "adopt.hint": "عند التفعيل (افتراضي) تُكتب Git Bash كـ shell لطرفية dsh-better-sidebar؛ وعند التعطيل تُستعاد القيمة السابقة ولا تُلمس القيمة المختارة يدويًا أبدًا. أداة bash الخاصة بالنموذج لا تتأثر — الإضافة نفسها توفرها.",
106
106
  },
@@ -114,7 +114,7 @@ window.__ModuleLoader__.load({
114
114
  "switch.on": "Aktivieren",
115
115
  "switch.off": "Deaktivieren",
116
116
  "error": "Schreiben fehlgeschlagen",
117
- "hint": "Aktiviert: Alle Tools (bash-Befehle und workdir sowie Pfadargumente von read/write/edit/read_image/glob/grep) verwenden POSIX-Pfade ab der MSYS-Laufwerkswurzel (/c/Users/...); der Host übersetzt die Pfadfelder der Dateitools automatisch. Deaktiviert stellt das dsh-native Verhalten wieder her (Windows-Pfade für Dateitools). Bash ist bei installiertem Plugin immer Git Bash; dieser Schalter steuert nur den Pfaddialekt zwischen den Tools.",
117
+ "hint": "Aktiviert: Alle Tools (bash-Befehle und workdir sowie Pfadargumente von read/write/edit/read_image/glob/grep und present) verwenden POSIX-Pfade ab der MSYS-Laufwerkswurzel (/c/Users/...); bash-eigene Gewohnheiten — ~ für das Home-Verzeichnis, /tmp, /dev/null, /usr — funktionieren in jedem Tool genau so, wie bash sie selbst auflöst, und der Host übersetzt die Pfadfelder automatisch. Deaktiviert stellt das dsh-native Verhalten wieder her (Windows-Pfade für Dateitools). Bash ist bei installiertem Plugin immer Git Bash; dieser Schalter steuert nur den Pfaddialekt zwischen den Tools.",
118
118
  "adopt.label": "Sidebar-Terminal übernehmen",
119
119
  "adopt.hint": "Ein (Standard) schreibt Git Bash als Shell des dsh-better-sidebar-Terminals; Aus stellt den vorherigen Wert wieder her und fasst nie eine manuell gewählte Einstellung an. Das bash-Werkzeug des Modells bleibt unberührt — das liefert das Plugin selbst.",
120
120
  },
@@ -128,7 +128,7 @@ window.__ModuleLoader__.load({
128
128
  "switch.on": "Activer",
129
129
  "switch.off": "Désactiver",
130
130
  "error": "échec de l'écriture",
131
- "hint": "Activé : tous les outils (commandes bash et workdir, arguments de chemin de read/write/edit/read_image/glob/grep) utilisent des chemins POSIX à racine de lecteur MSYS (/c/Users/...) ; l'hôte traduit automatiquement les champs de chemin des outils de fichiers. Désactivé rétablit le comportement natif de dsh (chemins Windows pour les outils de fichiers). Bash reste toujours Git Bash tant que le plugin est installé ; ce commutateur ne régit que le dialecte de chemins entre outils.",
131
+ "hint": "Activé : tous les outils (commandes bash et workdir, arguments de chemin de read/write/edit/read_image/glob/grep et de present) utilisent des chemins POSIX à racine de lecteur MSYS (/c/Users/...) ; les habitudes natives de bash — ~ pour le dossier personnel, /tmp, /dev/null, /usr — fonctionnent dans chaque outil exactement comme bash les résout lui-même, et l'hôte traduit les champs de chemin automatiquement. Désactivé rétablit le comportement natif de dsh (chemins Windows pour les outils de fichiers). Bash reste toujours Git Bash tant que le plugin est installé ; ce commutateur ne régit que le dialecte de chemins entre outils.",
132
132
  "adopt.label": "Adopter le terminal latéral",
133
133
  "adopt.hint": "Activé (par défaut) : écrit Git Bash comme shell du terminal dsh-better-sidebar ; désactivé : restaure la valeur davant et ne touche jamais un choix manuel. Loutil bash du modèle nest pas concerné — le plugin le fournit lui-même.",
134
134
  },
@@ -142,7 +142,7 @@ window.__ModuleLoader__.load({
142
142
  "switch.on": "सक्षम करें",
143
143
  "switch.off": "अक्षम करें",
144
144
  "error": "लिखना विफल रहा",
145
- "hint": "सक्षम होने पर: सभी टूल (bash कमांड और workdir, तथा read/write/edit/read_image/glob/grep के पथ तर्क) MSYS ड्राइव-रूट POSIX पथ (/c/Users/...) का उपयोग करते हैं; फ़ाइल टूल के पथ फ़ील्ड होस्ट स्वतः अनुवादित करता है। अक्षम करने पर dsh का मूल व्यवहार बहाल होता है (फ़ाइल टूल के लिए Windows पथ)। जब तक प्लगइन इंस्टॉल है bash हमेशा Git Bash रहता है; यह स्विच केवल टूल के बीच पथ शैली नियंत्रित करता है।",
145
+ "hint": "सक्षम होने पर: सभी टूल (bash कमांड और workdir, तथा read/write/edit/read_image/glob/grep और present के पथ तर्क) MSYS ड्राइव-रूट POSIX पथ (/c/Users/...) का उपयोग करते हैं; bash की मूल आदतें — होम के लिए ~, /tmp, /dev/null, /usr — हर टूल में ठीक वैसे काम करती हैं जैसे bash स्वयं उन्हें हल करता है, और होस्ट पथ फ़ील्ड स्वतः अनुवादित करता है। अक्षम करने पर dsh का मूल व्यवहार बहाल होता है (फ़ाइल टूल के लिए Windows पथ)। जब तक प्लगइन इंस्टॉल है bash हमेशा Git Bash रहता है; यह स्विच केवल टूल के बीच पथ शैली नियंत्रित करता है।",
146
146
  "adopt.label": "साइडबार टर्मिनल अपनाएँ",
147
147
  "adopt.hint": "चालू (डिफ़ॉल्ट) होने पर dsh-better-sidebar के टर्मिनल में Git Bash लिखा जाता है; बंद करने पर पहले वाली मान बहाल होती है और मैन्युअल चुनी गई सेटिंग कभी नहीं छुई जाती। मॉडल का bash टूल अप्रभावित रहता है — वह प्लगइन स्वयं देता है।",
148
148
  },
@@ -156,7 +156,7 @@ window.__ModuleLoader__.load({
156
156
  "switch.on": "Aktifkan",
157
157
  "switch.off": "Nonaktifkan",
158
158
  "error": "gagal menulis",
159
- "hint": "Saat aktif: semua alat (perintah bash dan workdir, serta argumen path read/write/edit/read_image/glob/grep) memakai path POSIX berakar drive MSYS (/c/Users/...); host menerjemahkan bidang path untuk alat berkas secara otomatis. Saat nonaktif, perilaku asli dsh dipulihkan (path Windows untuk alat berkas). Bash selalu Git Bash selama plugin terpasang; sakelar ini hanya mengatur dialek path antaralat.",
159
+ "hint": "Saat aktif: semua alat (perintah bash dan workdir, serta argumen path read/write/edit/read_image/glob/grep dan present) memakai path POSIX berakar drive MSYS (/c/Users/...); kebiasaan asli bash — ~ untuk direktori home, /tmp, /dev/null, /usr — bekerja di setiap alat persis seperti bash sendiri menyelesaikannya, dan host menerjemahkan bidang path secara otomatis. Saat nonaktif, perilaku asli dsh dipulihkan (path Windows untuk alat berkas). Bash selalu Git Bash selama plugin terpasang; sakelar ini hanya mengatur dialek path antaralat.",
160
160
  "adopt.label": "Adopsi terminal sidebar",
161
161
  "adopt.hint": "Nyala (bawaan) menulis Git Bash sebagai shell terminal dsh-better-sidebar; dimatikan memulihkan nilai sebelumnya dan tidak pernah menyentuh pilihan manual. Alat bash sisi model tak terpengaruh — plugin sendiri yang menyediakannya.",
162
162
  },
@@ -170,7 +170,7 @@ window.__ModuleLoader__.load({
170
170
  "switch.on": "Attiva",
171
171
  "switch.off": "Disattiva",
172
172
  "error": "scrittura non riuscita",
173
- "hint": "Attivato: tutti gli strumenti (comandi bash e workdir, argomenti di percorso di read/write/edit/read_image/glob/grep) usano percorsi POSIX con radice di unità MSYS (/c/Users/...); l'host traduce automaticamente i campi di percorso degli strumenti per i file. Disattivato ripristina il comportamento nativo di dsh (percorsi Windows per gli strumenti per i file). Bash resta sempre Git Bash finché il plugin è installato; questo interruttore governa solo il dialetto dei percorsi tra gli strumenti.",
173
+ "hint": "Attivato: tutti gli strumenti (comandi bash e workdir, argomenti di percorso di read/write/edit/read_image/glob/grep e di present) usano percorsi POSIX con radice di unità MSYS (/c/Users/...); le abitudini native di bash — ~ per la home, /tmp, /dev/null, /usr — funzionano in ogni strumento esattamente come le risolve bash stesso, e l'host traduce automaticamente i campi di percorso. Disattivato ripristina il comportamento nativo di dsh (percorsi Windows per gli strumenti per i file). Bash resta sempre Git Bash finché il plugin è installato; questo interruttore governa solo il dialetto dei percorsi tra gli strumenti.",
174
174
  "adopt.label": "Adotta il terminale laterale",
175
175
  "adopt.hint": "Attivo (predefinito) imposta Git Bash come shell del terminale di dsh-better-sidebar; disattivo ripristina il valore precedente e non tocca mai una scelta manuale. Lo strumento bash del modello non è interessato — lo fornisce il plugin stesso.",
176
176
  },
@@ -184,7 +184,7 @@ window.__ModuleLoader__.load({
184
184
  "switch.on": "有効にする",
185
185
  "switch.off": "無効にする",
186
186
  "error": "書き込みに失敗しました",
187
- "hint": "有効にすると、すべてのツール(bash コマンドと workdir、read/write/edit/read_image/glob/grep のパス引数)が MSYS ドライブルートの POSIX パス(/c/Users/...)を使います。ファイルツールのパスフィールドはホストが自動的に変換します。無効にすると dsh 本来の動作(ファイルツールは Windows パス)に戻ります。bash はプラグインがインストールされている限り常に Git Bash で、このスイッチの影響を受けません。",
187
+ "hint": "有効にすると、すべてのツール(bash コマンドと workdir、read/write/edit/read_image/glob/grep と present のパス引数)が MSYS ドライブルートの POSIX パス(/c/Users/...)を使います。bash のネイティブな習慣——ホームの ~、/tmp、/dev/null、/usr——も、bash 自身が解決するのとまったく同じようにすべてのツールで動作し、パスフィールドはホストが自動的に変換します。無効にすると dsh 本来の動作(ファイルツールは Windows パス)に戻ります。bash はプラグインがインストールされている限り常に Git Bash で、このスイッチの影響を受けません。",
188
188
  "adopt.label": "サイドバーターミナルを引き継ぐ",
189
189
  "adopt.hint": "オン(既定)では dsh-better-sidebar のターミナルシェルに Git Bash を書き込みます。オフにすると以前の値へ戻し、手動で選択した設定には決して触れません。モデル側の bash ツールには影響しません——それはプラグイン本体が提供します。",
190
190
  },
@@ -198,7 +198,7 @@ window.__ModuleLoader__.load({
198
198
  "switch.on": "사용",
199
199
  "switch.off": "사용 안 함",
200
200
  "error": "쓰기 실패",
201
- "hint": "켜면 모든 도구(bash 명령과 workdir, read/write/edit/read_image/glob/grep의 경로 인수)가 MSYS 드라이브 루트 POSIX 경로(/c/Users/...)를 사용합니다. 파일 도구의 경로 필드는 호스트가 자동으로 변환합니다. 끄면 dsh 기본 동작(파일 도구에 Windows 경로 사용)으로 돌아갑니다. 플러그인이 설치되어 있는 동안 bash는 항상 Git Bash이며 이 스위치의 영향을 받지 않습니다.",
201
+ "hint": "켜면 모든 도구(bash 명령과 workdir, read/write/edit/read_image/glob/grep 및 present의 경로 인수)가 MSYS 드라이브 루트 POSIX 경로(/c/Users/...)를 사용합니다. bash 고유 습관——홈의 ~, /tmp, /dev/null, /usr——도 bash 자신이 해석하는 그대로 모든 도구에서 작동하며 호스트가 경로 필드를 자동 변환합니다. 끄면 dsh 기본 동작(파일 도구에 Windows 경로 사용)으로 돌아갑니다. 플러그인이 설치되어 있는 동안 bash는 항상 Git Bash이며 이 스위치의 영향을 받지 않습니다.",
202
202
  "adopt.label": "사이드바 터미널 인수",
203
203
  "adopt.hint": "켜짐(기본)이면 dsh-better-sidebar 터미널 셸에 Git Bash를 기록하고, 끄면 이전 값을 복원하며 수동으로 고른 설정은 절대 건드리지 않습니다. 모델 측 bash 도구에는 영향이 없습니다 — 플러그인이 직접 제공합니다.",
204
204
  },
@@ -212,7 +212,7 @@ window.__ModuleLoader__.load({
212
212
  "switch.on": "Inschakelen",
213
213
  "switch.off": "Uitschakelen",
214
214
  "error": "schrijven mislukt",
215
- "hint": "Ingeschakeld: alle tools (bash-opdrachten en workdir, padargumenten van read/write/edit/read_image/glob/grep) gebruiken POSIX-paden vanaf de MSYS-schijfwortel (/c/Users/...); de host vertaalt de padvelden voor de bestandstools automatisch. Uitgeschakeld herstelt het oorspronkelijke dsh-gedrag (Windows-paden voor bestandstools). Bash is altijd Git Bash zolang de plugin is geïnstalleerd; deze schakelaar bepaalt alleen het paddialect tussen de tools.",
215
+ "hint": "Ingeschakeld: alle tools (bash-opdrachten en workdir, padargumenten van read/write/edit/read_image/glob/grep en present) gebruiken POSIX-paden vanaf de MSYS-schijfwortel (/c/Users/...); bash-eigen gewoonten — ~ voor de home-map, /tmp, /dev/null, /usr — werken in elke tool precies zoals bash ze zelf oplost, en de host vertaalt de padvelden automatisch. Uitgeschakeld herstelt het oorspronkelijke dsh-gedrag (Windows-paden voor bestandstools). Bash is altijd Git Bash zolang de plugin is geïnstalleerd; deze schakelaar bepaalt alleen het paddialect tussen de tools.",
216
216
  "adopt.label": "Zijbalkterminal overnemen",
217
217
  "adopt.hint": "Aan (standaard) schrijft Git Bash als shell van de dsh-better-sidebar-terminal; uit herstelt de vorige waarde en raakt nooit een handmatige keuze aan. De bash-tool van het model blijft buiten beschouwing — die levert de plugin zelf.",
218
218
  },
@@ -226,7 +226,7 @@ window.__ModuleLoader__.load({
226
226
  "switch.on": "Włącz",
227
227
  "switch.off": "Wyłącz",
228
228
  "error": "zapis nie powiódł się",
229
- "hint": "Po włączeniu: wszystkie narzędzia (polecenia bash i workdir oraz argumenty ścieżek w read/write/edit/read_image/glob/grep) używają ścieżek POSIX od korzenia dysku MSYS (/c/Users/...); host automatycznie tłumaczy pola ścieżek narzędzi plikowych. Wyłączenie przywraca natywne zachowanie dsh (ścieżki Windows dla narzędzi plikowych). Bash jest zawsze Git Bash, dopóki wtyczka jest zainstalowana; ten przełącznik reguluje wyłącznie dialekt ścieżek między narzędziami.",
229
+ "hint": "Po włączeniu: wszystkie narzędzia (polecenia bash i workdir, argumenty ścieżek w read/write/edit/read_image/glob/grep i w present) używają ścieżek POSIX od korzenia dysku MSYS (/c/Users/...); natywne nawyki basha — ~ dla katalogu domowego, /tmp, /dev/null, /usr — działają w każdym narzędziu dokładnie tak, jak rozwiązuje je sam bash, a host automatycznie tłumaczy pola ścieżek. Wyłączenie przywraca natywne zachowanie dsh (ścieżki Windows dla narzędzi plikowych). Bash jest zawsze Git Bash, dopóki wtyczka jest zainstalowana; ten przełącznik reguluje wyłącznie dialekt ścieżek między narzędziami.",
230
230
  "adopt.label": "Przejmij terminal boczny",
231
231
  "adopt.hint": "Włączony (domyślnie) zapisuje Git Bash jako shell terminala dsh-better-sidebar; wyłączony przywraca poprzednią wartość i nigdy nie narusza ręcznie wybranego ustawienia. Narzędzie bash po stronie modelu jest nietknięte — zapewnia je sama wtyczka.",
232
232
  },
@@ -240,7 +240,7 @@ window.__ModuleLoader__.load({
240
240
  "switch.on": "Ativar",
241
241
  "switch.off": "Desativar",
242
242
  "error": "falha ao gravar",
243
- "hint": "Ativada: todas as ferramentas (comandos bash e workdir, argumentos de caminho de read/write/edit/read_image/glob/grep) usam caminhos POSIX a partir da raiz da unidade MSYS (/c/Users/...); o host traduz automaticamente os campos de caminho das ferramentas de arquivo. Desativada restaura o comportamento nativo do dsh (caminhos Windows para as ferramentas de arquivo). O bash é sempre Git Bash enquanto o plugin estiver instalado; este interruptor rege apenas o dialeto de caminhos entre ferramentas.",
243
+ "hint": "Ativada: todas as ferramentas (comandos bash e workdir, argumentos de caminho de read/write/edit/read_image/glob/grep e de present) usam caminhos POSIX a partir da raiz da unidade MSYS (/c/Users/...); os hábitos nativos do bash — ~ para a pasta pessoal, /tmp, /dev/null, /usr — funcionam em cada ferramenta exatamente como o próprio bash os resolve, e o host traduz os campos de caminho automaticamente. Desativada restaura o comportamento nativo do dsh (caminhos Windows para as ferramentas de arquivo). O bash é sempre Git Bash enquanto o plugin estiver instalado; este interruptor rege apenas o dialeto de caminhos entre ferramentas.",
244
244
  "adopt.label": "Adotar o terminal lateral",
245
245
  "adopt.hint": "Ligado (padrão) grava o Git Bash como shell do terminal do dsh-better-sidebar; desligado restaura o valor anterior e nunca altera uma escolha manual. A ferramenta bash do modelo não é afetada — o próprio plug-in a fornece.",
246
246
  },
@@ -254,7 +254,7 @@ window.__ModuleLoader__.load({
254
254
  "switch.on": "Включить",
255
255
  "switch.off": "Выключить",
256
256
  "error": "не удалось записать",
257
- "hint": "Включено: все инструменты (команды bash и workdir, аргументы путей read/write/edit/read_image/glob/grep) используют POSIX-пути от корня диска MSYS (/c/Users/...); хост автоматически преобразует поля путей для файловых инструментов. Выключение возвращает исходное поведение dsh (пути Windows для файловых инструментов). Пока плагин установлен, bash всегда остаётся Git Bash; этот переключатель управляет только диалектом путей между инструментами.",
257
+ "hint": "Включено: все инструменты (команды bash и workdir, аргументы путей read/write/edit/read_image/glob/grep и present) используют POSIX-пути от корня диска MSYS (/c/Users/...); собственные привычки bash — ~ для домашнего каталога, /tmp, /dev/null, /usr — работают в каждом инструменте ровно так, как их разрешает сам bash, а хост автоматически преобразует поля путей. Выключение возвращает исходное поведение dsh (пути Windows для файловых инструментов). Пока плагин установлен, bash всегда остаётся Git Bash; этот переключатель управляет только диалектом путей между инструментами.",
258
258
  "adopt.label": "Перехватывать терминал боковой панели",
259
259
  "adopt.hint": "Включено (по умолчанию): Git Bash записывается как shell терминала dsh-better-sidebar; выключено — восстанавливается прежнее значение, вручную выбранные настройки не трогаются. Инструмент bash у модели не затронут — его предоставляет сам плагин.",
260
260
  },
@@ -268,7 +268,7 @@ window.__ModuleLoader__.load({
268
268
  "switch.on": "Aktivera",
269
269
  "switch.off": "Inaktivera",
270
270
  "error": "skrivning misslyckades",
271
- "hint": "Aktiverad: alla verktyg (bash-kommandon och workdir, sökvägsargument för read/write/edit/read_image/glob/grep) använder POSIX-sökvägar från MSYS-enhetsroten (/c/Users/...); värden översätter sökvägsfälten för filverktygen automatiskt. Inaktiverad återställer dsh:s ursprungliga beteende (Windows-sökvägar för filverktyg). Bash är alltid Git Bash så länge insticksprogrammet är installerat; den här växeln styr bara sökvägsdialekten mellan verktygen.",
271
+ "hint": "Aktiverad: alla verktyg (bash-kommandon och workdir, sökvägsargument för read/write/edit/read_image/glob/grep och present) använder POSIX-sökvägar från MSYS-enhetsroten (/c/Users/...); bash egna vanor — ~ för hemkatalogen, /tmp, /dev/null, /usr — fungerar i varje verktyg exakt som bash själv löser dem, och värden översätter sökvägsfälten automatiskt. Inaktiverad återställer dsh:s ursprungliga beteende (Windows-sökvägar för filverktygen). Bash är alltid Git Bash så länge insticksprogrammet är installerat; den här växeln styr bara sökvägsdialekten mellan verktygen.",
272
272
  "adopt.label": "Överta sidolistterminal",
273
273
  "adopt.hint": "På (standard) skriver Git Bash som terminal-shell i dsh-better-sidebar; av återställer det tidigare värdet och rör aldrig ett manuellt val. Modellens bash-verktyg påverkas inte — det tillhandahålls av pluginet självt.",
274
274
  },
@@ -282,7 +282,7 @@ window.__ModuleLoader__.load({
282
282
  "switch.on": "เปิดใช้งาน",
283
283
  "switch.off": "ปิดใช้งาน",
284
284
  "error": "เขียนไม่สำเร็จ",
285
- "hint": "เมื่อเปิดใช้งาน: เครื่องมือทั้งหมด (คำสั่ง bash และ workdir รวมถึงอาร์กิวเมนต์พาธของ read/write/edit/read_image/glob/grep) ใช้พาธ POSIX จากรากไดรฟ์ของ MSYS (/c/Users/...) โฮสต์จะแปลฟิลด์พาธของเครื่องมือจัดการไฟล์ให้อัตโนมัติ เมื่อปิดใช้งานจะกลับสู่พฤติกรรมดั้งเดิมของ dsh (เครื่องมือจัดการไฟล์ใช้พาธ Windows) bash เป็น Git Bash เสมอเมื่อติดตั้งปลั๊กอินนี้ สวิตช์นี้ควบคุมเฉพาะรูปแบบพาธระหว่างเครื่องมือเท่านั้น",
285
+ "hint": "เมื่อเปิดใช้งาน: เครื่องมือทั้งหมด (คำสั่ง bash และ workdir รวมถึงอาร์กิวเมนต์พาธของ read/write/edit/read_image/glob/grep และ present) ใช้พาธ POSIX จากรากไดรฟ์ของ MSYS (/c/Users/...); พฤติกรรมดั้งเดิมของ bash — ~ สำหรับโฮม, /tmp, /dev/null, /usr — ทำงานในทุกเครื่องมือเหมือนที่ bash แก้เองพอดี และโฮสต์แปลฟิลด์พาธให้อัตโนมัติ เมื่อปิดใช้งานจะกลับสู่พฤติกรรมดั้งเดิมของ dsh (เครื่องมือจัดการไฟล์ใช้พาธ Windows) bash เป็น Git Bash เสมอเมื่อติดตั้งปลั๊กอินนี้ สวิตช์นี้ควบคุมเฉพาะรูปแบบพาธระหว่างเครื่องมือเท่านั้น",
286
286
  "adopt.label": "รับช่วงเทอร์มินัลแถบข้าง",
287
287
  "adopt.hint": "เปิด (ค่าเริ่มต้น) จะเขียน Git Bash เป็น shell ของเทอร์มินัล dsh-better-sidebar; ปิดแล้วคืนค่าเดิมและไม่แตะการตั้งค่าที่เลือกเอง เครื่องมือ bash ของโมเดลไม่กระทบ — ปลั๊กอินจัดหาเอง",
288
288
  },
@@ -296,7 +296,7 @@ window.__ModuleLoader__.load({
296
296
  "switch.on": "Etkinleştir",
297
297
  "switch.off": "Devre dışı bırak",
298
298
  "error": "yazma başarısız",
299
- "hint": "Etkinleştirildiğinde: tüm araçlar (bash komutları ve workdir ile read/write/edit/read_image/glob/grep yol bağımsız değişkenleri) MSYS sürücü kökünden POSIX yolları (/c/Users/...) kullanır; ana süreç dosya araçlarının yol alanlarını otomatik olarak çevirir. Devre dışı bırakıldığında dsh yerel davranışına döner (dosya araçları için Windows yolları). Eklenti kurulu olduğu sürece bash her zaman Git Bash olarak kalır; bu anahtar yalnızca araçlar arası yol lehçesini yönetir.",
299
+ "hint": "Etkinleştirildiğinde: tüm araçlar (bash komutları ve workdir ile read/write/edit/read_image/glob/grep ve present yol bağımsız değişkenleri) MSYS sürücü kökünden POSIX yolları (/c/Users/...) kullanır; bash'e özgü alışkanlıklar — ev için ~, /tmp, /dev/null, /usr — her araçta tam bash'in kendisinin çözdüğü gibi çalışır ve ana süreç yol alanlarını otomatik olarak çevirir. Devre dışı bırakıldığında dsh yerel davranışına döner (dosya araçları için Windows yolları). Eklenti kurulu olduğu sürece bash her zaman Git Bash olarak kalır; bu anahtar yalnızca araçlar arası yol lehçesini yönetir.",
300
300
  "adopt.label": "Kenar çubuğu terminalini devral",
301
301
  "adopt.hint": "Açık (varsayılan) Git Bashi dsh-better-sidebar terminalinin shelli olarak yazar; kapalı önceki değeri geri getirir ve elle seçilmiş bir ayara asla dokunmaz. Model tarafındaki bash aracı etkilenmez — onu eklentinin kendisi sağlar.",
302
302
  },
@@ -310,7 +310,7 @@ window.__ModuleLoader__.load({
310
310
  "switch.on": "Bật",
311
311
  "switch.off": "Tắt",
312
312
  "error": "ghi thất bại",
313
- "hint": "Khi bật: mọi công cụ (lệnh bash và workdir, tham số đường dẫn của read/write/edit/read_image/glob/grep) dùng đường dẫn POSIX từ gốc ổ đĩa MSYS (/c/Users/...); máy chủ tự động chuyển đổi trường đường dẫn cho các công cụ tệp. Khi tắt, hành vi gốc của dsh được khôi phục (công cụ tệp dùng đường dẫn Windows). bash luôn là Git Bash khi plugin được cài đặt; công tắc này chỉ điều khiển phương ngữ đường dẫn giữa các công cụ.",
313
+ "hint": "Khi bật: mọi công cụ (lệnh bash và workdir, tham số đường dẫn của read/write/edit/read_image/glob/grep và present) dùng đường dẫn POSIX từ gốc ổ đĩa MSYS (/c/Users/...); thói quen gốc của bash — ~ cho thư mục home, /tmp, /dev/null, /usr — hoạt động trong mọi công cụ đúng như chính bash giải quyết chúng, và máy chủ tự động chuyển đổi trường đường dẫn. Khi tắt, hành vi gốc của dsh được khôi phục (công cụ tệp dùng đường dẫn Windows). bash luôn là Git Bash khi plugin được cài đặt; công tắc này chỉ điều khiển phương ngữ đường dẫn giữa các công cụ.",
314
314
  "adopt.label": "Tiếp nhận terminal thanh bên",
315
315
  "adopt.hint": "Bật (mặc định) ghi Git Bash làm shell terminal của dsh-better-sidebar; tắt khôi phục giá trị trước đó và không bao giờ đụng tới lựa chọn thủ công. Công cụ bash phía mô hình không bị ảnh hưởng — chính plugin cung cấp nó.",
316
316
  },
@@ -324,7 +324,7 @@ window.__ModuleLoader__.load({
324
324
  "switch.on": "啟用",
325
325
  "switch.off": "停用",
326
326
  "error": "寫入失敗",
327
- "hint": "啟用後:所有工具(bash 指令同 workdir、read/write/edit/read_image/glob/grep 嘅路徑參數)統一使用 MSYS 磁碟根 POSIX 路徑(/c/Users/...);檔案工具嘅路徑參數由宿主自動翻譯,模型無感。停用後回復 dsh 原生行為(檔案工具用 Windows 路徑)。bash 永遠係 Git Bash,唔受呢個開關影響。",
327
+ "hint": "啟用後:所有工具(bash 指令同 workdir、read/write/edit/read_image/glob/grep 同 present 嘅路徑參數)統一使用 MSYS 磁碟根 POSIX 路徑(/c/Users/...);bash 原生習慣——~ 家目錄、/tmp、/dev/null、/usr——喺所有工具一樣有效(同 Git Bash 掛載表一致),路徑參數由宿主自動翻譯,模型無感。停用後回復 dsh 原生行為(檔案工具用 Windows 路徑)。bash 永遠係 Git Bash,唔受呢個開關影響。",
328
328
  "adopt.label": "接管側欄終端",
329
329
  "adopt.hint": "開啟(預設)會把 dsh-better-sidebar 的終端 shell 寫成 Git Bash;關閉後還原接管前的值,手動改過的設定永不被碰。模型側的 bash 工具不受影響——由插件本身提供。",
330
330
  },
@@ -338,7 +338,7 @@ window.__ModuleLoader__.load({
338
338
  "switch.on": "啟用",
339
339
  "switch.off": "停用",
340
340
  "error": "寫入失敗",
341
- "hint": "啟用後:所有工具(bash 指令同 workdir、read/write/edit/read_image/glob/grep 嘅路徑參數)統一使用 MSYS 磁碟根 POSIX 路徑(/c/Users/...);檔案工具嘅路徑參數由宿主自動翻譯,模型無感。停用後回復 dsh 原生行為(檔案工具用 Windows 路徑)。bash 永遠係 Git Bash,唔受呢個開關影響。",
341
+ "hint": "啟用後:所有工具(bash 指令同 workdir、read/write/edit/read_image/glob/grep 同 present 嘅路徑參數)統一使用 MSYS 磁碟根 POSIX 路徑(/c/Users/...);bash 原生習慣——~ 家目錄、/tmp、/dev/null、/usr——喺所有工具一樣有效(同 Git Bash 掛載表一致),路徑參數由宿主自動翻譯,模型無感。停用後回復 dsh 原生行為(檔案工具用 Windows 路徑)。bash 永遠係 Git Bash,唔受呢個開關影響。",
342
342
  "adopt.label": "接管側欄終端",
343
343
  "adopt.hint": "開啟(預設)會把 dsh-better-sidebar 的終端 shell 寫成 Git Bash;關閉後還原接管前的值,手動改過的設定永不被碰。模型側的 bash 工具不受影響——由插件本身提供。",
344
344
  },
@@ -352,7 +352,7 @@ window.__ModuleLoader__.load({
352
352
  "switch.on": "啟用",
353
353
  "switch.off": "停用",
354
354
  "error": "寫入失敗",
355
- "hint": "啟用後:所有工具(bash 指令與 workdir,以及 read/write/edit/read_image/glob/grep 的路徑參數)統一使用 MSYS 磁碟根 POSIX 路徑(/c/Users/...);檔案工具的路徑參數由宿主自動轉譯,模型無感。停用後恢復 dsh 原生行為(檔案工具使用 Windows 路徑)。bash 一律是 Git Bash,不受此開關影響。",
355
+ "hint": "啟用後:所有工具(bash 指令與 workdir,以及 read/write/edit/read_image/glob/grep 與 present 的路徑參數)統一使用 MSYS 磁碟根 POSIX 路徑(/c/Users/...);bash 原生習慣——~ 家目錄、/tmp、/dev/null、/usr——在所有工具同樣有效(與 Git Bash 掛載表一致),路徑參數由宿主自動轉譯,模型無感。停用後恢復 dsh 原生行為(檔案工具使用 Windows 路徑)。bash 一律是 Git Bash,不受此開關影響。",
356
356
  "adopt.label": "接管側欄終端機",
357
357
  "adopt.hint": "開啟(預設)會把 dsh-better-sidebar 的終端機 shell 寫成 Git Bash;關閉後還原接管前的值,手動改過的設定永不被碰。模型側的 bash 工具不受影響——由插件本身提供。",
358
358
  },
package/src/index.js CHANGED
@@ -50,6 +50,7 @@ import {
50
50
  rmSync,
51
51
  writeFileSync,
52
52
  } from 'node:fs'
53
+ import { homedir, tmpdir } from 'node:os'
53
54
  import { dirname, join } from 'node:path'
54
55
  import { fileURLToPath } from 'node:url'
55
56
 
@@ -662,7 +663,7 @@ export function installRegisterShim(reg) {
662
663
  const SETTINGS_NAMESPACE = 'gitbash-shell'
663
664
 
664
665
  /** The full directive text, injected only while posixPaths is on. */
665
- const POSIX_DIRECTIVE_TEXT = 'The working shell is Git for Windows bash: paths use MSYS drive roots (/c/Users/...), and every tool accepts that form directly.'
666
+ const POSIX_DIRECTIVE_TEXT = 'The working shell is Git for Windows bash: paths use MSYS drive roots (/c/Users/...), and every tool accepts that form directly — including the bash-native habits (~ home shorthand, /tmp, /dev/null, /usr/bin/...), which resolve in every tool exactly as bash itself resolves them.'
666
667
 
667
668
  // Official runtime counterpart to the directive (v0.11.0): dsh-shell-env is
668
669
  // the host-plane registry behind the model-visible $DSH_* facts, and the
@@ -670,7 +671,7 @@ const POSIX_DIRECTIVE_TEXT = 'The working shell is Git for Windows bash: paths u
670
671
  // lives there, verifiable at runtime instead of only stated in the prompt.
671
672
  const PATH_DIALECT_KEY = 'DSH_PATH_DIALECT'
672
673
  const PATH_DIALECT_VALUE = 'msys'
673
- const PATH_DIALECT_DESCRIPTION = 'Path dialect for tool calls and tool results: MSYS drive roots (/c/Users/...); every tool accepts this form directly.'
674
+ const PATH_DIALECT_DESCRIPTION = 'Path dialect for tool calls and tool results: MSYS drive roots (/c/Users/...) plus the bash-native mounts (~, /tmp, /dev/null, /usr); every tool accepts these forms directly.'
674
675
 
675
676
  /** Read the posixPaths switch from the live settings service; never throws. */
676
677
  export function readPosixPaths(ctxLike) {
@@ -723,30 +724,44 @@ function posixSeparators(value) {
723
724
  : value
724
725
  }
725
726
 
726
- /**
727
- * Rewrite result METADATA path fields to the MSYS dialect (pure, 0.10.4).
728
- * Only the well-known metadata fields per tool are touched — read/write/edit
729
- * `path` (absolute, via windowsToMsys), glob `paths[]` and grep
730
- * `matches[].path` (workspace-relative, separator-normalized). File CONTENT
731
- * (read lines, grep match text) and error results are never modified: the
732
- * tool result is data, only its path metadata changes dialect so the model
733
- * never meets a Windows-form path flowing back from a successful call.
734
- * @returns {object} a new value when any field changed, the input otherwise.
735
- */
736
- export function rewriteResultPaths(name, value) {
727
+ export function rewriteResultPaths(name, value, env) {
737
728
  if (!value || typeof value !== 'object') return value
729
+ // v0.17.0: paths under the user TEMP directory echo back in the bash
730
+ // /tmp dialect (that IS what /tmp means in Git Bash), so the model sees
731
+ // the same short root bash itself prints for $TMP.
732
+ const tempRoot = env && env.tmpDir ? windowsToMsys(env.tmpDir) : ''
733
+ const toMsys = (p) => {
734
+ const out = windowsToMsys(p)
735
+ if (!tempRoot) return out
736
+ const lower = out.toLowerCase()
737
+ const rootLower = tempRoot.toLowerCase()
738
+ if (lower === rootLower) return '/tmp'
739
+ if (lower.startsWith(rootLower + '/')) return '/tmp' + out.slice(tempRoot.length)
740
+ return out
741
+ }
738
742
  if (name === 'read' || name === 'read_image' || name === 'write' || name === 'edit') {
739
743
  if (typeof value.path === 'string' && value.path !== '') {
740
- const out = windowsToMsys(value.path)
744
+ const out = toMsys(value.path)
741
745
  if (out !== value.path) return { ...value, path: out }
742
746
  }
743
747
  return value
744
748
  }
749
+ if (name === 'present' && Array.isArray(value.files)) {
750
+ let changed = false
751
+ const files = value.files.map((file) => {
752
+ if (!file || typeof file !== 'object' || typeof file.path !== 'string' || file.path === '') return file
753
+ const out = posixSeparators(toMsys(file.path))
754
+ if (out === file.path) return file
755
+ changed = true
756
+ return { ...file, path: out }
757
+ })
758
+ return changed ? { ...value, files } : value
759
+ }
745
760
  if (name === 'glob' && Array.isArray(value.paths)) {
746
761
  let changed = false
747
762
  const paths = value.paths.map((p) => {
748
763
  if (typeof p !== 'string') return p
749
- const out = posixSeparators(windowsToMsys(p))
764
+ const out = posixSeparators(toMsys(p))
750
765
  if (out === p) return p
751
766
  changed = true
752
767
  return out
@@ -757,7 +772,7 @@ export function rewriteResultPaths(name, value) {
757
772
  let changed = false
758
773
  const matches = value.matches.map((m) => {
759
774
  if (!m || typeof m !== 'object' || typeof m.path !== 'string') return m
760
- const out = posixSeparators(windowsToMsys(m.path))
775
+ const out = posixSeparators(toMsys(m.path))
761
776
  if (out === m.path) return m
762
777
  changed = true
763
778
  return { ...m, path: out }
@@ -782,28 +797,128 @@ export function rewriteResultPaths(name, value) {
782
797
  // directive still stands).
783
798
 
784
799
  const MSYS_DRIVE_ROOT = /^\/([a-z])\/(.*)$/i
800
+ const MSYS_BARE_DRIVE = /^\/([a-z])$/i
785
801
  const TRANSLATABLE_PATH_FIELDS = new Set(['file_path', 'path', 'workdir'])
786
802
 
787
- /** '/c/Users/x' -> 'C:/Users/x'; any other shape returns the input unchanged. */
788
- export function translateMsysPath(value) {
803
+ // ── virtual mounts + env (v0.17.0) ─────────────────────────────────────
804
+ //
805
+ // Git Bash resolves a few POSIX roots through its OWN mount table before
806
+ // any drive root applies (`mount` inside Git Bash): /tmp is the user TEMP
807
+ // dir (usertemp), /dev/null is the Windows NUL device, and /usr /bin /etc
808
+ // /var /home /root /mnt live under the Git install root. The model carries
809
+ // those bash habits into EVERY tool, so the translation layer mirrors the
810
+ // same table or the file tools silently misplace them (/tmp becomes a
811
+ // fresh C:/tmp on the current drive; /dev/null becomes a real file under
812
+ // C:/dev). All matches are segment-bounded and case-sensitive, exactly
813
+ // like the msys mount table itself (/Tmp does NOT match).
814
+ //
815
+ // /home is deliberately the GIT mount (git-root/home, usually empty), NOT
816
+ // the user profile: bash itself resolves /home/x there, while the home
817
+ // SHORTHAND ~ expands to $HOME (= C:/Users/<u>) — mirroring both keeps
818
+ // file tools and bash in perfect agreement. A bare '/' is never
819
+ // translated: it would scope tools to the whole Git install tree.
820
+
821
+ /** The Windows null device — the exact target of Git Bash's /dev/null. */
822
+ const NUL_DEVICE = '\\\\.\\NUL'
823
+
824
+ /** Git-root mounts: MSYS path -> subdirectory under the Git install root. */
825
+ const GIT_MOUNTS = new Map([
826
+ ['/usr', 'usr'],
827
+ ['/bin', 'usr/bin'],
828
+ ['/etc', 'etc'],
829
+ ['/var', 'var'],
830
+ ['/home', 'home'],
831
+ ['/root', 'root'],
832
+ ['/mnt', 'mnt'],
833
+ ])
834
+
835
+ /**
836
+ * The runtime facts the virtual mounts resolve against (all optional — a
837
+ * missing fact simply leaves its paths untranslated). Probed once per
838
+ * process: tmpdir/homedir are cheap; the Git root walks the candidate
839
+ * bash.exe list (the default install plus every PATH entry — which also
840
+ * covers a Git-usr-bin PATH entry) and accepts a root only when
841
+ * <root>/usr/bin exists, which excludes the WSL bash shim in WindowsApps.
842
+ */
843
+ let translateEnvCache
844
+ export function buildTranslateEnv() {
845
+ if (translateEnvCache) return translateEnvCache
846
+ const env = { tmpDir: undefined, home: undefined, gitRoot: undefined }
847
+ try {
848
+ const t = tmpdir()
849
+ if (t && existsSync(t)) env.tmpDir = t.replace(/\\/g, '/')
850
+ } catch { /* keep undefined */ }
851
+ try {
852
+ const h = homedir()
853
+ if (h && existsSync(h)) env.home = h.replace(/\\/g, '/')
854
+ } catch { /* keep undefined */ }
855
+ try {
856
+ const candidates = [DEFAULT_GIT_BASH]
857
+ for (const dir of String(process.env.PATH ?? '').split(';')) {
858
+ const clean = dir && dir.trim()
859
+ if (clean) candidates.push(clean.replace(/\\/g, '/') + '/bash.exe')
860
+ }
861
+ for (const candidate of candidates) {
862
+ try {
863
+ if (!existsSync(candidate)) continue
864
+ const root = dirname(dirname(candidate))
865
+ if (existsSync(join(root, 'usr', 'bin'))) {
866
+ env.gitRoot = root.replace(/\\/g, '/')
867
+ break
868
+ }
869
+ } catch { /* try the next candidate */ }
870
+ }
871
+ } catch { /* keep undefined */ }
872
+ translateEnvCache = env
873
+ return env
874
+ }
875
+
876
+ /**
877
+ * '/c/Users/x' -> 'C:/Users/x'. With an env, the bash-native habits resolve
878
+ * first (~ -> home) and the virtual mounts (/tmp, /dev/null, /usr & friends)
879
+ * resolve after the drive roots; any other shape returns the input
880
+ * unchanged. Pure: the env is a plain fact bag, nothing is probed here.
881
+ */
882
+ export function translateMsysPath(value, env) {
789
883
  if (typeof value !== 'string') return value
884
+ if (env && env.home && (value === '~' || value.startsWith('~/'))) {
885
+ return value === '~' ? env.home : env.home + '/' + value.slice(2)
886
+ }
790
887
  const match = MSYS_DRIVE_ROOT.exec(value)
791
- if (!match) return value
792
- return match[1].toUpperCase() + ':/' + match[2]
888
+ if (match) return match[1].toUpperCase() + ':/' + match[2]
889
+ const bare = MSYS_BARE_DRIVE.exec(value)
890
+ if (bare) return bare[1].toUpperCase() + ':/'
891
+ if (value === '/dev/null') return NUL_DEVICE
892
+ if (env && env.tmpDir && (value === '/tmp' || value.startsWith('/tmp/'))) {
893
+ return value === '/tmp' ? env.tmpDir : env.tmpDir + value.slice(4)
894
+ }
895
+ if (env && env.gitRoot) {
896
+ for (const [mount, target] of GIT_MOUNTS) {
897
+ if (value === mount || value.startsWith(mount + '/')) {
898
+ const rest = value === mount ? '' : value.slice(mount.length)
899
+ return env.gitRoot + '/' + target + rest
900
+ }
901
+ }
902
+ }
903
+ return value
793
904
  }
794
905
 
795
906
  /**
796
907
  * Return an arguments object whose path fields carry drive-letter roots.
797
908
  * Pure: builds a NEW object when any field changes and returns the ORIGINAL
798
909
  * reference otherwise — the registry deep-freezes exec.arguments at exec
799
- * construction (0.10.2 lesson: in-place writes throw in strict mode and were
800
- * silently swallowed), so the caller REPLACES the exec.arguments property
801
- * (the exec object itself is not frozen until tools/result; signal
802
- * replacement is the registry's own precedent).
910
+ * construction (0.10.2 lesson: in-place writes throw in strict mode and
911
+ * were silently swallowed), so the caller REPLACES the exec.arguments
912
+ * property (the exec object itself is not frozen until tools/result;
913
+ * signal replacement is the registry's own precedent).
914
+ *
915
+ * v0.17.0: present's NESTED files[].path rides along (the field-name
916
+ * whitelist only sees top-level keys, and present is the one tool whose
917
+ * path arguments live one level down).
803
918
  * @returns {object} the translated arguments object, or the input when
804
919
  * nothing changed (also the input itself when args is not an object).
805
920
  */
806
- export function translatePathArguments(args) {
921
+ export function translatePathArguments(args, env) {
807
922
  if (!args || typeof args !== 'object') return args
808
923
  let changed = false
809
924
  const next = { ...args }
@@ -811,14 +926,80 @@ export function translatePathArguments(args) {
811
926
  if (!TRANSLATABLE_PATH_FIELDS.has(key)) continue
812
927
  const value = next[key]
813
928
  if (typeof value !== 'string') continue
814
- const translated = translateMsysPath(value)
929
+ const translated = translateMsysPath(value, env)
815
930
  if (translated === value) continue
816
931
  next[key] = translated
817
932
  changed = true
818
933
  }
934
+ if (Array.isArray(next.files)) {
935
+ const files = next.files.map((file) => {
936
+ if (!file || typeof file !== 'object' || typeof file.path !== 'string') return file
937
+ const translated = translateMsysPath(file.path, env)
938
+ return translated === file.path ? file : { ...file, path: translated }
939
+ })
940
+ if (files.some((file, index) => file !== next.files[index])) {
941
+ next.files = files
942
+ changed = true
943
+ }
944
+ }
819
945
  return changed ? next : args
820
946
  }
821
947
 
948
+ // ── absolute glob patterns (v0.17.0) ────────────────────────────────────
949
+ //
950
+ // The glob tool takes its search root in the `path` argument and expects a
951
+ // RELATIVE pattern; an absolute pattern ('/c/Users/x/*.md', or the Windows
952
+ // form) is treated as relative text and silently matches nothing. A model
953
+ // raised on bash writes absolute patterns anyway, so for the glob tool the
954
+ // wrapper splits an absolute pattern at the last directory separator
955
+ // before the first wildcard and moves the translated directory into
956
+ // `path` ('/c/a/*/b/*.md' -> path 'C:/a', pattern '*/b/*.md'). An absolute
957
+ // path WITHOUT wildcards splits into directory + basename. Runs only for
958
+ // the glob tool and only for patterns that start absolute.
959
+
960
+ const GLOB_META = /[*?\[\]]/
961
+
962
+ /** Split an absolute glob pattern into { prefix, rest }; null when relative. */
963
+ function splitGlobPrefix(pattern) {
964
+ if (typeof pattern !== 'string') return null
965
+ let p = pattern
966
+ // A Windows drive head ('C:' + separator) is normalized to the MSYS form
967
+ // first so both absolute dialects split through the same code path.
968
+ const winHead = /^([A-Za-z]):[\\/]+(.*)$/.exec(p)
969
+ if (winHead) p = '/' + winHead[1].toLowerCase() + '/' + winHead[2].replace(/[\\/]+/g, '/')
970
+ if (p[0] !== '/') return null
971
+ const meta = GLOB_META.exec(p)
972
+ const cut = meta ? p.lastIndexOf('/', meta.index) : p.lastIndexOf('/')
973
+ if (cut <= 0) return null
974
+ const rest = p.slice(cut + 1)
975
+ if (rest === '') return null
976
+ return { prefix: p.slice(0, cut), rest }
977
+ }
978
+
979
+
980
+ /** Translate the (always MSYS-form) directory prefix; null when it is not one. */
981
+ function normalizeAbsolutePrefix(prefix, env) {
982
+ const translated = translateMsysPath(prefix, env)
983
+ return translated !== prefix ? translated : null
984
+ }
985
+
986
+
987
+ /**
988
+ * Rewrite a glob call's absolute pattern into the { path, pattern } pair
989
+ * the tool actually supports; any existing `path` is superseded (an
990
+ * absolute pattern already names its root). Pure; returns the input on any
991
+ * miss.
992
+ */
993
+ export function translateGlobArguments(args, env) {
994
+ if (!args || typeof args !== 'object' || typeof args.pattern !== 'string') return args
995
+ const split = splitGlobPrefix(args.pattern)
996
+ if (!split) return args
997
+ const prefix = normalizeAbsolutePrefix(split.prefix, env)
998
+ if (!prefix) return args
999
+ return { ...args, pattern: split.rest, path: prefix }
1000
+ }
1001
+
1002
+
822
1003
  // ── dsh-better-sidebar shell cooperation ───────────────────────────────────
823
1004
  //
824
1005
  // dsh-better-sidebar (v0.15.2+) resolves its terminal shell PER OPEN: a
@@ -1160,7 +1341,12 @@ export async function apply(ctx, config = {}) {
1160
1341
  ctx.on('tools/execute', (exec, next) => {
1161
1342
  try {
1162
1343
  if (exec && exec.arguments && typeof exec.arguments === 'object' && readPosixPaths(ctx)) {
1163
- const translated = translatePathArguments(exec.arguments)
1344
+ const env = buildTranslateEnv()
1345
+ let translated = translatePathArguments(exec.arguments, env)
1346
+ if (exec.name === 'glob') {
1347
+ const globTranslated = translateGlobArguments(translated, env)
1348
+ if (globTranslated !== translated) translated = globTranslated
1349
+ }
1164
1350
  if (translated !== exec.arguments) exec.arguments = translated
1165
1351
  }
1166
1352
  } catch { /* never block a call on translation */ }
@@ -1185,7 +1371,7 @@ export async function apply(ctx, config = {}) {
1185
1371
  let patch
1186
1372
  try {
1187
1373
  if (readPosixPaths(ctx) && result && result.isError === false && result.value && typeof result.value === 'object') {
1188
- const value = rewriteResultPaths(exec && exec.name, result.value)
1374
+ const value = rewriteResultPaths(exec && exec.name, result.value, buildTranslateEnv())
1189
1375
  if (value !== result.value) patch = value
1190
1376
  }
1191
1377
  } catch { /* never block a result */ }
@@ -1324,4 +1510,4 @@ export async function apply(ctx, config = {}) {
1324
1510
  }
1325
1511
 
1326
1512
  // Test surface: pure helpers, no Cordis context required.
1327
- export const _internal = { PRESET_IDS, translateMsysPath, translatePathArguments, readPosixPaths, readAdoptSidebar, windowsToMsys, rewriteResultPaths, 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 }
1513
+ export const _internal = { PRESET_IDS, translateMsysPath, translatePathArguments, translateGlobArguments, buildTranslateEnv, readPosixPaths, readAdoptSidebar, windowsToMsys, rewriteResultPaths, 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 }