@ran-sh/dsh-crew 2.0.5 → 2.0.7

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-crew",
3
- "version": "2.0.5",
3
+ "version": "2.0.7",
4
4
  "description": "Dispatch subtasks to DeepSeek Harness (DSH) agents as native subagents with live progress",
5
5
  "author": {
6
6
  "name": "ZSeven-W"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ran-sh/dsh-crew",
3
- "version": "2.0.5",
3
+ "version": "2.0.7",
4
4
  "type": "module",
5
5
  "main": "./src/hub/entry.mjs",
6
6
  "bin": {
package/scripts/setup.mjs CHANGED
@@ -11,7 +11,7 @@ import { fileURLToPath } from 'node:url';
11
11
  import { dirname, join, resolve } from 'node:path';
12
12
  import { homedir } from 'node:os';
13
13
  import * as realInstaller from '../src/install/install.mjs';
14
- import { crewDshHome, crewProfileDir, CREW_PROFILE_NAME } from '../src/install/install.mjs';
14
+ import { crewDshHome, crewProfileDir, CREW_PROFILE_NAME, claudeIntegrationLine } from '../src/install/install.mjs';
15
15
  import {
16
16
  ensureCrewDshRuntime,
17
17
  ensureCrewPluginRegistration,
@@ -136,6 +136,30 @@ function mark(log, ok, text) {
136
136
  return ok;
137
137
  }
138
138
 
139
+ /**
140
+ * The Claude Code step of a checkout install.
141
+ *
142
+ * Extracted so it can be exercised directly: the rest of setupInstall runs a real
143
+ * dependency install and client build, which a unit test must not do. Both
144
+ * install entries render the same result through `claudeIntegrationLine`, so a
145
+ * result one entry learned to handle cannot be silently checkmarked by the other.
146
+ */
147
+ export async function runClaudeIntegrationStep({
148
+ dryRun = false,
149
+ log = console.log,
150
+ root = ROOT,
151
+ home = homedir(),
152
+ installer = realInstaller,
153
+ } = {}) {
154
+ if (dryRun) { mark(log, true, 'Claude Code integration (dry-run)'); return { ok: true }; }
155
+ const r = installer.installClaudeCode
156
+ ? await installer.installClaudeCode({ home, root })
157
+ : await realInstaller.installClaudeCode({ home, root });
158
+ log(claudeIntegrationLine(r));
159
+ if (r.ok === false) return { ok: false, error: 'Claude Code integration failed' };
160
+ return { ok: true };
161
+ }
162
+
139
163
  export async function setupInstall({
140
164
  dryRun = false,
141
165
  log = console.log,
@@ -250,13 +274,8 @@ export async function setupInstall({
250
274
  }
251
275
 
252
276
  if (commandExists('claude')) {
253
- if (dryRun) mark(log, true, 'Claude Code integration (dry-run)');
254
- else {
255
- const r = installer.installClaudeCode ? await installer.installClaudeCode({ home, root }) : await realInstaller.installClaudeCode({ home, root });
256
- const ok = r.ok !== false;
257
- mark(log, ok, ok ? 'Claude Code integration' : 'Claude Code integration failed');
258
- if (!ok) return { ok: false, error: 'Claude Code integration failed' };
259
- }
277
+ const r = await runClaudeIntegrationStep({ dryRun, log, root, home, installer });
278
+ if (!r.ok) return r;
260
279
  } else log('- Claude Code not detected, skipped');
261
280
 
262
281
  log('');
@@ -492,6 +492,24 @@ export function uninstallCodex({ home = homedir(), env = process.env } = {}) {
492
492
  return { ok: true, actions: actions.length ? actions : ['nothing to remove'] };
493
493
  }
494
494
 
495
+ /**
496
+ * The one line both install entries print for a Claude Code integration result.
497
+ *
498
+ * A missing `claude` CLI is a supported install, so `installClaudeCode` stays
499
+ * best-effort and keeps `ok: true`. A CLI that is present and fails or times out
500
+ * is a different thing: it leaves Claude Code without the plugin snapshot while
501
+ * the settings still name it, which is the state `installStatus` later reads as
502
+ * "needs repair". This used to be rendered separately in each entry — and only
503
+ * one of them learned about `degraded`, so the other kept printing a checkmark.
504
+ */
505
+ export function claudeIntegrationLine(result) {
506
+ if (result?.ok === false) return '✗ Claude Code integration failed';
507
+ if (result?.degraded === true) {
508
+ return `- Claude Code integration registered, but not loaded: ${result.reason ?? 'plugin snapshot not refreshed'}`;
509
+ }
510
+ return '✓ Claude Code integration';
511
+ }
512
+
495
513
  export async function installClaudeCode({ home = homedir(), statusline = false, root = ROOT } = {}) {
496
514
  const actions = [];
497
515
 
@@ -581,7 +599,12 @@ export async function installClaudeCode({ home = homedir(), statusline = false,
581
599
  }
582
600
  try {
583
601
  const { execSync } = await import('node:child_process');
584
- const run = (cmd) => execSync(cmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 120_000 });
602
+ // 300s, not 120: the install below runs after the uninstall, so it does a real
603
+ // copy of the plugin tree rather than the no-op an already-installed plugin
604
+ // gets. Measured on this machine: `marketplace add` 3s, `uninstall` 3s,
605
+ // `install` 163s (no-op install: 6s). The old ceiling killed the copy partway
606
+ // and left Claude Code without the plugin the same run had just removed.
607
+ const run = (cmd) => execSync(cmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 300_000 });
585
608
  try { run(`claude plugin marketplace add ${JSON.stringify(mpDir)}`); actions.push('cli: marketplace registered'); }
586
609
  catch { actions.push('cli: marketplace add skipped (already registered)'); }
587
610
  // `plugin install` on an already-installed plugin is a no-op and leaves a
@@ -41,7 +41,7 @@ import { fileURLToPath } from 'node:url';
41
41
  import { homedir } from 'node:os';
42
42
  import * as realInstaller from './install.mjs';
43
43
  import { samePayloadContent, capturePayloadContent } from './payload-content.mjs';
44
- import { crewDshHome, crewProfileDir } from './install.mjs';
44
+ import { crewDshHome, crewProfileDir, claudeIntegrationLine } from './install.mjs';
45
45
  import { releaseClaimsState } from '../release-in-use.mjs';
46
46
  import { compareProcessToken, processStartToken } from '../process-identity.mjs';
47
47
  import { renameTree } from './tree-move.mjs';
@@ -2626,25 +2626,6 @@ function currentInstallationHealth({ home }) {
2626
2626
  return { installed: true, healthy: validated.ok && registered, validated, registered, pointer };
2627
2627
  }
2628
2628
 
2629
- /**
2630
- * What the operator is told about the Claude Code integration.
2631
- *
2632
- * A missing `claude` CLI is a supported install, so `installClaudeCode` stays
2633
- * best-effort and keeps `ok: true`. A CLI that is present and fails or times out
2634
- * is a different thing: it leaves Claude Code without the plugin snapshot while
2635
- * the settings still name it, which is the state that later reads as "needs
2636
- * repair". This line used to be `${ok === false ? '✗' : '✓'}`, and since `ok` was
2637
- * never false, the failure branch could not fire and a broken integration was
2638
- * reported as a checkmark.
2639
- */
2640
- export function claudeIntegrationLine(result) {
2641
- if (result?.ok === false) return '✗ Claude Code integration failed';
2642
- if (result?.degraded === true) {
2643
- return `- Claude Code integration registered, but not loaded: ${result.reason ?? 'plugin snapshot not refreshed'}`;
2644
- }
2645
- return '✓ Claude Code integration';
2646
- }
2647
-
2648
2629
  async function activateRelease({ home, releaseDir, manifest, log, installer, supervisorRoot = runningPackageRoot() }) {
2649
2630
  const registration = ensureCrewPluginRegistration({ home, root: releaseDir, name: manifest.name });
2650
2631
  if (!registration.ok) {
@@ -36,7 +36,7 @@ export {
36
36
  // included in the identity contract.
37
37
  const RUNTIME_ID = randomUUID();
38
38
 
39
- export const RUNTIME_VERSION = '2.0.5';
39
+ export const RUNTIME_VERSION = '2.0.7';
40
40
  export const HUB_PROTOCOL_VERSION = 1;
41
41
 
42
42
  export const HUB_CAPABILITIES = Object.freeze([