@ran-sh/dsh-crew 0.5.0 → 0.5.2

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,98 @@
1
+ import {
2
+ copyFileSync,
3
+ existsSync,
4
+ mkdirSync,
5
+ readFileSync,
6
+ rmSync,
7
+ writeFileSync,
8
+ } from 'node:fs';
9
+ import { dirname, join } from 'node:path';
10
+ import { homedir } from 'node:os';
11
+
12
+ export const WINDOWS_STARTUP_FILENAME = 'DSH Crew.vbs';
13
+ export const WINDOWS_LAUNCHER_FILENAME = 'start-dsh-crew.cmd';
14
+
15
+ function defaultStartupDir({ home, env }) {
16
+ if (home === homedir() && env.APPDATA) {
17
+ return join(env.APPDATA, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
18
+ }
19
+ return join(home, 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
20
+ }
21
+
22
+ function paths({ home, startupDir, env }) {
23
+ const launcherFile = join(home, '.config', 'dsh-crew', 'launchers', WINDOWS_LAUNCHER_FILENAME);
24
+ const resolvedStartupDir = startupDir ?? defaultStartupDir({ home, env });
25
+ return {
26
+ launcherFile,
27
+ startupFile: join(resolvedStartupDir, WINDOWS_STARTUP_FILENAME),
28
+ };
29
+ }
30
+
31
+ function renderVbs(template, launcherFile) {
32
+ const escaped = launcherFile.replace(/"/g, '""');
33
+ return template.replace('__LAUNCHER__', escaped);
34
+ }
35
+
36
+ export function windowsStartupStatus({
37
+ home = homedir(),
38
+ startupDir,
39
+ platform = process.platform,
40
+ env = process.env,
41
+ } = {}) {
42
+ if (platform !== 'win32') return { supported: false, installed: false, ready: false };
43
+ const resolved = paths({ home, startupDir, env });
44
+ const installed = existsSync(resolved.startupFile) || existsSync(resolved.launcherFile);
45
+ let ready = existsSync(resolved.startupFile) && existsSync(resolved.launcherFile);
46
+ if (ready) {
47
+ try {
48
+ const text = readFileSync(resolved.startupFile, 'utf16le').replace(/^\uFEFF/, '');
49
+ ready = text.includes(resolved.launcherFile);
50
+ } catch { ready = false; }
51
+ }
52
+ return { supported: true, installed, ready, ...resolved };
53
+ }
54
+
55
+ export function installWindowsStartup({
56
+ home = homedir(),
57
+ root,
58
+ startupDir,
59
+ platform = process.platform,
60
+ env = process.env,
61
+ } = {}) {
62
+ if (platform !== 'win32') return { ok: true, supported: false, changed: false };
63
+ if (!root) return { ok: false, supported: true, code: 'STARTUP_SOURCE_REQUIRED' };
64
+ const sourceLauncher = join(root, 'windows', WINDOWS_LAUNCHER_FILENAME);
65
+ const sourceVbs = join(root, 'windows', 'start-dsh-crew.vbs');
66
+ if (!existsSync(sourceLauncher) || !existsSync(sourceVbs)) {
67
+ return { ok: false, supported: true, code: 'STARTUP_ASSET_MISSING' };
68
+ }
69
+ const resolved = paths({ home, startupDir, env });
70
+ mkdirSync(dirname(resolved.launcherFile), { recursive: true });
71
+ mkdirSync(dirname(resolved.startupFile), { recursive: true });
72
+ const beforeLauncher = existsSync(resolved.launcherFile) ? readFileSync(resolved.launcherFile) : null;
73
+ const beforeStartup = existsSync(resolved.startupFile) ? readFileSync(resolved.startupFile) : null;
74
+ copyFileSync(sourceLauncher, resolved.launcherFile);
75
+ const rendered = renderVbs(readFileSync(sourceVbs, 'utf8'), resolved.launcherFile);
76
+ writeFileSync(resolved.startupFile, `\uFEFF${rendered}`, 'utf16le');
77
+ const changed = !beforeLauncher?.equals(readFileSync(resolved.launcherFile))
78
+ || !beforeStartup?.equals(readFileSync(resolved.startupFile));
79
+ return { ok: true, supported: true, changed, ...resolved };
80
+ }
81
+
82
+ export function uninstallWindowsStartup({
83
+ home = homedir(),
84
+ startupDir,
85
+ platform = process.platform,
86
+ env = process.env,
87
+ } = {}) {
88
+ if (platform !== 'win32') return { ok: true, supported: false, removed: false };
89
+ const resolved = paths({ home, startupDir, env });
90
+ let removed = false;
91
+ for (const file of [resolved.startupFile, resolved.launcherFile]) {
92
+ if (existsSync(file)) {
93
+ rmSync(file, { force: true });
94
+ removed = true;
95
+ }
96
+ }
97
+ return { ok: true, supported: true, removed, ...resolved };
98
+ }
@@ -0,0 +1,316 @@
1
+ // ZCode host integration. This module is deliberately file-only: it never
2
+ // launches ZCode, mutates credentials, or overwrites an unowned MCP server.
3
+ // The generated files make ZCode dispatch through the same dsh-crew MCP server
4
+ // used by Codex and Claude while keeping ZCode's native config precedence.
5
+
6
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
7
+ import { createHash } from 'node:crypto';
8
+ import { dirname, join, resolve } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { homedir } from 'node:os';
11
+
12
+ const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
13
+ const HOST = 'zcode';
14
+ const SERVER = 'dsh-crew';
15
+ const POLICY_START = '<!-- DSH CREW MANAGED ZCODE POLICY:START -->';
16
+ const POLICY_END = '<!-- DSH CREW MANAGED ZCODE POLICY:END -->';
17
+ const OWNERSHIP_FILE = ({ home = homedir() } = {}) => join(home, '.config', 'dsh-crew', 'integrations', 'zcode.json');
18
+
19
+ // Keep the allowlist explicit. ZCode rejects wildcard tool permissions, and a
20
+ // future server tool is not silently exposed until a template intentionally
21
+ // opts into it.
22
+ export const ZCODE_MCP_TOOLS = Object.freeze([
23
+ 'dsh_run_worker',
24
+ 'dsh_spawn_worker',
25
+ 'dsh_worker_status',
26
+ 'dsh_worker_result',
27
+ 'dsh_worker_cancel',
28
+ 'dsh_worker_config',
29
+ ]);
30
+
31
+ function readText(file) {
32
+ try { return readFileSync(file, 'utf8'); } catch { return null; }
33
+ }
34
+
35
+ function readJson(file, fallback = null) {
36
+ try { return JSON.parse(readFileSync(file, 'utf8')); } catch { return fallback; }
37
+ }
38
+
39
+ function contentHash(text) {
40
+ return createHash('sha256').update(text, 'utf8').digest('hex');
41
+ }
42
+
43
+ function writeJson(file, value) {
44
+ mkdirSync(dirname(file), { recursive: true });
45
+ writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
46
+ }
47
+
48
+ function backup(file) {
49
+ if (!existsSync(file)) return null;
50
+ const path = `${file}.dsh-crew-backup-${Date.now()}`;
51
+ copyFileSync(file, path);
52
+ return path;
53
+ }
54
+
55
+ function normalizePath(value) {
56
+ if (typeof value !== 'string' || !value.trim()) return null;
57
+ try {
58
+ const path = resolve(value);
59
+ return process.platform === 'win32' ? path.toLowerCase() : path;
60
+ } catch { return null; }
61
+ }
62
+
63
+ function serverTarget(server) {
64
+ if (!server || typeof server !== 'object') return null;
65
+ if (server.command !== 'node' || !Array.isArray(server.args) || typeof server.args[0] !== 'string') return null;
66
+ return normalizePath(server.args[0]);
67
+ }
68
+
69
+ function nativeServers(config) {
70
+ const servers = config?.mcp?.servers;
71
+ return servers && typeof servers === 'object' && !Array.isArray(servers) ? servers : {};
72
+ }
73
+
74
+ function sharedServers(config) {
75
+ const servers = config?.mcpServers;
76
+ return servers && typeof servers === 'object' && !Array.isArray(servers) ? servers : {};
77
+ }
78
+
79
+ function configSource({ home = homedir() } = {}) {
80
+ const nativeFile = join(home, '.zcode', 'cli', 'config.json');
81
+ const sharedFile = join(home, '.agents', 'mcp.json');
82
+ const native = readJson(nativeFile, {});
83
+ const shared = readJson(sharedFile, {});
84
+ const nativeMap = nativeServers(native);
85
+ const sharedMap = sharedServers(shared);
86
+ // ZCode shadows .agents/mcp.json whenever its native config has at least
87
+ // one server. If native is empty, the shared file is the least-surprising
88
+ // compatibility path for users who already manage MCP centrally.
89
+ if (Object.keys(nativeMap).length > 0 || Object.keys(sharedMap).length === 0) {
90
+ return { file: nativeFile, kind: 'native', config: native, servers: nativeMap };
91
+ }
92
+ return { file: sharedFile, kind: 'shared', config: shared, servers: sharedMap };
93
+ }
94
+
95
+ export function resolveZCodeMcpTarget({ home = homedir() } = {}) {
96
+ const source = configSource({ home });
97
+ return serverTarget(source.servers[SERVER]);
98
+ }
99
+
100
+ function expectedTarget({ root = ROOT } = {}) {
101
+ return resolve(join(root, 'src', 'server.mjs'));
102
+ }
103
+
104
+ function managedPolicyBlock(root) {
105
+ const template = readText(join(root, 'zcode', 'AGENTS.md'))?.trim();
106
+ return template ? `${POLICY_START}\n${template}\n${POLICY_END}` : null;
107
+ }
108
+
109
+ function installPolicy({ home, root }) {
110
+ const file = join(home, '.zcode', 'AGENTS.md');
111
+ const block = managedPolicyBlock(root);
112
+ if (!block) return { ok: false, code: 'ZCODE_POLICY_TEMPLATE_MISSING' };
113
+ mkdirSync(dirname(file), { recursive: true });
114
+ const current = readText(file) ?? '';
115
+ const escapedStart = POLICY_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
116
+ const escapedEnd = POLICY_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
117
+ const managed = new RegExp(`${escapedStart}[\\s\\S]*?${escapedEnd}`, 'm');
118
+ const next = managed.test(current)
119
+ ? current.replace(managed, block)
120
+ : `${current.trimEnd()}${current.trim() ? '\n\n' : ''}${block}\n`;
121
+ if (next !== current) { backup(file); writeFileSync(file, next); }
122
+ return { ok: true, file };
123
+ }
124
+
125
+ function removePolicy({ home }) {
126
+ const file = join(home, '.zcode', 'AGENTS.md');
127
+ const current = readText(file);
128
+ if (typeof current !== 'string') return false;
129
+ const start = POLICY_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
130
+ const end = POLICY_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
131
+ const managed = new RegExp(`(?:\\r?\\n){0,2}${start}[\\s\\S]*?${end}(?:\\r?\\n)?`, 'm');
132
+ if (!managed.test(current)) return false;
133
+ const next = current.replace(managed, '').trimEnd();
134
+ backup(file);
135
+ if (next.trim()) writeFileSync(file, `${next}\n`);
136
+ else rmSync(file, { force: true });
137
+ return true;
138
+ }
139
+
140
+ function ownership({ home = homedir() } = {}) {
141
+ const data = readJson(OWNERSHIP_FILE({ home }), null);
142
+ return data && typeof data === 'object' && data.host === HOST ? data : null;
143
+ }
144
+
145
+ function writeOwnership({ home, configFile, configKind, target, files = [] }) {
146
+ writeJson(OWNERSHIP_FILE({ home }), {
147
+ schema_version: 1,
148
+ host: HOST,
149
+ server: SERVER,
150
+ config_file: configFile,
151
+ config_kind: configKind,
152
+ target,
153
+ files,
154
+ managed_at: new Date().toISOString(),
155
+ });
156
+ }
157
+
158
+ function templateFiles({ home, root }) {
159
+ return [
160
+ ['agents', 'ds-worker.md'],
161
+ ['agents', 'ds-reviewer.md'],
162
+ ['commands', 'dsh-config.md'],
163
+ ['commands', 'dsh-status.md'],
164
+ ].map(([dir, file]) => ({ source: join(root, 'zcode', dir, file), dest: join(home, '.zcode', dir, file) }));
165
+ }
166
+
167
+ function installTemplates({ home, root, priorFiles = [] }) {
168
+ const actions = [];
169
+ const records = [];
170
+ for (const { source, dest } of templateFiles({ home, root })) {
171
+ if (!existsSync(source)) return { ok: false, code: 'ZCODE_TEMPLATE_MISSING', source };
172
+ mkdirSync(dirname(dest), { recursive: true });
173
+ const before = readText(dest);
174
+ const rendered = readFileSync(source, 'utf8');
175
+ const previous = priorFiles.find((entry) => entry?.path === dest);
176
+ let backupFile = previous?.backup ?? null;
177
+ if (before !== rendered) {
178
+ if (before !== null && !backupFile) backupFile = backup(dest);
179
+ writeFileSync(dest, rendered);
180
+ }
181
+ actions.push(dest);
182
+ records.push({
183
+ path: dest,
184
+ preexisting: previous?.preexisting === true || (previous === undefined && before !== null),
185
+ ...(backupFile ? { backup: backupFile } : {}),
186
+ managed_sha256: contentHash(rendered),
187
+ });
188
+ }
189
+ return { ok: true, actions, records };
190
+ }
191
+
192
+ function updateMcp({ home, root }) {
193
+ const source = configSource({ home });
194
+ const target = expectedTarget({ root });
195
+ const current = source.servers[SERVER];
196
+ const currentTarget = serverTarget(current);
197
+ const owned = ownership({ home });
198
+ if (current && currentTarget !== target) {
199
+ // Only an exact previously-owned entry may be repaired. Any foreign
200
+ // command, even if it is also named dsh-crew, is a hard collision.
201
+ if (!(owned && owned.config_file === source.file && normalizePath(owned.target) === currentTarget)) {
202
+ return { ok: false, code: 'ZCODE_MCP_COLLISION', config_file: source.file };
203
+ }
204
+ }
205
+
206
+ const next = { ...source.config };
207
+ if (source.kind === 'native') next.mcp = { ...(next.mcp ?? {}), servers: { ...source.servers, [SERVER]: { command: 'node', args: [target] } } };
208
+ else next.mcpServers = { ...source.servers, [SERVER]: { command: 'node', args: [target] } };
209
+ const changed = JSON.stringify(next) !== JSON.stringify(source.config);
210
+ if (changed) { backup(source.file); writeJson(source.file, next); }
211
+ return { ok: true, changed, config_file: source.file, config_kind: source.kind, target };
212
+ }
213
+
214
+ export function installZCode({ home = homedir(), root = ROOT } = {}) {
215
+ const templates = templateFiles({ home, root });
216
+ const missing = templates.find(({ source }) => !existsSync(source));
217
+ if (missing) return { ok: false, code: 'ZCODE_TEMPLATE_MISSING', source: missing.source };
218
+ const priorFiles = ownership({ home })?.files ?? [];
219
+ // Check for collisions before writing any other integration surface so a
220
+ // failed install is transaction-like and leaves user files untouched.
221
+ const mcp = updateMcp({ home, root });
222
+ if (!mcp.ok) return mcp;
223
+ const policy = installPolicy({ home, root });
224
+ if (!policy.ok) return policy;
225
+ const installed = installTemplates({ home, root, priorFiles });
226
+ if (!installed.ok) return installed;
227
+ writeOwnership({
228
+ home,
229
+ configFile: mcp.config_file,
230
+ configKind: mcp.config_kind,
231
+ target: mcp.target,
232
+ files: installed.records,
233
+ });
234
+ return { ok: true, ...mcp, policy_file: policy.file, files: installed.actions };
235
+ }
236
+
237
+ function zcodeComponents({ home = homedir(), root = ROOT } = {}) {
238
+ const expected = normalizePath(expectedTarget({ root }));
239
+ const source = configSource({ home });
240
+ const configured = serverTarget(source.servers[SERVER]);
241
+ const owned = ownership({ home });
242
+ const components = {
243
+ mcp: configured === expected,
244
+ policy: typeof readText(join(home, '.zcode', 'AGENTS.md')) === 'string' && readText(join(home, '.zcode', 'AGENTS.md')).includes(POLICY_START),
245
+ worker_agent: existsSync(join(home, '.zcode', 'agents', 'ds-worker.md')),
246
+ reviewer_agent: existsSync(join(home, '.zcode', 'agents', 'ds-reviewer.md')),
247
+ config_prompt: existsSync(join(home, '.zcode', 'commands', 'dsh-config.md')),
248
+ status_prompt: existsSync(join(home, '.zcode', 'commands', 'dsh-status.md')),
249
+ ownership: !!owned && owned.config_file === source.file && normalizePath(owned.target) === expected,
250
+ };
251
+ return { components, source, expected, configured, owned };
252
+ }
253
+
254
+ export function zcodeStatus({ home = homedir(), root = ROOT } = {}) {
255
+ const { components, source, expected, configured } = zcodeComponents({ home, root });
256
+ const missing = Object.entries(components).filter(([, value]) => !value).map(([key]) => key);
257
+ const installed = Object.values(components).some(Boolean);
258
+ return {
259
+ installed,
260
+ ready: missing.length === 0,
261
+ components,
262
+ missing,
263
+ config_file: source.file,
264
+ config_kind: source.kind,
265
+ target: configured ?? expected,
266
+ };
267
+ }
268
+
269
+ export function uninstallZCode({ home = homedir() } = {}) {
270
+ const actions = [];
271
+ const owned = ownership({ home });
272
+ if (owned) {
273
+ const file = owned.config_file;
274
+ const config = readJson(file, null);
275
+ if (config && typeof config === 'object') {
276
+ const kind = owned.config_kind === 'shared' ? 'shared' : 'native';
277
+ const servers = kind === 'shared' ? sharedServers(config) : nativeServers(config);
278
+ const current = servers[SERVER];
279
+ if (normalizePath(owned.target) === serverTarget(current)) {
280
+ const next = { ...config };
281
+ if (kind === 'shared') {
282
+ next.mcpServers = { ...servers }; delete next.mcpServers[SERVER];
283
+ if (Object.keys(next.mcpServers).length === 0) delete next.mcpServers;
284
+ } else {
285
+ next.mcp = { ...(next.mcp ?? {}), servers: { ...servers } }; delete next.mcp.servers[SERVER];
286
+ if (Object.keys(next.mcp.servers).length === 0) delete next.mcp.servers;
287
+ if (Object.keys(next.mcp).length === 0) delete next.mcp;
288
+ }
289
+ backup(file); writeJson(file, next); actions.push(`mcp: removed ${SERVER} from ${file}`);
290
+ }
291
+ }
292
+ rmSync(OWNERSHIP_FILE({ home }), { force: true });
293
+ }
294
+ if (removePolicy({ home })) actions.push('policy: removed managed ZCode block');
295
+ const managedFiles = Array.isArray(owned?.files) && owned.files.length
296
+ ? owned.files
297
+ : ['agents/ds-worker.md', 'agents/ds-reviewer.md', 'commands/dsh-config.md', 'commands/dsh-status.md']
298
+ .map((rel) => ({ path: join(home, '.zcode', rel) }));
299
+ for (const entry of managedFiles) {
300
+ const file = entry?.path;
301
+ if (!file || !existsSync(file)) continue;
302
+ const current = readText(file);
303
+ const unchanged = !entry.managed_sha256 || (current !== null && contentHash(current) === entry.managed_sha256);
304
+ if (!unchanged) continue;
305
+ if (entry.preexisting && entry.backup && existsSync(entry.backup)) {
306
+ copyFileSync(entry.backup, file);
307
+ actions.push(`restored: ${file}`);
308
+ } else {
309
+ rmSync(file, { force: true });
310
+ actions.push(`removed: ${file}`);
311
+ }
312
+ }
313
+ return { ok: true, actions };
314
+ }
315
+
316
+ export function zcodeOwnershipFile({ home = homedir() } = {}) { return OWNERSHIP_FILE({ home }); }
@@ -8,7 +8,7 @@
8
8
  // Keep this module pure and dependency-free so Hub, MCP and tests all use the
9
9
  // exact same compatibility rules.
10
10
 
11
- export const RUNTIME_VERSION = '0.5.0';
11
+ export const RUNTIME_VERSION = '0.5.2';
12
12
  export const HUB_PROTOCOL_VERSION = 1;
13
13
 
14
14
  export const HUB_CAPABILITIES = Object.freeze([
@@ -0,0 +1,55 @@
1
+ @echo off
2
+ setlocal EnableExtensions
3
+ title DSH Crew Launcher
4
+
5
+ set "CREW_HOME=%USERPROFILE%\.config\dsh-crew\harness"
6
+ set "OFFICIAL_HOME=%USERPROFILE%\.dsh"
7
+ set "DSH_CLI=%CREW_HOME%\runtime\node_modules\.bin\dsh.cmd"
8
+ set "CREW_URL=http://127.0.0.1:3210"
9
+ set "UI_URL=http://127.0.0.1:3080"
10
+ set "LAUNCH_LOG=%TEMP%\dsh-crew-launcher.log"
11
+
12
+ if not exist "%DSH_CLI%" goto :not_installed
13
+ if not exist "%CREW_HOME%\profiles\dsh-crew\package.json" goto :not_installed
14
+ if not exist "%OFFICIAL_HOME%\profiles\web\package.json" goto :official_missing
15
+
16
+ call :ensure_service 3210 dsh-crew "%CREW_HOME%" "%CREW_URL%"
17
+ if errorlevel 1 goto :failed
18
+ call :ensure_service 3080 web "%OFFICIAL_HOME%" "%UI_URL%"
19
+ if errorlevel 1 goto :failed
20
+ exit /b 0
21
+
22
+ :ensure_service
23
+ set "LAUNCH_PORT=%~1"
24
+ set "LAUNCH_PROFILE=%~2"
25
+ set "LAUNCH_HOME=%~3"
26
+ set "LAUNCH_URL=%~4"
27
+
28
+ call :health_check "%LAUNCH_URL%"
29
+ if not errorlevel 1 exit /b 0
30
+
31
+ powershell.exe -NoLogo -NoProfile -NonInteractive -Command "$listener=Get-NetTCPConnection -State Listen -LocalPort $env:LAUNCH_PORT -ErrorAction SilentlyContinue; if ($listener) { exit 0 } else { exit 1 }" >nul 2>&1
32
+ if not errorlevel 1 exit /b 1
33
+
34
+ powershell.exe -NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -Command "try { $env:DSH_HOME=$env:LAUNCH_HOME; Start-Process -FilePath $env:DSH_CLI -ArgumentList @('--profile',$env:LAUNCH_PROFILE,'--host','127.0.0.1','--port',$env:LAUNCH_PORT,'--no-open') -WindowStyle Hidden -ErrorAction Stop } catch { ('['+(Get-Date -Format s)+'] Failed to start '+$env:LAUNCH_PROFILE+': '+$_.Exception.Message) | Add-Content -LiteralPath $env:LAUNCH_LOG; exit 1 }" >nul 2>&1
35
+ if errorlevel 1 exit /b 1
36
+
37
+ powershell.exe -NoLogo -NoProfile -NonInteractive -Command "$lastError=$null; $deadline=(Get-Date).AddSeconds(90); do { try { $r=Invoke-RestMethod -Uri ($env:LAUNCH_URL+'/_dsh/dsh-crew/extension') -TimeoutSec 2; if ($r.ok -eq $true -and $r.extension.runtime.runtime_version) { exit 0 } } catch { $lastError=$_.Exception.Message }; Start-Sleep -Milliseconds 500 } while ((Get-Date) -lt $deadline); if (-not $lastError) { $lastError='No healthy response before the startup deadline.' }; ('['+(Get-Date -Format s)+'] '+$env:LAUNCH_PROFILE+' health check failed: '+$lastError) | Add-Content -LiteralPath $env:LAUNCH_LOG; exit 1" >nul 2>&1
38
+ exit /b %ERRORLEVEL%
39
+
40
+ :health_check
41
+ set "HEALTH_URL=%~1"
42
+ powershell.exe -NoLogo -NoProfile -NonInteractive -Command "try { $r=Invoke-RestMethod -Uri ($env:HEALTH_URL+'/_dsh/dsh-crew/extension') -TimeoutSec 2; if ($r.ok -eq $true -and $r.extension.runtime.runtime_version) { exit 0 } } catch {}; exit 1" >nul 2>&1
43
+ exit /b %ERRORLEVEL%
44
+
45
+ :not_installed
46
+ echo [%date% %time%] DSH Crew is not installed completely.>>"%LAUNCH_LOG%"
47
+ exit /b 1
48
+
49
+ :official_missing
50
+ echo [%date% %time%] Official DeepSeek Harness web profile was not found.>>"%LAUNCH_LOG%"
51
+ exit /b 1
52
+
53
+ :failed
54
+ echo [%date% %time%] DSH Crew startup failed.>>"%LAUNCH_LOG%"
55
+ exit /b 1
@@ -0,0 +1,9 @@
1
+ Option Explicit
2
+
3
+ Dim shell, launcher, command
4
+ Set shell = CreateObject("WScript.Shell")
5
+ launcher = "__LAUNCHER__"
6
+ command = shell.ExpandEnvironmentStrings("%COMSPEC%") & " /d /c " & _
7
+ Chr(34) & Chr(34) & launcher & Chr(34) & Chr(34)
8
+
9
+ shell.Run command, 0, False
@@ -0,0 +1,21 @@
1
+ # Global capability-aware delegation policy for ZCode
2
+
3
+ ZCode is a host adapter for DSH Crew. Use the `dsh-crew` MCP server for Crew
4
+ work only after its live capability and readiness surfaces have been checked.
5
+
6
+ - Discover the current Crew configuration, capabilities, activation state and
7
+ readiness before delegating substantial work. Installed, configured, enabled
8
+ and callable are different states; do not infer one from another.
9
+ - Match a bounded work unit to an available Crew role/model and preserve the
10
+ repository/worktree and Result Contract boundaries. Keep planning, ambiguous
11
+ requirements, integration, external side effects and final communication in
12
+ the host agent.
13
+ - If Crew is selected and any required capability is unavailable, non-callable,
14
+ or returns an unknown/runtime/configuration/credential/routing/timeout error,
15
+ pause. Report the evidence and wait for the operator to choose repair Crew or
16
+ continue locally; never silently fall back or retry blindly.
17
+ - Validate returned evidence, changed scope, tests and completion state before
18
+ accepting delegated work. Do not expose credentials or raw provider payloads.
19
+
20
+ This file is installed as a managed block in `~/.zcode/AGENTS.md`; user-authored
21
+ instructions outside the block are preserved.
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: ds-reviewer
3
+ description: Independent DSH Crew reviewer dispatcher. Inspect completed changes read-only and return a structured verdict.
4
+ mcpServers:
5
+ - dsh-crew
6
+ tools:
7
+ - mcp__dsh-crew__dsh_run_worker
8
+ - mcp__dsh-crew__dsh_worker_status
9
+ - mcp__dsh-crew__dsh_worker_result
10
+ - mcp__dsh-crew__dsh_worker_cancel
11
+ - mcp__dsh-crew__dsh_worker_config
12
+ ---
13
+
14
+ You are a thin, read-only dispatcher. Never edit files or implement fixes.
15
+
16
+ Pass the review request verbatim to `dsh_run_worker` with role `reviewer` and
17
+ the current workspace as `cwd`; omit effort unless explicitly requested. Wait
18
+ for the result and return its Review Findings, Evidence, Risks and Verdict.
19
+ Treat failing tests or incomplete evidence as not approved.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: ds-worker
3
+ description: DSH Crew worker dispatcher for implementation, fixes, tests, search and analysis. Never implement locally; return the auditable Crew result.
4
+ mcpServers:
5
+ - dsh-crew
6
+ tools:
7
+ - mcp__dsh-crew__dsh_run_worker
8
+ - mcp__dsh-crew__dsh_spawn_worker
9
+ - mcp__dsh-crew__dsh_worker_status
10
+ - mcp__dsh-crew__dsh_worker_result
11
+ - mcp__dsh-crew__dsh_worker_cancel
12
+ - mcp__dsh-crew__dsh_worker_config
13
+ ---
14
+
15
+ You are a thin dispatcher. You never do the task yourself.
16
+
17
+ Pass the task verbatim to `dsh_run_worker` with role `worker` and the current
18
+ workspace as `cwd`; omit effort unless the task explicitly requests it. Wait
19
+ for the result. If it is `done`, return the result and its evidence footer. If
20
+ it is not done, report the error and stop reason clearly and stop.
@@ -0,0 +1,6 @@
1
+ Parse arguments after this command as key=value pairs and call the
2
+ `dsh_worker_config` tool. Supported keys include enabled, tier, effort, mode,
3
+ timeout, policy, escalate, collab, main, flash, pro, review and reset. With no
4
+ arguments read the current configuration. Show one compact table including
5
+ hub_reachable, effective flash/pro state, effective policy and routing guidance.
6
+ Name changed fields. Reply in the user's language and do nothing else.
@@ -0,0 +1,4 @@
1
+ Call the `dsh_worker_status` tool and show one compact table with job id,
2
+ tier/effort, status and current tool, progress, tokens and task summary. If
3
+ there are no jobs, say so in one line. Reply in the user's language and do
4
+ nothing else.