dsh-gitbash-shell 0.11.1 → 0.13.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.
@@ -0,0 +1,282 @@
1
+ # ERA (persona-split): dsh >= 0.1.3-alpha.2 text — the persona row uses the
2
+ # `prefix:`/`suffix:` keys (40792330c0 split dsh-persona single text key).
3
+ # The pre-split texts live in the sibling files named in the banner below; the
4
+ # plugin probes the roster AND the persona form at materialization and writes
5
+ # the picked text as the preset own agent.cordis.yml.
6
+ # ERA: dsh >= 0.1.2 text (built-in preset id `ptc`, presented with mode: ptc).
7
+ # The dsh <= 0.1.1 era text (built-in id `code`) lives in agent.cordis.yml; the
8
+ # plugin probes the roster at materialization and writes the picked text as the
9
+ # preset's own agent.cordis.yml.
10
+ # The `ptc` agent preset: the standard coding agent, presented as PTC mode.
11
+ #
12
+ # Most of `standard` is here unchanged. The deliberate exception is the
13
+ # general-purpose `workflow` tool: PTC mode uses `run_code` as its model-authored
14
+ # composition surface. The `tool-presentation` row turns the remaining registry
15
+ # into a generated SDK, so a sequence that would be five round trips becomes one.
16
+ #
17
+ # The registry itself stays on the host plane — the agent loop's scheduler and
18
+ # the API proxy's presenters are its consumers — so what this preset owns is
19
+ # the PRESENTATION of that registry for this agent alone. Native sessions run
20
+ # beside this one in the same process, each seeing its own catalog.
21
+ #
22
+ # This file is an AGENT-PLANE composition. It is mounted under one agent's
23
+ # scope context, so every tool and prompt section it registers belongs to that
24
+ # session alone. The host composition (`base.cordis.yml` + `web.cordis.yml`)
25
+ # keeps everything a preset must not own: the registries themselves, the
26
+ # sandbox and approval stack, persistence, and the model route.
27
+ #
28
+ # A service row here MUST sit inside a group carrying an `isolate` realm.
29
+ # Without one it publishes into the root realm, where it is process-global
30
+ # rather than per-session and the second session mounting this preset collides
31
+ # with the first; `dsh-agent-presets` rejects that at mount. `true` means an
32
+ # entry-local realm — one private instance per mounted session, which is the
33
+ # default this deployment wants. A shared label would instead pool one instance
34
+ # across every session naming it.
35
+
36
+ # ── identity ────────────────────────────────────────────────────────────────
37
+
38
+ # The preset's own persona, shadowing the deployment default for this agent.
39
+ # `{{model}}` and `{{cwd}}` resolve from the agent's own route and workspace.
40
+ - id: persona
41
+ name: '@deepseek-ai/dsh-persona'
42
+ config:
43
+ suffix: Your working directory is {{cwd}}.
44
+ prefix: >-
45
+ You are a coding agent powered by the {{model}} model.
46
+
47
+ - id: agent-instructions
48
+ name: '@deepseek-ai/dsh-agent-instructions'
49
+ config:
50
+ maxBytes: 65536
51
+
52
+ # ── shell ───────────────────────────────────────────────────────────────────
53
+
54
+ # `shell-env` stays in the HOST composition: `apps/cli/src/web.ts` injects it to
55
+ # publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
56
+ # the criterion for host-plane ownership — injection resolves before any session
57
+ # exists, so there is no agent to key by. Behind a preset realm those variables
58
+ # never reached the model's shell at all. Both shell tools consume the host
59
+ # registry from here; the host executor is the Git Bash provider (`dsh-gitbash-shell`)
60
+ # host-plane too.
61
+ - id: tool-bash
62
+ name: '@deepseek-ai/dsh-tool-bash'
63
+ # Git Bash on Windows: the host executor is dsh-gitbash-shell (see $DSH_HOME/cordis.patch.yml).
64
+ disabled: false
65
+
66
+ - id: tool-pwsh
67
+ name: '@deepseek-ai/dsh-tool-pwsh'
68
+ disabled: true
69
+
70
+ # ── filesystem ──────────────────────────────────────────────────────────────
71
+
72
+ # Both register into the host `tools` registry and provide nothing, so
73
+ # they need no realm. The `fs` service and its policy stay in the host.
74
+ - id: tool-fs
75
+ name: '@deepseek-ai/dsh-tool-fs'
76
+
77
+ - id: tool-fs-search
78
+ name: '@deepseek-ai/dsh-tool-fs-search'
79
+ config:
80
+ sampleOverCapGlobResults: false
81
+
82
+ # ── background jobs ────────────────────────────────────────────────────────
83
+
84
+ # Only the model-facing controls. The task REGISTRY stays on the host plane:
85
+ # its producers sit outside any realm this file could put it in — `tool-bash`
86
+ # above resolves it with `ctx.get`, and an entry-local realm here is invisible
87
+ # to every sibling row, so `run_in_background` would answer "background jobs
88
+ # unavailable" while these controls sat in the catalog. The registry is keyed by
89
+ # owning agent anyway, so one host instance serves every session. What a preset
90
+ # chooses is whether its agent can collect and stop background work at all.
91
+ - id: tool-jobs
92
+ name: '@deepseek-ai/dsh-tool-jobs'
93
+
94
+ # ── skills ──────────────────────────────────────────────────────────────────
95
+
96
+ # The skill REGISTRY lives in the host composition and is layered per scope:
97
+ # these rows register into THIS preset's layer of it, so they need no realm.
98
+ # `skill-filesystem` contributes local-root discovery for agents on this preset, and
99
+ # `tool-skill` gives them the catalog and loader; the merged catalog also
100
+ # carries whatever the deployment registered globally (repository plugins).
101
+ - id: skill-filesystem
102
+ name: '@deepseek-ai/dsh-skill-filesystem'
103
+
104
+ - id: tool-skill
105
+ name: '@deepseek-ai/dsh-tool-skill'
106
+
107
+ # ── goals ───────────────────────────────────────────────────────────────────
108
+
109
+ # The goal service and session driver stay on the host plane, where the Gateway
110
+ # can resolve them. The human command and model-facing tool register into this
111
+ # preset's scoped layers.
112
+ - id: command-goal
113
+ name: '@deepseek-ai/dsh-command-goal'
114
+
115
+ - id: tool-goal
116
+ name: '@deepseek-ai/dsh-tool-goal'
117
+
118
+ # ── plan mode ───────────────────────────────────────────────────────────────
119
+
120
+ # Plan state is per-agent by nature, so an entry-local realm is not a
121
+ # workaround here — it is the correct lifetime.
122
+ - id: planning
123
+ name: cordis:group
124
+ group: true
125
+ isolate:
126
+ planMode: true
127
+ config:
128
+ - id: plan-mode
129
+ name: '@deepseek-ai/dsh-plan-mode'
130
+ config:
131
+ section: |
132
+ 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.
133
+
134
+ 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.
135
+
136
+ 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.
137
+
138
+ 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.
139
+
140
+ 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.
141
+
142
+ 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.
143
+
144
+ # ── compaction ──────────────────────────────────────────────────────────────
145
+
146
+ # `compaction-basic` reads `toolResultPrune` through `ctx.get`, so the pruner must
147
+ # share this realm rather than sit outside it.
148
+ #
149
+ # `tokenMeter` is deliberately NOT in this realm: the meter stays on the HOST
150
+ # plane, and the rows here resolve that one instance. It takes no configuration,
151
+ # keys every fold by Session, and owns the context-meter projection units the
152
+ # browser reads for every session — behind a realm those units would come and go
153
+ # with whichever presets happen to be mounted. What a preset chooses is whether
154
+ # its agent compacts at all, which is `compaction-basic` below.
155
+ - id: compaction
156
+ name: cordis:group
157
+ group: true
158
+ isolate:
159
+ compaction: true
160
+ toolResultPruner: true
161
+ config:
162
+ - id: compaction-basic
163
+ name: '@deepseek-ai/dsh-compaction-basic'
164
+
165
+ - id: command-compact
166
+ name: '@deepseek-ai/dsh-command-compact'
167
+
168
+ - id: tool-result-pruner
169
+ name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
170
+ config:
171
+ thresholdChars: 8192
172
+ headChars: 4096
173
+ tailChars: 1024
174
+
175
+ # ── delegation and workflows ────────────────────────────────────────────────
176
+
177
+ # The `subagents` registry and its spawn/fork backends live in the HOST
178
+ # composition: the registry is a process singleton whose cross-session queries
179
+ # the api-proxy serves to the browser, and a provider name may only be
180
+ # registered once. This preset contributes the delegation TOOLS, which resolve
181
+ # that host registry.
182
+ #
183
+ # `workflows` is different — nothing outside an agent reads it — so every row
184
+ # that reaches it shares one entry-local realm here, and a consumer left
185
+ # outside would resolve a host registry this preset does not populate.
186
+ - id: delegation
187
+ name: cordis:group
188
+ group: true
189
+ isolate:
190
+ workflowEngine: true
191
+ config:
192
+ - id: tool-subagent-control
193
+ name: '@deepseek-ai/dsh-tool-subagent-control'
194
+
195
+ - id: tool-subagent-list-agents
196
+ name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
197
+
198
+ - id: tool-subagent
199
+ name: '@deepseek-ai/dsh-tool-subagent'
200
+ config:
201
+ provider: spawn
202
+ toolName: subagent
203
+ modelSelectionSettings: true
204
+ backgroundMode: continuable
205
+
206
+ # Fork omits model selection so provider/model stay equal to the parent and
207
+ # the inherited history remains eligible for KV Cache reuse. This preset
208
+ # keeps fork continuable; parent and child inherit the same messaging tool,
209
+ # while the parent id and return guidance follow the inherited history.
210
+ - id: tool-subagent-fork
211
+ name: '@deepseek-ai/dsh-tool-subagent'
212
+ config:
213
+ provider: fork
214
+ toolName: subagent_fork
215
+ backgroundMode: continuable
216
+
217
+ # Production dsh does not install these optional providers. Install the
218
+ # matching Bundle in this Profile and restart the Host, then copy this
219
+ # preset and remove `disabled` from the matching tool row. Host availability
220
+ # alone grants no tool.
221
+ - id: tool-subagent-codex
222
+ name: '@deepseek-ai/dsh-tool-subagent'
223
+ disabled: true
224
+ config:
225
+ provider: codex
226
+ toolName: subagent_codex
227
+ backgroundMode: one-shot
228
+ maxDepth: provider-managed
229
+
230
+ - id: tool-subagent-claude-code
231
+ name: '@deepseek-ai/dsh-tool-subagent'
232
+ disabled: true
233
+ config:
234
+ provider: claude-code
235
+ toolName: subagent_claude_code
236
+ backgroundMode: one-shot
237
+ maxDepth: provider-managed
238
+
239
+ - id: workflow-worker-thread
240
+ name: '@deepseek-ai/dsh-workflow-worker-thread'
241
+ config:
242
+ provider: spawn
243
+
244
+ - id: tool-workflow
245
+ name: '@deepseek-ai/dsh-tool-workflow'
246
+ # Keep the engine above for `ralph`, but do not publish a second
247
+ # model-authored orchestration surface beside `run_code` in PTC mode.
248
+ disabled: true
249
+
250
+ - id: tool-ralph
251
+ name: '@deepseek-ai/dsh-tool-ralph'
252
+ config:
253
+ subagentProvider: spawn
254
+ maxRounds: 64
255
+
256
+ # ── remaining model-facing rows ─────────────────────────────────────────────
257
+
258
+ - id: tool-ask-user
259
+ name: '@deepseek-ai/dsh-tool-ask-user'
260
+
261
+ - id: tool-todo
262
+ name: '@deepseek-ai/dsh-tool-todo'
263
+ config:
264
+ allowParallelInProgress: true
265
+
266
+ # The `web` service and its search provider stay in the host composition; only
267
+ # the model-facing tool is per-session.
268
+ - id: tool-web
269
+ name: '@deepseek-ai/dsh-tool-web'
270
+ config:
271
+ fetch: true
272
+ searchTimeoutMs: 60000
273
+
274
+ # ── presentation ────────────────────────────────────────────────────────────
275
+
276
+ # PTC mode for this agent alone. The row waits for the host's `codeRuntime`
277
+ # rather than assuming it: a deployment that composes no TypeScript runtime
278
+ # fails this preset at mount, naming this id, instead of at the first request.
279
+ - id: tool-presentation
280
+ name: '@deepseek-ai/dsh-agent-tool-presentation'
281
+ config:
282
+ mode: ptc
@@ -0,0 +1,273 @@
1
+ # ERA (persona-split): dsh >= 0.1.3-alpha.2 text — the persona row uses the
2
+ # `prefix:`/`suffix:` keys (40792330c0 split dsh-persona single text key).
3
+ # The pre-split texts live in the sibling files named in the banner below; the
4
+ # plugin probes the roster AND the persona form at materialization and writes
5
+ # the picked text as the preset own agent.cordis.yml.
6
+ # ERA: dsh >= 0.1.2 text (renamed built-in rows, incl. the new `command-goal`).
7
+ # The dsh <= 0.1.1 era text lives in agent.cordis.yml; the plugin probes the
8
+ # roster at materialization and writes the picked text as the preset's own
9
+ # agent.cordis.yml.
10
+ # The `cordis` agent preset: the standard coding agent, plus the ability to
11
+ # read and write the runtime it is running in.
12
+ #
13
+ # It exists so a person can ask an agent to author another agent. Everything in
14
+ # `standard` is here unchanged; what is added is the self-referential Cordis
15
+ # toolset, a skill that teaches composition authoring, and a persona that says
16
+ # which of the two planes an edit belongs to.
17
+ #
18
+ # TRUST: `cordis_mount` evaluates model-written JavaScript against the live
19
+ # runtime, and a composition this agent writes becomes a preset other sessions
20
+ # mount. Treat a session on this preset as shell access — the toolset's own
21
+ # documentation makes the same statement.
22
+
23
+
24
+ # The preset's own persona, shadowing the deployment default for this agent.
25
+ # `{{model}}` and `{{cwd}}` resolve from the agent's own route and workspace.
26
+ - id: persona
27
+ name: '@deepseek-ai/dsh-persona'
28
+ config:
29
+ suffix: Your working directory is {{cwd}}.
30
+ prefix: |-
31
+ You are a coding agent powered by the {{model}} model, running on the DeepSeek Harness.
32
+
33
+ You can read and modify the harness you run on. Its composition is Cordis: every capability is a plugin row in a `cordis.yml`, and an agent preset is one such file mounted for a single session.
34
+
35
+ Two planes decide where an edit belongs. The HOST composition holds the registries and anything shared across sessions — persistence, the sandbox and approval stack, the model route, the subagent registry and its backends. An AGENT PRESET holds what one session contributes to those registries: its tools, its persona, its prompt sections. A row that publishes a service belongs in the host composition, or inside an `isolate` realm if the preset genuinely owns that service and nothing outside one agent reads it.
36
+
37
+ Presets you author live one directory per preset under `${DSH_HOME:-$HOME/.dsh}/.agent-presets/<id>/`; the roster reports each preset's real path, so take the one you edit from there. NEVER edit or delete the shipped preset install (the `agent-presets` directory beside the deployment's own config): it belongs to the deployment, an upgrade overwrites it, and corrupting the `cordis` preset would disable this very mode. To change what a shipped preset does, copy its composition into a new preset directory and edit the copy.
38
+
39
+ Load the `editing-cordis-compositions` skill before writing or changing a composition.
40
+
41
+ - id: agent-instructions
42
+ name: '@deepseek-ai/dsh-agent-instructions'
43
+ config:
44
+ maxBytes: 65536
45
+
46
+ # ── shell ───────────────────────────────────────────────────────────────────
47
+
48
+ # `shell-env` stays in the HOST composition: `apps/cli/src/web.ts` injects it to
49
+ # publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
50
+ # the criterion for host-plane ownership — injection resolves before any session
51
+ # exists, so there is no agent to key by. Behind a preset realm those variables
52
+ # never reached the model's shell at all. Both shell tools consume the host
53
+ # registry from here; the host executor is the Git Bash provider (`dsh-gitbash-shell`)
54
+ # host-plane too.
55
+ - id: tool-bash
56
+ name: '@deepseek-ai/dsh-tool-bash'
57
+ # Git Bash on Windows: the host executor is dsh-gitbash-shell (see $DSH_HOME/cordis.patch.yml).
58
+ disabled: false
59
+
60
+ - id: tool-pwsh
61
+ name: '@deepseek-ai/dsh-tool-pwsh'
62
+ disabled: true
63
+
64
+ # ── filesystem ──────────────────────────────────────────────────────────────
65
+
66
+ # Both register into the host `tools` registry and provide nothing, so
67
+ # they need no realm. The `fs` service and its policy stay in the host.
68
+ - id: tool-fs
69
+ name: '@deepseek-ai/dsh-tool-fs'
70
+
71
+ - id: tool-fs-search
72
+ name: '@deepseek-ai/dsh-tool-fs-search'
73
+ config:
74
+ sampleOverCapGlobResults: false
75
+
76
+ # ── background jobs ────────────────────────────────────────────────────────
77
+
78
+ # Only the model-facing controls. The task REGISTRY stays on the host plane:
79
+ # its producers sit outside any realm this file could put it in — `tool-bash`
80
+ # above resolves it with `ctx.get`, and an entry-local realm here is invisible
81
+ # to every sibling row, so `run_in_background` would answer "background jobs
82
+ # unavailable" while these controls sat in the catalog. The registry is keyed by
83
+ # owning agent anyway, so one host instance serves every session. What a preset
84
+ # chooses is whether its agent can collect and stop background work at all.
85
+ - id: tool-jobs
86
+ name: '@deepseek-ai/dsh-tool-jobs'
87
+
88
+ # ── goals ───────────────────────────────────────────────────────────────────
89
+
90
+ # The goal service and session driver stay on the host plane, where the Gateway
91
+ # can resolve them. The human command and model-facing tool register into this
92
+ # preset's scoped layers.
93
+ - id: command-goal
94
+ name: '@deepseek-ai/dsh-command-goal'
95
+
96
+ - id: tool-goal
97
+ name: '@deepseek-ai/dsh-tool-goal'
98
+
99
+ # ── plan mode ───────────────────────────────────────────────────────────────
100
+
101
+ # Plan state is per-agent by nature, so an entry-local realm is not a
102
+ # workaround here — it is the correct lifetime.
103
+ - id: planning
104
+ name: cordis:group
105
+ group: true
106
+ isolate:
107
+ planMode: true
108
+ config:
109
+ - id: plan-mode
110
+ name: '@deepseek-ai/dsh-plan-mode'
111
+ config:
112
+ section: |
113
+ 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.
114
+
115
+ 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.
116
+
117
+ 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.
118
+
119
+ 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.
120
+
121
+ 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.
122
+
123
+ 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.
124
+
125
+ # ── compaction ──────────────────────────────────────────────────────────────
126
+
127
+ # `compaction-basic` reads `toolResultPrune` through `ctx.get`, so the pruner must
128
+ # share this realm rather than sit outside it.
129
+ #
130
+ # `tokenMeter` is deliberately NOT in this realm: the meter stays on the HOST
131
+ # plane, and the rows here resolve that one instance. It takes no configuration,
132
+ # keys every fold by Session, and owns the context-meter projection units the
133
+ # browser reads for every session — behind a realm those units would come and go
134
+ # with whichever presets happen to be mounted. What a preset chooses is whether
135
+ # its agent compacts at all, which is `compaction-basic` below.
136
+ - id: compaction
137
+ name: cordis:group
138
+ group: true
139
+ isolate:
140
+ compaction: true
141
+ toolResultPruner: true
142
+ config:
143
+ - id: compaction-basic
144
+ name: '@deepseek-ai/dsh-compaction-basic'
145
+
146
+ - id: command-compact
147
+ name: '@deepseek-ai/dsh-command-compact'
148
+
149
+ - id: tool-result-pruner
150
+ name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
151
+ config:
152
+ thresholdChars: 8192
153
+ headChars: 4096
154
+ tailChars: 1024
155
+
156
+ # ── delegation and workflows ────────────────────────────────────────────────
157
+
158
+ # The `subagents` registry and its spawn/fork backends live in the HOST
159
+ # composition: the registry is a process singleton whose cross-session queries
160
+ # the api-proxy serves to the browser, and a provider name may only be
161
+ # registered once. This preset contributes the delegation TOOLS, which resolve
162
+ # that host registry.
163
+ #
164
+ # `workflows` is different — nothing outside an agent reads it — so every row
165
+ # that reaches it shares one entry-local realm here, and a consumer left
166
+ # outside would resolve a host registry this preset does not populate.
167
+ - id: delegation
168
+ name: cordis:group
169
+ group: true
170
+ isolate:
171
+ workflowEngine: true
172
+ config:
173
+ - id: tool-subagent-control
174
+ name: '@deepseek-ai/dsh-tool-subagent-control'
175
+
176
+ - id: tool-subagent-list-agents
177
+ name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
178
+
179
+ - id: tool-subagent
180
+ name: '@deepseek-ai/dsh-tool-subagent'
181
+ config:
182
+ provider: spawn
183
+ toolName: subagent
184
+ modelSelectionSettings: true
185
+ backgroundMode: continuable
186
+
187
+ # Fork omits model selection so provider/model stay equal to the parent and
188
+ # the inherited history remains eligible for KV Cache reuse. This preset
189
+ # keeps fork continuable; parent and child inherit the same messaging tool,
190
+ # while the parent id and return guidance follow the inherited history.
191
+ - id: tool-subagent-fork
192
+ name: '@deepseek-ai/dsh-tool-subagent'
193
+ config:
194
+ provider: fork
195
+ toolName: subagent_fork
196
+ backgroundMode: continuable
197
+
198
+ # Production dsh does not install these optional providers. Install the
199
+ # matching Bundle in this Profile and restart the Host, then copy this
200
+ # preset and remove `disabled` from the matching tool row. Host availability
201
+ # alone grants no tool.
202
+ - id: tool-subagent-codex
203
+ name: '@deepseek-ai/dsh-tool-subagent'
204
+ disabled: true
205
+ config:
206
+ provider: codex
207
+ toolName: subagent_codex
208
+ backgroundMode: one-shot
209
+ maxDepth: provider-managed
210
+
211
+ - id: tool-subagent-claude-code
212
+ name: '@deepseek-ai/dsh-tool-subagent'
213
+ disabled: true
214
+ config:
215
+ provider: claude-code
216
+ toolName: subagent_claude_code
217
+ backgroundMode: one-shot
218
+ maxDepth: provider-managed
219
+
220
+ - id: workflow-worker-thread
221
+ name: '@deepseek-ai/dsh-workflow-worker-thread'
222
+ config:
223
+ provider: spawn
224
+
225
+ - id: tool-workflow
226
+ name: '@deepseek-ai/dsh-tool-workflow'
227
+
228
+ - id: tool-ralph
229
+ name: '@deepseek-ai/dsh-tool-ralph'
230
+ config:
231
+ subagentProvider: spawn
232
+ maxRounds: 64
233
+
234
+ # ── remaining model-facing rows ─────────────────────────────────────────────
235
+
236
+ - id: tool-ask-user
237
+ name: '@deepseek-ai/dsh-tool-ask-user'
238
+
239
+ - id: tool-todo
240
+ name: '@deepseek-ai/dsh-tool-todo'
241
+ config:
242
+ allowParallelInProgress: true
243
+
244
+ # The `web` service and its search provider stay in the host composition; only
245
+ # the model-facing tool is per-session.
246
+ - id: tool-web
247
+ name: '@deepseek-ai/dsh-tool-web'
248
+ config:
249
+ fetch: true
250
+ searchTimeoutMs: 60000
251
+
252
+ # ── self-modification ───────────────────────────────────────────────────────
253
+
254
+ # Read the live runtime, mount a temporary plugin, unmount it. The toolset is a
255
+ # trust boundary, not a sandbox — see this file's header.
256
+ - id: tool-cordis
257
+ name: '@deepseek-ai/dsh-tool-cordis'
258
+
259
+ # The composition-authoring skill travels with this preset rather than living
260
+ # in the user's skill root: it documents THIS deployment's two planes, and a
261
+ # preset is the unit that gets copied and edited. `baseUrl` is the preset's
262
+ # own directory, so the root resolves wherever the preset is installed.
263
+ # Both rows register into THIS preset's layer of the host skill registry, so
264
+ # they need no realm; the agent's merged catalog also carries whatever the
265
+ # deployment registered globally (repository plugins).
266
+ - id: skill-filesystem
267
+ name: '@deepseek-ai/dsh-skill-filesystem'
268
+ config:
269
+ customSkillDirs:
270
+ - !!js "process.getBuiltinModule('node:url').fileURLToPath(new URL('skills/', baseUrl))"
271
+
272
+ - id: tool-skill
273
+ name: '@deepseek-ai/dsh-tool-skill'
@@ -0,0 +1,79 @@
1
+ # ERA (persona-split): dsh >= 0.1.3-alpha.2 text — the persona row uses the
2
+ # `prefix:`/`suffix:` keys (40792330c0 split dsh-persona single text key).
3
+ # The pre-split texts live in the sibling files named in the banner below; the
4
+ # plugin probes the roster AND the persona form at materialization and writes
5
+ # the picked text as the preset own agent.cordis.yml.
6
+ # ERA: one text serves both dsh eras — the built-in `minimal` preset did not
7
+ # change across the 0.1.1 → 0.1.2 code→ptc rename, so no agent.cordis.ptc.yml
8
+ # twin exists here. The plugin probes the roster and writes this file as the
9
+ # preset's own agent.cordis.yml either way.
10
+ # The `minimal` agent preset: a fixed-prompt, single-tool coding-agent composition.
11
+ #
12
+ # The persona is the complete system prompt, so global identity, Web orientation,
13
+ # tool guidance, and later assembly listeners cannot add prompt text. Runtime
14
+ # context snapshots are suppressed for this preset, and the model receives only
15
+ # the persistent shell (Git for Windows bash; bash on POSIX). Context compaction
16
+ # is absent.
17
+
18
+ - id: persona
19
+ name: '@deepseek-ai/dsh-persona'
20
+ config:
21
+ prefix: You are a helpful software engineer assistant.
22
+ complete: true
23
+ includeRuntimeContext: false
24
+
25
+ # The PTY registry is an agent-owned service, so it lives in an entry-local
26
+ # realm. The backend still consumes the host sandbox policy and subprocess
27
+ # implementation, while the tool registers into this agent's scoped catalog.
28
+ # Exactly one shell stack mounts per host: the bash stack gates off win32 and
29
+ # its pwsh twin gates off POSIX, mirroring the one-shot shell rows.
30
+ - id: persistent-shell
31
+ name: cordis:group
32
+ group: true
33
+ isolate:
34
+ terminals: true
35
+ config:
36
+ - id: pty
37
+ name: '@deepseek-ai/dsh-terminal'
38
+
39
+ - id: terminal-bash
40
+ name: '@deepseek-ai/dsh-terminal-bash'
41
+ disabled: false
42
+ config:
43
+ timeoutMs: 300000
44
+ shellPath: !!js "process.platform === 'win32' ? 'C:/Program Files/Git/bin/bash.exe' : '/bin/bash'"
45
+
46
+ - id: persistent-bash
47
+ name: '@deepseek-ai/dsh-tool-bash-persistent'
48
+ disabled: false
49
+ config:
50
+ timeoutMs: 300000
51
+ description: |-
52
+ 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)
53
+ * When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped.
54
+ * Network access depends on the task environment. Prefer configured mirrors/proxies when they are available.
55
+ * State is persistent across command calls and discussions with the user.
56
+ * To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.
57
+ * Please avoid commands that may produce a very large amount of output.
58
+ * Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.
59
+
60
+ - id: terminal-pwsh
61
+ name: '@deepseek-ai/dsh-terminal-bash'
62
+ disabled: true
63
+ config:
64
+ shellDialect: pwsh
65
+ timeoutMs: 300000
66
+
67
+ - id: persistent-pwsh
68
+ name: '@deepseek-ai/dsh-tool-pwsh-persistent'
69
+ disabled: true
70
+ config:
71
+ timeoutMs: 300000
72
+ description: |-
73
+ Run commands in a PowerShell shell
74
+ * When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped.
75
+ * You don't have access to the internet via this tool.
76
+ * State is persistent across command calls and discussions with the user.
77
+ * Use native Windows paths (C:\...) and $env:NAME variables; this is PowerShell, not bash.
78
+ * Please avoid commands that may produce a very large amount of output.
79
+ * Please run long lived commands in the background, e.g. 'Start-Job' or start a server with Start-Process.
@@ -2,13 +2,13 @@
2
2
  # change across the 0.1.1 → 0.1.2 code→ptc rename, so no agent.cordis.ptc.yml
