gaslamp 1.0.1 → 2.1.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/README.md CHANGED
@@ -6,18 +6,34 @@
6
6
  [![node](https://img.shields.io/node/v/gaslamp)](https://www.npmjs.com/package/gaslamp)
7
7
  [![license](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue)](LICENSE)
8
8
 
9
- Minimal MCP server that lets Claude and Codex talk to each other.
9
+ Let Claude Code and Codex consult each other from the shell.
10
10
 
11
- Gaslamp wires the two CLIs to each other as plain MCP servers:
11
+ ```sh
12
+ gaslamp codex "review this diff for races: …" # Claude asks Codex
13
+ gaslamp claude "verify this proof sketch: …" # Codex asks Claude
14
+ git diff | gaslamp codex "review for races:" # piped stdin rides as evidence
15
+ gaslamp codex - < notes/design-spar.md # or: the whole prompt from a file
16
+ gaslamp claude --thread api-spar "counterpoint: …" # named session, continued
17
+ ```
18
+
19
+ Each call blocks until the reply, then prints it plus a greppable trailer:
20
+
21
+ ```
22
+ lamplight
23
+ [gaslamp] job: cx-20260612-142457-744e · session: 019ebdb9-… · resume: gaslamp codex --resume 019ebdb9-… · tok: 32k→60
24
+ ```
12
25
 
13
- | direction | server | tools |
14
- |-----------|-----------|----------------------|
15
- | **Claude → Codex** | `codex mcp-server` registered in Claude as `gaslamp` | `gaslamp/codex`, `gaslamp/codex-reply` |
16
- | **Codex → Claude** | this package, registered in Codex as `gaslamp` | `gaslamp`, `gaslamp-reply` |
26
+ The agent backgrounds the call with its own facility (Claude Code:
27
+ `run_in_background`; Codex: its background terminal), keeps working, and gets
28
+ the reply when it lands — so several consults run in parallel and replies
29
+ trickle in. No MCP servers, no daemon: asynchrony is the harness's job, and
30
+ gaslamp stays a thin wrapper around `claude -p` / `codex exec`.
17
31
 
18
- `claude mcp serve` only exposes Claude's
19
- *tools* and a one-shot `Agent`, not a persistent
20
- conversation, so we need a shim in `src/server.mjs`.
32
+ > 1.x was a pair of MCP servers. 2.0 replaced them with this CLI: MCP tool
33
+ > calls are blocking and serial, Codex's MCP client enforced a per-call
34
+ > timeout that killed long consults, and its per-directory trust gate could
35
+ > stall calls invisibly. All three problems are structural to MCP and absent
36
+ > from a shell call.
21
37
 
22
38
  ## Requirements
23
39
 
@@ -28,54 +44,243 @@ and [Codex](https://developers.openai.com/codex). Node ≥ 18.
28
44
 
29
45
  ```sh
30
46
  npm install -g gaslamp
31
- gaslamp setup # register both directions
32
- gaslamp doctor # verify binaries + registrations
47
+ gaslamp setup # allowlist the command in ~/.claude/settings.json
48
+ gaslamp setup --skill # …and install the gaslamp skill for BOTH agents
33
49
  ```
34
50
 
35
- Zero-install also works:
51
+ `setup` allowlists the command so a consult doesn't stall on a permission
52
+ prompt. It does **not** touch your agent instructions: a CLI can't
53
+ self-advertise the way MCP tools did, so each agent needs to learn gaslamp
54
+ exists from somewhere. The clean way is `--skill`: both CLIs speak the same
55
+ SKILL.md standard, so one flag installs a direction-aware skill on each side
56
+ (`~/.claude/skills/gaslamp/` teaching Claude to consult Codex,
57
+ `$CODEX_HOME/skills/gaslamp/` teaching Codex to consult Claude). The manual
58
+ alternative is a short note pasted into each agent's instruction file.
36
59
 
37
- ```sh
38
- npx gaslamp setup
39
- ```
60
+ **`~/.claude/CLAUDE.md`** — so Claude consults Codex:
61
+
62
+ > Hand work to Codex for a second pair of eyes — verify a fix, spar on a design,
63
+ > review a diff, diagnose with fresh context. Send raw evidence (errors, diffs,
64
+ > commands), not just your framing.
65
+ >
66
+ > ```
67
+ > gaslamp codex [--thread <name>] [--model <m>] [--sandbox <mode>] "<prompt>"
68
+ > git diff | gaslamp codex "…" # piped stdin rides as an evidence block
69
+ > gaslamp codex --json --schema '<json-schema>' "…" # typed reply in .data
70
+ > gaslamp fleet codex -n N "…" # fan out N takes in one command
71
+ > ```
72
+ >
73
+ > Run it as a background shell task (`run_in_background`) and keep working — the
74
+ > reply lands when it's done; several can run in parallel. A consult is one hop
75
+ > (the consulted agent can't consult back), so own the synthesis. `gaslamp jobs`
76
+ > lists records, `gaslamp poll <job|--last>` fetches one (exit 10 = running),
77
+ > `gaslamp threads` lists named sessions.
78
+
79
+ **`~/.codex/AGENTS.md`** — so Codex consults Claude: the same block with
80
+ `claude`/`Claude` in place of `codex`/`Codex`, and "run it in your background
81
+ terminal, check back between steps" in place of the `run_in_background` line.
40
82
 
41
83
  ### From source
42
84
 
43
85
  ```sh
44
86
  git clone https://github.com/a9lim/gaslamp
45
87
  cd gaslamp
46
- ./setup.sh # === node bin/gaslamp.mjs setup --local
88
+ ./setup.sh # === gaslamp setup --local (pins this checkout's path)
47
89
  ```
48
90
 
49
91
  ## CLI
50
92
 
51
93
  ```
52
- gaslamp [serve] Run the MCP server on stdio (what Codex spawns).
53
- gaslamp setup Register both directions (add --local for this checkout).
54
- gaslamp doctor Check binaries + registrations.
55
- gaslamp --version Print version.
56
- gaslamp --help Print help.
94
+ gaslamp claude [opts] <prompt|-> Consult Claude (blocks until the reply).
95
+ gaslamp codex [opts] <prompt|-> Consult Codex (blocks until the reply).
96
+ gaslamp fleet <backend> [opts] Fan out a fleet of consults in one command.
97
+ gaslamp fleet --resume <fleet-id> Finish an interrupted fleet.
98
+ gaslamp jobs [-n N] [--json] List consult records, newest first.
99
+ gaslamp threads List named threads, latest activity first.
100
+ gaslamp poll <job|fleet|--last> [--json] One record's reply/status (exit 10 = running).
101
+ gaslamp tail <job|--last> Follow one consult's events as one-liners.
102
+ gaslamp setup [--local] [--skill] Allowlist the command; --skill installs the
103
+ gaslamp skill for both agents.
104
+
105
+ Consult options:
106
+ --resume <session|job> Continue a session (a prior job id works too).
107
+ --thread <name> Named session: continue if bound, else start + bind.
108
+ --model <m> Backend model override.
109
+ --effort <level> Reasoning effort (native on both backends).
110
+ --schema <file|json> JSON Schema for the reply; --json gains data.
111
+ --label <s> Tag the job record (shows in gaslamp jobs).
112
+ --sandbox <mode> claude: read-only | danger-full-access
113
+ codex: read-only | workspace-write | danger-full-access
114
+ --raw Skip the consult preamble.
115
+ --cwd <dir> Working directory for the consult.
116
+ --json {backend, jobId, sessionId, status, exitCode,
117
+ content, label?, usage?, data?}
118
+
119
+ Fleet options (plus the consult options above, applied to every consult):
120
+ -n, --count N Replicate the prompt across N fresh sessions.
121
+ -j, --concurrency N Max consults in flight (default 4).
122
+ --stream Print replies as they complete (--json: JSONL).
123
+ - < tasks.jsonl One task per line: {"prompt", model?, sandbox?,
124
+ effort?, cwd?, resume?, thread?, label?, schema?,
125
+ raw?} or a bare prompt.
126
+ ```
127
+
128
+ ## Evidence on stdin
129
+
130
+ A prompt argument plus piped stdin composes them: the prompt frames, stdin is
131
+ the evidence, appended as a `<stdin>` block (codex's own convention for the
132
+ case, applied to both backends):
133
+
134
+ ```sh
135
+ git diff | gaslamp codex "review this diff for races:"
136
+ cargo test 2>&1 | gaslamp claude "why is this failing?"
137
+ ```
138
+
139
+ Send raw evidence — errors, diffs, failing output — not just your framing of
140
+ it; the fresh-context advantage works on facts. A bare `-` (or piping with no
141
+ prompt argument) still reads the whole prompt from stdin.
142
+
143
+ ## Threads
144
+
145
+ `--thread <name>` is a named session: continue it if the name is bound, start
146
+ and bind it if not — idempotent, no fishing session UUIDs out of old trailers.
147
+
148
+ ```sh
149
+ gaslamp codex --thread api-spar "opening take: …"
150
+ gaslamp codex --thread api-spar "counterpoint: …" # same conversation
151
+ gaslamp threads # list them
152
+ ```
153
+
154
+ A thread is one pointer file under `~/.gaslamp/threads/`, re-pointed on every
155
+ consult. This matters doubly for claude, where each resume forks a *new*
156
+ session id — the pointer chases it automatically, which manual `--resume`
157
+ juggling cannot.
158
+
159
+ ## Typed replies
160
+
161
+ `--schema <file|inline-json>` plumbs a JSON Schema to each backend's native
162
+ structured output (`claude --json-schema` / `codex exec --output-schema`); the
163
+ `--json` envelope then carries `data`, the parsed object. A reply that fails to
164
+ parse marks the consult **failed** — trust the exit code.
165
+
166
+ ```sh
167
+ gaslamp codex --json --schema '{"type":"object","properties":{"verdict":
168
+ {"type":"string","enum":["correct","broken","unsure"]}},"required":["verdict"]}' \
169
+ "is this fix correct? …" | jq .data.verdict
170
+ ```
171
+
172
+ One backend asymmetry, papered over: OpenAI's endpoint only accepts *strict*
173
+ schemas (every object needs `additionalProperties:false` and all properties
174
+ required), so gaslamp strictifies schemas on the way to codex — spell optional
175
+ fields nullable (`{"type":["string","null"]}`) rather than leaving them out of
176
+ `required`. Claude takes the schema as written.
177
+
178
+ ## The consult preamble
179
+
180
+ A consulted agent gets a few fixed lines of framing (claude via
181
+ `--append-system-prompt`, codex prepended as a `<gaslamp_consult>` block): it
182
+ is a one-hop consult, its final message returns verbatim to the calling agent,
183
+ deliver the deliverable. Without this, consulted agents reply conversationally
184
+ or promise work instead of doing it. `--raw` drops it. It is fixed and tiny by
185
+ design — not a context-injection hook.
186
+
187
+ ## Fleets
188
+
189
+ `gaslamp fleet` fires a whole batch of consults from one command and blocks
190
+ until every reply is in — `-n N "<prompt>"` runs one prompt across N fresh
191
+ sessions (voting, diverse sampling), or a JSONL manifest on stdin runs one
192
+ `{prompt, model?, sandbox?, cwd?, resume?, label?}` task per line:
193
+
194
+ ```sh
195
+ gaslamp fleet codex -n 5 "spot the worst bug in this diff: …" # 5 takes, vote
196
+ gaslamp fleet claude - < tasks.jsonl # one task/line
197
+ gaslamp fleet codex -n 8 --json "…" | jq '.results[].content' # structured
198
+ gaslamp fleet codex -n 4 --stream --json "…" # JSONL as they land
199
+ gaslamp fleet --resume fl-20260702-… # finish an interrupted one
57
200
  ```
58
201
 
202
+ Each task is a normal consult — its own job record and resumable session — so a
203
+ killed fleet costs in-flight turns, not sessions. Two safety defaults at N:
204
+ fleets run `--sandbox read-only` unless you opt into writes (N agents writing one
205
+ directory race), and a manifest that resumes the same session or thread twice is
206
+ refused (they'd deadlock on the session lock). `gaslamp poll <fleet-id>` regroups
207
+ a fleet's replies later. It's the same move as backgrounding one consult, one
208
+ layer up — and it's the only one-command fan-out Codex has, since (unlike Claude
209
+ Code) it has no built-in agent-workflow tool.
210
+
211
+ `--stream` prints each reply the moment it completes (completion order) so the
212
+ caller can peek partial output mid-run; with `--json` it becomes explicit JSONL —
213
+ one result line per reply, then a `fleet.done` line — leaving the collected
214
+ single-object `--json` contract untouched.
215
+
216
+ `fleet --resume <fleet-id>` finishes an interrupted fleet from its stored
217
+ manifest: done children replay their recorded replies, a killed child whose
218
+ session was captured is resumed with a fixed finish-and-deliver nudge (the
219
+ session already holds the prompt and partial work), and everything else reruns
220
+ fresh. The new fleet record points back via `resumedFrom`.
221
+
222
+ ## Job records
223
+
224
+ Every consult writes through to `~/.gaslamp/jobs/<id>/` as it runs: the
225
+ prompt, the raw backend event stream, the reply, stderr, and a `meta.json`
226
+ whose session id is recorded the moment the backend reports it. If the
227
+ harness kills a consult mid-flight, the whole child process group dies with
228
+ it (no orphans silently burning API quota) — but the session survives on the
229
+ backend's side, so recovery is `--resume <session>`, not a lost half hour.
230
+
231
+ Token usage lands in the record too (claude's result usage + cost; codex's
232
+ per-turn usage, summed) and shows in the trailer — fleets are quota-shaped,
233
+ so quota is visible. `gaslamp jobs --json` and `gaslamp poll <id> --json`
234
+ make the records scriptable; `gaslamp tail <job|--last>` follows a running
235
+ consult's events as uniform one-liners across both backends' schemas.
236
+
237
+ Two concurrent consults into the same session would race, so gaslamp refuses
238
+ the second (exit 5) until the first finishes.
239
+
59
240
  ## Permissions
60
241
 
61
- A Codex→Claude consult with no `sandbox` arg runs with your own Claude config
62
- (permission mode, allow/deny rules, model, MCP) — just as your interactive Claude
63
- would. Per-call overrides: `read-only` (advisory, no edits) or `danger-full-access`
64
- (full read/write). Claude has no filesystem sandbox, so there's no `workspace-write`.
242
+ A consult with no `--sandbox` runs with the consulted agent's own config —
243
+ exactly as that agent would behave if you drove it yourself. Overrides:
244
+ `read-only` forces an advisory, no-edit reviewer on either backend;
245
+ `danger-full-access` forces full read/write. Claude has no filesystem
246
+ sandbox, so `workspace-write` there is a usage error rather than a silent
247
+ lie (Codex supports all three).
248
+
249
+ Note for sandboxed Codex setups: the gaslamp CLI runs *inside* Codex's
250
+ per-command sandbox (1.x's MCP server ran outside it). Under the default
251
+ `workspace-write` sandbox, network is disabled and a consulted agent could
252
+ reach neither Anthropic nor OpenAI — gaslamp detects this
253
+ (`CODEX_SANDBOX_NETWORK_DISABLED=1`) and refuses with instructions instead of
254
+ timing out. Grant the call escalated permissions, or set
255
+ `network_access = true` / `danger-full-access` in `~/.codex/config.toml`.
256
+
257
+ ## Recursion
258
+
259
+ A consult is **one hop**: agents gaslamp spawns carry `GASLAMP_NESTED=1`
260
+ (forced into Codex's shell env via config override, since a strict
261
+ `shell_environment_policy` would strip it) and the consult verbs refuse under
262
+ it. This is loop prevention, not a security boundary. Set
263
+ `GASLAMP_ALLOW_RECURSION=1` to restore unbounded handoffs.
65
264
 
66
265
  ## Env knobs
67
266
 
68
267
  | var | default | meaning |
69
268
  |-----|---------|---------|
70
- | `GASLAMP_ALLOWED_TOOLS` | `Read Grep Glob WebFetch WebSearch` | tools the `read-only` override permits |
71
- | `GASLAMP_LOGFILE` | `~/.codex/gaslamp.log` | transcript path; `off` to disable |
72
- | `GASLAMP_DEBUG` | unset | verbose stderr (raw JSON-RPC) |
73
- | `CLAUDE_BIN` | autodetected | path to the `claude` binary |
269
+ | `GASLAMP_HOME` | `~/.gaslamp` | state dir (jobs + locks + threads) |
270
+ | `GASLAMP_ALLOWED_TOOLS` | `Read,Grep,Glob,WebFetch,WebSearch` + read-only git (`Bash(git diff:*)` etc) | tools the claude `read-only` override permits, comma-separated |
271
+ | `GASLAMP_ALLOW_RECURSION` | unset | allow consulted agents to consult back |
272
+ | `GASLAMP_DEBUG` | unset | verbose stderr (spawn argv) |
273
+ | `CLAUDE_BIN` / `CODEX_BIN` | autodetected | backend binaries |
274
+ | `CODEX_HOME` | `~/.codex` | where `setup --skill` puts Codex's copy |
275
+
276
+ Exit codes: `0` ok · `1` consult failed/killed · `2` usage · `3` nested
277
+ refusal · `4` network-disabled sandbox · `5` session busy · `poll`: `10`
278
+ still running.
74
279
 
75
280
  ## Development
76
281
 
77
282
  ```sh
78
- npm test # node --test against a stub claude binary
283
+ npm test # node --test against stub claude and codex binaries
79
284
  npm run check # node -c syntax check on every source file
80
285
  ```
81
286
 
package/bin/gaslamp.mjs CHANGED
@@ -1,15 +1,14 @@
1
1
  #!/usr/bin/env node
2
- // gaslamp: Minimal MCP server that lets Claude and Codex talk to each other.
2
+ // gaslamp: let Claude Code and Codex consult each other from the shell.
3
3
  //
4
- // gaslamp run the MCP server on stdio (what Codex spawns)
5
- // gaslamp serve same as above, explicit
6
- // gaslamp setup register both directions (add --local for this checkout)
7
- // gaslamp doctor verify the install without a round-trip
8
- // gaslamp --version print the version
9
- // gaslamp --help print this help
4
+ // gaslamp claude … / codex … one blocking consult (the heart of it)
5
+ // gaslamp fleet <backend> … fan out a bounded fleet of consults
6
+ // gaslamp jobs / poll read the durable consult records
7
+ // gaslamp setup [--local] allowlist the command for Claude Code
10
8
  //
11
- // The bare/`serve` path speaks newline-delimited JSON-RPC on stdout and must
12
- // stay quiet otherwise; `setup`/`doctor` are interactive and print freely.
9
+ // Each agent backgrounds the call with its own facility, so consults run in
10
+ // parallel and replies trickle in as they finish. No MCP servers, no daemon:
11
+ // asynchrony is the harness's job.
13
12
 
14
13
  import { readFileSync } from "node:fs";
15
14
  import { dirname, join } from "node:path";
@@ -18,34 +17,128 @@ import { fileURLToPath } from "node:url";
18
17
  const HERE = dirname(fileURLToPath(import.meta.url));
19
18
  const pkg = JSON.parse(readFileSync(join(HERE, "..", "package.json"), "utf8"));
20
19
 
21
- const HELP = `gaslamp ${pkg.version}: Minimal MCP server that lets Claude and Codex talk to each other.
20
+ const HELP = `gaslamp ${pkg.version}: let Claude Code and Codex consult each other from the shell.
22
21
 
23
22
  Usage:
24
- gaslamp [serve] Run the MCP server on stdio (what Codex spawns).
25
- gaslamp setup Register both directions as \`gaslamp\` (published / npx).
26
- gaslamp setup --local Register THIS checkout instead of the published package.
27
- gaslamp doctor Check binaries + registrations (no round-trip).
28
- gaslamp --version Print the version.
29
- gaslamp --help Print this help.
23
+ gaslamp claude [opts] <prompt|-> Consult Claude (blocks until the reply).
24
+ gaslamp codex [opts] <prompt|-> Consult Codex (blocks until the reply).
25
+ gaslamp fleet <backend> [opts] Fan out a fleet of consults (one command).
26
+ -n N <prompt> Replicate one prompt across N fresh sessions.
27
+ - < tasks.jsonl Or one task per line on stdin (see below).
28
+ gaslamp fleet --resume <fleet-id> Finish an interrupted fleet: done children
29
+ replay their recorded replies, killed ones
30
+ are nudged to conclude via their sessions,
31
+ the rest rerun from the stored manifest.
32
+ gaslamp jobs [-n N] [--json] List consult records, newest first.
33
+ gaslamp threads List named threads, latest activity first.
34
+ gaslamp poll <job|fleet|--last> [--json]
35
+ Print one record's reply/status.
36
+ gaslamp tail <job|--last> Follow one consult's events as one-liners
37
+ (both backends' schemas, uniformly).
38
+ gaslamp setup [--local] [--skill] Allowlist the command in Claude Code so
39
+ consults don't stall on a permission prompt.
40
+ --local pins this checkout's bin path.
41
+ --skill also installs the gaslamp skill
42
+ for BOTH agents (~/.claude/skills/,
43
+ $CODEX_HOME/skills/).
30
44
 
31
- A Codex->Claude consult with no \`sandbox\` arg uses the user's own ~/.claude
32
- config (permission mode, allow/deny, model, MCP). \`read-only\` and
33
- \`danger-full-access\` are per-call overrides.
45
+ Consult options:
46
+ --resume <session|job> Continue a session (a prior job id works too).
47
+ --thread <name> Named session: continue it if the name is bound,
48
+ else start fresh and bind it. The pointer chases
49
+ claude's forked session ids so the name stays hot.
50
+ --model <m> Backend model override.
51
+ --effort <level> Reasoning effort (claude --effort / codex
52
+ model_reasoning_effort).
53
+ --label <s> Tag the job record (shows in gaslamp jobs).
54
+ --sandbox <mode> claude: read-only | danger-full-access
55
+ codex: read-only | workspace-write | danger-full-access
56
+ Omit to defer to the consulted agent's own config.
57
+ --cwd <dir> Working directory for the consult (default: here).
58
+ --schema <file|json> JSON Schema for the reply (native on both backends:
59
+ claude --json-schema / codex --output-schema). The
60
+ --json envelope gains data (the parsed object); a
61
+ reply that doesn't parse marks the consult failed.
62
+ --raw Skip the consult preamble (the few fixed lines that
63
+ tell the consultee its reply returns verbatim).
64
+ --json Machine envelope on stdout:
65
+ {backend, jobId, sessionId, status, exitCode,
66
+ content, data?}
67
+ - Read the prompt from stdin (default when piped).
68
+ With a prompt argument, piped stdin is instead
69
+ appended as a <stdin> evidence block:
70
+ git diff | gaslamp codex "review for races:"
34
71
 
35
- Env knobs (read by the server):
36
- GASLAMP_ALLOWED_TOOLS tools the \`read-only\` override permits (space/comma list)
37
- GASLAMP_LOGFILE transcript path (default ~/.codex/gaslamp.log; "off" disables)
38
- GASLAMP_DEBUG verbose stderr (raw JSON-RPC)
39
- CLAUDE_BIN path to the claude binary (autodetected otherwise)
72
+ Fleet options (in addition to --model / --sandbox / --cwd / --json, applied to
73
+ every consult unless a per-task field on a stdin manifest overrides it):
74
+ -n, --count N Replicate the prompt across N fresh sessions.
75
+ -j, --concurrency N Max consults in flight (default 4 — quota-shaped).
76
+ --stream Print each reply the moment it completes instead of
77
+ collecting to the end (with --json: JSONL — one
78
+ result line per reply, then a fleet.done line).
79
+ - < tasks.jsonl One task per line: a {"prompt",…} JSON object (also
80
+ model/sandbox/effort/cwd/resume/thread/label/schema/
81
+ raw — schema may be an inline JSON object), or a
82
+ bare prompt.
83
+ A prompt argument + piped stdin shares one <stdin>
84
+ evidence block across every replica.
85
+ A fleet defaults its consults to --sandbox read-only (N writers in one cwd
86
+ race); pass --sandbox to opt into writes. It blocks until every consult is in,
87
+ then prints them all (--json: a results array, manifest order). A killed fleet
88
+ leaves each child resumable; gaslamp poll <fleet-id> regroups them later.
89
+
90
+ Run a consult (or a fleet) as a background shell task and keep working — several
91
+ can run in parallel, replies land as the tasks finish. Every consult writes through to
92
+ ~/.gaslamp/jobs/<id>/ (prompt, raw events, reply, stderr), and the session id
93
+ is recorded the moment the backend reports it: a killed consult costs the
94
+ in-flight turn, not the session. Recovery is --resume, not orphans.
95
+
96
+ Exit codes:
97
+ 0 ok · 1 consult failed/killed · 2 usage · 3 nested (one-hop) refusal ·
98
+ 4 network-disabled sandbox · 5 session busy · poll: 10 still running
99
+
100
+ Env knobs:
101
+ GASLAMP_HOME state dir (default ~/.gaslamp)
102
+ GASLAMP_ALLOWED_TOOLS tools the claude read-only override permits,
103
+ comma-separated (default: Read,Grep,Glob,WebFetch,
104
+ WebSearch + read-only git via Bash(git diff:*) etc)
105
+ GASLAMP_ALLOW_RECURSION let consulted agents consult back (off = one hop)
106
+ GASLAMP_DEBUG verbose stderr (spawn argv)
107
+ CLAUDE_BIN / CODEX_BIN backend binaries (autodetected otherwise)
40
108
  `;
41
109
 
42
110
  const [cmd, ...rest] = process.argv.slice(2);
43
111
 
44
112
  switch (cmd) {
45
- case undefined:
46
- case "serve": {
47
- const { startServer } = await import(new URL("../src/server.mjs", import.meta.url));
48
- startServer();
113
+ case "claude":
114
+ case "codex": {
115
+ const { runConsult } = await import(new URL("../src/consult.mjs", import.meta.url));
116
+ runConsult(cmd, rest);
117
+ break;
118
+ }
119
+ case "fleet": {
120
+ const { runFleet } = await import(new URL("../src/fleet.mjs", import.meta.url));
121
+ runFleet(rest[0], rest.slice(1));
122
+ break;
123
+ }
124
+ case "jobs": {
125
+ const { runJobs } = await import(new URL("../src/jobs.mjs", import.meta.url));
126
+ runJobs(rest);
127
+ break;
128
+ }
129
+ case "threads": {
130
+ const { runThreads } = await import(new URL("../src/threads.mjs", import.meta.url));
131
+ runThreads();
132
+ break;
133
+ }
134
+ case "tail": {
135
+ const { runTail } = await import(new URL("../src/tail.mjs", import.meta.url));
136
+ runTail(rest);
137
+ break;
138
+ }
139
+ case "poll": {
140
+ const { runPoll } = await import(new URL("../src/jobs.mjs", import.meta.url));
141
+ runPoll(rest);
49
142
  break;
50
143
  }
51
144
  case "setup": {
@@ -53,15 +146,11 @@ switch (cmd) {
53
146
  runSetup(rest);
54
147
  break;
55
148
  }
56
- case "doctor": {
57
- const { runDoctor } = await import(new URL("../src/doctor.mjs", import.meta.url));
58
- runDoctor(rest);
59
- break;
60
- }
61
149
  case "-v":
62
150
  case "--version":
63
151
  process.stdout.write(pkg.version + "\n");
64
152
  break;
153
+ case undefined:
65
154
  case "-h":
66
155
  case "--help":
67
156
  process.stdout.write(HELP);
package/package.json CHANGED
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "gaslamp",
3
- "version": "1.0.1",
4
- "description": "Minimal MCP server that lets Claude and Codex talk to each other.",
3
+ "version": "2.1.0",
4
+ "description": "Let Claude Code and Codex consult each other from the shell.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "gaslamp": "bin/gaslamp.mjs"
8
8
  },
9
- "exports": {
10
- ".": "./src/server.mjs"
11
- },
12
9
  "files": [
13
10
  "bin/",
14
11
  "src/",
@@ -18,20 +15,18 @@
18
15
  "scripts": {
19
16
  "test": "node --test tests/*.test.mjs",
20
17
  "check": "for f in bin/gaslamp.mjs src/*.mjs tests/*.mjs; do node -c \"$f\"; done",
21
- "setup": "node bin/gaslamp.mjs setup",
22
- "doctor": "node bin/gaslamp.mjs doctor"
18
+ "setup": "node bin/gaslamp.mjs setup"
23
19
  },
24
20
  "keywords": [
25
- "mcp",
26
- "model-context-protocol",
27
21
  "claude",
28
22
  "claude-code",
29
23
  "codex",
30
24
  "llm",
31
25
  "agent",
32
26
  "ai",
27
+ "cli",
33
28
  "code-review",
34
- "stdio"
29
+ "consult"
35
30
  ],
36
31
  "author": "a9lim <mx@a9l.im>",
37
32
  "license": "AGPL-3.0-or-later",