@ran-sh/dsh-crew 2.0.4 → 2.0.6

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.4",
3
+ "version": "2.0.6",
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.4",
3
+ "version": "2.0.6",
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
 
@@ -600,7 +618,20 @@ export async function installClaudeCode({ home = homedir(), statusline = false,
600
618
  } catch (err) {
601
619
  actions.push(`cli: plugin install failed — run manually: claude plugin install ${PLUGIN_KEY} (${String(err?.message ?? err).slice(0, 120)})`);
602
620
  }
603
- return { ok: true, actions };
621
+ // Report the state that resulted, not the step that was attempted. The CLI is
622
+ // best-effort — a machine without `claude` is a supported install, and its
623
+ // settings alone are correct — but a CLI that is present and slow, timed out,
624
+ // or failed leaves the snapshot exactly as stale as it was, and it is the
625
+ // snapshot that `installStatus` reads. Saying so here is what lets the caller
626
+ // stop printing a checkmark for a state it never verified.
627
+ if (claudeSnapshotReady(home, root)) return { ok: true, actions };
628
+ return {
629
+ ok: true,
630
+ degraded: true,
631
+ code: 'CLAUDE_PLUGIN_SNAPSHOT_STALE',
632
+ reason: `Claude Code has not loaded the plugin; run: claude plugin install ${PLUGIN_KEY}`,
633
+ actions,
634
+ };
604
635
  }
605
636
 
606
637
  export function installCodex({ home = homedir(), scope, root = ROOT, env = process.env } = {}) {
@@ -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';
@@ -2668,10 +2668,10 @@ async function activateRelease({ home, releaseDir, manifest, log, installer, sup
2668
2668
 
2669
2669
  const claude = await installer.installClaudeCode({ home, root: integrationRoot });
2670
2670
  if (claude.ok === false) {
2671
- log(`✗ Claude Code integration failed`);
2671
+ log(claudeIntegrationLine(claude));
2672
2672
  return false;
2673
2673
  }
2674
- log('✓ Claude Code integration');
2674
+ log(claudeIntegrationLine(claude));
2675
2675
 
2676
2676
  // The official ~/.dsh/profiles/web tree is read-only: activation never
2677
2677
  // repairs or mutates the official bridge. The isolated 3210 Crew backend
@@ -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.4';
39
+ export const RUNTIME_VERSION = '2.0.6';
40
40
  export const HUB_PROTOCOL_VERSION = 1;
41
41
 
42
42
  export const HUB_CAPABILITIES = Object.freeze([