3
3
  # twin exists here. The plugin probes the roster and writes this file as the
4
4
  # preset's own agent.cordis.yml either way.
5
- # The `minimal` agent preset: a fixed-prompt, two-tool coding-agent composition.
5
+ # The `minimal` agent preset: a fixed-prompt, single-tool coding-agent composition.
6
6
  #
7
7
  # The persona is the complete system prompt, so global identity, Web orientation,
8
8
  # tool guidance, and later assembly listeners cannot add prompt text. Runtime
9
- # context snapshots are suppressed for this preset, and the model composes only
10
- # the persistent shell (Git for Windows bash; bash on POSIX) and
11
- # `str_replace_editor`. Context compaction is absent.
9
+ # context snapshots are suppressed for this preset, and the model receives only
10
+ # the persistent shell (Git for Windows bash; bash on POSIX). Context compaction
11
+ # is absent.
12
12
 
13
13
  - id: persona
14
14
  name: '@deepseek-ai/dsh-persona'
@@ -46,8 +46,7 @@
46
46
  description: |-
47
47
  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)
48
48
  * When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped.
49
- * You don't have access to the internet via this tool.
50
- * You do have access to a mirror of common linux and python packages via apt and pip.
49
+ * Network access depends on the task environment. Prefer configured mirrors/proxies when they are available.
51
50
  * State is persistent across command calls and discussions with the user.
