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 +235 -30
- package/bin/gaslamp.mjs +122 -33
- package/package.json +5 -10
- package/src/consult.mjs +467 -0
- package/src/fleet.mjs +455 -0
- package/src/jobs.mjs +273 -0
- package/src/setup.mjs +130 -100
- package/src/tail.mjs +93 -0
- package/src/threads.mjs +56 -0
- package/src/doctor.mjs +0 -61
- package/src/server.mjs +0 -242
package/README.md
CHANGED
|
@@ -6,18 +6,34 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/gaslamp)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Let Claude Code and Codex consult each other from the shell.
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 #
|
|
32
|
-
gaslamp
|
|
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
|
-
|
|
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
|
-
|
|
38
|
-
|
|
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 # ===
|
|
88
|
+
./setup.sh # === gaslamp setup --local (pins this checkout's path)
|
|
47
89
|
```
|
|
48
90
|
|
|
49
91
|
## CLI
|
|
50
92
|
|
|
51
93
|
```
|
|
52
|
-
gaslamp [
|
|
53
|
-
gaslamp
|
|
54
|
-
gaslamp
|
|
55
|
-
gaslamp --
|
|
56
|
-
gaslamp --
|
|
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
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
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
|
|
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:
|
|
2
|
+
// gaslamp: let Claude Code and Codex consult each other from the shell.
|
|
3
3
|
//
|
|
4
|
-
// gaslamp
|
|
5
|
-
// gaslamp
|
|
6
|
-
// gaslamp
|
|
7
|
-
// gaslamp
|
|
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
|
-
//
|
|
12
|
-
//
|
|
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}:
|
|
20
|
+
const HELP = `gaslamp ${pkg.version}: let Claude Code and Codex consult each other from the shell.
|
|
22
21
|
|
|
23
22
|
Usage:
|
|
24
|
-
gaslamp [
|
|
25
|
-
gaslamp
|
|
26
|
-
gaslamp
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
gaslamp --
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
46
|
-
case "
|
|
47
|
-
const {
|
|
48
|
-
|
|
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
|
|
4
|
-
"description": "
|
|
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
|
-
"
|
|
29
|
+
"consult"
|
|
35
30
|
],
|
|
36
31
|
"author": "a9lim <mx@a9l.im>",
|
|
37
32
|
"license": "AGPL-3.0-or-later",
|