dsh-gitbash-shell 0.23.0 → 0.24.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/cordis.patch.yml +4 -1
- package/dsh.plugin.json +1 -1
- package/icon.svg +7 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +10 -3
- package/src/client.js +38 -3
- package/src/compositions.js +298 -0
- package/src/index.js +213 -10
package/cordis.patch.yml
CHANGED
|
@@ -34,7 +34,10 @@
|
|
|
34
34
|
timeoutMs: 60000
|
|
35
35
|
bashPath: "C:/Program Files/Git/bin/bash.exe"
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
# Row id 'gitbash-shell' (was 'gitbash-presets' through v0.23.0): dsh >= 0.1.7
|
|
38
|
+
# keys a plugin's live Config form and the one-shot legacy settings.yaml
|
|
39
|
+
# import by the row id, so it now matches the settings namespace string.
|
|
40
|
+
- id: gitbash-shell
|
|
38
41
|
name: dsh-gitbash-shell
|
|
39
42
|
|
|
40
43
|
# Edit the dsh-better-sidebar row when it is mounted (their own bundle
|
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.24.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/icon.svg
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" fill="none">
|
|
2
|
+
<rect x="4" y="6" width="28" height="24" rx="3.5" stroke="#4493f8" stroke-width="1.8"/>
|
|
3
|
+
<path d="M4 12h28" stroke="#4493f8" stroke-width="1.4"/>
|
|
4
|
+
<circle cx="7.6" cy="9" r="1" fill="#4493f8"/>
|
|
5
|
+
<circle cx="11" cy="9" r="1" fill="#4493f8"/>
|
|
6
|
+
<path d="M10 18.5l4 3.5-4 3.5M17 25.5h8" stroke="#f0883e" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/>
|
|
7
|
+
</svg>
|
package/locale/en.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "Git Bash Shell",
|
|
4
|
+
"description": "Swaps Windows' PowerShell shell for Git for Windows bash: a bash-sandbox-compatible executor, a unified MSYS path dialect across every tool (posixPaths and friends, live-editable), and the four * Git Bash agent presets declared straight onto the roster on dsh 0.1.7+."
|
|
5
|
+
}
|
|
6
|
+
}
|
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-gitbash-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.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": {
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"exports": {
|
|
12
12
|
".": "./src/index.js",
|
|
13
13
|
"./shell": "./src/shell.js",
|
|
14
|
-
"./client": "./src/client.js"
|
|
14
|
+
"./client": "./src/client.js",
|
|
15
|
+
"./locale/*.json": "./locale/*.json"
|
|
15
16
|
},
|
|
16
17
|
"engines": {
|
|
17
18
|
"node": ">=18",
|
|
@@ -38,7 +39,9 @@
|
|
|
38
39
|
"dsh.plugin.json",
|
|
39
40
|
"README.md",
|
|
40
41
|
"README_EN.md",
|
|
41
|
-
"LICENSE"
|
|
42
|
+
"LICENSE",
|
|
43
|
+
"locale",
|
|
44
|
+
"icon.svg"
|
|
42
45
|
],
|
|
43
46
|
"scripts": {
|
|
44
47
|
"test": "node --test tests/smoke.mjs",
|
|
@@ -63,5 +66,9 @@
|
|
|
63
66
|
"homepage": "https://github.com/KannaKuron/dsh-gitbash-shell",
|
|
64
67
|
"bugs": {
|
|
65
68
|
"url": "https://github.com/KannaKuron/dsh-gitbash-shell/issues"
|
|
69
|
+
},
|
|
70
|
+
"icon": "./icon.svg",
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@deepseek-ai/schemastery": "^3.18.1"
|
|
66
73
|
}
|
|
67
74
|
}
|
package/src/client.js
CHANGED
|
@@ -1058,11 +1058,46 @@ window.__ModuleLoader__.load({
|
|
|
1058
1058
|
|
|
1059
1059
|
exports.name = "dsh-gitbash-shell/client";
|
|
1060
1060
|
|
|
1061
|
-
/**
|
|
1062
|
-
|
|
1061
|
+
/**
|
|
1062
|
+
* Required client services: only era-guaranteed ones are hard-injected;
|
|
1063
|
+
* the settings face is acquired OPTIONALLY (dsh 0.1.7 removed the
|
|
1064
|
+
* settingsScope service and a hard inject would leave this fiber PENDING
|
|
1065
|
+
* forever, taking the card down with it).
|
|
1066
|
+
*/
|
|
1067
|
+
exports.inject = ["locale", "slots"];
|
|
1063
1068
|
|
|
1064
1069
|
exports.apply = function (ctx) {
|
|
1065
|
-
|
|
1070
|
+
// Era-split settings face (same contract both eras: getSnapshot/set/unset).
|
|
1071
|
+
var scope = null;
|
|
1072
|
+
|
|
1073
|
+
// OLD era (dsh <= 0.1.6): bound settings scope.
|
|
1074
|
+
try {
|
|
1075
|
+
ctx.inject(["settingsScope"], function (sctx) {
|
|
1076
|
+
try {
|
|
1077
|
+
var svc = sctx && sctx.settingsScope;
|
|
1078
|
+
if (svc && typeof svc.bind === "function") scope = svc.bind({ namespace: SETTINGS_NAMESPACE });
|
|
1079
|
+
} catch (error) {
|
|
1080
|
+
console.warn(TAG + " settingsScope acquisition failed:", error && error.message ? error.message : error);
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
} catch (error) {
|
|
1084
|
+
console.warn(TAG + " settingsScope wiring failed:", error && error.message ? error.message : error);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// NEW era (dsh >= 0.1.7): one ConfigForm per live profile entry; the form
|
|
1088
|
+
// key is the row id "gitbash-shell" (same string as the old namespace).
|
|
1089
|
+
try {
|
|
1090
|
+
ctx.inject(["configForms"], function (fctx) {
|
|
1091
|
+
try {
|
|
1092
|
+
var forms = fctx && fctx.configForms;
|
|
1093
|
+
if (forms && typeof forms.get === "function") scope = forms.get(SETTINGS_NAMESPACE);
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
console.warn(TAG + " configForms acquisition failed:", error && error.message ? error.message : error);
|
|
1096
|
+
}
|
|
1097
|
+
});
|
|
1098
|
+
} catch (error) {
|
|
1099
|
+
console.warn(TAG + " configForms wiring failed:", error && error.message ? error.message : error);
|
|
1100
|
+
}
|
|
1066
1101
|
/* The card's only copy entry point: a live lookup, never a captured
|
|
1067
1102
|
dictionary — the language preference switches without a reload. */
|
|
1068
1103
|
var t = translatorOf(ctx);
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-gitbash-shell — declarative composition rows (dsh >= 0.1.7).
|
|
3
|
+
*
|
|
4
|
+
* dsh 0.1.7 removed directory presets; a preset is a definition registered
|
|
5
|
+
* through agentPresets.register(). This module is the committed, reviewable
|
|
6
|
+
* composition DATA for the four Git Bash variants — the new-era counterpart
|
|
7
|
+
* of the committed assets/<variant>/agent.cordis*.yml texts the materializer
|
|
8
|
+
* writes on older hosts.
|
|
9
|
+
*
|
|
10
|
+
* Every variant mirrors the official 0.1.7 preset it replaces (packages/
|
|
11
|
+
* bundle/web-app/presets/{standard,minimal,ptc,cordis}.patch.yml) with the
|
|
12
|
+
* one Git Bash delta: the bash rows are always on and the pwsh rows are
|
|
13
|
+
* always off (the host executor is this plugin's shell.js — on non-Windows
|
|
14
|
+
* hosts that is the native stack anyway, which is exactly what the old
|
|
15
|
+
* materialized variants encoded).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** Default Git Bash binary — must match src/shell.js and the patch config. */
|
|
19
|
+
const DEFAULT_GIT_BASH = 'C:/Program Files/Git/bin/bash.exe'
|
|
20
|
+
|
|
21
|
+
function off(value) {
|
|
22
|
+
return value === true ? { disabled: true } : {}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const PLAN_SECTION = `You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
|
|
26
|
+
|
|
27
|
+
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
|
|
28
|
+
|
|
29
|
+
The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
|
|
30
|
+
|
|
31
|
+
Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
|
|
32
|
+
|
|
33
|
+
Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
|
|
34
|
+
|
|
35
|
+
When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The shared full-tool rows for standard / ptc / cordis variants (pure).
|
|
40
|
+
* @param {object} input
|
|
41
|
+
* @param {'standard'|'ptc'|'cordis'} input.kind - which official preset this
|
|
42
|
+
* variant mirrors (workflow rows follow it: enabled on standard/cordis,
|
|
43
|
+
* disabled on ptc).
|
|
44
|
+
* @param {boolean} input.gitBash - bash rows always on, pwsh rows always off.
|
|
45
|
+
* @param {string|undefined} input.skillsDir - Creation authoring skills
|
|
46
|
+
* directory (cordis variant only; resolved beside the agent-preset package).
|
|
47
|
+
* @returns {object[]} the declarative plugins list.
|
|
48
|
+
*/
|
|
49
|
+
export function pluginsFor({ kind, gitBash, skillsDir }) {
|
|
50
|
+
const win = typeof process !== 'undefined' && process.platform === 'win32'
|
|
51
|
+
const bashDisabled = gitBash ? false : win
|
|
52
|
+
const pwshDisabled = gitBash ? true : !win
|
|
53
|
+
const workflowOn = kind !== 'ptc'
|
|
54
|
+
const rows = [
|
|
55
|
+
{
|
|
56
|
+
id: 'persona',
|
|
57
|
+
name: '@deepseek-ai/dsh-persona',
|
|
58
|
+
config: {
|
|
59
|
+
prefix: 'You are a coding agent powered by the {{model}} model.',
|
|
60
|
+
suffix: 'Your working directory is {{cwd}}.',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'agent-instructions',
|
|
65
|
+
name: '@deepseek-ai/dsh-agent-instructions',
|
|
66
|
+
config: { maxBytes: 65536 },
|
|
67
|
+
},
|
|
68
|
+
{ id: 'tool-bash', name: '@deepseek-ai/dsh-tool-bash', ...off(bashDisabled) },
|
|
69
|
+
{ id: 'tool-pwsh', name: '@deepseek-ai/dsh-tool-pwsh', ...off(pwshDisabled) },
|
|
70
|
+
{ id: 'tool-fs', name: '@deepseek-ai/dsh-tool-fs' },
|
|
71
|
+
{
|
|
72
|
+
id: 'tool-fs-search',
|
|
73
|
+
name: '@deepseek-ai/dsh-tool-fs-search',
|
|
74
|
+
config: { sampleOverCapGlobResults: false },
|
|
75
|
+
},
|
|
76
|
+
{ id: 'tool-jobs', name: '@deepseek-ai/dsh-tool-jobs' },
|
|
77
|
+
...((kind === 'standard' || kind === 'ptc') ? [
|
|
78
|
+
{ id: 'skill-filesystem', name: '@deepseek-ai/dsh-skill-filesystem' },
|
|
79
|
+
{ id: 'tool-skill', name: '@deepseek-ai/dsh-tool-skill' },
|
|
80
|
+
] : []),
|
|
81
|
+
{ id: 'command-goal', name: '@deepseek-ai/dsh-command-goal' },
|
|
82
|
+
{ id: 'tool-goal', name: '@deepseek-ai/dsh-tool-goal' },
|
|
83
|
+
{
|
|
84
|
+
id: 'planning',
|
|
85
|
+
name: 'cordis:group',
|
|
86
|
+
group: true,
|
|
87
|
+
isolate: { planMode: true },
|
|
88
|
+
config: [
|
|
89
|
+
{
|
|
90
|
+
id: 'plan-mode',
|
|
91
|
+
name: '@deepseek-ai/dsh-plan-mode',
|
|
92
|
+
config: { section: PLAN_SECTION },
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'compaction',
|
|
98
|
+
name: 'cordis:group',
|
|
99
|
+
group: true,
|
|
100
|
+
isolate: { compaction: true, toolResultPruner: true },
|
|
101
|
+
config: [
|
|
102
|
+
{ id: 'compaction-basic', name: '@deepseek-ai/dsh-compaction-basic' },
|
|
103
|
+
{ id: 'command-compact', name: '@deepseek-ai/dsh-command-compact' },
|
|
104
|
+
{
|
|
105
|
+
id: 'tool-result-pruner',
|
|
106
|
+
name: '@deepseek-ai/dsh-compaction-tool-result-pruner',
|
|
107
|
+
config: { thresholdChars: 8192, headChars: 4096, tailChars: 1024 },
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'delegation',
|
|
113
|
+
name: 'cordis:group',
|
|
114
|
+
group: true,
|
|
115
|
+
isolate: { workflowEngine: true },
|
|
116
|
+
config: [
|
|
117
|
+
{ id: 'tool-subagent-control', name: '@deepseek-ai/dsh-tool-subagent-control' },
|
|
118
|
+
{ id: 'tool-subagent-list-agents', name: '@deepseek-ai/dsh-tool-subagent-control/list-agents' },
|
|
119
|
+
{
|
|
120
|
+
id: 'tool-subagent',
|
|
121
|
+
name: '@deepseek-ai/dsh-tool-subagent',
|
|
122
|
+
config: { provider: 'spawn', toolName: 'subagent', modelSelectionSettings: true, backgroundMode: 'continuable' },
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: 'tool-subagent-fork',
|
|
126
|
+
name: '@deepseek-ai/dsh-tool-subagent',
|
|
127
|
+
config: { provider: 'fork', toolName: 'subagent_fork', backgroundMode: 'continuable' },
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: 'tool-subagent-codex',
|
|
131
|
+
name: '@deepseek-ai/dsh-tool-subagent',
|
|
132
|
+
disabled: true,
|
|
133
|
+
config: { provider: 'codex', toolName: 'subagent_codex', backgroundMode: 'one-shot', maxDepth: 'provider-managed' },
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: 'tool-subagent-claude-code',
|
|
137
|
+
name: '@deepseek-ai/dsh-tool-subagent',
|
|
138
|
+
disabled: true,
|
|
139
|
+
config: { provider: 'claude-code', toolName: 'subagent_claude_code', backgroundMode: 'one-shot', maxDepth: 'provider-managed' },
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: 'workflow-ptc',
|
|
143
|
+
name: '@deepseek-ai/dsh-workflow-ptc',
|
|
144
|
+
...off(!workflowOn),
|
|
145
|
+
config: { provider: 'spawn' },
|
|
146
|
+
},
|
|
147
|
+
{ id: 'tool-workflow', name: '@deepseek-ai/dsh-tool-workflow', ...off(!workflowOn) },
|
|
148
|
+
{
|
|
149
|
+
id: 'tool-ralph',
|
|
150
|
+
name: '@deepseek-ai/dsh-tool-ralph',
|
|
151
|
+
disabled: true,
|
|
152
|
+
config: { subagentProvider: 'spawn', maxRounds: 64 },
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
{ id: 'tool-ask-user', name: '@deepseek-ai/dsh-tool-ask-user' },
|
|
157
|
+
{
|
|
158
|
+
id: 'tool-todo',
|
|
159
|
+
name: '@deepseek-ai/dsh-tool-todo',
|
|
160
|
+
config: { allowParallelInProgress: true },
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: 'tool-web',
|
|
164
|
+
name: '@deepseek-ai/dsh-tool-web',
|
|
165
|
+
config: { fetch: true, searchTimeoutMs: 60000 },
|
|
166
|
+
},
|
|
167
|
+
]
|
|
168
|
+
if (kind === 'ptc') {
|
|
169
|
+
rows.push({
|
|
170
|
+
id: 'tool-presentation',
|
|
171
|
+
name: '@deepseek-ai/dsh-agent-tool-presentation',
|
|
172
|
+
config: { mode: 'ptc' },
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
if (kind === 'cordis') {
|
|
176
|
+
rows.push({ id: 'tool-cordis', name: '@deepseek-ai/dsh-tool-cordis' })
|
|
177
|
+
}
|
|
178
|
+
if (kind === 'cordis') {
|
|
179
|
+
rows.push({ id: 'skill-filesystem', name: '@deepseek-ai/dsh-skill-filesystem', ...(skillsDir ? { config: { customSkillDirs: [skillsDir] } } : {}) })
|
|
180
|
+
rows.push({ id: 'tool-skill', name: '@deepseek-ai/dsh-tool-skill' })
|
|
181
|
+
}
|
|
182
|
+
rows.push({ id: 'present', name: '@deepseek-ai/dsh-tool-present' })
|
|
183
|
+
rows.push({
|
|
184
|
+
id: 'tool-plugin-manager',
|
|
185
|
+
name: '@deepseek-ai/dsh-plugin-manager/tools',
|
|
186
|
+
// The official 0.1.7 rows: ptc/standard ship it disabled, cordis keeps it
|
|
187
|
+
// on wherever a profile context exists (every web deployment does).
|
|
188
|
+
...off(kind !== 'cordis'),
|
|
189
|
+
})
|
|
190
|
+
return rows
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The minimal variant: the official single-tool preset with the Git Bash
|
|
195
|
+
* delta — bash rows always on (the terminal row pins the Git Bash binary on
|
|
196
|
+
* Windows), pwsh rows always off (pure).
|
|
197
|
+
* @returns {object[]} the declarative plugins list.
|
|
198
|
+
*/
|
|
199
|
+
export function minimalPluginsFor() {
|
|
200
|
+
const win = typeof process !== 'undefined' && process.platform === 'win32'
|
|
201
|
+
return [
|
|
202
|
+
{
|
|
203
|
+
id: 'persona',
|
|
204
|
+
name: '@deepseek-ai/dsh-persona',
|
|
205
|
+
config: {
|
|
206
|
+
prefix: 'You are a helpful software engineer assistant.',
|
|
207
|
+
complete: true,
|
|
208
|
+
includeRuntimeContext: false,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
id: 'persistent-shell',
|
|
213
|
+
name: 'cordis:group',
|
|
214
|
+
group: true,
|
|
215
|
+
isolate: { terminals: true },
|
|
216
|
+
config: [
|
|
217
|
+
{ id: 'pty', name: '@deepseek-ai/dsh-terminal' },
|
|
218
|
+
{
|
|
219
|
+
id: 'terminal-bash',
|
|
220
|
+
name: '@deepseek-ai/dsh-terminal-bash',
|
|
221
|
+
disabled: false,
|
|
222
|
+
config: {
|
|
223
|
+
timeoutMs: 300000,
|
|
224
|
+
shellPath: win ? DEFAULT_GIT_BASH : '/bin/bash',
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
id: 'persistent-bash',
|
|
229
|
+
name: '@deepseek-ai/dsh-tool-bash-persistent',
|
|
230
|
+
disabled: false,
|
|
231
|
+
config: {
|
|
232
|
+
timeoutMs: 300000,
|
|
233
|
+
description: [
|
|
234
|
+
'Run commands in a bash shell (Git for Windows bash on Windows: native paths like C:/... work, and PATH plus the rest of the environment are inherited from the host)',
|
|
235
|
+
'* When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped.',
|
|
236
|
+
'* Network access depends on the task environment. Prefer configured mirrors/proxies when they are available.',
|
|
237
|
+
'* State is persistent across command calls and discussions with the user.',
|
|
238
|
+
'* To inspect a particular line range of a file, e.g. lines 10-25, try \'sed -n 10,25p /path/to/the/file\'.',
|
|
239
|
+
'* Please avoid commands that may produce a very large amount of output.',
|
|
240
|
+
'* Please run long lived commands in the background, e.g. \'sleep 10 &\' or start a server in the background.',
|
|
241
|
+
].join('\n'),
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
id: 'terminal-pwsh',
|
|
246
|
+
name: '@deepseek-ai/dsh-terminal-bash',
|
|
247
|
+
disabled: true,
|
|
248
|
+
config: { shellDialect: 'pwsh', timeoutMs: 300000 },
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: 'persistent-pwsh',
|
|
252
|
+
name: '@deepseek-ai/dsh-tool-pwsh-persistent',
|
|
253
|
+
disabled: true,
|
|
254
|
+
config: {
|
|
255
|
+
timeoutMs: 300000,
|
|
256
|
+
description: [
|
|
257
|
+
"Run commands in a PowerShell shell",
|
|
258
|
+
"* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.",
|
|
259
|
+
"* You don't have access to the internet via this tool.",
|
|
260
|
+
"* State is persistent across command calls and discussions with the user.",
|
|
261
|
+
"* Use native Windows paths (C:\\...) and $env:NAME variables; this is PowerShell, not bash.",
|
|
262
|
+
"* Please avoid commands that may produce a very large amount of output.",
|
|
263
|
+
"* Please run long lived commands in the background, e.g. 'Start-Job' or start a server with Start-Process.",
|
|
264
|
+
].join('\n') + '\n',
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
},
|
|
269
|
+
]
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Display metadata for the four registrations (mirrors the committed preset.yml files). */
|
|
273
|
+
export const PRESET_META = {
|
|
274
|
+
'standard-gitbash': {
|
|
275
|
+
kind: 'standard',
|
|
276
|
+
name: '标准模式 · Git Bash',
|
|
277
|
+
description: '功能完整的编码 Agent,支持文件编辑、Shell、文件与网页检索、Skills、计划、目标、子代理和工作流。(Shell 使用 Git Bash)',
|
|
278
|
+
order: 1,
|
|
279
|
+
},
|
|
280
|
+
'code-gitbash': {
|
|
281
|
+
kind: 'ptc',
|
|
282
|
+
name: 'PTC 模式 · Git Bash',
|
|
283
|
+
description: '功能完整的编码 Agent,但默认不提供 workflow 工具;其他工具通过 PTC 模式 SDK 呈现,让模型用一个 TypeScript 程序组合多步操作。(Shell 使用 Git Bash)',
|
|
284
|
+
order: 2,
|
|
285
|
+
},
|
|
286
|
+
'minimal-gitbash': {
|
|
287
|
+
kind: 'minimal',
|
|
288
|
+
name: '极简模式 · Git Bash',
|
|
289
|
+
description: '仅提供持久 shell 的单工具编码 Agent。(Shell 使用 Git Bash)',
|
|
290
|
+
order: 3,
|
|
291
|
+
},
|
|
292
|
+
'cordis-gitbash': {
|
|
293
|
+
kind: 'cordis',
|
|
294
|
+
name: '创造模式 · Git Bash',
|
|
295
|
+
description: '用于创建自定义 Agent preset:具备标准模式的全部能力,并提供运行时检查、插件实验和 preset 创作指导。(Shell 使用 Git Bash)',
|
|
296
|
+
order: 4,
|
|
297
|
+
},
|
|
298
|
+
}
|
package/src/index.js
CHANGED
|
@@ -52,8 +52,11 @@ import {
|
|
|
52
52
|
} from 'node:fs'
|
|
53
53
|
import { homedir, tmpdir } from 'node:os'
|
|
54
54
|
import { dirname, join } from 'node:path'
|
|
55
|
+
import Schema from '@deepseek-ai/schemastery'
|
|
55
56
|
import { fileURLToPath } from 'node:url'
|
|
56
57
|
|
|
58
|
+
import { PRESET_META, minimalPluginsFor, pluginsFor } from './compositions.js'
|
|
59
|
+
|
|
57
60
|
/** Plugin identity for cordis.yml rows. */
|
|
58
61
|
export const name = 'dsh-gitbash-shell'
|
|
59
62
|
|
|
@@ -662,6 +665,79 @@ export function installRegisterShim(reg) {
|
|
|
662
665
|
|
|
663
666
|
const SETTINGS_NAMESPACE = 'gitbash-shell'
|
|
664
667
|
|
|
668
|
+
/**
|
|
669
|
+
* dsh >= 0.1.7 marks a Config field live-editable without remount; a
|
|
670
|
+
* 0.1.6-era schemastery predates the method and the guard keeps this module
|
|
671
|
+
* loadable there (values then behave as ordinary config, read through the
|
|
672
|
+
* registered settings namespace instead).
|
|
673
|
+
*/
|
|
674
|
+
function live(schema) {
|
|
675
|
+
return typeof schema.volatile === 'function' ? schema.volatile() : schema
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Row Config = the settings surface on dsh >= 0.1.7 (values persist under the
|
|
680
|
+
* row id; the patch row id is 'gitbash-shell', the same string as the old
|
|
681
|
+
* settings namespace, so the one-shot legacy settings.yaml import maps old
|
|
682
|
+
* user values onto the new home). Inert metadata on older hosts.
|
|
683
|
+
*/
|
|
684
|
+
export const Config = Schema.object({
|
|
685
|
+
// Row-level knob predating the Config surface (invariant 6): which preset
|
|
686
|
+
// variants to serve. Ordinary config — an edit remounts the plugin, which
|
|
687
|
+
// is the right weight for a list that changes the roster.
|
|
688
|
+
presets: Schema.array(Schema.string()).default(PRESET_IDS),
|
|
689
|
+
posixPaths: live(Schema.boolean().default(true)),
|
|
690
|
+
virtualMounts: live(Schema.boolean().default(true)),
|
|
691
|
+
globSplit: live(Schema.boolean().default(true)),
|
|
692
|
+
errorDialect: live(Schema.boolean().default(true)),
|
|
693
|
+
codePaths: live(Schema.boolean().default(true)),
|
|
694
|
+
gitAutocrlf: live(Schema.boolean().default(true)),
|
|
695
|
+
bashPath: live(Schema.string().default('')),
|
|
696
|
+
adoptSidebar: live(Schema.boolean().default(true)),
|
|
697
|
+
})
|
|
698
|
+
|
|
699
|
+
/** Read one Config value across eras: Volatile ref (>= 0.1.7) or plain value. */
|
|
700
|
+
export function valueOf(value) {
|
|
701
|
+
return value && typeof value.get === 'function' ? value.get() : value
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Era-aware live settings reader for everything wired inside apply(): the
|
|
706
|
+
* NEW era reads this plugin's own Config refs (no listener needed — every
|
|
707
|
+
* consumer here already re-reads per dispatch/assembly), the OLD era keeps
|
|
708
|
+
* reading the registered namespace through the settings service.
|
|
709
|
+
* @param {object} ctx - the plugin's mounting context.
|
|
710
|
+
* @param {object|undefined} config - apply()'s resolved Config.
|
|
711
|
+
* @returns {{ dialect: () => object, posix: () => boolean, adoptSidebar: () => boolean }}
|
|
712
|
+
*/
|
|
713
|
+
function makeLiveReader(ctx, config) {
|
|
714
|
+
let legacy = false
|
|
715
|
+
try {
|
|
716
|
+
const settings = ctx.get('settings')
|
|
717
|
+
legacy = !!(settings && typeof settings.register === 'function')
|
|
718
|
+
} catch { legacy = false }
|
|
719
|
+
const hasConfig = !!(config && typeof config === 'object')
|
|
720
|
+
const dialect = () => {
|
|
721
|
+
if (legacy || !hasConfig) return readDialectSettings(ctx)
|
|
722
|
+
const bash = valueOf(config.bashPath)
|
|
723
|
+
return {
|
|
724
|
+
posixPaths: valueOf(config.posixPaths) === true,
|
|
725
|
+
virtualMounts: valueOf(config.virtualMounts) === true,
|
|
726
|
+
globSplit: valueOf(config.globSplit) === true,
|
|
727
|
+
errorDialect: valueOf(config.errorDialect) === true,
|
|
728
|
+
codePaths: valueOf(config.codePaths) === true,
|
|
729
|
+
gitAutocrlf: valueOf(config.gitAutocrlf) === true,
|
|
730
|
+
bashPath: typeof bash === 'string' ? bash : '',
|
|
731
|
+
adoptSidebar: valueOf(config.adoptSidebar) !== false,
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return {
|
|
735
|
+
dialect,
|
|
736
|
+
posix: () => dialect().posixPaths === true,
|
|
737
|
+
adoptSidebar: () => dialect().adoptSidebar !== false,
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
665
741
|
/** The full directive text, injected only while posixPaths is on. */
|
|
666
742
|
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.'
|
|
667
743
|
|
|
@@ -1630,7 +1706,7 @@ function adoptSidebarShell(ctx, bashPath) {
|
|
|
1630
1706
|
|
|
1631
1707
|
const run = () => {
|
|
1632
1708
|
tried += 1
|
|
1633
|
-
const ok = reconcileSidebar(
|
|
1709
|
+
const ok = reconcileSidebar(liveSettings.adoptSidebar())
|
|
1634
1710
|
if (ok) {
|
|
1635
1711
|
// The namespace scope may register after the settings service; hook
|
|
1636
1712
|
// the live watch once it exists (idempotent via the effect disposer).
|
|
@@ -1722,6 +1798,105 @@ function purgeOrphans(userRoot, keep) {
|
|
|
1722
1798
|
}
|
|
1723
1799
|
}
|
|
1724
1800
|
|
|
1801
|
+
// ── declarative registration (dsh >= 0.1.7) ──────────────────────────────────
|
|
1802
|
+
//
|
|
1803
|
+
// dsh 0.1.7 removed directory presets: nothing reads ~/.dsh/.agent-presets
|
|
1804
|
+
// any more, and a preset is a definition registered through
|
|
1805
|
+
// agentPresets.register(). The era probe is the register method itself; the
|
|
1806
|
+
// composition data lives in src/compositions.js (the new-era counterpart of
|
|
1807
|
+
// the committed assets/<variant>/ texts).
|
|
1808
|
+
|
|
1809
|
+
/** Resolve the Creation-skills dir beside @deepseek-ai/dsh-agent-preset. */
|
|
1810
|
+
async function resolveSkillsDir() {
|
|
1811
|
+
try {
|
|
1812
|
+
const { createRequire } = await import('node:module')
|
|
1813
|
+
const here = createRequire(import.meta.url)
|
|
1814
|
+
const pkg = here.resolve('@deepseek-ai/dsh-agent-preset/package.json')
|
|
1815
|
+
return join(dirname(pkg), 'skills')
|
|
1816
|
+
} catch {
|
|
1817
|
+
return undefined
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
/** The legacy preset root, best effort (roster roots are gone on 0.1.7). */
|
|
1822
|
+
function legacyPresetRoot() {
|
|
1823
|
+
try {
|
|
1824
|
+
const env = process.env.DSH_HOME
|
|
1825
|
+
if (env && env.trim() !== '') return join(env, '.agent-presets')
|
|
1826
|
+
return join(homedir(), '.dsh', '.agent-presets')
|
|
1827
|
+
} catch {
|
|
1828
|
+
return undefined
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
/**
|
|
1833
|
+
* Remove the stale materialized trees a previous (pre-0.1.7-host) version
|
|
1834
|
+
* left behind, when they are still byte-identical to what we wrote (the
|
|
1835
|
+
* marker judges). User-modified and foreign trees stay untouched — the same
|
|
1836
|
+
* ownership rules the materializer itself follows.
|
|
1837
|
+
*/
|
|
1838
|
+
function cleanupLegacyTrees(presetIds) {
|
|
1839
|
+
const root = legacyPresetRoot()
|
|
1840
|
+
if (!root) return
|
|
1841
|
+
for (const presetId of presetIds) {
|
|
1842
|
+
const target = join(root, presetId)
|
|
1843
|
+
const state = classify(target)
|
|
1844
|
+
if (state === 'unmodified') {
|
|
1845
|
+
try {
|
|
1846
|
+
rmSync(target, { recursive: true, force: true })
|
|
1847
|
+
console.log(TAG + ' removed the stale materialized preset at ' + target + ' (directory presets are no longer read on this host; the declarative registration replaces it)')
|
|
1848
|
+
} catch (error) {
|
|
1849
|
+
console.log(TAG + ' stale preset cleanup failed: ' + (error && error.message ? error.message : error))
|
|
1850
|
+
}
|
|
1851
|
+
} else if (state === 'user-modified') {
|
|
1852
|
+
console.log(TAG + ' a user-modified preset tree remains at ' + target + ' — left untouched (this host ignores it; delete it manually if unwanted)')
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
/**
|
|
1858
|
+
* Register one variant definition; returns the unregister function. Mount
|
|
1859
|
+
* failures stay visible through the roster's broken diagnostic instead of
|
|
1860
|
+
* failing the boot.
|
|
1861
|
+
*/
|
|
1862
|
+
async function registerVariant(ctx, presetId, { gitBashActive, skillsDir }) {
|
|
1863
|
+
const meta = PRESET_META[presetId]
|
|
1864
|
+
if (!meta) return undefined
|
|
1865
|
+
const plugins = meta.kind === 'minimal'
|
|
1866
|
+
? minimalPluginsFor()
|
|
1867
|
+
: pluginsFor({ kind: meta.kind, gitBash: gitBashActive, skillsDir })
|
|
1868
|
+
return ctx.agentPresets.register({
|
|
1869
|
+
id: presetId,
|
|
1870
|
+
name: meta.name,
|
|
1871
|
+
description: meta.description,
|
|
1872
|
+
order: meta.order,
|
|
1873
|
+
plugins,
|
|
1874
|
+
})
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
/**
|
|
1878
|
+
* Declarative-era wiring: register every configured variant once at startup
|
|
1879
|
+
* and keep exactly one registration per variant for the plugin's lifetime.
|
|
1880
|
+
* The roster picks the entries up immediately; sessions already pinned to a
|
|
1881
|
+
* variant keep their revision until recomposed.
|
|
1882
|
+
*/
|
|
1883
|
+
async function runDeclarativeEra(ctx, presetIds) {
|
|
1884
|
+
cleanupLegacyTrees(presetIds)
|
|
1885
|
+
const gitBashActive = process.platform === 'win32'
|
|
1886
|
+
const skillsDir = await resolveSkillsDir()
|
|
1887
|
+
const unregisters = []
|
|
1888
|
+
for (const presetId of presetIds) {
|
|
1889
|
+
try {
|
|
1890
|
+
const unregister = await registerVariant(ctx, presetId, { gitBashActive, skillsDir })
|
|
1891
|
+
if (unregister) unregisters.push(unregister)
|
|
1892
|
+
} catch (error) {
|
|
1893
|
+
console.log(TAG + " preset '" + presetId + "' registration failed: " + (error && error.message ? error.message : error))
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
console.log(TAG + ' registered ' + unregisters.length + ' preset(s) declaratively (' + presetIds.join(', ') + ')')
|
|
1897
|
+
ctx.effect(() => () => { for (const off of unregisters) void off() }, 'dsh-gitbash-shell: declarative preset registrations')
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1725
1900
|
export async function apply(ctx, config = {}) {
|
|
1726
1901
|
const presetIds = Array.isArray(config.presets) && config.presets.length > 0 ? config.presets : PRESET_IDS
|
|
1727
1902
|
|
|
@@ -1757,12 +1932,28 @@ export async function apply(ctx, config = {}) {
|
|
|
1757
1932
|
const disposeGitBash = ctx.provide('gitBash', gitBashCapability)
|
|
1758
1933
|
ctx.effect(() => disposeGitBash, 'dsh-gitbash-shell: gitBash capability')
|
|
1759
1934
|
|
|
1760
|
-
//
|
|
1935
|
+
// Era-aware live settings: NEW hosts read this plugin's own Config refs
|
|
1936
|
+
// (volatile, re-read per dispatch/assembly), OLD hosts keep the registered
|
|
1937
|
+
// namespace. Every consumer below already re-reads on every use, so a
|
|
1938
|
+
// flipped knob lands on the next dispatch with zero wiring.
|
|
1939
|
+
const liveSettings = makeLiveReader(ctx, config)
|
|
1940
|
+
|
|
1941
|
+
// ── settings namespace: the posixPaths switch (default ON, v0.1.0) — OLD
|
|
1942
|
+
// hosts only; on dsh >= 0.1.7 the row Config above IS the surface.
|
|
1943
|
+
|
|
1761
1944
|
// Served on the host so the Plugins tab pairs it with the browser card
|
|
1762
1945
|
// (the tab dispatches Host-served namespaces ∩ registered cards). Dynamic
|
|
1763
1946
|
// imports keep the zero-dependency smoke path importable; the schema MUST
|
|
1764
1947
|
// be a callable schemastery object (dsh-settings calls schema(merged)).
|
|
1765
|
-
|
|
1948
|
+
const legacySettings = (() => {
|
|
1949
|
+
try {
|
|
1950
|
+
const settings = ctx.get('settings')
|
|
1951
|
+
return !!(settings && typeof settings.register === 'function')
|
|
1952
|
+
} catch {
|
|
1953
|
+
return false
|
|
1954
|
+
}
|
|
1955
|
+
})()
|
|
1956
|
+
if (legacySettings) try {
|
|
1766
1957
|
ctx.inject(['settings'], (sctx) => {
|
|
1767
1958
|
Promise.all([import('@deepseek-ai/dsh-settings'), import('@deepseek-ai/schemastery')])
|
|
1768
1959
|
.then(([ds, sm]) => {
|
|
@@ -1832,7 +2023,7 @@ export async function apply(ctx, config = {}) {
|
|
|
1832
2023
|
// own env layer (src/shell.js), not this registry.
|
|
1833
2024
|
variables: { [PATH_DIALECT_KEY]: { description: PATH_DIALECT_DESCRIPTION } },
|
|
1834
2025
|
resolve() {
|
|
1835
|
-
return
|
|
2026
|
+
return liveSettings.posix() ? { [PATH_DIALECT_KEY]: PATH_DIALECT_VALUE } : {}
|
|
1836
2027
|
},
|
|
1837
2028
|
})
|
|
1838
2029
|
envCtx.effect(() => unregister, 'dsh-gitbash-shell: shellEnv path-dialect fact')
|
|
@@ -1860,7 +2051,7 @@ export async function apply(ctx, config = {}) {
|
|
|
1860
2051
|
try {
|
|
1861
2052
|
ctx.on('system-prompt/assemble', (assembly, _assembleContext, next) => {
|
|
1862
2053
|
try {
|
|
1863
|
-
if (
|
|
2054
|
+
if (liveSettings.posix() && assembly && typeof assembly === 'object') {
|
|
1864
2055
|
for (const section of Array.isArray(assembly.sections) ? assembly.sections : []) {
|
|
1865
2056
|
if (section && typeof section.text === 'string') section.text = windowsToMsys(section.text)
|
|
1866
2057
|
}
|
|
@@ -1987,7 +2178,7 @@ export async function apply(ctx, config = {}) {
|
|
|
1987
2178
|
ctx.on('tools/post-execute', (exec, result, next) => {
|
|
1988
2179
|
let contentPatch
|
|
1989
2180
|
try {
|
|
1990
|
-
const dialect =
|
|
2181
|
+
const dialect = liveSettings.dialect()
|
|
1991
2182
|
if (dialect.posixPaths && dialect.errorDialect && exec && result && typeof result === 'object'
|
|
1992
2183
|
&& result.isError === true && Array.isArray(result.content)) {
|
|
1993
2184
|
const env = dialect.virtualMounts ? buildTranslateEnv(dialect.bashPath) : null
|
|
@@ -2036,10 +2227,9 @@ export async function apply(ctx, config = {}) {
|
|
|
2036
2227
|
order: 126,
|
|
2037
2228
|
text: () => {
|
|
2038
2229
|
try {
|
|
2039
|
-
const
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
return value.virtualMounts === false ? POSIX_DIRECTIVE_TEXT_STRICT : POSIX_DIRECTIVE_TEXT
|
|
2230
|
+
const dialect = liveSettings.dialect()
|
|
2231
|
+
if (dialect.posixPaths !== true) return ''
|
|
2232
|
+
return dialect.virtualMounts === false ? POSIX_DIRECTIVE_TEXT_STRICT : POSIX_DIRECTIVE_TEXT
|
|
2043
2233
|
} catch {
|
|
2044
2234
|
return ''
|
|
2045
2235
|
}
|
|
@@ -2063,6 +2253,19 @@ export async function apply(ctx, config = {}) {
|
|
|
2063
2253
|
adoptSidebarShell(ctx, gitBashCapability.bashPath)
|
|
2064
2254
|
}
|
|
2065
2255
|
|
|
2256
|
+
// ── era split: declarative registration on dsh >= 0.1.7 ──────────────────
|
|
2257
|
+
// The register method IS the era signal. On the new host the whole
|
|
2258
|
+
// materialization path below is dead code (nothing reads the directory any
|
|
2259
|
+
// more), so this branch serves the variants and returns.
|
|
2260
|
+
if (ctx.agentPresets && typeof ctx.agentPresets.register === 'function') {
|
|
2261
|
+
try {
|
|
2262
|
+
await runDeclarativeEra(ctx, presetIds)
|
|
2263
|
+
} catch (error) {
|
|
2264
|
+
console.log(TAG + ' declarative registration failed: ' + (error && error.message ? error.message : error))
|
|
2265
|
+
}
|
|
2266
|
+
return
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2066
2269
|
const roots = ctx.agentPresets?.roots ?? []
|
|
2067
2270
|
const userRoot = firstUserRoot(roots)
|
|
2068
2271
|
if (!userRoot) {
|