hail-hydra-cc 1.2.0 → 1.2.2
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 +16 -3
- package/bin/cli.js +2 -2
- package/files/SKILL.md +29 -32
- package/files/agents/hydra-analyst.md +1 -1
- package/files/agents/hydra-coder.md +1 -1
- package/files/agents/hydra-git.md +4 -4
- 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/commands/hydra/config.md +37 -0
- package/files/commands/hydra/guard.md +71 -0
- package/files/commands/hydra/help.md +41 -0
- package/files/commands/hydra/quiet.md +14 -0
- package/files/commands/hydra/status.md +72 -0
- package/files/commands/hydra/update.md +35 -0
- package/files/commands/hydra/verbose.md +27 -0
- package/files/hooks/hydra-auto-guard.js +54 -0
- package/files/hooks/hydra-check-update.js +99 -0
- package/files/hooks/hydra-statusline.js +87 -0
- package/package.json +6 -2
- package/src/display.js +66 -14
- package/src/files.js +17 -1
- package/src/installer.js +144 -10
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ npx hail-hydra-cc --help # Show help
|
|
|
44
44
|
|
|
45
45
|
```
|
|
46
46
|
~/.claude/ (or ./.claude/ for local)
|
|
47
|
-
├── agents/
|
|
47
|
+
├── agents/ # 7 agent definitions
|
|
48
48
|
│ ├── hydra-scout.md
|
|
49
49
|
│ ├── hydra-runner.md
|
|
50
50
|
│ ├── hydra-scribe.md
|
|
@@ -52,8 +52,21 @@ npx hail-hydra-cc --help # Show help
|
|
|
52
52
|
│ ├── hydra-git.md
|
|
53
53
|
│ ├── hydra-coder.md
|
|
54
54
|
│ └── hydra-analyst.md
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
├── commands/hydra/ # 7 slash commands
|
|
56
|
+
│ ├── help.md # /hydra:help
|
|
57
|
+
│ ├── status.md # /hydra:status
|
|
58
|
+
│ ├── update.md # /hydra:update
|
|
59
|
+
│ ├── config.md # /hydra:config
|
|
60
|
+
│ ├── guard.md # /hydra:guard
|
|
61
|
+
│ ├── quiet.md # /hydra:quiet
|
|
62
|
+
│ └── verbose.md # /hydra:verbose
|
|
63
|
+
├── hooks/ # 3 lifecycle hooks
|
|
64
|
+
│ ├── hydra-check-update.js # SessionStart — version check
|
|
65
|
+
│ ├── hydra-statusline.js # StatusLine — status bar
|
|
66
|
+
│ └── hydra-auto-guard.js # PostToolUse — file tracker
|
|
67
|
+
└── skills/hydra/ # Skill (Claude Code discoverable via /skills)
|
|
68
|
+
├── SKILL.md # Orchestrator instructions
|
|
69
|
+
├── VERSION # Installed version
|
|
57
70
|
└── references/
|
|
58
71
|
├── routing-guide.md
|
|
59
72
|
└── model-capabilities.md
|
package/bin/cli.js
CHANGED
|
@@ -39,8 +39,8 @@ Examples:
|
|
|
39
39
|
|
|
40
40
|
What gets installed:
|
|
41
41
|
~/.claude/agents/ 7 Hydra agent .md files
|
|
42
|
-
~/.claude/hydra/SKILL.md Core framework instructions
|
|
43
|
-
~/.claude/hydra/references/ Model capabilities & routing guides
|
|
42
|
+
~/.claude/skills/hydra/SKILL.md Core framework instructions
|
|
43
|
+
~/.claude/skills/hydra/references/ Model capabilities & routing guides
|
|
44
44
|
`);
|
|
45
45
|
|
|
46
46
|
// ── Main action ───────────────────────────────────────────────────────────────
|
package/files/SKILL.md
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hydra
|
|
3
3
|
description: >
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
as "always on" infrastructure, like an OS scheduler. If you are running as Opus and about
|
|
11
|
-
to do work that a faster model could handle, delegate it. "Cut off one head, two more
|
|
12
|
-
shall take its place" — except here, every head is working FOR you.
|
|
4
|
+
Multi-agent orchestration framework for Claude Code. Automatically delegates
|
|
5
|
+
tasks to cheaper, faster sub-agents (Haiku 4.5, Sonnet 4.6) while maintaining
|
|
6
|
+
Opus-level quality through verification. Use when working on any coding task —
|
|
7
|
+
Hydra activates automatically to route file exploration, test running,
|
|
8
|
+
documentation, code writing, debugging, security scanning, and git operations
|
|
9
|
+
to the optimal agent. Saves ~50% on API costs.
|
|
13
10
|
---
|
|
14
11
|
|
|
15
12
|
# 🐉 Hydra — Multi-Headed Speculative Execution
|
|
@@ -41,7 +38,7 @@ User Request
|
|
|
41
38
|
│ │
|
|
42
39
|
▼ ▼
|
|
43
40
|
┌─────────────────────────────┐ ┌──────────────────────────────┐
|
|
44
|
-
│ 🧠 ORCHESTRATOR (Opus) │ │ 🟢 hydra-scout
|
|
41
|
+
│ 🧠 ORCHESTRATOR (Opus) │ │ 🟢 hydra-scout │
|
|
45
42
|
│ Classifies task │ │ IMMEDIATE pre-dispatch: │
|
|
46
43
|
│ Plans waves │ │ "Find files relevant to │
|
|
47
44
|
│ Decides blocking / not │ │ [user's request]" │
|
|
@@ -364,7 +361,7 @@ Wave 4 → launch E (needs D complete)
|
|
|
364
361
|
Classify every incoming task before executing. This is fast — just a mental check, not a separate
|
|
365
362
|
step the user sees.
|
|
366
363
|
|
|
367
|
-
### Tier 1 → Haiku 4.5 Heads (hydra-scout
|
|
364
|
+
### Tier 1 → Haiku 4.5 Heads (hydra-scout, hydra-runner, hydra-scribe, hydra-guard, hydra-git)
|
|
368
365
|
|
|
369
366
|
Route to Haiku when the task is **mechanical, read-heavy, or well-defined**:
|
|
370
367
|
|
|
@@ -382,7 +379,7 @@ Route to Haiku when the task is **mechanical, read-heavy, or well-defined**:
|
|
|
382
379
|
**Heuristic**: If you could describe the task as a single imperative sentence with no ambiguity
|
|
383
380
|
(e.g., "find all files importing X", "run the test suite"), it's Tier 1.
|
|
384
381
|
|
|
385
|
-
### Tier 2 → Sonnet 4.6 Heads (hydra-coder
|
|
382
|
+
### Tier 2 → Sonnet 4.6 Heads (hydra-coder, hydra-analyst)
|
|
386
383
|
|
|
387
384
|
Route to Sonnet when the task requires **reasoning about code, but within well-understood patterns**:
|
|
388
385
|
|
|
@@ -419,7 +416,7 @@ Keep it yourself when the task demands **deep reasoning, novel architecture, or
|
|
|
419
416
|
- **When in doubt, go one tier up.** Better to use Sonnet for a Haiku task than Haiku for a
|
|
420
417
|
Sonnet task. Quality is never sacrificed.
|
|
421
418
|
- **Compound tasks should be decomposed.** "Read the codebase and redesign the auth system"
|
|
422
|
-
becomes: hydra-scout
|
|
419
|
+
becomes: hydra-scout reads, then you design (Opus).
|
|
423
420
|
- **Iterative tasks escalate naturally.** If a Sonnet draft isn't right, don't retry with Sonnet —
|
|
424
421
|
do it yourself.
|
|
425
422
|
|
|
@@ -501,20 +498,20 @@ These output types require no orchestrator judgment — accept and pass through:
|
|
|
501
498
|
|
|
502
499
|
| Agent | Auto-Accept When |
|
|
503
500
|
|-------|-----------------|
|
|
504
|
-
| hydra-scout
|
|
505
|
-
| hydra-runner
|
|
506
|
-
| hydra-scribe
|
|
501
|
+
| hydra-scout | Returns file paths, directory listings, search results, grep output — factual data with no interpretation |
|
|
502
|
+
| hydra-runner | Reports all tests passing, clean build, clean lint — unambiguous pass/fail |
|
|
503
|
+
| hydra-scribe | Produces docs/comments for NON-CRITICAL content (internal docstrings, changelogs) |
|
|
507
504
|
|
|
508
505
|
### Manual Verify (orchestrator reviews before accepting)
|
|
509
506
|
These outputs require judgment — scan before passing to user or downstream agents:
|
|
510
507
|
|
|
511
508
|
| Agent | Always Verify When |
|
|
512
509
|
|-------|-------------------|
|
|
513
|
-
| hydra-coder
|
|
514
|
-
| hydra-analyst
|
|
515
|
-
| hydra-runner
|
|
516
|
-
| hydra-scribe
|
|
517
|
-
| hydra-scout
|
|
510
|
+
| hydra-coder | ALWAYS — code changes are never auto-accepted |
|
|
511
|
+
| hydra-analyst | ALWAYS — diagnoses and recommendations need validation |
|
|
512
|
+
| hydra-runner | Reports test FAILURES — verify the failures are real and not environment issues |
|
|
513
|
+
| hydra-scribe | Writing user-facing docs (README, API docs) — verify accuracy |
|
|
514
|
+
| hydra-scout | Returns analysis or interpretation (not raw data) — verify conclusions |
|
|
518
515
|
|
|
519
516
|
### Verification Decision Flowchart
|
|
520
517
|
|
|
@@ -554,8 +551,8 @@ When manual verification is required, match depth to risk:
|
|
|
554
551
|
|
|
555
552
|
## Auto-Guard Protocol
|
|
556
553
|
|
|
557
|
-
After hydra-coder
|
|
558
|
-
hydra-guard
|
|
554
|
+
After hydra-coder produces any code changes, AUTOMATICALLY dispatch
|
|
555
|
+
hydra-guard to scan the changes before presenting to the user. This is
|
|
559
556
|
a non-blocking, low-cost quality gate that runs in the same wave as any final validation.
|
|
560
557
|
|
|
561
558
|
### Dispatch Rules
|
|
@@ -718,8 +715,8 @@ Hydra's heads live in `agents/`. Install them where Claude Code discovers subage
|
|
|
718
715
|
## Configuration
|
|
719
716
|
|
|
720
717
|
At session start, check for a Hydra configuration file at:
|
|
721
|
-
1. `.claude/hydra/hydra.config.md` (project-level, takes precedence)
|
|
722
|
-
2. `~/.claude/hydra/hydra.config.md` (user-level, fallback)
|
|
718
|
+
1. `.claude/skills/hydra/config/hydra.config.md` (project-level, takes precedence)
|
|
719
|
+
2. `~/.claude/skills/hydra/config/hydra.config.md` (user-level, fallback)
|
|
723
720
|
|
|
724
721
|
If found, apply the settings. If not found, use defaults:
|
|
725
722
|
- **mode**: balanced
|
|
@@ -748,13 +745,13 @@ If the user types any of these exact phrases, respond with the corresponding act
|
|
|
748
745
|
|
|
749
746
|
| Head | Model | Role | Tools |
|
|
750
747
|
|------|-------|------|-------|
|
|
751
|
-
| `hydra-scout
|
|
752
|
-
| `hydra-runner
|
|
753
|
-
| `hydra-scribe
|
|
754
|
-
| `hydra-guard
|
|
755
|
-
| `hydra-git
|
|
756
|
-
| `hydra-coder
|
|
757
|
-
| `hydra-analyst
|
|
748
|
+
| `hydra-scout` | 🟢 Haiku 4.5 | Codebase exploration, file search, reading | Read, Grep, Glob |
|
|
749
|
+
| `hydra-runner` | 🟢 Haiku 4.5 | Test execution, builds, linting, validation | Read, Bash, Glob, Grep |
|
|
750
|
+
| `hydra-scribe` | 🟢 Haiku 4.5 | Documentation, READMEs, comments, changelogs | Read, Write, Edit, Glob, Grep |
|
|
751
|
+
| `hydra-guard` | 🟢 Haiku 4.5 | Security/quality gate after code changes | Read, Grep, Glob, Bash |
|
|
752
|
+
| `hydra-git` | 🟢 Haiku 4.5 | Git operations: commit, branch, diff, log | Read, Bash, Glob, Grep |
|
|
753
|
+
| `hydra-coder` | 🔵 Sonnet 4.6 | Code writing, implementation, refactoring | Read, Write, Edit, Bash, Glob, Grep |
|
|
754
|
+
| `hydra-analyst` | 🔵 Sonnet 4.6 | Code review, debugging, architecture analysis | Read, Grep, Glob, Bash |
|
|
758
755
|
|
|
759
756
|
## Measuring Impact
|
|
760
757
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: hydra-git
|
|
2
|
+
name: hydra-git
|
|
3
3
|
description: >
|
|
4
4
|
🟢 Hydra's git operations specialist. Handles all version control tasks: staging,
|
|
5
5
|
committing with well-crafted Conventional Commits messages, branching, merging,
|
|
6
6
|
rebasing, stashing, cherry-picking, log inspection, diff analysis, and conflict
|
|
7
7
|
detection. Runs on Haiku 4.5 — git operations are mechanical and well-defined.
|
|
8
|
-
Use hydra-analyst
|
|
8
|
+
Use hydra-analyst for merge conflict RESOLUTION (requires code
|
|
9
9
|
comprehension) but hydra-git for conflict DETECTION and all other git operations.
|
|
10
10
|
May run in parallel with other Hydra agents — produces self-contained, clearly
|
|
11
11
|
structured output so the orchestrator can merge results from multiple simultaneous agents.
|
|
@@ -42,7 +42,7 @@ You are hydra-git — Hydra's version control specialist. You handle git operati
|
|
|
42
42
|
3. **Never force-push without explicit orchestrator instruction.** Ask before any destructive push.
|
|
43
43
|
|
|
44
44
|
4. **Detect conflicts, don't resolve them.** If a merge or rebase hits a conflict, stop, report
|
|
45
|
-
which files are conflicted and why, and flag for hydra-analyst
|
|
45
|
+
which files are conflicted and why, and flag for hydra-analyst to resolve.
|
|
46
46
|
|
|
47
47
|
5. **Auto-stage sensibly.** When committing, stage files related to the described change.
|
|
48
48
|
Do not stage .env, credentials, or large binaries. Flag these if you encounter them.
|
|
@@ -89,7 +89,7 @@ Changes in this diff:
|
|
|
89
89
|
|
|
90
90
|
- Never force-push to main/master without explicit instruction
|
|
91
91
|
- Never commit .env files, credential files, or secrets
|
|
92
|
-
- Never resolve merge conflicts — detect and escalate to hydra-analyst
|
|
92
|
+
- Never resolve merge conflicts — detect and escalate to hydra-analyst
|
|
93
93
|
- Never amend published commits without explicit instruction
|
|
94
94
|
- Never skip pre-commit hooks (--no-verify) without explicit instruction
|
|
95
95
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: hydra-guard
|
|
2
|
+
name: hydra-guard
|
|
3
3
|
description: >
|
|
4
4
|
🟢 Hydra's security and quality gate agent. Automatically invoked after hydra-coder
|
|
5
5
|
(Sonnet 4.6) produces code changes. Performs a fast scan for common security issues
|
|
@@ -7,7 +7,7 @@ description: >
|
|
|
7
7
|
code quality checks (unused imports, dead code, missing error handling on async
|
|
8
8
|
operations), and leftover debug artifacts (console.log, TODO/FIXME/HACK comments).
|
|
9
9
|
Runs on Haiku 4.5 for speed — this is a fast gate, not a deep audit. For deep
|
|
10
|
-
security review, use hydra-analyst
|
|
10
|
+
security review, use hydra-analyst instead.
|
|
11
11
|
May run in parallel with other Hydra agents — produces self-contained, clearly
|
|
12
12
|
structured output so the orchestrator can merge results from multiple simultaneous agents.
|
|
13
13
|
tools: Read, Grep, Glob, Bash
|
|
@@ -91,7 +91,7 @@ Note: Savings calculated against Opus 4.6 ($5/$25 per MTok). These are warnings
|
|
|
91
91
|
- Never modify source files
|
|
92
92
|
- Never block or delay delivery of hydra-coder's output
|
|
93
93
|
- Never flag false positives — verify the pattern before reporting
|
|
94
|
-
- Never perform deep architectural security analysis — that's hydra-analyst
|
|
94
|
+
- Never perform deep architectural security analysis — that's hydra-analyst
|
|
95
95
|
- If a scan would take more than 30 seconds, report what you found and stop
|
|
96
96
|
|
|
97
97
|
## Collaboration Protocol
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show current Hydra configuration and how to customize it
|
|
3
|
+
allowed-tools: Read, Bash
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Hydra Config
|
|
7
|
+
|
|
8
|
+
Show the current Hydra configuration:
|
|
9
|
+
|
|
10
|
+
1. Check for project-level config first:
|
|
11
|
+
```bash
|
|
12
|
+
cat .claude/skills/hydra/config/hydra.config.md 2>/dev/null
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
2. If not found, check global config:
|
|
16
|
+
```bash
|
|
17
|
+
cat ~/.claude/skills/hydra/config/hydra.config.md 2>/dev/null
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
3. If neither found, show defaults:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
🐉 Hydra Configuration (defaults — no config file found)
|
|
24
|
+
─────────────────────────────
|
|
25
|
+
Mode: balanced
|
|
26
|
+
Dispatch Log: on
|
|
27
|
+
Auto-Guard: on
|
|
28
|
+
Model Overrides: none
|
|
29
|
+
─────────────────────────────
|
|
30
|
+
To customize, create a config file:
|
|
31
|
+
Global: ~/.claude/skills/hydra/config/hydra.config.md
|
|
32
|
+
Project: .claude/skills/hydra/config/hydra.config.md
|
|
33
|
+
|
|
34
|
+
Run /hydra:status to see current agent assignments.
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
4. If a config file IS found, display its contents and note where it was loaded from.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Manually run the Hydra security and quality scan on specified files or directories
|
|
3
|
+
allowed-tools: Read, Grep, Glob, Bash
|
|
4
|
+
model: haiku
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Hydra Guard — Manual Security Scan
|
|
8
|
+
|
|
9
|
+
Run a focused security and quality scan on the specified files.
|
|
10
|
+
|
|
11
|
+
**Target**: $ARGUMENTS
|
|
12
|
+
|
|
13
|
+
If no arguments provided, scan all files changed since the last commit:
|
|
14
|
+
```bash
|
|
15
|
+
git diff --name-only HEAD 2>/dev/null || echo "Not a git repository or no changes"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Scan Checklist
|
|
19
|
+
|
|
20
|
+
For each target file, check for:
|
|
21
|
+
|
|
22
|
+
### CRITICAL (security)
|
|
23
|
+
- Hardcoded secrets, API keys, tokens, passwords (patterns: `sk-`, `ghp_`, `AKIA`, `password =`, `secret =`, `token =`, `api_key =`)
|
|
24
|
+
- SQL injection vulnerabilities (string concatenation in queries)
|
|
25
|
+
- XSS vulnerabilities (unescaped user input in HTML output)
|
|
26
|
+
- Unsafe deserialization (`eval()`, `pickle.loads()`, `unserialize()`)
|
|
27
|
+
- Path traversal (`../` in file operations without validation)
|
|
28
|
+
- Command injection (user input passed to shell commands)
|
|
29
|
+
|
|
30
|
+
### WARNING (quality)
|
|
31
|
+
- `console.log` / `print()` debug leftovers
|
|
32
|
+
- TODO/FIXME/HACK comments
|
|
33
|
+
- Unused imports (obvious cases only)
|
|
34
|
+
- Missing error handling on async operations (no try/catch, no .catch())
|
|
35
|
+
- Empty catch blocks
|
|
36
|
+
- Hardcoded URLs or magic numbers without constants
|
|
37
|
+
|
|
38
|
+
### INFO (style)
|
|
39
|
+
- Functions longer than 100 lines
|
|
40
|
+
- Deeply nested conditionals (3+ levels)
|
|
41
|
+
- Inconsistent naming conventions within the same file
|
|
42
|
+
|
|
43
|
+
## Output Format
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
🐉 Hydra Guard — Security & Quality Report
|
|
47
|
+
═══════════════════════════════════════════
|
|
48
|
+
Files scanned: 3
|
|
49
|
+
|
|
50
|
+
🔴 CRITICAL (2 findings)
|
|
51
|
+
src/auth.py:45 Hardcoded API key: OPENAI_KEY = "sk-..."
|
|
52
|
+
src/db.py:78 SQL injection: f-string in query construction
|
|
53
|
+
|
|
54
|
+
⚠️ WARNING (3 findings)
|
|
55
|
+
src/auth.py:12 console.log left in production code
|
|
56
|
+
src/utils.py:89 Empty catch block — errors silently swallowed
|
|
57
|
+
src/api.py:34 TODO: "fix this later" (line 34)
|
|
58
|
+
|
|
59
|
+
ℹ️ INFO (1 finding)
|
|
60
|
+
src/handler.py:1 Function process_request is 142 lines long
|
|
61
|
+
|
|
62
|
+
Summary: 2 critical · 3 warnings · 1 info
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
If no issues found:
|
|
66
|
+
```
|
|
67
|
+
🐉 Hydra Guard — All Clear ✅
|
|
68
|
+
Files scanned: 3 | No issues found.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Important**: This is a FAST scan, not a deep audit. For thorough security review, use hydra-analyst instead.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show all available Hydra commands, agents, and a quick reference guide
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Hydra Help
|
|
6
|
+
|
|
7
|
+
Display the following help reference directly — do NOT search files or run commands:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
🐉 Hydra Framework — Quick Reference
|
|
11
|
+
═══════════════════════════════════════
|
|
12
|
+
COMMANDS
|
|
13
|
+
/hydra:help Show this help screen
|
|
14
|
+
/hydra:status Show installed agents, version, config
|
|
15
|
+
/hydra:update Update Hydra to the latest version
|
|
16
|
+
/hydra:config Show current configuration
|
|
17
|
+
/hydra:guard Run security scan on files (usage: /hydra:guard src/auth.py)
|
|
18
|
+
/hydra:quiet Suppress dispatch logs for this session
|
|
19
|
+
/hydra:verbose Enable verbose dispatch logs with timing
|
|
20
|
+
|
|
21
|
+
AGENTS
|
|
22
|
+
🟢 hydra-scout (Haiku 4.5) — Explore codebase, find files, map structure
|
|
23
|
+
🟢 hydra-runner (Haiku 4.5) — Run tests, linters, build commands
|
|
24
|
+
🟢 hydra-scribe (Haiku 4.5) — Write docs, comments, READMEs
|
|
25
|
+
🟢 hydra-guard (Haiku 4.5) — Security scan, quality gate
|
|
26
|
+
🟢 hydra-git (Haiku 4.5) — Git operations, commits, branches
|
|
27
|
+
🔵 hydra-coder (Sonnet 4.6) — Write and edit code
|
|
28
|
+
🔵 hydra-analyst (Sonnet 4.6) — Debug, diagnose, review
|
|
29
|
+
|
|
30
|
+
HOW IT WORKS
|
|
31
|
+
The Opus 4.6 orchestrator automatically delegates tasks to cheaper,
|
|
32
|
+
faster agents (Haiku 4.5 and Sonnet 4.6) — saving ~50% on API costs
|
|
33
|
+
while maintaining Opus-level quality through verification.
|
|
34
|
+
|
|
35
|
+
You don't need to do anything. Just work normally.
|
|
36
|
+
Hydra operates invisibly unless you check the dispatch log.
|
|
37
|
+
|
|
38
|
+
LINKS
|
|
39
|
+
GitHub: https://github.com/AR6420/Hail_Hydra
|
|
40
|
+
npm: https://www.npmjs.com/package/hail-hydra-cc
|
|
41
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Suppress Hydra dispatch logs for the rest of this session
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Hydra Quiet Mode
|
|
6
|
+
|
|
7
|
+
Acknowledge this command and remember for the rest of this session:
|
|
8
|
+
|
|
9
|
+
**Do NOT display the Hydra Dispatch Log footer after tasks.**
|
|
10
|
+
|
|
11
|
+
Respond with:
|
|
12
|
+
"🐉 Quiet mode enabled. Dispatch logs suppressed for this session. Use /hydra:verbose to re-enable."
|
|
13
|
+
|
|
14
|
+
Continue operating Hydra normally (delegation, verification, auto-guard) — just don't show the dispatch log table.
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
## Display Format
|
|
47
|
+
|
|
48
|
+
Present results as a clean status card:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
🐉 Hydra Framework Status
|
|
52
|
+
──────────────────────────────
|
|
53
|
+
Version: 1.0.0 (latest: 1.0.0 ✅) OR (update available: 1.1.0 ⚡)
|
|
54
|
+
Install: Global (~/.claude/)
|
|
55
|
+
Agents (7):
|
|
56
|
+
🟢 hydra-scout (Haiku 4.5) ✅
|
|
57
|
+
🟢 hydra-runner (Haiku 4.5) ✅
|
|
58
|
+
🟢 hydra-scribe (Haiku 4.5) ✅
|
|
59
|
+
🟢 hydra-guard (Haiku 4.5) ✅
|
|
60
|
+
🟢 hydra-git (Haiku 4.5) ✅
|
|
61
|
+
🔵 hydra-coder (Sonnet 4.6) ✅
|
|
62
|
+
🔵 hydra-analyst (Sonnet 4.6) ✅
|
|
63
|
+
Commands (7): update, status, help, config, guard, quiet, verbose
|
|
64
|
+
Hooks (3): check-update ✅, statusline ✅, auto-guard ✅
|
|
65
|
+
Config: balanced mode, dispatch log on, auto-guard on
|
|
66
|
+
──────────────────────────────
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If an update is available, add:
|
|
70
|
+
```
|
|
71
|
+
⚡ Update available! Run /hydra:update to get the latest version.
|
|
72
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Update the Hydra framework to the latest version from npm
|
|
3
|
+
allowed-tools: Bash
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Hydra Update
|
|
7
|
+
|
|
8
|
+
Run the following steps to update Hydra to the latest version:
|
|
9
|
+
|
|
10
|
+
1. First, check the current installed version:
|
|
11
|
+
```bash
|
|
12
|
+
cat ~/.claude/skills/hydra/VERSION 2>/dev/null || echo "VERSION file not found"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
2. Check the latest available version on npm:
|
|
16
|
+
```bash
|
|
17
|
+
npm view hail-hydra-cc version 2>/dev/null || echo "Package not found on npm"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
3. If an update is available (versions differ), run the installer:
|
|
21
|
+
```bash
|
|
22
|
+
npx hail-hydra-cc@latest --global
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
4. After installation completes, verify the new version:
|
|
26
|
+
```bash
|
|
27
|
+
cat ~/.claude/skills/hydra/VERSION
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
5. Report to the user:
|
|
31
|
+
- If updated: "🐉 Hydra updated from [old] → [new]. All heads refreshed."
|
|
32
|
+
- If already current: "🐉 Hydra is already at the latest version ([version])."
|
|
33
|
+
- If error: Show the error and suggest running `npx hail-hydra-cc@latest --global` manually in their terminal.
|
|
34
|
+
|
|
35
|
+
**Important**: After updating, the user should restart Claude Code to reload the updated agent files, commands, and hooks.
|
|
@@ -0,0 +1,27 @@
|
|
|
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."
|
|
@@ -0,0 +1,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
|
+
} catch (e) {
|
|
51
|
+
// Silently fail — NEVER block Claude Code
|
|
52
|
+
}
|
|
53
|
+
process.exit(0);
|
|
54
|
+
});
|
|
@@ -0,0 +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
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Hydra StatusLine — persistent status bar at bottom of Claude Code
|
|
4
|
+
// Receives session JSON via stdin, outputs one formatted line to stdout.
|
|
5
|
+
//
|
|
6
|
+
// Display format:
|
|
7
|
+
// 🐉 │ Opus │ Ctx: 37% ████░░░░░░ │ $0.42 │ my-project │ ⚡ Update available
|
|
8
|
+
//
|
|
9
|
+
// Context bar is color-coded:
|
|
10
|
+
// Green (0-49%) → Yellow (50-79%) → Red (80%+)
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const os = require('os');
|
|
15
|
+
|
|
16
|
+
const cacheFile = path.join(os.homedir(), '.claude', 'cache', 'hydra-update-check.json');
|
|
17
|
+
|
|
18
|
+
let input = '';
|
|
19
|
+
process.stdin.on('data', (chunk) => (input += chunk));
|
|
20
|
+
process.stdin.on('end', () => {
|
|
21
|
+
try {
|
|
22
|
+
const data = JSON.parse(input);
|
|
23
|
+
|
|
24
|
+
// === Model ===
|
|
25
|
+
const model = data.model?.display_name || 'Unknown';
|
|
26
|
+
|
|
27
|
+
// === Context Usage ===
|
|
28
|
+
// Use precomputed used_percentage from Claude Code (most reliable)
|
|
29
|
+
const ctxPct = Math.round(data.context_window?.used_percentage || 0);
|
|
30
|
+
|
|
31
|
+
// Build visual context bar (10 chars wide)
|
|
32
|
+
const filled = Math.round(ctxPct / 10);
|
|
33
|
+
const empty = 10 - filled;
|
|
34
|
+
const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(empty);
|
|
35
|
+
|
|
36
|
+
// Color-code: Green <50%, Yellow 50-79%, Red 80%+
|
|
37
|
+
let ctxColor;
|
|
38
|
+
if (ctxPct < 50) {
|
|
39
|
+
ctxColor = '\x1b[32m'; // Green
|
|
40
|
+
} else if (ctxPct < 80) {
|
|
41
|
+
ctxColor = '\x1b[33m'; // Yellow
|
|
42
|
+
} else {
|
|
43
|
+
ctxColor = '\x1b[31m'; // Red
|
|
44
|
+
}
|
|
45
|
+
const reset = '\x1b[0m';
|
|
46
|
+
const dim = '\x1b[2m';
|
|
47
|
+
|
|
48
|
+
const ctxDisplay = `${ctxColor}Ctx: ${ctxPct}% ${bar}${reset}`;
|
|
49
|
+
|
|
50
|
+
// === Session Cost ===
|
|
51
|
+
const cost = (data.cost?.total_cost_usd || 0).toFixed(2);
|
|
52
|
+
|
|
53
|
+
// === Working Directory ===
|
|
54
|
+
const dirName = path.basename(data.workspace?.current_dir || data.cwd || '');
|
|
55
|
+
|
|
56
|
+
// === Update Check (read from cache) ===
|
|
57
|
+
let updateNotice = '';
|
|
58
|
+
try {
|
|
59
|
+
const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
|
|
60
|
+
if (cache.update_available) {
|
|
61
|
+
updateNotice = ` \x1b[33m\u26A1 v${cache.latest} available${reset}`;
|
|
62
|
+
}
|
|
63
|
+
} catch (e) {
|
|
64
|
+
// No cache — skip update notice
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// === Compose Status Line ===
|
|
68
|
+
const parts = [
|
|
69
|
+
'\x1b[32m\uD83D\uDC32\x1b[0m', // Green dragon emoji (🐉)
|
|
70
|
+
`${dim}${model}${reset}`, // Dim model name
|
|
71
|
+
ctxDisplay, // Color-coded context bar
|
|
72
|
+
`${dim}$${cost}${reset}`, // Dim cost
|
|
73
|
+
`${dim}${dirName}${reset}`, // Dim directory
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
// Append update notice if available
|
|
77
|
+
if (updateNotice) {
|
|
78
|
+
parts.push(updateNotice);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
process.stdout.write(parts.join(' \u2502 '));
|
|
82
|
+
|
|
83
|
+
} catch (e) {
|
|
84
|
+
// Fallback if JSON parse fails
|
|
85
|
+
process.stdout.write('\uD83D\uDC32 Hydra');
|
|
86
|
+
}
|
|
87
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hail-hydra-cc",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Multi-headed speculative execution framework for Claude Code",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hail-hydra-cc": "bin/cli.js"
|
|
@@ -36,5 +36,9 @@
|
|
|
36
36
|
"bin/",
|
|
37
37
|
"src/",
|
|
38
38
|
"files/"
|
|
39
|
-
]
|
|
39
|
+
],
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"registry": "https://registry.npmjs.org/",
|
|
42
|
+
"access": "public"
|
|
43
|
+
}
|
|
40
44
|
}
|
package/src/display.js
CHANGED
|
@@ -37,17 +37,36 @@ function showFileInstalled(displayName, success, errorMsg) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
function showInstallComplete() {
|
|
40
|
+
function showInstallComplete(statusLineConfigured = true) {
|
|
41
41
|
console.log();
|
|
42
|
-
console.log(chalk.cyan.bold('
|
|
42
|
+
console.log(chalk.cyan.bold(' \uD83D\uDC09 Hail Hydra! Framework deployed and ready.'));
|
|
43
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(45)));
|
|
44
|
+
console.log(chalk.green(` \u2714 7 agents installed`));
|
|
45
|
+
console.log(chalk.green(` \u2714 7 slash commands installed`));
|
|
46
|
+
console.log(chalk.green(` \u2714 3 hooks registered`));
|
|
47
|
+
if (statusLineConfigured) {
|
|
48
|
+
console.log(chalk.green(` \u2714 StatusLine configured`));
|
|
49
|
+
} else {
|
|
50
|
+
console.log(chalk.yellow(` \u26a0 StatusLine skipped (existing config preserved)`));
|
|
51
|
+
}
|
|
52
|
+
console.log(chalk.green(` \u2714 Version tracked (${VERSION})`));
|
|
43
53
|
console.log();
|
|
44
|
-
console.log(chalk.gray('
|
|
54
|
+
console.log(chalk.gray(' Quick start: /hydra:help'));
|
|
55
|
+
console.log(chalk.gray(' Check status: /hydra:status'));
|
|
45
56
|
console.log(chalk.gray(' GitHub: https://github.com/AR6420/Hail_Hydra'));
|
|
46
57
|
console.log();
|
|
47
58
|
}
|
|
48
59
|
|
|
49
60
|
function showStatusTable(globalStatus, localStatus) {
|
|
50
|
-
const
|
|
61
|
+
const os = require('os');
|
|
62
|
+
const path = require('path');
|
|
63
|
+
const fs = require('fs');
|
|
64
|
+
|
|
65
|
+
function fileExists(p) {
|
|
66
|
+
try { fs.accessSync(p, fs.constants.F_OK); return true; } catch { return false; }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const divider = chalk.gray(' ' + '\u2500'.repeat(50));
|
|
51
70
|
|
|
52
71
|
console.log();
|
|
53
72
|
console.log(chalk.bold(' Installation Status'));
|
|
@@ -65,7 +84,9 @@ function showStatusTable(globalStatus, localStatus) {
|
|
|
65
84
|
const anyInstalled =
|
|
66
85
|
status.agents.some((a) => a.installed) ||
|
|
67
86
|
status.skill ||
|
|
68
|
-
status.references.some((r) => r.installed)
|
|
87
|
+
status.references.some((r) => r.installed) ||
|
|
88
|
+
(status.commands && status.commands.some((c) => c.installed)) ||
|
|
89
|
+
status.version;
|
|
69
90
|
|
|
70
91
|
if (!anyInstalled) {
|
|
71
92
|
console.log(chalk.gray(' (not installed)'));
|
|
@@ -73,29 +94,60 @@ function showStatusTable(globalStatus, localStatus) {
|
|
|
73
94
|
}
|
|
74
95
|
|
|
75
96
|
for (const agent of status.agents) {
|
|
76
|
-
const dot = agent.model === 'Haiku' ? chalk.green('
|
|
77
|
-
const name = chalk.bold(agent.display.split('
|
|
78
|
-
const role = chalk.gray('
|
|
97
|
+
const dot = agent.model === 'Haiku' ? chalk.green('\uD83D\uDFE2') : chalk.blue('\uD83D\uDD35');
|
|
98
|
+
const name = chalk.bold(agent.display.split('\u2014')[0].trim());
|
|
99
|
+
const role = chalk.gray('\u2014 ' + agent.display.split('\u2014')[1].trim());
|
|
79
100
|
if (agent.installed) {
|
|
80
|
-
console.log(` ${dot} ${chalk.green('
|
|
101
|
+
console.log(` ${dot} ${chalk.green('\u2714')} ${name} ${role}`);
|
|
81
102
|
} else {
|
|
82
|
-
console.log(` ${dot} ${chalk.gray('
|
|
103
|
+
console.log(` ${dot} ${chalk.gray('\u2716')} ${chalk.gray(name)} ${chalk.gray(role + ' (not installed)')}`);
|
|
83
104
|
}
|
|
84
105
|
}
|
|
85
106
|
|
|
86
107
|
if (status.skill) {
|
|
87
|
-
console.log(chalk.green('
|
|
108
|
+
console.log(chalk.green(' \u2714 SKILL.md'));
|
|
88
109
|
} else {
|
|
89
|
-
console.log(chalk.gray('
|
|
110
|
+
console.log(chalk.gray(' \u2716 SKILL.md (not installed)'));
|
|
90
111
|
}
|
|
91
112
|
|
|
92
113
|
for (const ref of status.references) {
|
|
93
114
|
if (ref.installed) {
|
|
94
|
-
console.log(chalk.green(`
|
|
115
|
+
console.log(chalk.green(` \u2714 ${ref.name}`));
|
|
95
116
|
} else {
|
|
96
|
-
console.log(chalk.gray(`
|
|
117
|
+
console.log(chalk.gray(` \u2716 ${ref.name} (not installed)`));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Commands
|
|
122
|
+
if (status.commands) {
|
|
123
|
+
for (const cmd of status.commands) {
|
|
124
|
+
if (cmd.installed) {
|
|
125
|
+
console.log(chalk.green(` \u2714 ${cmd.name}`));
|
|
126
|
+
} else {
|
|
127
|
+
console.log(chalk.gray(` \u2716 ${cmd.name} (not installed)`));
|
|
128
|
+
}
|
|
97
129
|
}
|
|
98
130
|
}
|
|
131
|
+
|
|
132
|
+
// Version
|
|
133
|
+
if (status.version) {
|
|
134
|
+
console.log(chalk.green(` \u2714 hydra/VERSION (${status.version})`));
|
|
135
|
+
} else {
|
|
136
|
+
console.log(chalk.gray(' \u2716 hydra/VERSION (not installed)'));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Global hooks (always ~/.claude/hooks/)
|
|
141
|
+
console.log();
|
|
142
|
+
console.log(chalk.bold(' Global Hooks (~/.claude/hooks/)'));
|
|
143
|
+
const hookKeys = ['hydra-check-update', 'hydra-statusline', 'hydra-auto-guard'];
|
|
144
|
+
for (const key of hookKeys) {
|
|
145
|
+
const dest = path.join(os.homedir(), '.claude', 'hooks', `${key}.js`);
|
|
146
|
+
if (fileExists(dest)) {
|
|
147
|
+
console.log(chalk.green(` \u2714 ${key}.js`));
|
|
148
|
+
} else {
|
|
149
|
+
console.log(chalk.gray(` \u2716 ${key}.js (not installed)`));
|
|
150
|
+
}
|
|
99
151
|
}
|
|
100
152
|
|
|
101
153
|
console.log();
|
package/src/files.js
CHANGED
|
@@ -62,4 +62,20 @@ const references = {
|
|
|
62
62
|
'model-capabilities': readBundled('references/model-capabilities.md'),
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
const commands = {
|
|
66
|
+
'update': readBundled('commands/hydra/update.md'),
|
|
67
|
+
'status': readBundled('commands/hydra/status.md'),
|
|
68
|
+
'help': readBundled('commands/hydra/help.md'),
|
|
69
|
+
'config': readBundled('commands/hydra/config.md'),
|
|
70
|
+
'guard': readBundled('commands/hydra/guard.md'),
|
|
71
|
+
'quiet': readBundled('commands/hydra/quiet.md'),
|
|
72
|
+
'verbose': readBundled('commands/hydra/verbose.md'),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const hooks = {
|
|
76
|
+
'hydra-check-update': readBundled('hooks/hydra-check-update.js'),
|
|
77
|
+
'hydra-statusline': readBundled('hooks/hydra-statusline.js'),
|
|
78
|
+
'hydra-auto-guard': readBundled('hooks/hydra-auto-guard.js'),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
module.exports = { agents, skill, references, commands, hooks };
|
package/src/installer.js
CHANGED
|
@@ -5,8 +5,8 @@ const path = require('path');
|
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const chalk = require('chalk');
|
|
7
7
|
|
|
8
|
-
const { agents, skill, references } = require('./files');
|
|
9
|
-
const { showInstallHeader, showFileInstalled, showInstallComplete, showStatusTable } = require('./display');
|
|
8
|
+
const { agents, skill, references, commands, hooks } = require('./files');
|
|
9
|
+
const { showInstallHeader, showFileInstalled, showInstallComplete, showStatusTable, VERSION } = require('./display');
|
|
10
10
|
|
|
11
11
|
// ── Install locations ────────────────────────────────────────────────────────
|
|
12
12
|
|
|
@@ -28,9 +28,9 @@ function buildManifest(base) {
|
|
|
28
28
|
const skillEntry = {
|
|
29
29
|
type: 'skill',
|
|
30
30
|
key: 'SKILL.md',
|
|
31
|
-
display: 'SKILL.md',
|
|
31
|
+
display: 'skills/hydra/SKILL.md',
|
|
32
32
|
content: skill,
|
|
33
|
-
dest: path.join(base, 'hydra', 'SKILL.md'),
|
|
33
|
+
dest: path.join(base, 'skills', 'hydra', 'SKILL.md'),
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
const refEntries = Object.entries(references).map(([key, content]) => ({
|
|
@@ -38,10 +38,26 @@ function buildManifest(base) {
|
|
|
38
38
|
key,
|
|
39
39
|
display: `references/${key}.md`,
|
|
40
40
|
content,
|
|
41
|
-
dest: path.join(base, 'hydra', 'references', `${key}.md`),
|
|
41
|
+
dest: path.join(base, 'skills', 'hydra', 'references', `${key}.md`),
|
|
42
42
|
}));
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
const commandEntries = Object.entries(commands).map(([key, content]) => ({
|
|
45
|
+
type: 'command',
|
|
46
|
+
key,
|
|
47
|
+
display: `commands/hydra/${key}.md`,
|
|
48
|
+
content,
|
|
49
|
+
dest: path.join(base, 'commands', 'hydra', `${key}.md`),
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
const versionEntry = {
|
|
53
|
+
type: 'version',
|
|
54
|
+
key: 'VERSION',
|
|
55
|
+
display: 'skills/hydra/VERSION',
|
|
56
|
+
content: VERSION,
|
|
57
|
+
dest: path.join(base, 'skills', 'hydra', 'VERSION'),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return [...agentEntries, skillEntry, ...refEntries, ...commandEntries, versionEntry];
|
|
45
61
|
}
|
|
46
62
|
|
|
47
63
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
@@ -68,6 +84,94 @@ function hasAnyInstalled(base) {
|
|
|
68
84
|
return buildManifest(base).some((entry) => fileExists(entry.dest));
|
|
69
85
|
}
|
|
70
86
|
|
|
87
|
+
// ── Hooks & settings ─────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
function installHooks() {
|
|
90
|
+
const hooksDir = path.join(GLOBAL_BASE, 'hooks');
|
|
91
|
+
ensureDir(hooksDir);
|
|
92
|
+
|
|
93
|
+
for (const [key, content] of Object.entries(hooks)) {
|
|
94
|
+
const dest = path.join(hooksDir, `${key}.js`);
|
|
95
|
+
try {
|
|
96
|
+
writeFileSafe(dest, content);
|
|
97
|
+
try { fs.chmodSync(dest, 0o755); } catch {}
|
|
98
|
+
showFileInstalled(`hooks/${key}.js`, true);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
showFileInstalled(`hooks/${key}.js`, false, err.message);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function registerHooksInSettings() {
|
|
106
|
+
const settingsFile = path.join(GLOBAL_BASE, 'settings.json');
|
|
107
|
+
|
|
108
|
+
let settings = {};
|
|
109
|
+
try {
|
|
110
|
+
settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
|
|
111
|
+
} catch {}
|
|
112
|
+
|
|
113
|
+
if (!settings.hooks) settings.hooks = {};
|
|
114
|
+
if (!settings.hooks.SessionStart) settings.hooks.SessionStart = [];
|
|
115
|
+
if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = [];
|
|
116
|
+
|
|
117
|
+
const isHydraHook = (entry) =>
|
|
118
|
+
Array.isArray(entry.hooks) && entry.hooks.some(h => h.command && h.command.includes('hydra-'));
|
|
119
|
+
|
|
120
|
+
// Remove stale Hydra entries (clean reinstall)
|
|
121
|
+
settings.hooks.SessionStart = settings.hooks.SessionStart.filter(x => !isHydraHook(x));
|
|
122
|
+
settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(x => !isHydraHook(x));
|
|
123
|
+
|
|
124
|
+
settings.hooks.SessionStart.push({
|
|
125
|
+
hooks: [{ type: 'command', command: 'node ~/.claude/hooks/hydra-check-update.js' }]
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
settings.hooks.PostToolUse.push({
|
|
129
|
+
matcher: 'Write|Edit|MultiEdit',
|
|
130
|
+
hooks: [{ type: 'command', command: 'node ~/.claude/hooks/hydra-auto-guard.js' }]
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
let statusLineConfigured = false;
|
|
134
|
+
if (!settings.statusLine || (settings.statusLine.command && settings.statusLine.command.includes('hydra-'))) {
|
|
135
|
+
settings.statusLine = {
|
|
136
|
+
type: 'command',
|
|
137
|
+
command: 'node ~/.claude/hooks/hydra-statusline.js',
|
|
138
|
+
padding: 0,
|
|
139
|
+
};
|
|
140
|
+
statusLineConfigured = true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
writeFileSafe(settingsFile, JSON.stringify(settings, null, 2));
|
|
144
|
+
return { statusLineConfigured };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function deregisterHooks() {
|
|
148
|
+
const settingsFile = path.join(GLOBAL_BASE, 'settings.json');
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
let settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
|
|
152
|
+
|
|
153
|
+
const isHydraHook = (entry) =>
|
|
154
|
+
Array.isArray(entry.hooks) && entry.hooks.some(h => h.command && h.command.includes('hydra-'));
|
|
155
|
+
|
|
156
|
+
if (settings.hooks?.SessionStart) {
|
|
157
|
+
settings.hooks.SessionStart = settings.hooks.SessionStart.filter(x => !isHydraHook(x));
|
|
158
|
+
if (!settings.hooks.SessionStart.length) delete settings.hooks.SessionStart;
|
|
159
|
+
}
|
|
160
|
+
if (settings.hooks?.PostToolUse) {
|
|
161
|
+
settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(x => !isHydraHook(x));
|
|
162
|
+
if (!settings.hooks.PostToolUse.length) delete settings.hooks.PostToolUse;
|
|
163
|
+
}
|
|
164
|
+
if (settings.hooks && !Object.keys(settings.hooks).length) delete settings.hooks;
|
|
165
|
+
|
|
166
|
+
if (settings.statusLine?.command?.includes('hydra-')) delete settings.statusLine;
|
|
167
|
+
|
|
168
|
+
writeFileSafe(settingsFile, JSON.stringify(settings, null, 2));
|
|
169
|
+
console.log(chalk.green(' \u2714 Hooks deregistered from settings.json'));
|
|
170
|
+
} catch (err) {
|
|
171
|
+
console.log(chalk.yellow(` \u26a0 Could not update settings.json: ${err.message}`));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
71
175
|
// ── Core install ─────────────────────────────────────────────────────────────
|
|
72
176
|
|
|
73
177
|
function installToBase(base, label) {
|
|
@@ -125,10 +229,14 @@ async function runInstall(scope) {
|
|
|
125
229
|
if (results.some((r) => !r.success)) anyFailed = true;
|
|
126
230
|
}
|
|
127
231
|
|
|
232
|
+
// Hooks and settings are always global (once, not per-base)
|
|
233
|
+
installHooks();
|
|
234
|
+
const { statusLineConfigured } = registerHooksInSettings();
|
|
235
|
+
|
|
128
236
|
if (!anyFailed) {
|
|
129
|
-
showInstallComplete();
|
|
237
|
+
showInstallComplete(statusLineConfigured);
|
|
130
238
|
} else {
|
|
131
|
-
console.log(chalk.yellow('
|
|
239
|
+
console.log(chalk.yellow(' \u26a0 Some files failed to install. Check errors above.\n'));
|
|
132
240
|
}
|
|
133
241
|
}
|
|
134
242
|
|
|
@@ -189,11 +297,28 @@ async function runUninstall({ interactive = true } = {}) {
|
|
|
189
297
|
}
|
|
190
298
|
}
|
|
191
299
|
|
|
300
|
+
// Remove hook .js files from ~/.claude/hooks/
|
|
301
|
+
for (const key of Object.keys(hooks)) {
|
|
302
|
+
const dest = path.join(GLOBAL_BASE, 'hooks', `${key}.js`);
|
|
303
|
+
if (fileExists(dest)) {
|
|
304
|
+
try { fs.unlinkSync(dest); console.log(chalk.green(` \u2714 Removed hooks/${key}.js`)); }
|
|
305
|
+
catch (err) { console.log(chalk.red(` \u2716 Failed: hooks/${key}.js \u2014 ${err.message}`)); }
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Remove cache file
|
|
310
|
+
const cacheFile = path.join(GLOBAL_BASE, 'cache', 'hydra-update-check.json');
|
|
311
|
+
if (fileExists(cacheFile)) {
|
|
312
|
+
try { fs.unlinkSync(cacheFile); } catch {}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
deregisterHooks();
|
|
316
|
+
|
|
192
317
|
console.log();
|
|
193
318
|
if (failed === 0) {
|
|
194
|
-
console.log(chalk.cyan.bold('
|
|
319
|
+
console.log(chalk.cyan.bold(' \uD83D\uDC09 All heads severed. Hydra sleeps.\n'));
|
|
195
320
|
} else {
|
|
196
|
-
console.log(chalk.yellow(`
|
|
321
|
+
console.log(chalk.yellow(` \u26a0 ${removed} removed, ${failed} failed.\n`));
|
|
197
322
|
}
|
|
198
323
|
}
|
|
199
324
|
|
|
@@ -214,10 +339,19 @@ function getStatus(base) {
|
|
|
214
339
|
installed: fileExists(e.dest),
|
|
215
340
|
}));
|
|
216
341
|
|
|
342
|
+
const commandEntries = manifest.filter((e) => e.type === 'command').map((e) => ({
|
|
343
|
+
name: e.display,
|
|
344
|
+
installed: fileExists(e.dest),
|
|
345
|
+
}));
|
|
346
|
+
|
|
347
|
+
const versionEntry = manifest.find((e) => e.type === 'version');
|
|
348
|
+
|
|
217
349
|
return {
|
|
218
350
|
agents: agentEntries,
|
|
219
351
|
skill: skillEntry ? fileExists(skillEntry.dest) : false,
|
|
220
352
|
references: refEntries,
|
|
353
|
+
commands: commandEntries,
|
|
354
|
+
version: versionEntry ? (fileExists(versionEntry.dest) ? fs.readFileSync(versionEntry.dest, 'utf8').trim() : null) : null,
|
|
221
355
|
};
|
|
222
356
|
}
|
|
223
357
|
|