hail-hydra-cc 2.3.2 → 2.4.1

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,85 +1,85 @@
1
- ---
2
- description: Show Hydra framework status — installed agents, version, config, and update availability
3
- allowed-tools: Bash, Read, Glob
4
- ---
5
-
6
- # Hydra Status
7
-
8
- Show a comprehensive status report for the Hydra framework.
9
-
10
- ## 1. Version Info
11
- ```bash
12
- INSTALLED=$(cat ~/.claude/skills/hydra/VERSION 2>/dev/null || echo "unknown")
13
- echo "Installed: $INSTALLED"
14
- LATEST=$(npm view hail-hydra-cc version 2>/dev/null || echo "unknown")
15
- echo "Latest: $LATEST"
16
- ```
17
-
18
- ## 2. Installed Agents
19
- ```bash
20
- echo "=== Global Agents ==="
21
- ls -1 ~/.claude/agents/hydra-*.md 2>/dev/null || echo "None found"
22
- echo "=== Local Agents ==="
23
- ls -1 .claude/agents/hydra-*.md 2>/dev/null || echo "None found"
24
- ```
25
-
26
- ## 3. Installed Commands
27
- ```bash
28
- echo "=== Global Commands ==="
29
- ls -1 ~/.claude/commands/hydra/*.md 2>/dev/null || echo "None found"
30
- echo "=== Local Commands ==="
31
- ls -1 .claude/commands/hydra/*.md 2>/dev/null || echo "None found"
32
- ```
33
-
34
- ## 4. Hooks
35
- ```bash
36
- ls -1 ~/.claude/hooks/hydra-*.js 2>/dev/null || echo "None found"
37
- ```
38
-
39
- ## 5. Configuration
40
- ```bash
41
- cat ~/.claude/skills/hydra/config/hydra.config.md 2>/dev/null || \
42
- cat .claude/skills/hydra/config/hydra.config.md 2>/dev/null || \
43
- echo "No config file found (using defaults)"
44
- ```
45
-
46
- ## 6. Codebase Map
47
- ```bash
48
- if [ -f ".claude/hydra/codebase-map.json" ]; then
49
- echo "Map: ✅ Exists"
50
- node -e "const m=JSON.parse(require('fs').readFileSync('.claude/hydra/codebase-map.json','utf8'));console.log('Files:',m._meta.file_count);console.log('Built:',m._meta.built_at);console.log('Hash:',m._meta.git_hash);"
51
- else
52
- echo "Map: ❌ Not built yet (run /hydra:map rebuild)"
53
- fi
54
- ```
55
-
56
- ## Display Format
57
-
58
- Present results as a clean status card:
59
-
60
- ```
61
- 🐉 Hydra Framework Status
62
- ──────────────────────────────
63
- Version: 1.0.0 (latest: 1.0.0 ✅) OR (update available: 1.1.0 ⚡)
64
- Install: Global (~/.claude/)
65
- Agents (7):
66
- 🟢 hydra-scout (Haiku 4.5) ✅
67
- 🟢 hydra-runner (Haiku 4.5) ✅
68
- 🟢 hydra-scribe (Haiku 4.5) ✅
69
- 🟢 hydra-guard (Haiku 4.5) ✅
70
- 🟢 hydra-git (Haiku 4.5) ✅
71
- 🔵 hydra-coder (Sonnet 4.6) ✅
72
- 🔵 hydra-analyst (Sonnet 4.6) ✅
73
- Commands (12): update, status, help, config, guard, quiet, verbose, report, map, preflight, stats, stfu
74
- Hooks (4): check-update ✅, statusline ✅, auto-guard ✅, notify ✅
75
- Map: ✅ Current (487 files, built 2026-03-26)
76
- Config: balanced mode, dispatch log on, auto-guard on
77
- ──────────────────────────────
78
- ```
79
-
80
- If an update is available, add:
81
- ```
82
- ⚡ Update available! Run /hydra:update to get the latest version.
83
- ```
84
-
85
- For detailed token usage and savings, run: `/hydra:stats`
1
+ ---
2
+ description: Show Hydra framework status — installed agents, version, config, and update availability
3
+ allowed-tools: Bash, Read, Glob
4
+ ---
5
+
6
+ # Hydra Status
7
+
8
+ Show a comprehensive status report for the Hydra framework.
9
+
10
+ ## 1. Version Info
11
+ ```bash
12
+ INSTALLED=$(cat ~/.claude/skills/hydra/VERSION 2>/dev/null || echo "unknown")
13
+ echo "Installed: $INSTALLED"
14
+ LATEST=$(npm view hail-hydra-cc version 2>/dev/null || echo "unknown")
15
+ echo "Latest: $LATEST"
16
+ ```
17
+
18
+ ## 2. Installed Agents
19
+ ```bash
20
+ echo "=== Global Agents ==="
21
+ ls -1 ~/.claude/agents/hydra-*.md 2>/dev/null || echo "None found"
22
+ echo "=== Local Agents ==="
23
+ ls -1 .claude/agents/hydra-*.md 2>/dev/null || echo "None found"
24
+ ```
25
+
26
+ ## 3. Installed Commands
27
+ ```bash
28
+ echo "=== Global Commands ==="
29
+ ls -1 ~/.claude/commands/hydra/*.md 2>/dev/null || echo "None found"
30
+ echo "=== Local Commands ==="
31
+ ls -1 .claude/commands/hydra/*.md 2>/dev/null || echo "None found"
32
+ ```
33
+
34
+ ## 4. Hooks
35
+ ```bash
36
+ ls -1 ~/.claude/hooks/hydra-*.js 2>/dev/null || echo "None found"
37
+ ```
38
+
39
+ ## 5. Configuration
40
+ ```bash
41
+ cat ~/.claude/skills/hydra/config/hydra.config.md 2>/dev/null || \
42
+ cat .claude/skills/hydra/config/hydra.config.md 2>/dev/null || \
43
+ echo "No config file found (using defaults)"
44
+ ```
45
+
46
+ ## 6. Codebase Map
47
+ ```bash
48
+ if [ -f ".claude/hydra/codebase-map.json" ]; then
49
+ echo "Map: ✅ Exists"
50
+ node -e "const m=JSON.parse(require('fs').readFileSync('.claude/hydra/codebase-map.json','utf8'));console.log('Files:',m._meta.file_count);console.log('Built:',m._meta.built_at);console.log('Hash:',m._meta.git_hash);"
51
+ else
52
+ echo "Map: ❌ Not built yet (run /hydra:map rebuild)"
53
+ fi
54
+ ```
55
+
56
+ ## Display Format
57
+
58
+ Present results as a clean status card:
59
+
60
+ ```
61
+ 🐉 Hydra Framework Status
62
+ ──────────────────────────────
63
+ Version: 1.0.0 (latest: 1.0.0 ✅) OR (update available: 1.1.0 ⚡)
64
+ Install: Global (~/.claude/)
65
+ Agents (7):
66
+ 🟢 hydra-scout (Haiku 4.5) ✅
67
+ 🟢 hydra-runner (Haiku 4.5) ✅
68
+ 🟢 hydra-scribe (Haiku 4.5) ✅
69
+ 🟢 hydra-guard (Haiku 4.5) ✅
70
+ 🟢 hydra-git (Haiku 4.5) ✅
71
+ 🔵 hydra-coder (Sonnet 4.6) ✅
72
+ 🔵 hydra-analyst (Sonnet 4.6) ✅
73
+ Commands (12): update, status, help, config, guard, quiet, verbose, report, map, preflight, stats, stfu
74
+ Hooks (4): check-update ✅, statusline ✅, auto-guard ✅, notify ✅
75
+ Map: ✅ Current (487 files, built 2026-03-26)
76
+ Config: balanced mode, dispatch log on, auto-guard on
77
+ ──────────────────────────────
78
+ ```
79
+
80
+ If an update is available, add:
81
+ ```
82
+ ⚡ Update available! Run /hydra:update to get the latest version.
83
+ ```
84
+
85
+ For detailed token usage and savings, run: `/hydra:stats`
@@ -1,29 +1,29 @@
1
- ---
2
- description: Enable verbose Hydra dispatch logs with timing and token estimates
3
- ---
4
-
5
- # Hydra Verbose Mode
6
-
7
- Acknowledge this command and remember for the rest of this session:
8
-
9
- **Display DETAILED Hydra Dispatch Logs after every task that involves delegation.**
10
-
11
- The verbose log includes extra columns for timing:
12
-
13
- ```
14
- 🐉 Hydra Dispatch Log (verbose)
15
- | Step | Agent | Task | Time | Verdict |
16
- |------|---------------------|-----------------------|-------|-------------|
17
- | 1 | hydra-scout (Haiku 4.5) | Explored auth module | 3.2s | ✅ Accepted |
18
- | 2 | hydra-coder (Sonnet 4.6) | Fixed null check | 8.1s | ✅ Accepted |
19
- | 3 | hydra-guard (Haiku 4.5) | Security scan | 1.4s | ✅ Passed |
20
- | 4 | hydra-runner (Haiku 4.5) | Ran test suite | 4.7s | ✅ Accepted |
21
-
22
- Delegation: 4/4 (100%) | Accepted: 4 | Adjusted: 0 | Rejected: 0
23
- Total delegation time: 17.4s | Waves: 2
24
- ```
25
-
26
- Respond with:
27
- "🐉 Verbose mode enabled. Dispatch logs will include timing details. Use /hydra:quiet to suppress."
28
-
29
- Re-enable the task completion notification sound.
1
+ ---
2
+ description: Enable verbose Hydra dispatch logs with timing and token estimates
3
+ ---
4
+
5
+ # Hydra Verbose Mode
6
+
7
+ Acknowledge this command and remember for the rest of this session:
8
+
9
+ **Display DETAILED Hydra Dispatch Logs after every task that involves delegation.**
10
+
11
+ The verbose log includes extra columns for timing:
12
+
13
+ ```
14
+ 🐉 Hydra Dispatch Log (verbose)
15
+ | Step | Agent | Task | Time | Verdict |
16
+ |------|---------------------|-----------------------|-------|-------------|
17
+ | 1 | hydra-scout (Haiku 4.5) | Explored auth module | 3.2s | ✅ Accepted |
18
+ | 2 | hydra-coder (Sonnet 4.6) | Fixed null check | 8.1s | ✅ Accepted |
19
+ | 3 | hydra-guard (Haiku 4.5) | Security scan | 1.4s | ✅ Passed |
20
+ | 4 | hydra-runner (Haiku 4.5) | Ran test suite | 4.7s | ✅ Accepted |
21
+
22
+ Delegation: 4/4 (100%) | Accepted: 4 | Adjusted: 0 | Rejected: 0
23
+ Total delegation time: 17.4s | Waves: 2
24
+ ```
25
+
26
+ Respond with:
27
+ "🐉 Verbose mode enabled. Dispatch logs will include timing details. Use /hydra:quiet to suppress."
28
+
29
+ Re-enable the task completion notification sound.
@@ -1,54 +1,130 @@
1
- #!/usr/bin/env node
2
-
3
- // Hydra Auto-Guard Hook — PostToolUse (matcher: Write|Edit|MultiEdit)
4
- // Records changed file paths to a temp file for hydra-guard to scan later.
5
- // Does NOT run the scan itself — that would slow down every edit.
6
- // Overhead: <1ms per edit. Files deduped by path.
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
- const os = require('os');
11
-
12
- let input = '';
13
- process.stdin.on('data', (chunk) => (input += chunk));
14
- process.stdin.on('end', () => {
15
- try {
16
- const data = JSON.parse(input);
17
-
18
- // Extract the file path from the tool input
19
- const filePath = data.tool_input?.file_path ||
20
- data.tool_input?.path ||
21
- null;
22
-
23
- if (!filePath) {
24
- process.exit(0);
25
- }
26
-
27
- // Append to session-scoped changed files list
28
- const sessionId = data.session_id || 'unknown';
29
- const trackingDir = path.join(os.tmpdir(), 'hydra-guard');
30
- const trackingFile = path.join(trackingDir, `${sessionId}.txt`);
31
-
32
- // Ensure directory exists
33
- if (!fs.existsSync(trackingDir)) {
34
- fs.mkdirSync(trackingDir, { recursive: true });
35
- }
36
-
37
- // Read existing tracked files
38
- let existing = '';
39
- try {
40
- existing = fs.readFileSync(trackingFile, 'utf8');
41
- } catch (e) {
42
- // File doesn't exist yet — that's fine
43
- }
44
-
45
- // Only append if not already tracked (dedup)
46
- if (!existing.split('\n').includes(filePath)) {
47
- fs.appendFileSync(trackingFile, filePath + '\n');
48
- }
49
-
50
- } catch (e) {
51
- // Silently fail NEVER block Claude Code
52
- }
53
- process.exit(0);
54
- });
1
+ #!/usr/bin/env node
2
+
3
+ // Hydra Auto-Guard Hook — PostToolUse (matcher: Write|Edit|MultiEdit)
4
+ // Records changed file paths to a temp file for hydra-guard to scan later.
5
+ // Does NOT run the scan itself — that would slow down every edit.
6
+ // Overhead: <1ms per edit. Files deduped by path.
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const os = require('os');
11
+
12
+ let input = '';
13
+ process.stdin.on('data', (chunk) => (input += chunk));
14
+ process.stdin.on('end', () => {
15
+ try {
16
+ const data = JSON.parse(input);
17
+
18
+ // Extract the file path from the tool input
19
+ const filePath = data.tool_input?.file_path ||
20
+ data.tool_input?.path ||
21
+ null;
22
+
23
+ if (!filePath) {
24
+ process.exit(0);
25
+ }
26
+
27
+ // Append to session-scoped changed files list
28
+ const sessionId = data.session_id || 'unknown';
29
+ const trackingDir = path.join(os.tmpdir(), 'hydra-guard');
30
+ const trackingFile = path.join(trackingDir, `${sessionId}.txt`);
31
+
32
+ // Ensure directory exists
33
+ if (!fs.existsSync(trackingDir)) {
34
+ fs.mkdirSync(trackingDir, { recursive: true });
35
+ }
36
+
37
+ // Read existing tracked files
38
+ let existing = '';
39
+ try {
40
+ existing = fs.readFileSync(trackingFile, 'utf8');
41
+ } catch (e) {
42
+ // File doesn't exist yet — that's fine
43
+ }
44
+
45
+ // Only append if not already tracked (dedup)
46
+ if (!existing.split('\n').includes(filePath)) {
47
+ fs.appendFileSync(trackingFile, filePath + '\n');
48
+ }
49
+
50
+ // === Sentinel Pending Flag ===
51
+ // Write a flag file that the statusline hook reads.
52
+ // This shows "⚠ Sentinel pending" in the status bar until cleared.
53
+ const sentinelDir = path.join(os.tmpdir(), 'hydra-sentinel');
54
+ const sentinelFlag = path.join(sentinelDir, `${sessionId}-pending.json`);
55
+
56
+ if (!fs.existsSync(sentinelDir)) {
57
+ fs.mkdirSync(sentinelDir, { recursive: true });
58
+ }
59
+
60
+ // Read existing pending data or create new
61
+ let pending = { files: [], created_at: Date.now() };
62
+ try {
63
+ pending = JSON.parse(fs.readFileSync(sentinelFlag, 'utf8'));
64
+ } catch (e) {
65
+ // New flag
66
+ }
67
+
68
+ // Add file to pending list (dedup)
69
+ if (!pending.files.includes(filePath)) {
70
+ pending.files.push(filePath);
71
+ }
72
+ pending.updated_at = Date.now();
73
+
74
+ fs.writeFileSync(sentinelFlag, JSON.stringify(pending));
75
+
76
+ // New edit invalidates any prior "clean" marker
77
+ try {
78
+ const scanMarker = path.join(sentinelDir, `${sessionId}-last-scan`);
79
+ if (fs.existsSync(scanMarker)) fs.unlinkSync(scanMarker);
80
+ } catch (_) { /* silent — never block */ }
81
+
82
+ // === Substantial-Edit Detection + Directive Injection (v2.4.0+) ===
83
+ // For Write, MultiEdit, or large Edits, inject a directive recommending
84
+ // hydra-sentinel-scan dispatch. Trivial edits stay silent.
85
+ const toolName = data.tool_name || '';
86
+ const isWrite = toolName === 'Write';
87
+ const isMultiEdit = toolName === 'MultiEdit';
88
+ const oldString = data.tool_input?.old_string || '';
89
+ const newString = data.tool_input?.new_string || '';
90
+
91
+ let isSubstantial = isWrite || isMultiEdit;
92
+ if (!isSubstantial) {
93
+ const oldLines = (oldString.match(/\n/g) || []).length;
94
+ const newLines = (newString.match(/\n/g) || []).length;
95
+ isSubstantial = oldLines > 5 || newLines > 5 ||
96
+ oldString.length > 200 || newString.length > 200;
97
+ }
98
+
99
+ if (isSubstantial) {
100
+ // Best-effort codebase map risk lookup
101
+ let riskNote = '';
102
+ try {
103
+ const mapPath = path.join(process.cwd(), '.claude', 'hydra', 'codebase-map.json');
104
+ if (fs.existsSync(mapPath)) {
105
+ const map = JSON.parse(fs.readFileSync(mapPath, 'utf8'));
106
+ const relPath = path.relative(process.cwd(), filePath).split(path.sep).join('/');
107
+ const fileEntry = map.files && map.files[relPath];
108
+ if (fileEntry && fileEntry.risk) {
109
+ riskNote = ` Risk: ${fileEntry.risk} (${fileEntry.dependents_count || 0} dependents).`;
110
+ }
111
+ }
112
+ } catch (e) {
113
+ // Map missing or malformed — no risk note appended
114
+ }
115
+
116
+ const directive = {
117
+ hookSpecificOutput: {
118
+ hookEventName: 'PostToolUse',
119
+ additionalContext: `🐉 Hydra Auto-Guard: Substantial change to ${path.basename(filePath)}.${riskNote} Dispatch hydra-sentinel-scan to verify integration before presenting results to the user.`
120
+ }
121
+ };
122
+
123
+ process.stdout.write(JSON.stringify(directive));
124
+ }
125
+
126
+ } catch (e) {
127
+ // Silently fail — NEVER block Claude Code
128
+ }
129
+ process.exit(0);
130
+ });
@@ -1,99 +1,99 @@
1
- #!/usr/bin/env node
2
-
3
- // Hydra Update Checker — SessionStart hook
4
- // Spawns a DETACHED background process to check npm for updates.
5
- // Writes result to ~/.claude/cache/hydra-update-check.json
6
- // The statusline hook reads this cache file.
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
- const os = require('os');
11
- const { spawn } = require('child_process');
12
-
13
- const homeDir = os.homedir();
14
- const cacheDir = path.join(homeDir, '.claude', 'cache');
15
- const cacheFile = path.join(cacheDir, 'hydra-update-check.json');
16
-
17
- // VERSION file locations (check project first, then global)
18
- const projectVersionFile = path.join(process.cwd(), '.claude', 'hydra', 'VERSION');
19
- const globalVersionFile = path.join(homeDir, '.claude', 'hydra', 'VERSION');
20
-
21
- // Ensure cache directory exists
22
- if (!fs.existsSync(cacheDir)) {
23
- fs.mkdirSync(cacheDir, { recursive: true });
24
- }
25
-
26
- // Skip check if we checked within the last hour
27
- try {
28
- const existing = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
29
- const age = Date.now() - (existing.checked_at || 0);
30
- if (age < 3600000) { // 1 hour
31
- process.exit(0);
32
- }
33
- } catch (e) {
34
- // No cache or invalid — proceed with check
35
- }
36
-
37
- // Read stdin to prevent EPIPE (Claude Code pipes JSON to all hooks)
38
- let stdinData = '';
39
- process.stdin.on('data', (chunk) => (stdinData += chunk));
40
- process.stdin.on('end', () => {
41
- // Spawn background process (MUST be detached to not block Claude Code)
42
- const child = spawn(process.execPath, ['-e', `
43
- const fs = require('fs');
44
- const { execSync } = require('child_process');
45
-
46
- const cacheFile = ${JSON.stringify(cacheFile)};
47
- const projectVersionFile = ${JSON.stringify(projectVersionFile)};
48
- const globalVersionFile = ${JSON.stringify(globalVersionFile)};
49
-
50
- try {
51
- // Read installed version
52
- let installed = 'unknown';
53
- try {
54
- installed = fs.readFileSync(projectVersionFile, 'utf8').trim();
55
- } catch (e) {
56
- try {
57
- installed = fs.readFileSync(globalVersionFile, 'utf8').trim();
58
- } catch (e2) {}
59
- }
60
-
61
- // Fetch latest version from npm (with timeout)
62
- const latest = execSync('npm view hail-hydra-cc version', {
63
- encoding: 'utf8',
64
- timeout: 10000,
65
- windowsHide: true,
66
- stdio: ['pipe', 'pipe', 'pipe']
67
- }).trim();
68
-
69
- // Compare and write cache
70
- const updateAvailable = installed !== 'unknown' && latest !== installed;
71
-
72
- const result = {
73
- installed: installed,
74
- latest: latest,
75
- update_available: updateAvailable,
76
- checked_at: Date.now()
77
- };
78
-
79
- fs.writeFileSync(cacheFile, JSON.stringify(result, null, 2));
80
- } catch (e) {
81
- // Network error or npm not available — write a "no check" result
82
- const result = {
83
- installed: 'unknown',
84
- latest: 'unknown',
85
- update_available: false,
86
- checked_at: Date.now(),
87
- error: e.message
88
- };
89
- fs.writeFileSync(cacheFile, JSON.stringify(result, null, 2));
90
- }
91
- `], {
92
- stdio: 'ignore',
93
- windowsHide: true,
94
- detached: true // CRITICAL: prevents blocking Claude Code input on Windows
95
- });
96
-
97
- child.unref();
98
- process.exit(0);
99
- });
1
+ #!/usr/bin/env node
2
+
3
+ // Hydra Update Checker — SessionStart hook
4
+ // Spawns a DETACHED background process to check npm for updates.
5
+ // Writes result to ~/.claude/cache/hydra-update-check.json
6
+ // The statusline hook reads this cache file.
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const os = require('os');
11
+ const { spawn } = require('child_process');
12
+
13
+ const homeDir = os.homedir();
14
+ const cacheDir = path.join(homeDir, '.claude', 'cache');
15
+ const cacheFile = path.join(cacheDir, 'hydra-update-check.json');
16
+
17
+ // VERSION file locations (check project first, then global)
18
+ const projectVersionFile = path.join(process.cwd(), '.claude', 'hydra', 'VERSION');
19
+ const globalVersionFile = path.join(homeDir, '.claude', 'hydra', 'VERSION');
20
+
21
+ // Ensure cache directory exists
22
+ if (!fs.existsSync(cacheDir)) {
23
+ fs.mkdirSync(cacheDir, { recursive: true });
24
+ }
25
+
26
+ // Skip check if we checked within the last hour
27
+ try {
28
+ const existing = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
29
+ const age = Date.now() - (existing.checked_at || 0);
30
+ if (age < 3600000) { // 1 hour
31
+ process.exit(0);
32
+ }
33
+ } catch (e) {
34
+ // No cache or invalid — proceed with check
35
+ }
36
+
37
+ // Read stdin to prevent EPIPE (Claude Code pipes JSON to all hooks)
38
+ let stdinData = '';
39
+ process.stdin.on('data', (chunk) => (stdinData += chunk));
40
+ process.stdin.on('end', () => {
41
+ // Spawn background process (MUST be detached to not block Claude Code)
42
+ const child = spawn(process.execPath, ['-e', `
43
+ const fs = require('fs');
44
+ const { execSync } = require('child_process');
45
+
46
+ const cacheFile = ${JSON.stringify(cacheFile)};
47
+ const projectVersionFile = ${JSON.stringify(projectVersionFile)};
48
+ const globalVersionFile = ${JSON.stringify(globalVersionFile)};
49
+
50
+ try {
51
+ // Read installed version
52
+ let installed = 'unknown';
53
+ try {
54
+ installed = fs.readFileSync(projectVersionFile, 'utf8').trim();
55
+ } catch (e) {
56
+ try {
57
+ installed = fs.readFileSync(globalVersionFile, 'utf8').trim();
58
+ } catch (e2) {}
59
+ }
60
+
61
+ // Fetch latest version from npm (with timeout)
62
+ const latest = execSync('npm view hail-hydra-cc version', {
63
+ encoding: 'utf8',
64
+ timeout: 10000,
65
+ windowsHide: true,
66
+ stdio: ['pipe', 'pipe', 'pipe']
67
+ }).trim();
68
+
69
+ // Compare and write cache
70
+ const updateAvailable = installed !== 'unknown' && latest !== installed;
71
+
72
+ const result = {
73
+ installed: installed,
74
+ latest: latest,
75
+ update_available: updateAvailable,
76
+ checked_at: Date.now()
77
+ };
78
+
79
+ fs.writeFileSync(cacheFile, JSON.stringify(result, null, 2));
80
+ } catch (e) {
81
+ // Network error or npm not available — write a "no check" result
82
+ const result = {
83
+ installed: 'unknown',
84
+ latest: 'unknown',
85
+ update_available: false,
86
+ checked_at: Date.now(),
87
+ error: e.message
88
+ };
89
+ fs.writeFileSync(cacheFile, JSON.stringify(result, null, 2));
90
+ }
91
+ `], {
92
+ stdio: 'ignore',
93
+ windowsHide: true,
94
+ detached: true // CRITICAL: prevents blocking Claude Code input on Windows
95
+ });
96
+
97
+ child.unref();
98
+ process.exit(0);
99
+ });