@ran-sh/dsh-crew 2.0.7 → 2.0.9

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.7",
3
+ "version": "2.0.9",
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.7",
3
+ "version": "2.0.9",
4
4
  "type": "module",
5
5
  "main": "./src/hub/entry.mjs",
6
6
  "bin": {
@@ -15,6 +15,14 @@ import { integrationRoot } from './crew-paths.mjs';
15
15
  const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
16
16
  const MARKETPLACE_NAME = 'dsh-crew';
17
17
  const PLUGIN_KEY = `dsh-crew@${MARKETPLACE_NAME}`;
18
+ // A Claude Code plugin refresh is a real copy of the plugin tree, and its cost
19
+ // tracks machine load: 163s measured idle, ~6 minutes measured during an
20
+ // activation. The ceiling bounds the shell's patience, and the settle window
21
+ // keeps watching afterwards, because a timed-out child on Windows is not in the
22
+ // shell's process tree and goes on writing once the shell has given up.
23
+ const CLAUDE_PLUGIN_TIMEOUT_MS = 300_000;
24
+ const CLAUDE_SNAPSHOT_SETTLE_MS = 180_000;
25
+ const CLAUDE_SNAPSHOT_POLL_MS = 5_000;
18
26
  const POLICY_START = '<!-- DSH CREW MANAGED POLICY:START -->';
19
27
  const POLICY_END = '<!-- DSH CREW MANAGED POLICY:END -->';
20
28
 
@@ -510,6 +518,22 @@ export function claudeIntegrationLine(result) {
510
518
  return '✓ Claude Code integration';
511
519
  }
512
520
 
521
+ /**
522
+ * How long to keep watching the plugin snapshot after a failed CLI attempt.
523
+ *
524
+ * Zero unless that attempt timed out. The install is a real copy of the plugin
525
+ * tree, and a timed-out child on Windows is not in the shell's process tree, so
526
+ * it can go on writing after the shell has given up — that is the only case with
527
+ * something to wait for. A `claude` that is not installed exits with status 1 and
528
+ * never started a copy, and a CLI that failed on its own terms has already
529
+ * stopped; waiting on either cost a fixed 180s on every update of a machine
530
+ * without Claude Code, to learn nothing it did not already know.
531
+ */
532
+ export function claudeSnapshotSettleMs(err) {
533
+ const timedOut = err?.code === 'ETIMEDOUT' || err?.signal === 'SIGTERM';
534
+ return timedOut ? CLAUDE_SNAPSHOT_SETTLE_MS : 0;
535
+ }
536
+
513
537
  export async function installClaudeCode({ home = homedir(), statusline = false, root = ROOT } = {}) {
514
538
  const actions = [];
515
539
 
@@ -597,6 +621,9 @@ export async function installClaudeCode({ home = homedir(), statusline = false,
597
621
  actions.push('cli: skipped (non-default home; test mode)');
598
622
  return { ok: true, actions };
599
623
  }
624
+ // Set only when the CLI attempt threw, and only a timeout then justifies
625
+ // waiting for the snapshot: see claudeSnapshotSettleMs.
626
+ let installError = null;
600
627
  try {
601
628
  const { execSync } = await import('node:child_process');
602
629
  // 300s, not 120: the install below runs after the uninstall, so it does a real
@@ -604,7 +631,7 @@ export async function installClaudeCode({ home = homedir(), statusline = false,
604
631
  // gets. Measured on this machine: `marketplace add` 3s, `uninstall` 3s,
605
632
  // `install` 163s (no-op install: 6s). The old ceiling killed the copy partway
606
633
  // 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 });
634
+ const run = (cmd) => execSync(cmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: CLAUDE_PLUGIN_TIMEOUT_MS });
608
635
  try { run(`claude plugin marketplace add ${JSON.stringify(mpDir)}`); actions.push('cli: marketplace registered'); }
609
636
  catch { actions.push('cli: marketplace add skipped (already registered)'); }
610
637
  // `plugin install` on an already-installed plugin is a no-op and leaves a
@@ -621,6 +648,7 @@ export async function installClaudeCode({ home = homedir(), statusline = false,
621
648
  }
622
649
  actions.push(`cli: plugin snapshot refreshed (${PLUGIN_KEY})`);
623
650
  } catch (err) {
651
+ installError = err;
624
652
  actions.push(`cli: plugin install failed — run manually: claude plugin install ${PLUGIN_KEY} (${String(err?.message ?? err).slice(0, 120)})`);
625
653
  }
626
654
  // Report the state that resulted, not the step that was attempted. The CLI is
@@ -630,6 +658,21 @@ export async function installClaudeCode({ home = homedir(), statusline = false,
630
658
  // snapshot that `installStatus` reads. Saying so here is what lets the caller
631
659
  // stop printing a checkmark for a state it never verified.
632
660
  if (claudeSnapshotReady(home, root)) return { ok: true, actions };
661
+ // The CLI can outlive the ceiling above. The install is a real copy of the
662
+ // plugin tree — 163s measured on an idle machine, and ~6 minutes measured
663
+ // during an activation, where the record landed well after any ceiling — and a
664
+ // timed-out child on Windows is not in the shell's process tree, so a copy that
665
+ // is still running keeps writing while this function has already given up on
666
+ // it. Wait for the snapshot to settle before reporting it missing, or the
667
+ // update says "not loaded" about a plugin that is loading.
668
+ const settleDeadline = Date.now() + claudeSnapshotSettleMs(installError);
669
+ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
670
+ while (Date.now() < settleDeadline) {
671
+ await wait(CLAUDE_SNAPSHOT_POLL_MS);
672
+ if (claudeSnapshotReady(home, root)) {
673
+ return { ok: true, actions: [...actions, 'cli: snapshot confirmed current after the ceiling'] };
674
+ }
675
+ }
633
676
  return {
634
677
  ok: true,
635
678
  degraded: true,
@@ -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.7';
39
+ export const RUNTIME_VERSION = '2.0.9';
40
40
  export const HUB_PROTOCOL_VERSION = 1;
41
41
 
42
42
  export const HUB_CAPABILITIES = Object.freeze([