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.
- package/README.md +99 -99
- package/bin/cli.js +105 -105
- package/files/SKILL.md +1172 -1217
- package/files/agents/hydra-analyst.md +1 -1
- package/files/agents/hydra-coder.md +2 -2
- package/files/agents/hydra-git.md +1 -1
- package/files/agents/hydra-guard.md +3 -3
- package/files/agents/hydra-runner.md +1 -1
- package/files/agents/hydra-scout.md +1 -1
- package/files/agents/hydra-scribe.md +1 -1
- package/files/agents/hydra-sentinel-scan.md +19 -1
- package/files/agents/hydra-sentinel.md +19 -1
- package/files/commands/hydra/config.md +37 -37
- package/files/commands/hydra/guard.md +71 -71
- package/files/commands/hydra/help.md +47 -47
- package/files/commands/hydra/quiet.md +16 -16
- package/files/commands/hydra/stats.md +31 -0
- package/files/commands/hydra/status.md +85 -85
- package/files/commands/hydra/verbose.md +29 -29
- package/files/hooks/hydra-auto-guard.js +130 -54
- package/files/hooks/hydra-check-update.js +99 -99
- package/files/hooks/hydra-statusline.js +131 -128
- package/files/references/model-capabilities.md +164 -164
- package/files/references/routing-guide.md +303 -303
- package/package.json +1 -1
- package/src/display.js +1 -1
- package/src/files.js +110 -106
- package/src/installer.js +401 -393
- package/src/prompts.js +80 -80
|
@@ -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
|
-
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
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
|
+
});
|