52
51
  * To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.
53
52
  * Please avoid commands that may produce a very large amount of output.
@@ -73,21 +72,3 @@
73
72
  * Use native Windows paths (C:\...) and $env:NAME variables; this is PowerShell, not bash.
74
73
  * Please avoid commands that may produce a very large amount of output.
75
74
  * Please run long lived commands in the background, e.g. 'Start-Job' or start a server with Start-Process.
76
-
77
- # The bare local filesystem shadows the host's sandboxed provider only for this
78
- # preset. The editor shares that realm and requires absolute paths.
79
- - id: filesystem
80
- name: cordis:group
81
- group: true
82
- isolate:
83
- fs: true
84
- config:
85
- - id: fs-local
86
- name: '@deepseek-ai/dsh-fs-local'
87
- config:
88
- cwd: !!js process.env.DSH_CWD ?? process.cwd()
89
-
90
- - id: str-replace-editor
91
- name: '@deepseek-ai/dsh-tool-str-replace-editor'
92
- config:
93
- maxOutputChars: 16000
@@ -1,3 +1,3 @@
1
1
  name: 极简模式 · Git Bash
2
- description: 仅提供持久 bash 与 str_replace_editor 的双工具编码 Agent。(Shell 使用 Git Bash)
2
+ description: 仅提供持久 shell 的单工具编码 Agent。(Shell 使用 Git Bash)
3
3
  order: 3
