dsh-gitbash-shell 0.7.2 → 0.9.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/dsh.plugin.json +1 -1
- package/package.json +14 -3
- package/src/client.js +280 -0
- package/src/index.js +138 -10
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-gitbash-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "DSH plugin: run every agent shell command through Git for Windows bash on Windows instead of PowerShell. Replaces the pwsh executor with a Git Bash ctx.shell provider and materializes Git Bash variants of the standard/minimal/code/cordis agent presets into your user preset root at startup. On Windows it also adopts dsh-better-sidebar's terminal shell (UI tabs and the model-facing terminal_* tools) through the sidebar's official settings seam, and pairs with dsh-ptc-cordis-preset so the PTC Creation-mode preset materializes as its Git Bash variant. Ships both composition eras for dsh <= 0.1.1 (built-in preset id 'code') and dsh >= 0.1.2 (renamed id 'ptc') — the era is probed per boot and re-materialized automatically after a dsh upgrade, in either upgrade order; preset ids never change so pinned sessions stay valid.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"main": "src/index.js",
|
|
11
11
|
"exports": {
|
|
12
12
|
".": "./src/index.js",
|
|
13
|
-
"./shell": "./src/shell.js"
|
|
13
|
+
"./shell": "./src/shell.js",
|
|
14
|
+
"./client": "./src/client.js"
|
|
14
15
|
},
|
|
15
16
|
"engines": {
|
|
16
17
|
"node": ">=18"
|
|
@@ -18,6 +19,15 @@
|
|
|
18
19
|
"dsh": {
|
|
19
20
|
"bundle": {
|
|
20
21
|
"patch": "./cordis.patch.yml"
|
|
22
|
+
},
|
|
23
|
+
"client": {
|
|
24
|
+
"inject": [
|
|
25
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
26
|
+
"@deepseek-ai/dsh-client-locale",
|
|
27
|
+
"@deepseek-ai/dsh-client-ui-slots",
|
|
28
|
+
"@deepseek-ai/dsh-client-ui-settings"
|
|
29
|
+
],
|
|
30
|
+
"platform": "web"
|
|
21
31
|
}
|
|
22
32
|
},
|
|
23
33
|
"files": [
|
|
@@ -42,7 +52,8 @@
|
|
|
42
52
|
],
|
|
43
53
|
"license": "MIT",
|
|
44
54
|
"peerDependencies": {
|
|
45
|
-
"@deepseek-ai/cordis": "^4.0.1"
|
|
55
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
56
|
+
"@deepseek-ai/schemastery": "^3.18.1"
|
|
46
57
|
},
|
|
47
58
|
"publishConfig": {
|
|
48
59
|
"access": "public"
|
package/src/client.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-gitbash-shell — browser half (hand-written ModuleLoader bundle).
|
|
3
|
+
*
|
|
4
|
+
* ONE job: the Settings → Plugins card that gates the unified POSIX path
|
|
5
|
+
* dialect. The Plugins tab dispatches the intersection of Host-served
|
|
6
|
+
* namespaces and registered cards, so the card appears only when the host
|
|
7
|
+
* half (src/index.js) has registered the 'gitbash-shell' settings namespace.
|
|
8
|
+
*
|
|
9
|
+
* The card flips one boolean — posixPaths — through a bound settingsScope:
|
|
10
|
+
* on → the host injects the order-126 directive (every tool takes MSYS
|
|
11
|
+
* drive roots /c/...) and the tools/execute wrapper translates
|
|
12
|
+
* path-argument fields (/x/ → X:/) for the Node-backed file tools;
|
|
13
|
+
* off → directive text is empty (dropped at assembly, zero prompt noise)
|
|
14
|
+
* and the wrapper passes calls through untouched (dsh-native).
|
|
15
|
+
* Bash itself is always Git Bash while the plugin is installed; the switch
|
|
16
|
+
* only governs the cross-tool path dialect.
|
|
17
|
+
*
|
|
18
|
+
* Hand-written bundle rules (no build step in this repo):
|
|
19
|
+
* - ONE window.__ModuleLoader__.load({...}) call, id = package name;
|
|
20
|
+
* - require restricted to the client-module BASELINE whitelist
|
|
21
|
+
* (react and @deepseek-ai/dsh-client-ui-primitives only, smoke-enforced);
|
|
22
|
+
* - plain React.createElement, no JSX/TS; components at module level;
|
|
23
|
+
* - dsh.client.inject in package.json lists the packages that must load
|
|
24
|
+
* first so locale / settingsScope / slots exist when this applies.
|
|
25
|
+
*/
|
|
26
|
+
window.__ModuleLoader__.load({
|
|
27
|
+
id: "dsh-gitbash-shell",
|
|
28
|
+
factory: (require) => {
|
|
29
|
+
var module = { exports: {} };
|
|
30
|
+
var exports = module.exports;
|
|
31
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
32
|
+
|
|
33
|
+
var React = require("react");
|
|
34
|
+
var ui = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
35
|
+
|
|
36
|
+
var E = React.createElement;
|
|
37
|
+
var useState = React.useState;
|
|
38
|
+
|
|
39
|
+
var TAG = "[gitbash-shell/client]";
|
|
40
|
+
/** Dictionary namespace (the plugin's own t seat). */
|
|
41
|
+
var NS = "gitbashShell";
|
|
42
|
+
/** The settings namespace the HOST half serves. */
|
|
43
|
+
var SETTINGS_NAMESPACE = "gitbash-shell";
|
|
44
|
+
|
|
45
|
+
// ── dictionaries (zh/en MUST stay key-aligned; smoke-enforced) ─────────
|
|
46
|
+
|
|
47
|
+
var zh = {
|
|
48
|
+
"title": "Git Bash 路径方言",
|
|
49
|
+
"cardDesc": "所有工具统一 /c/ POSIX 路径形式(默认关闭)",
|
|
50
|
+
"state.label": "当前状态",
|
|
51
|
+
"state.on": "已启用",
|
|
52
|
+
"state.off": "已关闭",
|
|
53
|
+
"switch.on": "启用",
|
|
54
|
+
"switch.off": "停用",
|
|
55
|
+
"error": "写入失败",
|
|
56
|
+
"hint": "启用后:所有工具(bash 命令与 workdir、read/write/edit/read_image/glob/grep 的路径参数)统一使用 MSYS 盘根 POSIX 路径(/c/Users/...);文件工具的路径参数由宿主自动翻译,模型无感。停用后恢复 dsh 原生行为(文件工具用 Windows 路径)。bash 始终是 Git Bash,不受此开关影响。",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
var en = {
|
|
60
|
+
"title": "Git Bash path dialect",
|
|
61
|
+
"cardDesc": "One /c/ POSIX path style for every tool (off by default)",
|
|
62
|
+
"state.label": "Current state",
|
|
63
|
+
"state.on": "Enabled",
|
|
64
|
+
"state.off": "Disabled",
|
|
65
|
+
"switch.on": "Enable",
|
|
66
|
+
"switch.off": "Disable",
|
|
67
|
+
"error": "write failed",
|
|
68
|
+
"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.",
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// ── styles (gb- prefixed; tokens mirror PluginCard.module.css) ─────────
|
|
72
|
+
|
|
73
|
+
var STYLE_ID = "dsh-gitbash-shell-style";
|
|
74
|
+
|
|
75
|
+
var CSS = [
|
|
76
|
+
".gb-card{list-style:none;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-3);transition:border-color .16s,background .16s}",
|
|
77
|
+
".gb-card:hover{border-color:var(--dsw-alias-label-dimmed)}",
|
|
78
|
+
".gb-card.gb-open{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}",
|
|
79
|
+
".gb-header{width:100%;appearance:none;border:0;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer;display:flex;align-items:center;gap:12px;padding:14px 16px;border-radius:12px}",
|
|
80
|
+
".gb-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}",
|
|
81
|
+
".gb-headText{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}",
|
|
82
|
+
".gb-name{font-size:15px;font-weight:600;line-height:1.4;color:var(--dsw-alias-label-primary)}",
|
|
83
|
+
".gb-desc{font-size:13px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}",
|
|
84
|
+
".gb-chevron{flex:none;color:var(--dsw-alias-label-tertiary);transition:transform .16s}",
|
|
85
|
+
".gb-chevron.gb-chevronOpen{transform:rotate(180deg)}",
|
|
86
|
+
".gb-body{display:flex;flex-direction:column;gap:12px;padding:4px 16px 16px;max-width:640px}",
|
|
87
|
+
".gb-row{display:flex;align-items:baseline;gap:8px;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-secondary)}",
|
|
88
|
+
".gb-rowLabel{flex:none;color:var(--dsw-alias-label-tertiary)}",
|
|
89
|
+
".gb-rowValue{min-width:0;overflow-wrap:anywhere;color:var(--dsw-alias-label-primary)}",
|
|
90
|
+
".gb-seg{display:flex;gap:8px;flex-wrap:wrap}",
|
|
91
|
+
".gb-segBtn{appearance:none;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);font:inherit;font-size:13px;padding:6px 12px;cursor:pointer;transition:border-color .16s,color .16s}",
|
|
92
|
+
".gb-segBtn:hover{border-color:var(--dsw-alias-label-dimmed)}",
|
|
93
|
+
".gb-segBtn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}",
|
|
94
|
+
".gb-segBtn.gb-segActive{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-primary)}",
|
|
95
|
+
".gb-hint{margin:0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-tertiary)}",
|
|
96
|
+
".gb-error{margin:0;font-size:12px;color:var(--dsw-alias-status-danger, #e5484d)}",
|
|
97
|
+
].join("\n");
|
|
98
|
+
|
|
99
|
+
function ensureStyles() {
|
|
100
|
+
try {
|
|
101
|
+
if (typeof document === "undefined" || typeof document.getElementById !== "function") return function () {};
|
|
102
|
+
if (document.getElementById(STYLE_ID)) return function () {};
|
|
103
|
+
var style = document.createElement("style");
|
|
104
|
+
style.id = STYLE_ID;
|
|
105
|
+
style.textContent = CSS;
|
|
106
|
+
document.head.appendChild(style);
|
|
107
|
+
return function () {
|
|
108
|
+
try {
|
|
109
|
+
if (style.parentNode) style.parentNode.removeChild(style);
|
|
110
|
+
} catch (error) { /* best effort */ }
|
|
111
|
+
};
|
|
112
|
+
} catch (error) {
|
|
113
|
+
return function () {};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Defensive primitives lookup: an unknown icon name degrades to a text chevron. */
|
|
118
|
+
function icon(name) {
|
|
119
|
+
try {
|
|
120
|
+
var component = ui && ui[name];
|
|
121
|
+
return typeof component === "function" ? component : null;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── error boundary (the dsh-better-workspace QuietBoundary pattern) ──────
|
|
128
|
+
|
|
129
|
+
/** A render failure degrades THIS card, never the settings page. */
|
|
130
|
+
function QuietBoundary(props) {}
|
|
131
|
+
QuietBoundary.prototype = Object.create(React.Component.prototype);
|
|
132
|
+
QuietBoundary.prototype.constructor = QuietBoundary;
|
|
133
|
+
QuietBoundary.state = { failed: false };
|
|
134
|
+
QuietBoundary.getDerivedStateFromError = function () { return { failed: true }; };
|
|
135
|
+
QuietBoundary.prototype.componentDidCatch = function (error) {
|
|
136
|
+
console.warn(TAG + " settings card render failed:", error && error.message ? error.message : error);
|
|
137
|
+
};
|
|
138
|
+
QuietBoundary.prototype.render = function () {
|
|
139
|
+
if (this.state && this.state.failed) return null;
|
|
140
|
+
return this.props.children;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// ── settings card (module-level component) ───────────────────────────────
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The Settings → Plugins card. t arrives through the registration's
|
|
147
|
+
* locale field; scope arrives as a PLAIN prop from the inject factory.
|
|
148
|
+
* Snapshots are read per render; each write bumps a local tick so the
|
|
149
|
+
* card re-reads without an external-store hook adapter.
|
|
150
|
+
*/
|
|
151
|
+
function GitBashCard(props) {
|
|
152
|
+
var t = typeof props.t === "function" ? props.t : function (key) { return key; };
|
|
153
|
+
|
|
154
|
+
var openState = useState(false);
|
|
155
|
+
var open = openState[0];
|
|
156
|
+
var setOpen = openState[1];
|
|
157
|
+
|
|
158
|
+
var errorState = useState("");
|
|
159
|
+
var error = errorState[0];
|
|
160
|
+
var setError = errorState[1];
|
|
161
|
+
|
|
162
|
+
var tickState = useState(0);
|
|
163
|
+
var tick = tickState[0];
|
|
164
|
+
var bumpTick = tickState[1];
|
|
165
|
+
void tick;
|
|
166
|
+
|
|
167
|
+
var scope = props.scope;
|
|
168
|
+
|
|
169
|
+
var snap = { status: "unavailable" };
|
|
170
|
+
try {
|
|
171
|
+
if (scope && typeof scope.getSnapshot === "function") snap = scope.getSnapshot();
|
|
172
|
+
} catch (error_) { /* keep unavailable */ }
|
|
173
|
+
|
|
174
|
+
if (snap.status !== "ready") return null;
|
|
175
|
+
|
|
176
|
+
var value = snap.value || {};
|
|
177
|
+
var enabled = value.posixPaths === true;
|
|
178
|
+
|
|
179
|
+
function write(next) {
|
|
180
|
+
setError("");
|
|
181
|
+
scope.set("posixPaths", next)
|
|
182
|
+
.then(function () { bumpTick(function (n) { return n + 1; }); })
|
|
183
|
+
.catch(function (err) {
|
|
184
|
+
bumpTick(function (n) { return n + 1; });
|
|
185
|
+
setError(t("error") + ": " + (err && err.message ? err.message : String(err)));
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
var Chevron = icon("IconChevronDownOutline14");
|
|
190
|
+
|
|
191
|
+
return E("li", { className: "gb-card" + (open ? " gb-open" : "") },
|
|
192
|
+
E("button", {
|
|
193
|
+
type: "button",
|
|
194
|
+
className: "gb-header",
|
|
195
|
+
"aria-expanded": open,
|
|
196
|
+
onClick: function () { setOpen(!open); },
|
|
197
|
+
},
|
|
198
|
+
E("span", { className: "gb-headText" },
|
|
199
|
+
E("span", { className: "gb-name" }, t("title")),
|
|
200
|
+
E("span", { className: "gb-desc" }, t("cardDesc")),
|
|
201
|
+
),
|
|
202
|
+
Chevron
|
|
203
|
+
? E(Chevron, { className: "gb-chevron" + (open ? " gb-chevronOpen" : "") })
|
|
204
|
+
: E("span", { className: "gb-chevron" + (open ? " gb-chevronOpen" : "") }, "▾"),
|
|
205
|
+
),
|
|
206
|
+
open ? E("div", { className: "gb-body" },
|
|
207
|
+
E("div", { className: "gb-row" },
|
|
208
|
+
E("span", { className: "gb-rowLabel" }, t("state.label") + ":"),
|
|
209
|
+
E("span", { className: "gb-rowValue" }, enabled ? t("state.on") : t("state.off")),
|
|
210
|
+
),
|
|
211
|
+
E("div", { className: "gb-seg" },
|
|
212
|
+
E("button", {
|
|
213
|
+
type: "button",
|
|
214
|
+
className: "gb-segBtn" + (enabled ? " gb-segActive" : ""),
|
|
215
|
+
onClick: function () { if (!enabled) write(true); },
|
|
216
|
+
}, t("switch.on")),
|
|
217
|
+
E("button", {
|
|
218
|
+
type: "button",
|
|
219
|
+
className: "gb-segBtn" + (!enabled ? " gb-segActive" : ""),
|
|
220
|
+
onClick: function () { if (enabled) write(false); },
|
|
221
|
+
}, t("switch.off")),
|
|
222
|
+
),
|
|
223
|
+
error ? E("p", { className: "gb-error" }, error) : null,
|
|
224
|
+
E("p", { className: "gb-hint" }, t("hint")),
|
|
225
|
+
) : null,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ── plugin ────────────────────────────────────────────────────────────
|
|
230
|
+
|
|
231
|
+
exports.name = "dsh-gitbash-shell/client";
|
|
232
|
+
|
|
233
|
+
/** Required client services: locale runtime, settings scopes, slots. */
|
|
234
|
+
exports.inject = ["locale", "settingsScope", "slots"];
|
|
235
|
+
|
|
236
|
+
exports.apply = function (ctx) {
|
|
237
|
+
var scope = ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE });
|
|
238
|
+
|
|
239
|
+
ctx.effect(function () {
|
|
240
|
+
var disposers = [ensureStyles()];
|
|
241
|
+
try {
|
|
242
|
+
var disposeDict = ctx.locale.register(NS, { zh: zh, en: en });
|
|
243
|
+
if (typeof disposeDict === "function") disposers.push(disposeDict);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.warn(TAG + " dictionary registration failed:", error && error.message ? error.message : error);
|
|
246
|
+
}
|
|
247
|
+
return function () {
|
|
248
|
+
for (var i = 0; i < disposers.length; i++) {
|
|
249
|
+
try {
|
|
250
|
+
if (typeof disposers[i] === "function") disposers[i]();
|
|
251
|
+
} catch (error) { /* best effort */ }
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}, "dsh-gitbash-shell: styles, dictionaries");
|
|
255
|
+
|
|
256
|
+
// Guarded registration (the dsh-better-workspace pattern): a thrown
|
|
257
|
+
// register degrades this one seat, never the plugin fiber.
|
|
258
|
+
try {
|
|
259
|
+
var slots = ctx.slots;
|
|
260
|
+
if (!slots || typeof slots.inject !== "function") {
|
|
261
|
+
console.warn(TAG + " slots service unavailable; settings card idle");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
slots.inject("settings.plugin.item",
|
|
265
|
+
{
|
|
266
|
+
name: "settings.plugin.item",
|
|
267
|
+
key: SETTINGS_NAMESPACE,
|
|
268
|
+
locale: NS,
|
|
269
|
+
inject: function () { return { scope: scope }; },
|
|
270
|
+
},
|
|
271
|
+
function (props) {
|
|
272
|
+
return E(QuietBoundary, null, E(GitBashCard, props));
|
|
273
|
+
},
|
|
274
|
+
);
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.warn(TAG + " settings card registration failed:", error && error.message ? error.message : error);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
},
|
|
280
|
+
});
|
package/src/index.js
CHANGED
|
@@ -290,6 +290,75 @@ export function installRegisterShim(reg) {
|
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
// ── unified path dialect: settings namespace + gate (v0.9.0) ───────────────
|
|
294
|
+
//
|
|
295
|
+
// The dialect (directive + translation wrapper) is OFF by default and gated
|
|
296
|
+
// by the posixPaths boolean in the 'gitbash-shell' settings namespace,
|
|
297
|
+
// flipped from the Settings → Plugins card (src/client.js). The directive's
|
|
298
|
+
// text closure returns '' while off — empty context contributions are
|
|
299
|
+
// dropped at assembly, so a disabled dialect adds zero prompt noise; the
|
|
300
|
+
// wrapper reads the same value per dispatch and passes calls through.
|
|
301
|
+
|
|
302
|
+
const SETTINGS_NAMESPACE = 'gitbash-shell'
|
|
303
|
+
|
|
304
|
+
/** The full directive text, injected only while posixPaths is on. */
|
|
305
|
+
const POSIX_DIRECTIVE_TEXT = 'The host is Windows and the working shell is Git for Windows bash: use POSIX-style paths with MSYS drive roots everywhere — /c/Users/..., /e/project/... Every tool accepts this form: bash commands and the workdir parameter, and the file tools (read, write, edit, read_image, glob, grep) file_path/path arguments — MSYS roots are translated for them automatically, so never switch to a Windows form on their behalf. Never emit Windows drive-letter paths (C:\Users or C:/Users): when instructions, facts, or context show one, rewrite it to the POSIX form before use; when a tool result prints a Windows path, use its POSIX form (/c/...) in later calls.'
|
|
306
|
+
|
|
307
|
+
/** Read the posixPaths switch from the live settings service; never throws. */
|
|
308
|
+
export function readPosixPaths(ctxLike) {
|
|
309
|
+
try {
|
|
310
|
+
const settings = ctxLike && typeof ctxLike.get === 'function' ? ctxLike.get('settings') : undefined
|
|
311
|
+
const value = settings && typeof settings.get === 'function' ? settings.get(SETTINGS_NAMESPACE) : undefined
|
|
312
|
+
return !!(value && value.posixPaths === true)
|
|
313
|
+
} catch {
|
|
314
|
+
return false
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ── MSYS drive-root path translation (Windows, EVERY tool dispatch) ────────
|
|
319
|
+
//
|
|
320
|
+
// The directive below tells the model to use POSIX-style MSYS paths (/c/...)
|
|
321
|
+
// for EVERY tool. Bash digests those natively, but the Node-backed file tools
|
|
322
|
+
// resolve an MSYS root against the current drive (/c/Users -> C:/c/Users), so a
|
|
323
|
+
// global tools/execute wrapper rewrites the path-bearing ARGUMENT FIELDS (by
|
|
324
|
+
// field NAME, not by tool name — dynamic tools using the standard field names
|
|
325
|
+
// are covered too) into the drive-letter form the host accepts. The bash
|
|
326
|
+
// command field is deliberately untouched: MSYS roots are Git Bash native
|
|
327
|
+
// there. Defensive by design: the waterfall hands the same mutable exec object
|
|
328
|
+
// to the tool body, but if a future registry freezes it the write throws, is
|
|
329
|
+
// swallowed, and the call proceeds with the model original arguments (the
|
|
330
|
+
// directive still stands).
|
|
331
|
+
|
|
332
|
+
const MSYS_DRIVE_ROOT = /^\/([a-z])\/(.*)$/i
|
|
333
|
+
const TRANSLATABLE_PATH_FIELDS = new Set(['file_path', 'path', 'workdir'])
|
|
334
|
+
|
|
335
|
+
/** '/c/Users/x' -> 'C:/Users/x'; any other shape returns the input unchanged. */
|
|
336
|
+
export function translateMsysPath(value) {
|
|
337
|
+
if (typeof value !== 'string') return value
|
|
338
|
+
const match = MSYS_DRIVE_ROOT.exec(value)
|
|
339
|
+
if (!match) return value
|
|
340
|
+
return match[1].toUpperCase() + ':/' + match[2]
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Rewrite MSYS drive roots in an arguments object path fields, in place.
|
|
345
|
+
* @returns {boolean} whether any field changed (false also when args is not
|
|
346
|
+
* an object — the caller treats that as nothing to do).
|
|
347
|
+
*/
|
|
348
|
+
export function translatePathArguments(args) {
|
|
349
|
+
if (!args || typeof args !== 'object') return false
|
|
350
|
+
let changed = false
|
|
351
|
+
for (const key of Object.keys(args)) {
|
|
352
|
+
if (!TRANSLATABLE_PATH_FIELDS.has(key)) continue
|
|
353
|
+
const value = args[key]
|
|
354
|
+
if (typeof value !== 'string') continue
|
|
355
|
+
const translated = translateMsysPath(value)
|
|
356
|
+
if (translated === value) continue
|
|
357
|
+
try { args[key] = translated; changed = true } catch { /* frozen: degrade */ }
|
|
358
|
+
}
|
|
359
|
+
return changed
|
|
360
|
+
}
|
|
361
|
+
|
|
293
362
|
// ── dsh-better-sidebar shell cooperation ───────────────────────────────────
|
|
294
363
|
//
|
|
295
364
|
// dsh-better-sidebar (v0.15.2+) resolves its terminal shell PER OPEN: a
|
|
@@ -458,14 +527,65 @@ export async function apply(ctx, config = {}) {
|
|
|
458
527
|
const disposeGitBash = ctx.provide('gitBash', gitBashCapability)
|
|
459
528
|
ctx.effect(() => disposeGitBash, 'dsh-gitbash-shell: gitBash capability')
|
|
460
529
|
|
|
461
|
-
// ──
|
|
462
|
-
//
|
|
463
|
-
//
|
|
464
|
-
//
|
|
465
|
-
//
|
|
530
|
+
// ── settings namespace: the posixPaths switch (default OFF) ──────────────
|
|
531
|
+
// Served on the host so the Plugins tab pairs it with the browser card
|
|
532
|
+
// (the tab dispatches Host-served namespaces ∩ registered cards). Dynamic
|
|
533
|
+
// imports keep the zero-dependency smoke path importable; the schema MUST
|
|
534
|
+
// be a callable schemastery object (dsh-settings calls schema(merged)).
|
|
535
|
+
try {
|
|
536
|
+
ctx.inject(['settings'], (sctx) => {
|
|
537
|
+
Promise.all([import('@deepseek-ai/dsh-settings'), import('@deepseek-ai/schemastery')])
|
|
538
|
+
.then(([ds, sm]) => {
|
|
539
|
+
const settings = sctx && sctx.settings
|
|
540
|
+
if (!settings || typeof settings.register !== 'function') return
|
|
541
|
+
const Schema = sm.default
|
|
542
|
+
// Era probe: newer dsh register() takes a plain string; the older
|
|
543
|
+
// one accepted the branded helper — one call satisfies both.
|
|
544
|
+
const ns = typeof ds.settingsNamespace === 'function'
|
|
545
|
+
? ds.settingsNamespace(SETTINGS_NAMESPACE)
|
|
546
|
+
: SETTINGS_NAMESPACE
|
|
547
|
+
settings.register(ns, Schema.object({
|
|
548
|
+
posixPaths: Schema.boolean().default(false),
|
|
549
|
+
}))
|
|
550
|
+
console.log(`${TAG} settings namespace registered: ${SETTINGS_NAMESPACE} (posixPaths default off)`)
|
|
551
|
+
})
|
|
552
|
+
.catch((error) => {
|
|
553
|
+
console.log(`${TAG} settings namespace registration FAILED: ${error && error.stack || String(error)}`)
|
|
554
|
+
})
|
|
555
|
+
})
|
|
556
|
+
} catch (error) {
|
|
557
|
+
console.log(`${TAG} settings inject wiring failed: ${error?.message ?? error}`)
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// ── MSYS path translation on every tool dispatch (Windows only) ─────────
|
|
561
|
+
// Covers every preset and mode: the wrapper sits on the global
|
|
562
|
+
// tools/execute waterfall, through which model-direct calls, PTC run_code
|
|
563
|
+
// sub-dispatches, and dynamic-tool calls all pass.
|
|
564
|
+
if (process.platform === 'win32') {
|
|
565
|
+
try {
|
|
566
|
+
ctx.on('tools/execute', (exec, next) => {
|
|
567
|
+
try {
|
|
568
|
+
if (exec && exec.arguments && typeof exec.arguments === 'object' && readPosixPaths(ctx)) {
|
|
569
|
+
translatePathArguments(exec.arguments)
|
|
570
|
+
}
|
|
571
|
+
} catch { /* never block a call on translation */ }
|
|
572
|
+
return next()
|
|
573
|
+
})
|
|
574
|
+
console.log(TAG + ' MSYS drive-root translation active on tool dispatch')
|
|
575
|
+
} catch (error) {
|
|
576
|
+
console.log(TAG + ' tools/execute wiring failed: ' + (error?.message ?? error))
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// ── Unified POSIX path directive (Windows Git Bash, EVERY session) ─────
|
|
581
|
+
// ONE path style for every tool: MSYS drive roots (/c/Users/...). Bash
|
|
582
|
+
// digests them natively; the file tools receive the drive-letter form
|
|
583
|
+
// through the translation wrapper above, so the model never has to switch
|
|
584
|
+
// dialects. The directive also pins the rewrite rules that neutralize the
|
|
585
|
+
// Windows-form facts the harness injects elsewhere (backslash cwd strings,
|
|
586
|
+
// tool results printing drive-letter paths). Same channel as dsh-agent-lang;
|
|
466
587
|
// order 126 sits after the official CONTEXT_ORDERS 110/115/120 and beside
|
|
467
|
-
// the 125 free slot
|
|
468
|
-
// mounts inject nothing.
|
|
588
|
+
// the 125 free slot. Non-Windows mounts inject nothing.
|
|
469
589
|
if (process.platform === 'win32') {
|
|
470
590
|
try {
|
|
471
591
|
ctx.inject(['systemPrompt'], (pctx) => {
|
|
@@ -473,9 +593,17 @@ export async function apply(ctx, config = {}) {
|
|
|
473
593
|
pctx.effect(() => pctx.systemPrompt.context({
|
|
474
594
|
name: 'gitbash-shell:posix-paths',
|
|
475
595
|
order: 126,
|
|
476
|
-
text:
|
|
596
|
+
text: () => {
|
|
597
|
+
try {
|
|
598
|
+
const settings = pctx.get('settings')
|
|
599
|
+
const value = settings && typeof settings.get === 'function' ? settings.get(SETTINGS_NAMESPACE) : undefined
|
|
600
|
+
return value && value.posixPaths === true ? POSIX_DIRECTIVE_TEXT : ''
|
|
601
|
+
} catch {
|
|
602
|
+
return ''
|
|
603
|
+
}
|
|
604
|
+
},
|
|
477
605
|
}), 'dsh-gitbash-shell: posix-path context')
|
|
478
|
-
console.log(TAG + ' POSIX-path directive context active (win32)')
|
|
606
|
+
console.log(TAG + ' unified POSIX-path directive context active (win32, gated by the posixPaths setting)')
|
|
479
607
|
} catch (error) {
|
|
480
608
|
console.log(TAG + ' context registration failed: ' + (error?.message ?? error))
|
|
481
609
|
}
|
|
@@ -553,4 +681,4 @@ export async function apply(ctx, config = {}) {
|
|
|
553
681
|
}
|
|
554
682
|
|
|
555
683
|
// Test surface: pure helpers, no Cordis context required.
|
|
556
|
-
export const _internal = { PRESET_IDS, MARKER_FILE, classify, materialize, cleanupOnDispose, firstUserRoot, hashTree, skillsHashes, syncDecision, installRegisterShim, baseForRoster, detectBase, pickComposition }
|
|
684
|
+
export const _internal = { PRESET_IDS, translateMsysPath, translatePathArguments, readPosixPaths, MARKER_FILE, classify, materialize, cleanupOnDispose, firstUserRoot, hashTree, skillsHashes, syncDecision, installRegisterShim, baseForRoster, detectBase, pickComposition }
|