hydra-nano 0.0.0-stage → 0.2.0
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/LICENSE +21 -0
- package/README.md +121 -2
- package/package.json +61 -4
- package/skills/hydra/SKILL.md +51 -0
- package/skills/hydra/scripts/hooks/route-tests.mjs +52 -0
- package/skills/hydra/scripts/hooks/session-guide.mjs +29 -0
- package/skills/hydra/scripts/hydra.mjs +349 -0
- package/skills/hydra/scripts/install.mjs +65 -0
- package/skills/hydra/scripts/lib/answer.mjs +127 -0
- package/skills/hydra/scripts/lib/bootstrap.mjs +77 -0
- package/skills/hydra/scripts/lib/claude_hooks.mjs +162 -0
- package/skills/hydra/scripts/lib/exec.mjs +37 -0
- package/skills/hydra/scripts/lib/files.mjs +192 -0
- package/skills/hydra/scripts/lib/git.mjs +278 -0
- package/skills/hydra/scripts/lib/integrations.mjs +251 -0
- package/skills/hydra/scripts/lib/ledger.mjs +133 -0
- package/skills/hydra/scripts/lib/local.mjs +92 -0
- package/skills/hydra/scripts/lib/ollama.mjs +2 -0
- package/skills/hydra/scripts/lib/os.mjs +243 -0
- package/skills/hydra/scripts/lib/read.mjs +294 -0
- package/skills/hydra/scripts/lib/route.mjs +19 -0
- package/skills/hydra/scripts/lib/runtime.mjs +100 -0
- package/skills/hydra/scripts/lib/test_logs.mjs +243 -0
- package/skills/hydra/scripts/mcp.mjs +154 -0
- package/skills/hydra/scripts/runtime-manifest.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rahul05ranjan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,122 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/rahul05ranjan/hydra/main/assets/hydra-banner.svg" alt="Hydra: small local models that take the noisy work off your coding agent" width="100%">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/hydra-nano"><img src="https://img.shields.io/npm/v/hydra-nano?color=7c5cff&label=npm" alt="npm version"></a>
|
|
7
|
+
<a href="https://huggingface.co/rahul05ranjan/hydra-nano-git"><img src="https://img.shields.io/badge/model-Hydra%20Nano%20v2-22d3ee" alt="Hydra Nano v2 on Hugging Face"></a>
|
|
8
|
+
<img src="https://img.shields.io/badge/runs-100%25%20local-5eead4" alt="Runs 100% local">
|
|
9
|
+
<img src="https://img.shields.io/badge/node-%E2%89%A518-3c873a" alt="Node 18 or later">
|
|
10
|
+
<a href="https://github.com/rahul05ranjan/hydra/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT license"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<b>Your coding agent reads every line of every test log, git diff and process list it runs.<br>Hydra reads them on your machine and hands back only the lines that matter.</b>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Why Hydra
|
|
20
|
+
|
|
21
|
+
Frontier coding agents pay for every token of tool output: a failing test suite, a long `git log`, a `ps` dump. Most of it is noise, and all of it fills the context window.
|
|
22
|
+
|
|
23
|
+
Hydra puts small specialist models on your own machine between the agent and that output:
|
|
24
|
+
|
|
25
|
+
- **Less context, same answer.** A `pnpm test` run that printed ~13,807 tokens reached the agent as ~57: the failing test, the assertion diff and the source line.
|
|
26
|
+
- **Nothing leaves your machine.** The model runs locally on llama.cpp. Hydra's telemetry stays in `~/.hydra/ledger.jsonl`.
|
|
27
|
+
- **Read-only by design.** The model never writes a shell command. It picks from fixed, read-only menus; code builds and runs the command.
|
|
28
|
+
- **Verbatim, not paraphrased.** Hydra returns the command's own lines, so the agent sees evidence, not a summary.
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx hydra-nano
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
One command installs everything: a pinned llama.cpp runtime, the ~812 MB Hydra Nano model (SHA-256 checked), the Hydra skill, and the integration for each coding tool it finds.
|
|
37
|
+
|
|
38
|
+
Want the `hydra` command in your own terminal too?
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install -g hydra-nano
|
|
42
|
+
hydra doctor
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Requires Node 18+. Runtime packages for macOS, Linux and Windows on x64 and arm64; verified live on macOS and Windows. Prefer GitHub? `npx github:rahul05ranjan/hydra` installs the same thing.
|
|
46
|
+
|
|
47
|
+
## Works with your coding tools
|
|
48
|
+
|
|
49
|
+
| Tool | How Hydra joins | Status |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| **Claude Code** | Skill + hooks that route test/build runs and git questions | Tested |
|
|
52
|
+
| **Codex** | Skill + MCP server + `PreToolUse` hook | MCP listed by Codex; trust the hook with `/hooks` |
|
|
53
|
+
| **OpenCode** | MCP server + plugin | MCP connects |
|
|
54
|
+
| **Cursor** | MCP server + `beforeShellExecution` hook | Preview |
|
|
55
|
+
| **Gemini CLI** | MCP server + `BeforeTool` hook | Preview |
|
|
56
|
+
| **Antigravity** | Skill + MCP server | Preview |
|
|
57
|
+
|
|
58
|
+
Any other tool that speaks MCP can use Hydra too: point it at `hydra mcp` (stdio).
|
|
59
|
+
|
|
60
|
+
Hydra never overwrites your settings. Every file it edits is backed up once as `<file>.before-hydra`, only Hydra's own entries change, and `hydra agents remove` takes them out again.
|
|
61
|
+
|
|
62
|
+
## What it does
|
|
63
|
+
|
|
64
|
+
| Head | Ask it | It returns |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| **git** | "what changed in the last commit?", "who last touched `billing.js`?" | The matching commits, diff hunks or blame lines |
|
|
67
|
+
| **test** | `hydra run -- npm test` (pytest, cargo, go, tsc, gradle, ...) | Failing test names, assertion diffs and source locations, with the original exit code |
|
|
68
|
+
| **os** | "what is listening on port 3000?" | The matching ports, processes or disk lines, with secrets blocked |
|
|
69
|
+
| **read** | `hydra read src/server.ts handleRequest` | One symbol, a line range or an outline |
|
|
70
|
+
| **files** | `hydra search parseConfig` | Definition-ranked matches |
|
|
71
|
+
|
|
72
|
+
Through MCP, Hydra offers the three where it pays most: `hydra_git`, `hydra_run` and `hydra_os`.
|
|
73
|
+
|
|
74
|
+
## Benchmarks
|
|
75
|
+
|
|
76
|
+
**Hydra Nano v2**: a full-parameter fine-tune of a 0.8B model, one set of weights for every head, run as a Q8_0 GGUF.
|
|
77
|
+
|
|
78
|
+
| Set | Tasks | Solved |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| Git (sealed test set, never used for training) | 1,000 | **96.3%** |
|
|
81
|
+
| Files (held out) | 1,000 | **98.0%** |
|
|
82
|
+
| Read (held out) | 1,000 | **96.1%** |
|
|
83
|
+
| Test logs (second held-out set) | 1,000 | **97.5%** |
|
|
84
|
+
| OS (held out, synthetic probe outputs) | 1,000 | **100%** |
|
|
85
|
+
| Test-log benchmark across 8 frameworks | 20 | **19/20**, 47% fewer tokens |
|
|
86
|
+
|
|
87
|
+
Receipts: [model card](https://huggingface.co/rahul05ranjan/hydra-nano-git) and [`evals/results`](https://github.com/rahul05ranjan/hydra/tree/main/evals/results).
|
|
88
|
+
|
|
89
|
+
**Where Hydra does not help, measured.** In a controlled trial of 36 Claude Code sessions extracting functions from real source code, forcing that work through Hydra used **72% more tokens** than the agent's own tools. So Hydra leaves source reading and search to the agent, and routes only the noisy work: test and build logs, git history, system inspection. Full method and numbers in [evaluations](https://github.com/rahul05ranjan/hydra/blob/main/docs/evaluations.md).
|
|
90
|
+
|
|
91
|
+
## Commands
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
hydra ask "which branch am I on and what changed?" # git, files or os, routed by the question
|
|
95
|
+
hydra run -- pytest -q # a test or build run, digested
|
|
96
|
+
hydra os "what is using port 8080?" # read-only system inspection
|
|
97
|
+
hydra read src/app.py create_app # one symbol, a line range or an outline
|
|
98
|
+
hydra stats # tokens kept out of context
|
|
99
|
+
hydra improve --since 2026-10-17 # what the local telemetry says to improve
|
|
100
|
+
hydra agents status # Hydra in each coding tool
|
|
101
|
+
hydra doctor # is the local model ready?
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Need the raw log for one run? Prefix it with `HYDRA_RAW=1` and the hook lets it through.
|
|
105
|
+
|
|
106
|
+
## Privacy and control
|
|
107
|
+
|
|
108
|
+
- **Local only.** The model, the runtime and the telemetry live in `~/.hydra`. Nothing is sent anywhere.
|
|
109
|
+
- **Turn telemetry off** with `HYDRA_TELEMETRY=0`.
|
|
110
|
+
- **Skip parts of the install** with `--no-agents` (no other tools), `--no-hooks` (no hooks) or `--runtime-only` (no skill).
|
|
111
|
+
- **Uninstall cleanly.** `hydra agents remove` and `hydra hooks remove` undo every integration and restore your settings; `hydra stop` stops the local server; deleting `~/.hydra` removes the runtime, the model and the telemetry.
|
|
112
|
+
|
|
113
|
+
## Learn more
|
|
114
|
+
|
|
115
|
+
- [How Hydra works](https://github.com/rahul05ranjan/hydra/blob/main/docs/how-it-works.md): heads, backends (llama.cpp or Ollama), telemetry, every install path and the code layout.
|
|
116
|
+
- [Evaluations and trials](https://github.com/rahul05ranjan/hydra/blob/main/docs/evaluations.md): how each number above was measured.
|
|
117
|
+
- [What real use teaches](https://github.com/rahul05ranjan/hydra/blob/main/IMPROVEMENTS.md): the open and fixed findings from Hydra's own telemetry.
|
|
118
|
+
- [Hydra Nano on Hugging Face](https://huggingface.co/rahul05ranjan/hydra-nano-git).
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT. See [LICENSE](https://github.com/rahul05ranjan/hydra/blob/main/LICENSE).
|
package/package.json
CHANGED
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hydra-nano",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Small local models that take the noisy work off your coding agent: git history, test and build digests and OS inspection run on your machine, and only the lines that matter reach Claude Code, Codex, Cursor, OpenCode, Gemini CLI or Antigravity. Skill, MCP server and hooks.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude-code",
|
|
7
|
+
"codex",
|
|
8
|
+
"cursor",
|
|
9
|
+
"opencode",
|
|
10
|
+
"gemini-cli",
|
|
11
|
+
"antigravity",
|
|
12
|
+
"mcp",
|
|
13
|
+
"mcp-server",
|
|
14
|
+
"agent-skill",
|
|
15
|
+
"ai-agents",
|
|
16
|
+
"coding-agent",
|
|
17
|
+
"local-llm",
|
|
18
|
+
"llama.cpp",
|
|
19
|
+
"token-saving",
|
|
20
|
+
"context-window",
|
|
21
|
+
"git",
|
|
22
|
+
"test-runner",
|
|
23
|
+
"developer-tools"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"author": "rahul05ranjan",
|
|
27
|
+
"homepage": "https://github.com/rahul05ranjan/hydra#readme",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/rahul05ranjan/hydra.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/rahul05ranjan/hydra/issues"
|
|
34
|
+
},
|
|
35
|
+
"type": "module",
|
|
36
|
+
"bin": {
|
|
37
|
+
"hydra-nano": "skills/hydra/scripts/install.mjs",
|
|
38
|
+
"hydra": "skills/hydra/scripts/hydra.mjs",
|
|
39
|
+
"hydra-install": "skills/hydra/scripts/install.mjs"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"test": "node --test",
|
|
43
|
+
"eval": "node evals/run.mjs",
|
|
44
|
+
"eval:build": "node evals/git1/build.mjs",
|
|
45
|
+
"eval:test": "node evals/test_logs/run.mjs",
|
|
46
|
+
"eval:test:build": "node evals/test_logs/build.mjs",
|
|
47
|
+
"eval:read": "node evals/read/run.mjs",
|
|
48
|
+
"eval:read:build": "node evals/read/build.mjs",
|
|
49
|
+
"eval:files": "node evals/files/run.mjs",
|
|
50
|
+
"eval:strict": "node evals/trial/strict_trial.mjs",
|
|
51
|
+
"eval:frontier:prepare": "node evals/trial/frontier_trial.mjs",
|
|
52
|
+
"eval:frontier": "node evals/trial/frontier_trial.mjs --run",
|
|
53
|
+
"eval:real:prepare": "node evals/trial/real_repo_trial.mjs",
|
|
54
|
+
"eval:real": "node evals/trial/real_repo_trial.mjs --run",
|
|
55
|
+
"dashboard": "node dashboard/server.mjs"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=18"
|
|
59
|
+
},
|
|
60
|
+
"files": [
|
|
61
|
+
"skills/hydra"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hydra
|
|
3
|
+
description: Hand a read-only git question (repo state, history, blame), a where-is-it code search, targeted file slicing instead of whole-file reads, test/build failure digests, or system/OS inspections (ports, processes, disk) to hydra locally. Use before running git status, log, diff, show, blame, broad searches, whole file reads, ps/lsof dumps, or test/build commands where noisy output would flood your context.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Hydra
|
|
7
|
+
|
|
8
|
+
`hydra` runs read-only commands for repo questions, file slicing, test/build runs, and system inspections on a small local model and prints the lines that answer it. The raw output stays on this machine instead of entering your context.
|
|
9
|
+
|
|
10
|
+
## Call it
|
|
11
|
+
|
|
12
|
+
From the repository the question is about, naming the head that fits:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
node scripts/hydra.mjs ask --head git "<question>"
|
|
16
|
+
node scripts/hydra.mjs ask --head files "<question>"
|
|
17
|
+
node scripts/hydra.mjs ask --head read "<question>"
|
|
18
|
+
node scripts/hydra.mjs ask --head test "<question>"
|
|
19
|
+
node scripts/hydra.mjs ask --head os "<question>"
|
|
20
|
+
node scripts/hydra.mjs search <term...>
|
|
21
|
+
node scripts/hydra.mjs read <file> [symbol|--lines <start-end>|--outline]
|
|
22
|
+
node scripts/hydra.mjs run -- <command...>
|
|
23
|
+
node scripts/hydra.mjs os [question...]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`scripts/` is in this skill's directory. Ask one full question per call and name the file, branch, commit or author when you know it: the model may only use a path, revision or name that appears in your question.
|
|
27
|
+
- For finding definitions & files: use `hydra search <term>` to get definition-ranked matches without noise or raw grep dumps.
|
|
28
|
+
- For reading code: use `hydra read <file> [symbol|--lines]` instead of reading multi-thousand-line files. It returns the exact symbol or slice, or a structural outline of symbols.
|
|
29
|
+
- For tests and builds: run `hydra run -- <command...>` to execute the command locally and get the failure evidence with its exit code.
|
|
30
|
+
- For system and environment: use `hydra os` or `--head os` to inspect listening ports, top processes, disk space, and system info without dumping 500 lines of terminal output into context.
|
|
31
|
+
|
|
32
|
+
## What it answers
|
|
33
|
+
|
|
34
|
+
- **git**: current branch and sync state, uncommitted and staged changes, commit history, what this branch adds over a base branch, one commit's patch, blame, branches, worktrees, remotes, stashes, tags.
|
|
35
|
+
- **files**: where a name is defined or used, and which files are named like it.
|
|
36
|
+
- **read**: extracts symbol definitions (functions, classes, methods), line ranges (`--lines 10-50`), or structural definition outlines across TypeScript, JavaScript, Python, Rust, and Go without dumping whole files into context.
|
|
37
|
+
- **test**: runs and parses test and build outputs (`npm test`, `pytest`, `cargo test`, `tsc`). If tests fail, extracts the failing test name, assertion diff, and exact user file:line location, stripping node_modules and framework traces.
|
|
38
|
+
- **os**: inspects listening ports (`lsof`), running processes / CPU consumers (`ps`), disk space (`df -h`), and system info (`uptime`, `uname`) while strictly guarding against printing secrets.
|
|
39
|
+
|
|
40
|
+
Changes to the repository, and questions that need reasoning about what code does, stay with you.
|
|
41
|
+
|
|
42
|
+
## Read the result
|
|
43
|
+
|
|
44
|
+
- Lines under each `$ command` are the command's own output, copied verbatim. Build on these.
|
|
45
|
+
- `Selected evidence:` introduces verbatim lines chosen by the local model. Interpret these lines yourself; Hydra does not generate a factual lead.
|
|
46
|
+
- Git lists (status, commits, branches, stashes, tags, remotes, worktrees) are returned whole. Put an explicit count in commit-history questions; the supported maximum is 50.
|
|
47
|
+
- `(N more lines not shown)` means output was withheld. When the lines shown do not settle the question, run that one command yourself.
|
|
48
|
+
|
|
49
|
+
## When it cannot run
|
|
50
|
+
|
|
51
|
+
Exit code 2 with `hydra unavailable` means the configured model is not ready. Run `node scripts/hydra.mjs doctor` to check it. For the default llama.cpp backend, run the installed `scripts/install.mjs --runtime-only` to download the pinned runtime and GGUF after telling the user the download size and that a background server will stay running. The managed config is in `~/.hydra`; Hydra starts its server when needed and `node scripts/hydra.mjs stop` stops it. The initial bootstrap shares one Git model across heads. For an external server, set `HYDRA_LLAMA_URL` to its root (without `/v1`) and `HYDRA_MODEL` to its alias. For Ollama, set `HYDRA_BACKEND=ollama`; its `setup` downloads models, so tell the user before starting it. `npx skills add` installs skill/CLI files only; `npx github:rahul05ranjan/hydra` performs the full bootstrap. If the runtime cannot be made available, do the task directly and report the limitation.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Shell-command hook: a test or build run goes through `hydra run --` (or the hydra_run MCP tool),
|
|
3
|
+
// whose digest keeps failing test names, assertion diffs and source locations and drops the rest.
|
|
4
|
+
// Prefix a command with HYDRA_RAW=1 when the raw log is really needed.
|
|
5
|
+
// --format picks the tool's hook protocol:
|
|
6
|
+
// claude, codex PreToolUse; command at tool_input.command; deny via hookSpecificOutput.
|
|
7
|
+
// gemini BeforeTool on run_shell_command; deny via {"decision": "deny", "reason"}.
|
|
8
|
+
// cursor beforeShellExecution; command at command; must always answer with a permission.
|
|
9
|
+
// Each decision goes to the local ledger with the hook's own time, for `hydra improve`; a command
|
|
10
|
+
// that simply passes is recorded without its text.
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import { rawBypass, testRun } from '../lib/claude_hooks.mjs';
|
|
14
|
+
import { record } from '../lib/ledger.mjs';
|
|
15
|
+
|
|
16
|
+
const hydra = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'hydra.mjs');
|
|
17
|
+
const flag = process.argv.indexOf('--format');
|
|
18
|
+
const format = flag >= 0 ? process.argv[flag + 1] : 'claude';
|
|
19
|
+
|
|
20
|
+
const answers = {
|
|
21
|
+
claude: { pass: null, deny: reason => ({ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason } }) },
|
|
22
|
+
gemini: { pass: { decision: 'allow' }, deny: reason => ({ decision: 'deny', reason }) },
|
|
23
|
+
cursor: { pass: { permission: 'allow' }, deny: reason => ({ permission: 'deny', agent_message: reason,
|
|
24
|
+
user_message: 'Hydra sent this test/build run through hydra run to keep the full log out of context.' }) },
|
|
25
|
+
};
|
|
26
|
+
answers.codex = answers.claude;
|
|
27
|
+
const answer = answers[format] ?? answers.claude;
|
|
28
|
+
|
|
29
|
+
let input = '';
|
|
30
|
+
process.stdin.on('data', chunk => { input += chunk; }).on('end', () => {
|
|
31
|
+
let parsed;
|
|
32
|
+
try { parsed = JSON.parse(input) ?? {}; } catch {
|
|
33
|
+
if (answer.pass) process.stdout.write(JSON.stringify(answer.pass));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const command = String((format === 'cursor' ? parsed.command : parsed.tool_input?.command) ?? '');
|
|
37
|
+
const session = parsed.session_id ?? parsed.conversation_id;
|
|
38
|
+
const event = (decision, withCommand = true) => ({ ts: new Date().toISOString(), kind: 'hook', hook: 'route-tests',
|
|
39
|
+
...(format === 'claude' ? {} : { agent: format }), decision, ...(withCommand ? { command: command.slice(0, 300) } : {}),
|
|
40
|
+
...(session ? { session } : {}), hookMs: Math.round(process.uptime() * 1000) });
|
|
41
|
+
const run = testRun(command);
|
|
42
|
+
if (!run) {
|
|
43
|
+
record(rawBypass(command) ? event('raw') : event('pass', false));
|
|
44
|
+
if (answer.pass) process.stdout.write(JSON.stringify(answer.pass));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
record({ ...event('deny'), run });
|
|
48
|
+
process.stdout.write(JSON.stringify(answer.deny(`Test/build runs go through Hydra so the full log stays out of context. Run it as:\n` +
|
|
49
|
+
` node "${hydra}" run -- ${run}\n${format === 'claude' ? '' : 'or call the hydra_run tool with that command. '}` +
|
|
50
|
+
`(Keep any cd or env prefix before it.) Hydra returns the failing tests, assertion diffs and source locations ` +
|
|
51
|
+
`verbatim, or a short pass summary. If you truly need the raw log, prefix the original command with HYDRA_RAW=1.`)));
|
|
52
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Session-start hook: tells the agent where Hydra pays off, so it uses it without being asked.
|
|
3
|
+
// --format claude or codex answers with hookSpecificOutput; cursor with additional_context.
|
|
4
|
+
// The session start goes to the local ledger, so `hydra improve` can tell how many sessions used Hydra.
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { record } from '../lib/ledger.mjs';
|
|
8
|
+
|
|
9
|
+
const hydra = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'hydra.mjs');
|
|
10
|
+
const flag = process.argv.indexOf('--format');
|
|
11
|
+
const format = flag >= 0 ? process.argv[flag + 1] : 'claude';
|
|
12
|
+
const mcp = format === 'claude' ? '' : ' (or the hydra_git / hydra_run MCP tools)';
|
|
13
|
+
const guide = `Hydra is installed: it runs noisy read-only commands on a small local model and returns only the lines that matter.\n` +
|
|
14
|
+
`- For git history, diff, blame or "what changed" questions, first run: node "${hydra}" ask --head git "<question>"${mcp} ` +
|
|
15
|
+
`from the repository, instead of reading git log, git diff or git show output yourself. Use plain git for writes ` +
|
|
16
|
+
`and for short checks such as git status --short.\n` +
|
|
17
|
+
`- A hook sends test and build runs through: node "${hydra}" run -- <command>\n` +
|
|
18
|
+
`- Keep using your own file reading and search for source files; Hydra does not save tokens there.`;
|
|
19
|
+
|
|
20
|
+
let input = '';
|
|
21
|
+
process.stdin.on('data', chunk => { input += chunk; }).on('end', () => {
|
|
22
|
+
let start = {};
|
|
23
|
+
try { start = JSON.parse(input) ?? {}; } catch { /* The tool always sends JSON; record what there is. */ }
|
|
24
|
+
const session = start.session_id ?? start.conversation_id;
|
|
25
|
+
record({ ts: new Date().toISOString(), kind: 'session', ...(format === 'claude' ? {} : { agent: format }),
|
|
26
|
+
...(session ? { session } : {}), ...(start.source ? { source: start.source } : {}) });
|
|
27
|
+
process.stdout.write(JSON.stringify(format === 'cursor' ? { additional_context: guide }
|
|
28
|
+
: { hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: guide } }));
|
|
29
|
+
});
|