@@ -0,0 +1,262 @@
1
+ # ERA (persona-split): dsh >= 0.1.3-alpha.2 text — the persona row uses the
2
+ # `prefix:`/`suffix:` keys (40792330c0 split dsh-persona single text key).
3
+ # The pre-split texts live in the sibling files named in the banner below; the
4
+ # plugin probes the roster AND the persona form at materialization and writes
5
+ # the picked text as the preset own agent.cordis.yml.
6
+ # ERA: dsh >= 0.1.2 text (renamed built-in rows, incl. the new `command-goal`).
7
+ # The dsh <= 0.1.1 era text lives in agent.cordis.yml; the plugin probes the
8
+ # roster at materialization and writes the picked text as the preset's own
9
+ # agent.cordis.yml.
10
+ # The `standard` agent preset: the full coding agent, mounted once per process.
11
+ #
12
+ # This file is an AGENT-PLANE composition. The roster mounts it ONCE under a
13
+ # standing scope; every session naming it joins by scope parentage, so the
14
+ # tools and prompt sections registered here cover each joined agent while a
15
+ # session's own state stays keyed per Session/Agent inside the plugins. The
16
+ # host composition (`base.cordis.yml` + `web.cordis.yml`) keeps everything a
17
+ # preset must not own: the registries themselves, the sandbox and approval
18
+ # stack, persistence, and the model route.
19
+ #
20
+ # A service row here MUST sit inside a group carrying an `isolate` realm.
21
+ # Without one it publishes into the root realm, where it is process-global —
22
+ # another preset publishing the same name collides, and a host reader would
23
+ # resolve one preset's instance for every session; `dsh-agent-presets` rejects
24
+ # that at mount. `true` means an entry-local realm: this standing mount's own
25
+ # private instance, apart from every other preset's. (A shared label does NOT
26
+ # pool instances — `provide()` throws on the second registration under the
27
+ # same realm symbol; labels join REALMS, and are not what this file needs.)
28
+
29
+ # ── identity ────────────────────────────────────────────────────────────────
30
+
31
+ # The preset's own persona, shadowing the deployment default for this agent.
32
+ # `{{model}}` and `{{cwd}}` resolve from the agent's own route and workspace.
33
+ - id: persona
34
+ name: '@deepseek-ai/dsh-persona'
35
+ config:
36
+ suffix: Your working directory is {{cwd}}.
37
+ prefix: >-
38
+ You are a coding agent powered by the {{model}} model.
39
+
40
+ - id: agent-instructions
41
+ name: '@deepseek-ai/dsh-agent-instructions'
42
+ config:
43
+ maxBytes: 65536
44
+
45
+ # ── shell ───────────────────────────────────────────────────────────────────
46
+
47
+ # `shell-env` stays in the HOST composition: `apps/cli/src/web.ts` injects it to
48
+ # publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
49
+ # the criterion for host-plane ownership — injection resolves before any session
50
+ # exists, so there is no agent to key by. Behind a preset realm those variables
51
+ # never reached the model's shell at all. Both shell tools consume the host
52
+ # registry from here; the host executor is the Git Bash provider (`dsh-gitbash-shell`)
53
+ # host-plane too.
54
+ - id: tool-bash
55
+ name: '@deepseek-ai/dsh-tool-bash'
56
+ # Git Bash on Windows: the host executor is dsh-gitbash-shell (see $DSH_HOME/cordis.patch.yml).
57
+ disabled: false
58
+
59
+ - id: tool-pwsh
60
+ name: '@deepseek-ai/dsh-tool-pwsh'
61
+ disabled: true
62
+
63
+ # ── filesystem ──────────────────────────────────────────────────────────────
64
+
65
+ # Both register into the host `tools` registry and provide nothing, so
66
+ # they need no realm. The `fs` service and its policy stay in the host.
67
+ - id: tool-fs
68
+ name: '@deepseek-ai/dsh-tool-fs'
69
+
70
+ - id: tool-fs-search
71
+ name: '@deepseek-ai/dsh-tool-fs-search'
72
+ config:
73
+ sampleOverCapGlobResults: false
74
+
75
+ # ── background jobs ────────────────────────────────────────────────────────
76
+
77
+ # Only the model-facing controls. The task REGISTRY stays on the host plane:
78
+ # its producers sit outside any realm this file could put it in — `tool-bash`
79
+ # above resolves it with `ctx.get`, and an entry-local realm here is invisible
80
+ # to every sibling row, so `run_in_background` would answer "background jobs
81
+ # unavailable" while these controls sat in the catalog. The registry is keyed by
82
+ # owning agent anyway, so one host instance serves every session. What a preset
83
+ # chooses is whether its agent can collect and stop background work at all.
84
+ - id: tool-jobs
85
+ name: '@deepseek-ai/dsh-tool-jobs'
86
+
87
+ # ── skills ──────────────────────────────────────────────────────────────────
88
+
89
+ # The skill REGISTRY lives in the host composition and is layered per scope:
90
+ # these rows register into THIS preset's layer of it, so they need no realm.
91
+ # `skill-filesystem` contributes local-root discovery for agents on this preset, and
92
+ # `tool-skill` gives them the catalog and loader; the merged catalog also
93
+ # carries whatever the deployment registered globally (repository plugins).
94
+ - id: skill-filesystem
95
+ name: '@deepseek-ai/dsh-skill-filesystem'
96
+
97
+ - id: tool-skill
98
+ name: '@deepseek-ai/dsh-tool-skill'
99
+
100
+ # ── goals ───────────────────────────────────────────────────────────────────
101
+
102
+ # The goal service and session driver stay on the host plane, where the Gateway
103
+ # can resolve them. The human command and model-facing tool register into this
104
+ # preset's scoped layers.
105
+ - id: command-goal
106
+ name: '@deepseek-ai/dsh-command-goal'
107
+
108
+ - id: tool-goal
109
+ name: '@deepseek-ai/dsh-tool-goal'
110
+
111
+ # ── plan mode ───────────────────────────────────────────────────────────────
112
+
113
+ # Plan state is per-agent by nature, so an entry-local realm is not a
114
+ # workaround here — it is the correct lifetime.
115
+ - id: planning
116
+ name: cordis:group
117
+ group: true
118
+ isolate:
119
+ planMode: true
120
+ config:
121
+ - id: plan-mode
122
+ name: '@deepseek-ai/dsh-plan-mode'
123
+ config:
124
+ section: |
125
+ 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.
126
+
127
+ 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.
128
+
129
+ 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.
130
+
131
+ 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.
132
+
133
+ 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.
134
+
135
+ 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.
136
+
137
+ # ── compaction ──────────────────────────────────────────────────────────────
138
+
139
+ # `compaction-basic` reads `toolResultPrune` through `ctx.get`, so the pruner must
140
+ # share this realm rather than sit outside it.
141
+ #
142
+ # `tokenMeter` is deliberately NOT in this realm: the meter stays on the HOST
143
+ # plane, and the rows here resolve that one instance. It takes no configuration,
144
+ # keys every fold by Session, and owns the context-meter projection units the
145
+ # browser reads for every session — behind a realm those units would come and go
146
+ # with whichever presets happen to be mounted. What a preset chooses is whether
147
+ # its agent compacts at all, which is `compaction-basic` below.
148
+ - id: compaction
149
+ name: cordis:group
150
+ group: true
151
+ isolate:
152
+ compaction: true
153
+ toolResultPruner: true
154
+ config:
155
+ - id: compaction-basic
156
+ name: '@deepseek-ai/dsh-compaction-basic'
157
+
158
+ - id: command-compact
159
+ name: '@deepseek-ai/dsh-command-compact'
160
+
161
+ - id: tool-result-pruner
162
+ name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
163
+ config:
164
+ thresholdChars: 8192
165
+ headChars: 4096
166
+ tailChars: 1024
167
+
168
+ # ── delegation and workflows ────────────────────────────────────────────────
169
+
170
+ # The `subagents` registry and its spawn/fork backends live in the HOST
171
+ # composition: the registry is a process singleton whose cross-session queries
172
+ # the api-proxy serves to the browser, and a provider name may only be
173
+ # registered once. This preset contributes the delegation TOOLS, which resolve
174
+ # that host registry.
175
+ #
176
+ # `workflows` is different — nothing outside an agent reads it — so every row
177
+ # that reaches it shares one entry-local realm here, and a consumer left
178
+ # outside would resolve a host registry this preset does not populate.
179
+ - id: delegation
180
+ name: cordis:group
181
+ group: true
182
+ isolate:
183
+ workflowEngine: true
184
+ config:
185
+ - id: tool-subagent-control
186
+ name: '@deepseek-ai/dsh-tool-subagent-control'
187
+
188
+ - id: tool-subagent-list-agents
189
+ name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
190
+
191
+ - id: tool-subagent
192
+ name: '@deepseek-ai/dsh-tool-subagent'
193
+ config:
194
+ provider: spawn
195
+ toolName: subagent
196
+ modelSelectionSettings: true
197
+ backgroundMode: continuable
198
+
199
+ # Fork omits model selection so provider/model stay equal to the parent and
200
+ # the inherited history remains eligible for KV Cache reuse. This preset
201
+ # keeps fork continuable; parent and child inherit the same messaging tool,
202
+ # while the parent id and return guidance follow the inherited history.
203
+ - id: tool-subagent-fork
204
+ name: '@deepseek-ai/dsh-tool-subagent'
205
+ config:
206
+ provider: fork
207
+ toolName: subagent_fork
208
+ backgroundMode: continuable
209
+
210
+ # Production dsh does not install these optional providers. Install the
211
+ # matching Bundle in this Profile and restart the Host, then copy this
212
+ # preset and remove `disabled` from the matching tool row. Host availability
213
+ # alone grants no tool.
214
+ - id: tool-subagent-codex
215
+ name: '@deepseek-ai/dsh-tool-subagent'
216
+ disabled: true
217
+ config:
218
+ provider: codex
219
+ toolName: subagent_codex
220
+ backgroundMode: one-shot
221
+ maxDepth: provider-managed
222
+
223
+ - id: tool-subagent-claude-code
224
+ name: '@deepseek-ai/dsh-tool-subagent'
225
+ disabled: true
226
+ config:
227
+ provider: claude-code
228
+ toolName: subagent_claude_code
229
+ backgroundMode: one-shot
230
+ maxDepth: provider-managed
231
+
232
+ - id: workflow-worker-thread
233
+ name: '@deepseek-ai/dsh-workflow-worker-thread'
234
+ config:
235
+ provider: spawn
236
+
237
+ - id: tool-workflow
238
+ name: '@deepseek-ai/dsh-tool-workflow'
239
+
240
+ - id: tool-ralph
241
+ name: '@deepseek-ai/dsh-tool-ralph'
242
+ config:
243
+ subagentProvider: spawn
244
+ maxRounds: 64
245
+
246
+ # ── remaining model-facing rows ─────────────────────────────────────────────
247
+
248
+ - id: tool-ask-user
249
+ name: '@deepseek-ai/dsh-tool-ask-user'
250
+
251
+ - id: tool-todo
252
+ name: '@deepseek-ai/dsh-tool-todo'
253
+ config:
254
+ allowParallelInProgress: true
255
+
256
+ # The `web` service and its search provider stay in the host composition; only
257
+ # the model-facing tool is per-session.
258
+ - id: tool-web
259
+ name: '@deepseek-ai/dsh-tool-web'
260
+ config:
261
+ fetch: true
262
+ searchTimeoutMs: 60000
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-gitbash-shell",
3
- "version": "0.11.1",
3
+ "version": "0.12.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.11.1",
3
+ "version": "0.13.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": {
package/src/index.js CHANGED
@@ -170,24 +170,72 @@ function eraSuffix(base) {
170
170
  return base === 'ptc' ? '.ptc' : ''
171
171
  }
172
172
 
173
+ // ── persona-form detection (dsh 0.1.3-alpha.2 split the persona row) ──────
174
+
175
+ /**
176
+ * Which persona config shape does one composition text use? 0.1.3-alpha.2
177
+ * (40792330c0) split dsh-persona's single `text` key into `prefix:` +
178
+ * `suffix:` with no compatibility alias, so compositions carrying a persona
179
+ * row are persona-form-specific. Pure companion of `detectPersonaEra`.
180
+ */
181
+ export function personaEraForText(text) {
182
+ return /- id:\s*persona[\s\S]{0,600}?\bprefix:/.test(text) ? 'split' : 'text'
183
+ }
184
+
185
+ /**
186
+ * Async probe of a SHIPPED preset's composition through the live roster;
187
+ * never throws. Built-in ids only — this plugin's own variants sit on the
188
+ * roster too and would echo whichever form they were materialized with. The
189
+ * roster entry path is the preset file (or its directory); either way we
190
+ * read agent.cordis.yml. A missing probe conservatively maps to 'text'.
191
+ */
192
+ export async function detectPersonaEra(agentPresets) {
193
+ try {
194
+ const list = await agentPresets.list()
195
+ const entries = Array.isArray(list) ? list : []
196
+ const entry = ['ptc', 'standard', 'cordis', 'minimal']
197
+ .map((id) => entries.find((p) => p && p.id === id && typeof p.path === 'string'))
198
+ .find(Boolean)
199
+ if (!entry) return 'text'
200
+ const file = /\.yml$/.test(entry.path) ? entry.path : join(entry.path, 'agent.cordis.yml')
201
+ return personaEraForText(readFileSync(file, 'utf8'))
202
+ } catch (error) {
203
+ console.log(`${TAG} persona-form probe failed (${error?.message ?? error}) — assuming the pre-split 'text' form`)
204
+ return 'text'
205
+ }
206
+ }
207
+
173
208
  /**
174
209
  * Pick the committed composition asset inside one variant directory (pure).
175
- * The ptc-era twin is preferred when the roster says `ptc`; variants without
176
- * a twin (minimal — its built-in did not change) fall back to the plain base
177
- * file. Every candidate is a committed file — no runtime text synthesis.
210
+ * The ptc-era twin is preferred when the roster says `ptc`; the persona-split
211
+ * twin (`.ps`, v0.12.0) is preferred when the shipped persona row carries the
212
+ * split keys; variants without a twin fall back down the chain (minimal never
213
+ * had a ptc twin and gained its `.ps` twin instead). Every candidate is a
214
+ * committed file — no runtime text synthesis.
178
215
  */
179
- function pickComposition(base, available) {
216
+ function pickComposition(base, persona, available) {
180
217
  const era = eraSuffix(base)
181
- const candidates = [`agent.cordis${era}.yml`, 'agent.cordis.yml']
218
+ const ps = persona === 'split' ? '.ps' : ''
219
+ const candidates = base === 'ptc'
220
+ ? [`agent.cordis${era}${ps}.yml`, `agent.cordis${era}.yml`, ...(ps ? ['agent.cordis.ps.yml'] : []), 'agent.cordis.yml']
221
+ : ['agent.cordis.yml']
182
222
  for (const file of candidates) if (available.includes(file)) return file
183
223
  return 'agent.cordis.yml'
184
224
  }
185
225
 
186
226
  /** Startup decision for an existing unmodified tree: 'refresh' or 'idle'. */
187
- function syncDecision({ state, marker, version, sourceHashes, base = 'code' }) {
227
+ function syncDecision({ state, marker, version, sourceHashes, base = 'code', persona = 'text', present = false }) {
188
228
  if (state !== 'unmodified' || !marker) return 'refresh'
189
229
  if (marker.version !== version) return 'refresh'
190
230
  if (marker.base !== base) return 'refresh'
231
+ if ((marker.persona ?? 'text') !== persona) return 'refresh'
232
+ // Host-capability flip: @deepseek-ai/dsh-tool-present first shipped with dsh
233
+ // 0.1.5-alpha.2. A preset composed with the row on a host that lacks the
234
+ // package REJECTS THE WHOLE MOUNT (mount.ts: a row whose module failed to
235
+ // import already rejects the mount), so the row is injected only when the
236
+ // host resolves it — and a host upgrade past that point must re-materialize
237
+ // to gain it (same marker-dimension pattern as persona).
238
+ if ((marker.present ?? false) !== present) return 'refresh'
191
239
  const recorded = {}
192
240
  for (const k of Object.keys(marker.files)) if (k.startsWith('skills/')) recorded[k] = marker.files[k]
193
241
  if (sourceHashes === null) return Object.keys(recorded).length === 0 ? 'idle' : 'refresh'
@@ -200,14 +248,56 @@ function syncDecision({ state, marker, version, sourceHashes, base = 'code' }) {
200
248
 
201
249
  // ── materialization ─────────────────────────────────────────────────────────
202
250
 
251
+ /*
252
+ * Conditional present-row injection (v0.13.0, dsh 0.1.5-alpha.2 sync). The
253
+ * official ptc/standard/cordis compositions appended `- id: present / name:
254
+ * '@deepseek-ai/dsh-tool-present'` (immutable file-delivery download cards).
255
+ * The row is NOT committed into the ptc-era twin files: a host older than
256
+ * 0.1.5-alpha.2 lacks the package, and a composition row that fails to
257
+ * import rejects the WHOLE preset mount. Instead the materializer probes the
258
+ * host (hostHasToolPresent) and splices the row in as PLAIN TEXT — anchor
259
+ * string surgery only, never a YAML round-trip, so !!js literals survive.
260
+ * Placement mirrors the official compositions: right after the
261
+ * tool-presentation block when the composition has one (all ptc-derived
262
+ * twins), otherwise appended at the tail (cordis/standard twins). Only
263
+ * `.ptc.` files are ever touched: code-era (<= 0.1.1) snapshots and the
264
+ * minimal variants are frozen history (official minimal never gained the
265
+ * row — it is the single-tool preset).
266
+ */
267
+ const PRESENT_ROW = "\n- id: present\n name: '@deepseek-ai/dsh-tool-present'\n"
268
+ const PRESENT_ANCHOR = " name: '@deepseek-ai/dsh-agent-tool-presentation'\n config:\n mode: ptc\n"
269
+
270
+ function injectPresentRow(text) {
271
+ if (text.includes("'@deepseek-ai/dsh-tool-present'")) return text
272
+ const at = text.indexOf(PRESENT_ANCHOR)
273
+ if (at !== -1) return text.slice(0, at + PRESENT_ANCHOR.length) + PRESENT_ROW + text.slice(at + PRESENT_ANCHOR.length)
274
+ return text.endsWith('\n') ? text + PRESENT_ROW : text + '\n' + PRESENT_ROW
275
+ }
276
+
277
+ let toolPresentCache
278
+ /** Probe whether THIS host can resolve the present package (cached per boot). */
279
+ async function hostHasToolPresent() {
280
+ if (toolPresentCache !== undefined) return toolPresentCache
281
+ try {
282
+ const { createRequire } = await import('node:module')
283
+ createRequire(import.meta.url).resolve('@deepseek-ai/dsh-tool-present')
284
+ toolPresentCache = true
285
+ } catch {
286
+ toolPresentCache = false
287
+ }
288
+ return toolPresentCache
289
+ }
290
+
203
291
  /** Write one preset directory from scratch. Returns 'ok' or 'no-skills-source'. */
204
- function materialize({ target, presetId, skillsSource, version, base = 'code' }) {
292
+ function materialize({ target, presetId, skillsSource, version, base = 'code', persona = 'text', present = false }) {
205
293
  rmSync(target, { recursive: true, force: true })
206
294
  mkdirSync(target, { recursive: true })
207
295
 
208
296
  const available = readdirSync(join(pkgDir, 'assets', presetId)).filter((f) => f.endsWith('.yml'))
209
- const compositionFile = pickComposition(base, available)
210
- writeFileSync(join(target, 'agent.cordis.yml'), readFileSync(join(pkgDir, 'assets', presetId, compositionFile)))
297
+ const compositionFile = pickComposition(base, persona, available)
298
+ let composition = readFileSync(join(pkgDir, 'assets', presetId, compositionFile), 'utf8')
299
+ if (present && compositionFile.includes('.ptc.')) composition = injectPresentRow(composition)
300
+ writeFileSync(join(target, 'agent.cordis.yml'), composition)
211
301
  writeFileSync(join(target, 'preset.yml'), readFileSync(join(pkgDir, 'assets', presetId, 'preset.yml')))
212
302
 
213
303
  let skills = 'none'
@@ -221,7 +311,7 @@ function materialize({ target, presetId, skillsSource, version, base = 'code' })
221
311
  }
222
312
  }
223
313
 
224
- const marker = { managedBy: MANAGED_BY, version, presetId, base, files: hashTree(target) }
314
+ const marker = { managedBy: MANAGED_BY, version, presetId, base, persona, present, files: hashTree(target) }
225
315
  writeFileSync(join(target, MARKER_FILE), JSON.stringify(marker, null, 2) + '\n')
226
316
  return skills
227
317
  }
@@ -829,6 +919,8 @@ export async function apply(ctx, config = {}) {
829
919
 
830
920
  const skillsSource = await findSkillsSource(ctx.agentPresets)
831
921
  const base = await detectBase(ctx.agentPresets)
922
+ const persona = await detectPersonaEra(ctx.agentPresets)
923
+ const present = await hostHasToolPresent()
832
924
  const userRootPath = userRoot.path
833
925
  purgeOrphans(userRootPath, presetIds)
834
926
 
@@ -858,19 +950,19 @@ export async function apply(ctx, config = {}) {
858
950
 
859
951
  const sourceHashes = presetId === 'cordis-gitbash' ? skillsHashes(skillsSource) : null
860
952
  const marker = readMarker(target)
861
- if (state === 'unmodified' && syncDecision({ state, marker, version, sourceHashes, base }) === 'idle') {
862
- ctx.logger?.('gitbash-shell')?.debug?.( `preset '${presetId}' up to date (v${version}, ${base}-era) — idle`)
953
+ if (state === 'unmodified' && syncDecision({ state, marker, version, sourceHashes, base, persona, present }) === 'idle') {
954
+ ctx.logger?.('gitbash-shell')?.debug?.( `preset '${presetId}' up to date (v${version}, ${base}-era${persona === 'split' ? ', persona-split' : ''}) — idle`)
863
955
  continue
864
956
  }
865
957
 
866
- const skills = materialize({ target, presetId, skillsSource, version, base })
958
+ const skills = materialize({ target, presetId, skillsSource, version, base, persona, present })
867
959
  const verb = state === 'absent' ? 'materialized' : 'refreshed'
868
960
  console.log(
869
- `${TAG} ${verb} preset '${presetId}' into ${userRootPath} (v${version}, ${base}-era composition)` +
961
+ `${TAG} ${verb} preset '${presetId}' into ${userRootPath} (v${version}, ${base}-era composition${persona === 'split' ? ', persona-split' : ''})` +
870
962
  (skills === 'copied' ? " (skills copied from the installed 'cordis' preset)" : '')
871
963
  )
872
964
  }
873
965
  }
874
966
 
875
967
  // Test surface: pure helpers, no Cordis context required.
876
- export const _internal = { PRESET_IDS, translateMsysPath, translatePathArguments, readPosixPaths, windowsToMsys, rewriteResultPaths, MARKER_FILE, classify, materialize, cleanupOnDispose, firstUserRoot, hashTree, skillsHashes, syncDecision, installRegisterShim, baseForRoster, detectBase, pickComposition }
968
+ export const _internal = { PRESET_IDS, translateMsysPath, translatePathArguments, readPosixPaths, windowsToMsys, rewriteResultPaths, MARKER_FILE, classify, materialize, cleanupOnDispose, firstUserRoot, hashTree, skillsHashes, syncDecision, installRegisterShim, baseForRoster, detectBase, pickComposition, personaEraForText, detectPersonaEra, injectPresentRow, hostHasToolPresent }