hermes-to-claude 0.1.0 → 0.1.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/DESIGN.md CHANGED
@@ -1,4 +1,4 @@
1
- # H-Bridge (hbridge) Design
1
+ # H-Bridge (h2c) Design
2
2
 
3
3
  Hermes Bridge — local HTTP bridge from Hermes Agent to Claude Code.
4
4
 
@@ -6,11 +6,11 @@ Hermes Bridge — local HTTP bridge from Hermes Agent to Claude Code.
6
6
 
7
7
  ```
8
8
  npm install
9
- ├─ preinstall: build → dist/hbridge.mjs + dist/statusline.mjs
9
+ ├─ preinstall: build → dist/h2c.mjs + dist/statusline.mjs
10
10
  ├─ install: npm deps
11
11
  └─ postinstall: register statusLine + skill in ~/.claude configs
12
12
 
13
- /hbridge enable
13
+ /h2c enable
14
14
  └─ HTTP server on homePort(cwd)
15
15
  ├─ GET /health → {"status":"ok"}
16
16
  ├─ POST /v1/task/create → Bridge.createTask(prompt)
@@ -98,7 +98,7 @@ data: {"type":"done","exitCode":0}
98
98
  ### Task Lifecycle Diagram
99
99
 
100
100
  ```
101
- Hermes hbridge:<port> Session (Claude Code)
101
+ Hermes h2c:<port> Session (Claude Code)
102
102
  │ │ │
103
103
  │ POST /v1/task/create │ │
104
104
  │ {"prompt":"fix bug"} │ │
@@ -153,21 +153,21 @@ Hermes hbridge:<port> Session (Claud
153
153
 
154
154
  **Key is machine-global**, **port is per-directory**. This follows Claude Code's working-directory-centric model.
155
155
 
156
- Claude Code reads its context — `CLAUDE.md`, project skills, `.claude/settings.json` — from the **current working directory**. Each project has its own conventions, its own skills, its own CLAUDE.md. To serve the right context, hbridge must spawn Claude Code in the right directory.
156
+ Claude Code reads its context — `CLAUDE.md`, project skills, `.claude/settings.json` — from the **current working directory**. Each project has its own conventions, its own skills, its own CLAUDE.md. To serve the right context, h2c must spawn Claude Code in the right directory.
157
157
 
158
158
  ```
159
- /project-a/CLAUDE.md → hbridge :9200 → Claude Code (cwd=/project-a)
160
- /project-b/CLAUDE.md → hbridge :9201 → Claude Code (cwd=/project-b)
159
+ /project-a/CLAUDE.md → h2c :9200 → Claude Code (cwd=/project-a)
160
+ /project-b/CLAUDE.md → h2c :9201 → Claude Code (cwd=/project-b)
161
161
  ```
162
162
 
163
163
  **Port per directory** (`9200 + MD5(cwd) % 600`) means:
164
164
  - Each project gets a stable, predictable port
165
- - You can run hbridge for multiple projects on the same machine without conflict
165
+ - You can run h2c for multiple projects on the same machine without conflict
166
166
  - The port is deterministic — Hermes computes it from the directory path without asking the server
167
167
 
168
168
  **Key per machine** means:
169
169
  - Hermes only needs one credential per machine, not one per project
170
- - The key is random, generated once, persisted in `~/.hbridge_key`
170
+ - The key is random, generated once, persisted in `~/.h2c_key`
171
171
  - Same auth works across all directories on the same host
172
172
 
173
173
  Key format: `hb_` + 8 base52 chars (≈ 45 bits entropy, `crypto.randomBytes(8)`, stored once).
@@ -175,19 +175,19 @@ Key format: `hb_` + 8 base52 chars (≈ 45 bits entropy, `crypto.randomBytes(8)`
175
175
  ## Key & Port Derivation
176
176
 
177
177
  - **Port**: `9200 + (MD5(cwd)[0:2] % 600)` — range [9200, 9799], stable per directory
178
- - **Key**: `hb_` + 8 random base52 characters — generated once, stored in `~/.hbridge_key`. Same for all directories on one machine.
178
+ - **Key**: `hb_` + 8 random base52 characters — generated once, stored in `~/.h2c_key`. Same for all directories on one machine.
179
179
 
180
180
  ## Task Persistence
181
181
 
182
- Completed tasks are saved to `~/.hbridge_tasks.jsonl` (JSONL, append-only, max 2000 entries). On server restart, tasks are loaded into memory. `getTaskOutput` checks session → memory cache → disk.
182
+ Completed tasks are saved to `~/.h2c_tasks.jsonl` (JSONL, append-only, max 2000 entries). On server restart, tasks are loaded into memory. `getTaskOutput` checks session → memory cache → disk.
183
183
 
184
184
  ## Files
185
185
 
186
186
  ### Runtime (gitignored)
187
187
  | File | Purpose |
188
188
  |------|---------|
189
- | `~/.hbridge_state.json` | running/stopped flag + port + lastClientIP/lastActiveAt |
190
- | `~/.hbridge_tasks.jsonl` | Completed task records (JSONL, survive restart) |
189
+ | `~/.h2c_state.json` | running/stopped flag + port + lastClientIP/lastActiveAt |
190
+ | `~/.h2c_tasks.jsonl` | Completed task records (JSONL, survive restart) |
191
191
 
192
192
  ### Config (written by postinstall)
193
193
  | File | Purpose |
@@ -199,8 +199,8 @@ Completed tasks are saved to `~/.hbridge_tasks.jsonl` (JSONL, append-only, max 2
199
199
  Claude Code polls ~3-5s:
200
200
 
201
201
  ```
202
- ▶️ hbridge: on | :<port>
203
- ⏹️ hbridge: off
202
+ ▶️ h2c: on | :<port>
203
+ ⏹️ h2c: off
204
204
  ```
205
205
 
206
206
  Liveness is determined by polling `127.0.0.1:<port>/health` — no state-file dependency.
@@ -212,7 +212,7 @@ Liveness is determined by polling `127.0.0.1:<port>/health` — no state-file de
212
212
  - Old: `createTask` polled `setInterval` until previous task finished
213
213
  - New: Session pool with promise queue — no busy-polling
214
214
  - **Task timeout kills queue** ✅ Fixed (see #52)
215
- - **Persistence**: Completed tasks saved to `~/.hbridge_tasks.jsonl`, survive restart
215
+ - **Persistence**: Completed tasks saved to `~/.h2c_tasks.jsonl`, survive restart
216
216
  - **Session class**: One Claude Code child process per task
217
217
  - **Parallelism**: Up to 3 concurrent tasks (configurable via `maxConcurrent`)
218
218
 
@@ -221,9 +221,9 @@ Liveness is determined by polling `127.0.0.1:<port>/health` — no state-file de
221
221
  - Timeout is now opt-in: pass `taskTimeoutMs` to Bridge/Session constructor
222
222
 
223
223
  ### #53 — Home mode direct enable
224
- - Home mode (`HBRIDGE_HOME=1`) now calls `cmd_enable()` directly
224
+ - Home mode (`H2C_HOME=1`) now calls `cmd_enable()` directly
225
225
  - No longer goes through `--stdio` MCP layer
226
- - `--stdio` removed — hbridge is HTTP-only
226
+ - `--stdio` removed — h2c is HTTP-only
227
227
 
228
228
  ### #54+ — Permission pipeline
229
229
  - `--dangerously-skip-permissions` replaced by Hermes-controlled permission pipeline
package/README.md CHANGED
@@ -1,17 +1,17 @@
1
- # Hermes-Claude-Bridge (hbridge)
1
+ # Hermes-Claude-Bridge (h2c)
2
2
 
3
3
  **Hermes-Agent** controls multiple **Claude Code** instances via HTTP — one agent, many Claude workers. No Pro/Max subscription required.
4
4
 
5
5
  ```
6
- 📱 User ----HTTP----> 🤖 Hermes-Agent ----hbridge----> 🏭 Claude Code (deploy role)
7
- ├───hbridge───> 🔧 Claude Code (coding role)
8
- ├───hbridge───> 🧪 Claude Code (testing role)
9
- └───hbridge───> 🔬 Claude Code (building role)
6
+ 📱 User ----HTTP----> 🤖 Hermes-Agent ----h2c----> 🏭 Claude Code (deploy role)
7
+ ├───h2c───> 🔧 Claude Code (coding role)
8
+ ├───h2c───> 🧪 Claude Code (testing role)
9
+ └───h2c───> 🔬 Claude Code (building role)
10
10
  ```
11
11
 
12
12
  ---
13
13
 
14
- ## 1. hbridge Advantages
14
+ ## 1. h2c Advantages
15
15
 
16
16
  - **No Pro/Max required** — works with any Claude Code via stdio; no Anthropic subscription needed.
17
17
  - **One agent, many Claudes** — one Hermes-Agent routes tasks to multiple Claude Code instances, each in its own project directory with its own CLAUDE.md and skills.
@@ -25,7 +25,7 @@
25
25
 
26
26
  ### Prepare
27
27
 
28
- hbridge requires **Node.js ≥ 20**. Install it for your platform:
28
+ h2c requires **Node.js ≥ 20**. Install it for your platform:
29
29
 
30
30
  | Platform | Command |
31
31
  |----------|---------|
@@ -43,24 +43,24 @@ npm install && npm run build
43
43
 
44
44
  ### Start in Claude Code
45
45
 
46
- After installation, enable hbridge from inside Claude Code:
46
+ After installation, enable h2c from inside Claude Code:
47
47
 
48
48
  ```
49
- /hbridge enable
49
+ /h2c enable
50
50
  ```
51
51
 
52
52
  This starts the HTTP server. The port is derived from the working directory (see Authentication and Port below). Check status at any time:
53
53
 
54
54
  ```
55
- /hbridge status
55
+ /h2c status
56
56
  ```
57
57
 
58
58
  ### Home Mode — For Headless Machines
59
59
 
60
- When Hermes-Agent runs on a server with no display, no manual command is needed. Export the **environment variable** before starting hbridge:
60
+ When Hermes-Agent runs on a server with no display, no manual command is needed. Export the **environment variable** before starting h2c:
61
61
 
62
62
  ```bash
63
- export HBRIDGE_HOME=1 # environment variable — auto-starts hbridge
63
+ export H2C_HOME=1 # environment variable — auto-starts h2c
64
64
  ```
65
65
 
66
66
  With Home mode active, the server listens on `127.0.0.1` only. Authentication is **disabled** — safe because only local processes can reach it. Hermes-Agent connects without managing keys.
@@ -73,17 +73,17 @@ The port is **deterministic**: `MD5(cwd)` → first 2 bytes → `9200 + (value %
73
73
  - Claude reads its own CLAUDE.md, skills, and project files from that directory
74
74
  - Port = project — you always know which Claude you're talking to
75
75
 
76
- The auth key (`hb_` + 8 random base52 characters) is written to `~/.hbridge_key` once and reused across all directories on the same machine. All HTTP endpoints (except `/health`) require HTTP Basic Auth with username `bridge` and the key as password.
76
+ The auth key (`hb_` + 8 random base52 characters) is written to `~/.h2c_key` once and reused across all directories on the same machine. All HTTP endpoints (except `/health`) require HTTP Basic Auth with username `bridge` and the key as password.
77
77
 
78
78
  ---
79
79
 
80
80
  ## 3. For Hermes-Agent
81
81
 
82
- Hermes-Agent discovers and controls hbridge via HTTP. Here is the complete API.
82
+ Hermes-Agent discovers and controls h2c via HTTP. Here is the complete API.
83
83
 
84
84
  ### Security
85
85
 
86
- Like Claude Code CLI, hbridge requires authentication for all mutating endpoints. But Hermes-Agent can simplify — either handle the auth flow itself, or bypass it entirely with `skip_permissions` for trusted workloads. Hermes-Agent must handle the same authentication and permission flow that human users would, or explicitly choose to bypass it.
86
+ Like Claude Code CLI, h2c requires authentication for all mutating endpoints. But Hermes-Agent can simplify — either handle the auth flow itself, or bypass it entirely with `skip_permissions` for trusted workloads. Hermes-Agent must handle the same authentication and permission flow that human users would, or explicitly choose to bypass it.
87
87
 
88
88
  ### Create a Task
89
89
 
@@ -145,7 +145,7 @@ curl -X POST http://<host>:<port>/v1/task/cancel \
145
145
 
146
146
  ### Permission Pipeline
147
147
 
148
- hbridge includes a full permission pipeline — just like CI tools. Hermes-Agent can decide per-task how to handle tool approvals:
148
+ h2c includes a full permission pipeline — just like CI tools. Hermes-Agent can decide per-task how to handle tool approvals:
149
149
 
150
150
  | Mode | Behavior |
151
151
  |------|----------|
@@ -158,7 +158,7 @@ For quick development tasks, use `skip_permissions`. For production workloads th
158
158
  ### Architecture
159
159
 
160
160
  ```
161
- Hermes ──HTTP──▶ hbridge :<port> ──spawn──▶ Session (Claude Code)
161
+ Hermes ──HTTP──▶ h2c :<port> ──spawn──▶ Session (Claude Code)
162
162
  │ │ │
163
163
  │ Control Layer │ server.mjs (HTTP routing) │ reads CLAUDE.md
164
164
  │ (create/cancel/ │ bridge.mjs (session pool) │ loads skills
@@ -206,7 +206,7 @@ Event types pushed over SSE:
206
206
 
207
207
  #### NDJSON Transcript
208
208
 
209
- The raw Claude Code stdout is saved to `~/.hbridge_transcript.jsonl`. Each line is a complete NDJSON message from Claude Code — `assistant` messages, `stream_event` deltas, `tool_use` blocks, and `result` messages with usage data:
209
+ The raw Claude Code stdout is saved to `~/.h2c_transcript.jsonl`. Each line is a complete NDJSON message from Claude Code — `assistant` messages, `stream_event` deltas, `tool_use` blocks, and `result` messages with usage data:
210
210
 
211
211
  ```jsonl
212
212
  {"type":"stream_event","event":{"type":"content_block_delta","delta":{"type":"text_delta","text":"Fixing"}}}
@@ -223,7 +223,7 @@ Hermes-Agent can tail this file for full Claude Code session history, or consume
223
223
  | Document | Contents |
224
224
  |----------|----------|
225
225
  | [DESIGN.md](DESIGN.md) | Architecture, protocol, session lifecycle, permission pipeline, API reference, changelog |
226
- | [docs/local-mode.md](docs/local-mode.md) | HBRIDGE_HOME auto-start mode |
226
+ | [docs/local-mode.md](docs/local-mode.md) | H2C_HOME auto-start mode |
227
227
  | [docs/spawn-mechanism.md](docs/spawn-mechanism.md) | Claude Code spawn protocol, NDJSON format |
228
228
  | [docs/mcp-spec.md](docs/mcp-spec.md) | MCP tool definitions (Claude Code integration) |
229
229
 
package/build.mjs CHANGED
@@ -9,10 +9,10 @@ const SRC_DIR = path.resolve(__dirname, 'src')
9
9
  // Generate version from git
10
10
  const count = execSync("git rev-list --count HEAD", { encoding: "utf8" }).trim();
11
11
  const hash = execSync("git rev-parse --short HEAD", { encoding: "utf8" }).trim();
12
- const HBRIDGE_VERSION = `v${count}.0.${hash}`;
13
- console.log(`✓ version ${HBRIDGE_VERSION}`);
12
+ const H2C_VERSION = `v${count}.0.${hash}`;
13
+ console.log(`✓ version ${H2C_VERSION}`);
14
14
 
15
- // Build hbridge CLI
15
+ // Build h2c CLI (dist/hbridge.mjs)
16
16
  await esbuild.build({
17
17
  entryPoints: [path.join(SRC_DIR, 'hbridge', 'cli.mjs')],
18
18
  bundle: true,
@@ -21,7 +21,7 @@ await esbuild.build({
21
21
  outfile: 'dist/hbridge.mjs',
22
22
  external: ['zod', 'chalk', 'axios', '@anthropic-ai/sdk', 'qrcode', 'lodash-es', 'get-east-asian-width'],
23
23
  define: {
24
- 'globalThis.HBRIDGE_VERSION': JSON.stringify(HBRIDGE_VERSION),
24
+ 'globalThis.H2C_VERSION': JSON.stringify(H2C_VERSION),
25
25
  },
26
26
  });
27
27
 
package/dist/hbridge.mjs CHANGED
@@ -248,7 +248,7 @@ var StdioTransport = class {
248
248
  });
249
249
  this._transcriptPath = opts.transcriptPath;
250
250
  if (this._transcriptPath === void 0) {
251
- this._transcriptPath = join(homedir(), ".hbridge_transcript.jsonl");
251
+ this._transcriptPath = join(homedir(), ".h2c_transcript.jsonl");
252
252
  }
253
253
  this._transcriptStream = null;
254
254
  this._uploader = new SerialBatchEventUploader({
@@ -982,7 +982,7 @@ ${resultText}
982
982
  import { readFileSync, appendFileSync, writeFileSync, existsSync } from "fs";
983
983
  import { join as join2 } from "path";
984
984
  import { homedir as homedir2 } from "os";
985
- var TASKS_FILE = join2(homedir2(), ".hbridge_tasks.jsonl");
985
+ var TASKS_FILE = join2(homedir2(), ".h2c_tasks.jsonl");
986
986
  var MAX_TASKS = 2e3;
987
987
  function appendCompletedTask(task) {
988
988
  if (!task || !task.id) return;
@@ -1051,7 +1051,7 @@ function trimCompletedTasks() {
1051
1051
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
1052
1052
  import { join as join3 } from "path";
1053
1053
  import { homedir as homedir3 } from "os";
1054
- var STATE_FILE = join3(homedir3(), ".hbridge_state.json");
1054
+ var STATE_FILE = join3(homedir3(), ".h2c_state.json");
1055
1055
  var DEFAULT_STATE = {
1056
1056
  running: false,
1057
1057
  port: 9190,
@@ -1412,9 +1412,9 @@ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsS
1412
1412
  import { join as join4 } from "path";
1413
1413
  import { homedir as homedir4 } from "os";
1414
1414
  var BASE52 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1415
- var KEY_FILE = join4(homedir4(), ".hbridge_key");
1415
+ var KEY_FILE = join4(homedir4(), ".h2c_key");
1416
1416
  function isHome() {
1417
- return process.env.HBRIDGE_HOME == 1;
1417
+ return process.env.H2C_HOME == 1;
1418
1418
  }
1419
1419
  function homePort(cwd) {
1420
1420
  const hash = createHash("md5").update(Buffer.from(cwd, "utf-8")).digest();
@@ -1423,12 +1423,12 @@ function homePort(cwd) {
1423
1423
  function homeKey(_cwd) {
1424
1424
  if (existsSync3(KEY_FILE)) {
1425
1425
  const existing = readFileSync3(KEY_FILE, "utf8").trim();
1426
- if (existing && existing.startsWith("hb_")) {
1426
+ if (existing && existing.startsWith("h2c_")) {
1427
1427
  return existing;
1428
1428
  }
1429
1429
  }
1430
1430
  const bytes = randomBytes(8);
1431
- let key = "hb_";
1431
+ let key = "h2c_";
1432
1432
  for (const byte of bytes) {
1433
1433
  key += BASE52[byte % 52];
1434
1434
  }
@@ -1571,7 +1571,7 @@ function createServer(expectedKey, bridge = new Bridge()) {
1571
1571
  function startStatusBar(port) {
1572
1572
  function render() {
1573
1573
  const uptime = Math.floor((Date.now() - startTime) / 6e4);
1574
- process.stdout.write(`\r hbridge: on | port: ${port} | ${taskCount} tasks | \u2191 ${uptime}min `);
1574
+ process.stdout.write(`\r h2c: on | port: ${port} | ${taskCount} tasks | \u2191 ${uptime}min `);
1575
1575
  }
1576
1576
  render();
1577
1577
  return setInterval(render, 5e3);
@@ -1582,7 +1582,7 @@ function stopStatusBar(intervalId) {
1582
1582
 
1583
1583
  // src/hbridge/cli.mjs
1584
1584
  import { networkInterfaces } from "os";
1585
- var HBRIDGE_VERSION = "v177.0.c21a99a";
1585
+ var H2C_VERSION = "v184.0.572153f";
1586
1586
  var server = null;
1587
1587
  var statusBarInterval = null;
1588
1588
  function getLocalIPs() {
@@ -1594,13 +1594,13 @@ async function cmd_enable() {
1594
1594
  const key = homeKey(cwd);
1595
1595
  const ips = getLocalIPs();
1596
1596
  const ip = isHome() ? "127.0.0.1" : ips[0] || "127.0.0.1";
1597
- console.log("hbridge enabled");
1597
+ console.log("h2c enabled");
1598
1598
  console.log("\u{1F4C2} " + cwd);
1599
- console.log("\u{1F511} " + ip + ":" + port + " | " + key + " | " + HBRIDGE_VERSION);
1599
+ console.log("\u{1F511} " + ip + ":" + port + " | " + key + " | " + H2C_VERSION);
1600
1600
  server = createServer(key);
1601
1601
  server.on("error", (err) => {
1602
1602
  if (err.code === "EADDRINUSE") {
1603
- process.stderr.write(`Port ${port} already in use \u2014 hbridge may already be running
1603
+ process.stderr.write(`Port ${port} already in use \u2014 h2c may already be running
1604
1604
  `);
1605
1605
  } else {
1606
1606
  process.stderr.write(`Server error: ${err.message}
@@ -1617,14 +1617,14 @@ function cmd_disable() {
1617
1617
  server.close(() => {
1618
1618
  server = null;
1619
1619
  markStopped();
1620
- console.log(" hbridge: off");
1620
+ console.log(" h2c: off");
1621
1621
  stopStatusBar(statusBarInterval);
1622
1622
  process.exit(0);
1623
1623
  });
1624
1624
  setTimeout(() => process.exit(0), 1e3);
1625
1625
  } else {
1626
1626
  markStopped();
1627
- console.log(" hbridge: off");
1627
+ console.log(" h2c: off");
1628
1628
  process.exit(0);
1629
1629
  }
1630
1630
  }
@@ -1636,26 +1636,26 @@ function cmd_status() {
1636
1636
  const state = readState();
1637
1637
  const ip = isHome() ? "127.0.0.1" : ips[0] || "127.0.0.1";
1638
1638
  const running = state.running;
1639
- console.log(running ? "hbridge enabled" : "hbridge stopped");
1639
+ console.log(running ? "h2c enabled" : "h2c stopped");
1640
1640
  console.log("\u{1F4C2} " + cwd);
1641
- console.log("\u{1F511} " + ip + ":" + port + " | " + key + " | " + HBRIDGE_VERSION);
1641
+ console.log("\u{1F511} " + ip + ":" + port + " | " + key + " | " + H2C_VERSION);
1642
1642
  }
1643
1643
  function showHelp() {
1644
1644
  console.log(`
1645
- hbridge \u2014 Hermes Bridge
1645
+ h2c \u2014 Hermes Bridge
1646
1646
 
1647
1647
  COMMANDS:
1648
- hbridge --enable Start bridge with deterministic key
1649
- hbridge --disable Stop bridge
1650
- hbridge --status Show status + last client connection
1651
- hbridge --help Show this help
1648
+ h2c --enable Start bridge with deterministic key
1649
+ h2c --disable Stop bridge
1650
+ h2c --status Show status + last client connection
1651
+ h2c --help Show this help
1652
1652
 
1653
- Port is derived from the working directory; key is machine-global from ~/.hbridge_key.
1654
- Home mode (HBRIDGE_HOME=1) skips auth; remote mode enforces key.
1653
+ Port is derived from the working directory; key is machine-global from ~/.h2c_key.
1654
+ Home mode (H2C_HOME=1) skips auth; remote mode enforces key.
1655
1655
 
1656
1656
  EXAMPLES:
1657
- hbridge --enable Enable with dir-derived key
1658
- hbridge --status Show connected IP + last active time
1657
+ h2c --enable Enable with dir-derived key
1658
+ h2c --status Show connected IP + last active time
1659
1659
  `);
1660
1660
  process.exit(0);
1661
1661
  }
@@ -1669,9 +1669,9 @@ else if (cmd === "--status") cmd_status();
1669
1669
  else if (cmd === "--help" || cmd === "-h") {
1670
1670
  showHelp();
1671
1671
  } else {
1672
- console.log("hbridge \u2014 Hermes Bridge");
1673
- console.log(" hbridge --enable Start bridge");
1674
- console.log(" hbridge --disable Stop bridge");
1675
- console.log(" hbridge --status Show status");
1676
- console.log(" hbridge --help Show help");
1672
+ console.log("h2c \u2014 Hermes Bridge");
1673
+ console.log(" h2c --enable Start bridge");
1674
+ console.log(" h2c --disable Stop bridge");
1675
+ console.log(" h2c --status Show status");
1676
+ console.log(" h2c --help Show help");
1677
1677
  }
@@ -35,8 +35,8 @@ async function main() {
35
35
  const port = portFromCwd(process.cwd());
36
36
  const alive = await livenessCheck(port);
37
37
  const lastDir = process.cwd().split(/[/\\]/).filter(Boolean).pop() || process.cwd();
38
- const hbStatus = alive ? `\u25B6\uFE0F hbridge: on | :${port} | ${lastDir}` : `\u23F9\uFE0F hbridge: off`;
39
- const userCmdFile = join(homedir(), ".hbridge_user_statusline_cmd");
38
+ const hbStatus = alive ? `\u25B6\uFE0F h2c: on | :${port} | ${lastDir}` : `\u23F9\uFE0F h2c: off`;
39
+ const userCmdFile = join(homedir(), ".h2c_user_statusline_cmd");
40
40
  let userPart = "";
41
41
  if (existsSync(userCmdFile)) {
42
42
  try {
@@ -1,42 +1,42 @@
1
- # hbridge Home Local Mode
1
+ # h2c Home Local Mode
2
2
 
3
- Zero-config local collaboration between Hermes and Claude (OOB — Out-Of-Box). Unlike remote mode (manual `/hbridge enable` + authentication required), local mode starts **automatically, with no auth, and lives as long as Claude does**.
3
+ Zero-config local collaboration between Hermes and Claude (OOB — Out-Of-Box). Unlike remote mode (manual `/h2c enable` + authentication required), local mode starts **automatically, with no auth, and lives as long as Claude does**.
4
4
 
5
5
  ## Motivation
6
6
 
7
- A general-purpose AI agent (Hermes) cannot remember domain specifics — experience is locked in skill files. hbridge Home Local Mode lets Hermes focus on orchestration only, delegating skills, scripts, and workflows to Claude. Claude owns one task at a time with zero memory pollution.
7
+ A general-purpose AI agent (Hermes) cannot remember domain specifics — experience is locked in skill files. h2c Home Local Mode lets Hermes focus on orchestration only, delegating skills, scripts, and workflows to Claude. Claude owns one task at a time with zero memory pollution.
8
8
 
9
9
  ```
10
- Hermes → HBRIDGE_HOME=1 → hbridge auto-starts → compute port from cwd → connect localhost → send task → read result
10
+ Hermes → H2C_HOME=1 → h2c auto-starts → compute port from cwd → connect localhost → send task → read result
11
11
  Claude → read CLAUDE.md → load skill → execute → respond
12
12
  ```
13
13
 
14
14
  ## Trigger
15
15
 
16
- Hermes sets the environment variable `HBRIDGE_HOME=1` and hbridge picks it up automatically — zero user action. No MCP config changes, no Claude involvement needed.
16
+ Hermes sets the environment variable `H2C_HOME=1` and h2c picks it up automatically — zero user action. No MCP config changes, no Claude involvement needed.
17
17
 
18
18
  ```bash
19
- HBRIDGE_HOME=1 node dist/hbridge.mjs
19
+ H2C_HOME=1 node dist/h2c.mjs
20
20
  ```
21
21
 
22
- When running as a standalone CLI, hbridge auto-starts if no subcommand is given and `HBRIDGE_HOME=1`:
22
+ When running as a standalone CLI, h2c auto-starts if no subcommand is given and `H2C_HOME=1`:
23
23
 
24
24
  ```bash
25
- HBRIDGE_HOME=1 node dist/hbridge.mjs
26
- # → hbridge enabled (auto-start)
25
+ H2C_HOME=1 node dist/h2c.mjs
26
+ # → h2c enabled (auto-start)
27
27
  ```
28
28
 
29
29
  ## Behavioral Differences
30
30
 
31
31
  | | Remote Mode | Home Mode |
32
32
  |------|-------------|-----------|
33
- | Startup | Manual `/hbridge enable` | **Auto-start** (calls `hbridge enable` internally) |
33
+ | Startup | Manual `/h2c enable` | **Auto-start** (calls `h2c enable` internally) |
34
34
  | Auth | Basic Auth required (machine key) | **No auth** (localhost trust) |
35
35
  | Port | hash(cwd) → [9200, 9799] | **hash(cwd)** → [9200, 9799] |
36
36
  | Listen | All interfaces (0.0.0.0) | **127.0.0.1 only** |
37
37
  | Lifecycle | Manual `--disable` | **Auto-stop on process exit** (HTTP server lives in same Node process) |
38
38
 
39
- Both modes use the same machine-global key (stored in `~/.hbridge_key`); home mode simply skips the auth check.
39
+ Both modes use the same machine-global key (stored in `~/.h2c_key`); home mode simply skips the auth check.
40
40
 
41
41
  ## Port Mapping
42
42
 
@@ -48,11 +48,11 @@ Each working directory gets a deterministic port, stable across runs and machine
48
48
 
49
49
  ## Implementation
50
50
 
51
- Implemented in `src/hbridge/home.mjs`:
51
+ Implemented in `src/h2c/home.mjs`:
52
52
 
53
- 1. `isHome()` — checks `HBRIDGE_HOME == 1` (strict)
53
+ 1. `isHome()` — checks `H2C_HOME == 1` (strict)
54
54
  2. `homePort(cwd)` — returns `9200 + MD5(cwd)[0:2] % 600` (range [9200, 9799])
55
- 3. `homeKey()` — reads/generates machine-global key in `~/.hbridge_key`
55
+ 3. `homeKey()` — reads/generates machine-global key in `~/.h2c_key`
56
56
 
57
57
  Server behavior when home mode is active:
58
58
  - **HTTP server** (`server.mjs`): skips auth, listens on `127.0.0.1`
@@ -64,4 +64,4 @@ Identical to remote mode — only port derivation and auth differ.
64
64
 
65
65
  ## Status
66
66
 
67
- **Implemented and active.** First-class feature, not experimental. See `src/hbridge/home.mjs` for the implementation and `tests/test_home.mjs` for test coverage.
67
+ **Implemented and active.** First-class feature, not experimental. See `src/h2c/home.mjs` for the implementation and `tests/test_home.mjs` for test coverage.
package/docs/mcp-spec.md CHANGED
@@ -1,85 +1,85 @@
1
- # MCP Protocol Spec — hbridge Mapping
2
-
3
- Spec: https://modelcontextprotocol.io/specification/2024-11-05
4
-
5
- ## 1. Lifecycle — Initialization
6
-
7
- **Spec ref**: `Specification → Base Protocol → Lifecycle → Initialization`
8
-
9
- **Sequence**:
10
- ```
11
- Client → Server: initialize (protocolVersion + capabilities + clientInfo)
12
- Server → Client: initialize response (protocolVersion + capabilities + serverInfo)
13
- Client → Server: notifications/initialized (no response needed)
14
- ```
15
-
16
- **hbridge implementation**: `mcp.mjs` lines 79-88
17
-
18
- ## 2. Capabilities — tools MUST declare listChanged
19
-
20
- **Spec ref**: `Specification → Server Features → Tools → Capabilities`
21
-
22
- **Spec quote**: "Servers that support tools MUST declare the tools capability"
23
- ```
24
- {"capabilities": {"tools": {"listChanged": true}}}
25
- ```
26
-
27
- **hbridge implementation**: `mcp.mjs` line 85 — `capabilities: { tools: { listChanged: true } }`
28
-
29
- ## 3. Tool Definition Format
30
-
31
- **Spec ref**: `Specification → Server Features → Tools → Data Types → Tool`
32
-
33
- **Fields**:
34
- - `name` (string, required) — unique tool name
35
- - `description` (string, optional) — human-readable description
36
- - `inputSchema` (object, required) — JSON Schema for tool parameters
37
-
38
- **hbridge implementation**: `mcp.mjs` TOOLS array, lines 292-322 — all 4 tools have name + description + inputSchema
39
-
40
- ## 4. tools/list Response
41
-
42
- **Spec ref**: `Specification → Server Features → Tools → Protocol Messages → Listing Tools`
43
-
44
- **Response format**:
45
- ```
46
- {"tools": [Tool, Tool, ...]}
47
- ```
48
-
49
- **hbridge implementation**: `mcp.mjs` line 89 — respond({ result: { tools: TOOLS } })
50
-
51
- ## 5. tools/call Response
52
-
53
- **Spec ref**: `Specification → Server Features → Tools → Data Types → Text Content`
54
-
55
- **Format**:
56
- ```
57
- {"content": [{"type": "text", "text": "result string"}]}
58
- ```
59
-
60
- **hbridge implementation**: `mcp.mjs` lines 161-164 — respond({ result: { content: [{ type: "text", text: t }] } })
61
-
62
- ## 6. Available Tools
63
-
64
- | Tool | Description |
65
- |------|-------------|
66
- | `hbridge_enable` | Start hbridge HTTP server, show access key |
67
- | `hbridge_disable` | Stop hbridge server |
68
- | `hbridge_status` | Show server status + last client connection info |
69
- | `hbridge_status_bar` | Show/hide hbridge status in Claude Code status bar |
70
-
71
- ## 7. Additional Features
72
-
73
- - **Stdout guard** (`mcp.mjs` lines 25-32): Non-JSON stdout writes are redirected to stderr to prevent third-party console.log from corrupting the MCP JSON-RPC stream.
74
- - **Crash protection** (`mcp.mjs` lines 35-40): `uncaughtException` and `unhandledRejection` handlers keep the MCP process alive.
75
- - **HTTP inbox server** (`mcp.mjs` lines 180-285): When `HBRIDGE_HOME=1`, the MCP server also auto-starts an HTTP server on the deterministic port.
76
- - **Server info**: `serverInfo: { name: "hbridge", version: "1.0.0" }` in initialize response.
77
-
78
- ## 8. Validation Results
79
-
80
- ```
81
- ✅ initialize → protocolVersion:2024-11-05, capabilities.listChanged:true
82
- ✅ tools/list → 4 tools with name+description+inputSchema
83
- ✅ tools/call → content[{type:text,text:...}]
84
- ✅ notified/init → silent accept
85
- ```
1
+ # MCP Protocol Spec — h2c Mapping
2
+
3
+ Spec: https://modelcontextprotocol.io/specification/2024-11-05
4
+
5
+ ## 1. Lifecycle — Initialization
6
+
7
+ **Spec ref**: `Specification → Base Protocol → Lifecycle → Initialization`
8
+
9
+ **Sequence**:
10
+ ```
11
+ Client → Server: initialize (protocolVersion + capabilities + clientInfo)
12
+ Server → Client: initialize response (protocolVersion + capabilities + serverInfo)
13
+ Client → Server: notifications/initialized (no response needed)
14
+ ```
15
+
16
+ **h2c implementation**: `mcp.mjs` lines 79-88
17
+
18
+ ## 2. Capabilities — tools MUST declare listChanged
19
+
20
+ **Spec ref**: `Specification → Server Features → Tools → Capabilities`
21
+
22
+ **Spec quote**: "Servers that support tools MUST declare the tools capability"
23
+ ```
24
+ {"capabilities": {"tools": {"listChanged": true}}}
25
+ ```
26
+
27
+ **h2c implementation**: `mcp.mjs` line 85 — `capabilities: { tools: { listChanged: true } }`
28
+
29
+ ## 3. Tool Definition Format
30
+
31
+ **Spec ref**: `Specification → Server Features → Tools → Data Types → Tool`
32
+
33
+ **Fields**:
34
+ - `name` (string, required) — unique tool name
35
+ - `description` (string, optional) — human-readable description
36
+ - `inputSchema` (object, required) — JSON Schema for tool parameters
37
+
38
+ **h2c implementation**: `mcp.mjs` TOOLS array, lines 292-322 — all 4 tools have name + description + inputSchema
39
+
40
+ ## 4. tools/list Response
41
+
42
+ **Spec ref**: `Specification → Server Features → Tools → Protocol Messages → Listing Tools`
43
+
44
+ **Response format**:
45
+ ```
46
+ {"tools": [Tool, Tool, ...]}
47
+ ```
48
+
49
+ **h2c implementation**: `mcp.mjs` line 89 — respond({ result: { tools: TOOLS } })
50
+
51
+ ## 5. tools/call Response
52
+
53
+ **Spec ref**: `Specification → Server Features → Tools → Data Types → Text Content`
54
+
55
+ **Format**:
56
+ ```
57
+ {"content": [{"type": "text", "text": "result string"}]}
58
+ ```
59
+
60
+ **h2c implementation**: `mcp.mjs` lines 161-164 — respond({ result: { content: [{ type: "text", text: t }] } })
61
+
62
+ ## 6. Available Tools
63
+
64
+ | Tool | Description |
65
+ |------|-------------|
66
+ | `h2c_enable` | Start h2c HTTP server, show access key |
67
+ | `h2c_disable` | Stop h2c server |
68
+ | `h2c_status` | Show server status + last client connection info |
69
+ | `h2c_status_bar` | Show/hide h2c status in Claude Code status bar |
70
+
71
+ ## 7. Additional Features
72
+
73
+ - **Stdout guard** (`mcp.mjs` lines 25-32): Non-JSON stdout writes are redirected to stderr to prevent third-party console.log from corrupting the MCP JSON-RPC stream.
74
+ - **Crash protection** (`mcp.mjs` lines 35-40): `uncaughtException` and `unhandledRejection` handlers keep the MCP process alive.
75
+ - **HTTP inbox server** (`mcp.mjs` lines 180-285): When `H2C_HOME=1`, the MCP server also auto-starts an HTTP server on the deterministic port.
76
+ - **Server info**: `serverInfo: { name: "h2c", version: "1.0.0" }` in initialize response.
77
+
78
+ ## 8. Validation Results
79
+
80
+ ```
81
+ ✅ initialize → protocolVersion:2024-11-05, capabilities.listChanged:true
82
+ ✅ tools/list → 4 tools with name+description+inputSchema
83
+ ✅ tools/call → content[{type:text,text:...}]
84
+ ✅ notified/init → silent accept
85
+ ```
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "hermes-to-claude",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "Local HTTP bridge connecting Hermes Agent to Claude Code — no Pro/Max subscription required",
6
6
  "license": "MIT",
7
- "repository": "github:xuhancn/hermes-claude-bridge",
7
+ "repository": "github:xuhancn/hermes-to-claude",
8
8
  "bin": {
9
9
  "h2c": "dist/hbridge.mjs"
10
10
  },
@@ -12,14 +12,14 @@ const USER_SKILL_DIR = path.join(
12
12
  process.env.HOME || os.homedir(),
13
13
  ".claude",
14
14
  "skills",
15
- "hbridge",
15
+ "h2c",
16
16
  );
17
17
 
18
18
  try {
19
19
  // ---- statusLine hook (default ON — attaches to user's existing bar) ----
20
20
  const STATUSBAR_PATH = path.join(__dirname, "..", "dist", "statusline.mjs");
21
21
  const cmd = `node ${STATUSBAR_PATH}`;
22
- const USER_CMD_FILE = path.join(os.homedir(), ".hbridge_user_statusline_cmd");
22
+ const USER_CMD_FILE = path.join(os.homedir(), ".h2c_user_statusline_cmd");
23
23
 
24
24
  let settings = {};
25
25
  if (fs.existsSync(USER_SETTINGS)) {
@@ -35,19 +35,19 @@ try {
35
35
 
36
36
  fs.mkdirSync(path.dirname(USER_SETTINGS), { recursive: true });
37
37
  fs.writeFileSync(USER_SETTINGS, JSON.stringify(settings, null, 2));
38
- console.log("✓ hbridge statusLine registered (attaches to existing bar)");
38
+ console.log("✓ h2c statusLine registered (attaches to existing bar)");
39
39
 
40
- // ---- /hbridge slash command skill (global ~/.claude/skills/hbridge/) ----
41
- const SKILL_SRC = path.join(__dirname, "..", ".claude", "skills", "hbridge", "SKILL.md");
40
+ // ---- /h2c slash command skill (global ~/.claude/skills/h2c/) ----
41
+ const SKILL_SRC = path.join(__dirname, "..", ".claude", "skills", "h2c", "SKILL.md");
42
42
  const SKILL_DST = path.join(USER_SKILL_DIR, "SKILL.md");
43
43
 
44
44
  if (fs.existsSync(SKILL_SRC)) {
45
45
  fs.mkdirSync(USER_SKILL_DIR, { recursive: true });
46
46
  fs.copyFileSync(SKILL_SRC, SKILL_DST);
47
- console.log("✓ hbridge slash command installed (/hbridge)");
47
+ console.log("✓ h2c slash command installed (/h2c)");
48
48
  } else {
49
- console.warn("⚠ hbridge skill file not found — skipping slash command install");
49
+ console.warn("⚠ h2c skill file not found — skipping slash command install");
50
50
  }
51
51
  } catch (e) {
52
- console.error("⚠ Could not register hbridge in Claude Code config:", e.message);
52
+ console.error("⚠ Could not register h2c in Claude Code config:", e.message);
53
53
  }