gaslamp 2.2.3 → 3.0.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 +141 -295
- package/bin/gaslamp.mjs +54 -121
- package/package.json +3 -2
- package/skill/SKILL.md +60 -0
- package/src/backends.mjs +12 -32
- package/src/consult.mjs +331 -534
- package/src/jobs.mjs +79 -171
- package/src/setup.mjs +40 -165
- package/src/threads.mjs +13 -14
- package/src/fleet.mjs +0 -457
- package/src/tail.mjs +0 -104
package/bin/gaslamp.mjs
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// gaslamp: let
|
|
3
|
-
//
|
|
2
|
+
// gaslamp: let coding agents consult each other from the shell — Claude,
|
|
3
|
+
// Codex, Gemini and Grok.
|
|
4
4
|
//
|
|
5
|
-
// gaslamp claude
|
|
6
|
-
// gaslamp
|
|
7
|
-
// gaslamp
|
|
8
|
-
// gaslamp jobs / poll read the durable consult records
|
|
9
|
-
// gaslamp setup [--local] allowlist the command for Claude Code
|
|
5
|
+
// gaslamp claude|codex|gemini|grok … one blocking consult (the heart of it)
|
|
6
|
+
// gaslamp jobs / poll / threads read the durable records
|
|
7
|
+
// gaslamp setup link the skill, allowlist the command
|
|
10
8
|
//
|
|
11
9
|
// Each agent backgrounds the call with its own facility, so consults run in
|
|
12
10
|
// parallel and replies trickle in as they finish. No MCP servers, no daemon:
|
|
@@ -19,114 +17,54 @@ import { fileURLToPath } from "node:url";
|
|
|
19
17
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
20
18
|
const pkg = JSON.parse(readFileSync(join(HERE, "..", "package.json"), "utf8"));
|
|
21
19
|
|
|
22
|
-
const HELP = `gaslamp ${pkg.version}: let
|
|
23
|
-
(and consult Gemini and Grok).
|
|
20
|
+
const HELP = `gaslamp ${pkg.version}: let coding agents consult each other from the shell.
|
|
24
21
|
|
|
25
22
|
Usage:
|
|
26
|
-
gaslamp claude [opts] <prompt|->
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
gaslamp
|
|
30
|
-
gaslamp
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
replay their recorded replies, killed ones
|
|
35
|
-
are nudged to conclude via their sessions,
|
|
36
|
-
the rest rerun from the stored manifest.
|
|
37
|
-
gaslamp jobs [-n N] [--json] List consult records, newest first.
|
|
38
|
-
gaslamp threads List named threads, latest activity first.
|
|
39
|
-
gaslamp poll <job|fleet|--last> [--json]
|
|
40
|
-
Print one record's reply/status.
|
|
41
|
-
gaslamp tail <job|--last> Follow one consult's events as one-liners
|
|
42
|
-
(both backends' schemas, uniformly).
|
|
43
|
-
gaslamp setup [--local] [--skill] Allowlist the command in Claude Code so
|
|
44
|
-
consults don't stall on a permission prompt.
|
|
45
|
-
--local pins this checkout's bin path.
|
|
46
|
-
--skill also installs the gaslamp skill
|
|
47
|
-
for BOTH agents (~/.claude/skills/,
|
|
48
|
-
$CODEX_HOME/skills/).
|
|
23
|
+
gaslamp <claude|codex|gemini|grok> [opts] <prompt|->
|
|
24
|
+
Consult one; blocks until the reply. Run it in the
|
|
25
|
+
background and keep working.
|
|
26
|
+
gaslamp jobs [-n N] [--json] Recent consults, newest first.
|
|
27
|
+
gaslamp poll <job|--last> [--json] One consult's reply (exit 10 = running).
|
|
28
|
+
gaslamp threads Named conversations, latest first.
|
|
29
|
+
gaslamp setup Link the skill into Claude Code and Codex,
|
|
30
|
+
and allowlist the command for Claude Code.
|
|
49
31
|
|
|
50
32
|
Consult options:
|
|
51
|
-
--
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
--model <m>
|
|
56
|
-
|
|
57
|
-
(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
--
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
--cwd <dir> Working directory for the consult (default: here).
|
|
72
|
-
--schema <file|json> JSON Schema for the reply (native on both backends:
|
|
73
|
-
claude --json-schema / codex --output-schema). The
|
|
74
|
-
--json envelope gains data (the parsed object); a
|
|
75
|
-
reply that doesn't parse marks the consult failed.
|
|
76
|
-
--raw Skip the consult preamble (the few fixed lines that
|
|
77
|
-
tell the consultee its reply returns verbatim).
|
|
78
|
-
--json Machine envelope on stdout:
|
|
79
|
-
{backend, jobId, sessionId, status, exitCode,
|
|
80
|
-
content, data?}
|
|
81
|
-
- Read the prompt from stdin (default when piped).
|
|
82
|
-
With a prompt argument, piped stdin is instead
|
|
83
|
-
appended as a <stdin> evidence block:
|
|
84
|
-
git diff | gaslamp codex "review for races:"
|
|
33
|
+
--thread <name> Name the conversation: the first consult with a
|
|
34
|
+
name starts it, later ones continue it (from the
|
|
35
|
+
directory it started in).
|
|
36
|
+
--resume <job-id> Continue an unnamed consult's conversation.
|
|
37
|
+
--model <m> An exact id, or a tier: claude fable|opus|sonnet|
|
|
38
|
+
haiku; codex astra|sol|luna; gemini argon|flash
|
|
39
|
+
(default argon).
|
|
40
|
+
--effort <level> low|medium|high|xhigh|max (codex astra/sol: ultra).
|
|
41
|
+
--sandbox <mode> read-only | workspace-write | danger-full-access.
|
|
42
|
+
Omitted: claude and codex use their own config;
|
|
43
|
+
gemini and grok run read-only. The fenced modes
|
|
44
|
+
are OS-enforced; gemini's needs macOS.
|
|
45
|
+
--cwd <dir> Run the consult there (default: here).
|
|
46
|
+
--schema <file|json> JSON Schema for the reply; with --json the parsed
|
|
47
|
+
reply is in data, and one that doesn't parse fails.
|
|
48
|
+
--json {backend, jobId, status, thread, content, error?, data?}
|
|
49
|
+
- Read the prompt from stdin (the default when piped).
|
|
50
|
+
With a prompt argument, piped stdin is appended as
|
|
51
|
+
a <stdin> evidence block:
|
|
52
|
+
git diff | gaslamp codex "review for races:"
|
|
85
53
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
-j, --concurrency N Max consults in flight (default 4 — quota-shaped).
|
|
90
|
-
--stream Print each reply the moment it completes instead of
|
|
91
|
-
collecting to the end (with --json: JSONL — one
|
|
92
|
-
result line per reply, then a fleet.done line).
|
|
93
|
-
- < tasks.jsonl One task per line: a {"prompt",…} JSON object (also
|
|
94
|
-
model/sandbox/effort/cwd/resume/thread/label/schema/
|
|
95
|
-
raw — schema may be an inline JSON object), or a
|
|
96
|
-
bare prompt.
|
|
97
|
-
A prompt argument + piped stdin shares one <stdin>
|
|
98
|
-
evidence block across every replica.
|
|
99
|
-
A fleet defaults its consults to --sandbox read-only (N writers in one cwd
|
|
100
|
-
race); pass --sandbox to opt into writes. It blocks until every consult is in,
|
|
101
|
-
then prints them all (--json: a results array, manifest order). A killed fleet
|
|
102
|
-
leaves each child resumable; gaslamp poll <fleet-id> regroups them later.
|
|
103
|
-
|
|
104
|
-
Run a consult (or a fleet) as a background shell task and keep working — several
|
|
105
|
-
can run in parallel, replies land as the tasks finish. Every consult writes through to
|
|
106
|
-
~/.gaslamp/jobs/<id>/ (prompt, raw events, reply, stderr), and the session id
|
|
107
|
-
is recorded the moment the backend reports it: a killed consult costs the
|
|
108
|
-
in-flight turn, not the session. Recovery is --resume, not orphans.
|
|
54
|
+
Every consult writes through to ~/.gaslamp/jobs/<id>/ (prompt, raw events,
|
|
55
|
+
stderr, reply), and its session is recorded the moment the backend reports it:
|
|
56
|
+
a killed consult loses the turn in flight, not the conversation.
|
|
109
57
|
|
|
110
58
|
Exit codes:
|
|
111
|
-
0 ok · 1
|
|
112
|
-
4
|
|
59
|
+
0 ok · 1 failed or killed · 2 usage · 3 nested (one-hop) refusal ·
|
|
60
|
+
4 can't run here (no network, or no OS sandbox for the mode) ·
|
|
61
|
+
5 conversation busy · poll: 10 still running
|
|
113
62
|
|
|
114
|
-
Env
|
|
63
|
+
Env:
|
|
115
64
|
GASLAMP_HOME state dir (default ~/.gaslamp)
|
|
116
|
-
GASLAMP_ALLOWED_TOOLS tools the claude read-only override permits,
|
|
117
|
-
comma-separated (default: Read,Grep,Glob,WebFetch,
|
|
118
|
-
WebSearch + read-only git via Bash(git diff:*) etc)
|
|
119
65
|
GASLAMP_ALLOW_RECURSION let consulted agents consult back (off = one hop)
|
|
120
|
-
GASLAMP_DEBUG
|
|
121
|
-
CLAUDE_BIN / CODEX_BIN / AGY_BIN / GROK_BIN
|
|
122
|
-
backend binaries (autodetected otherwise)
|
|
123
|
-
GASLAMP_GEMINI_SEATBELT 0 = read-only gemini consults rely on Antigravity's
|
|
124
|
-
preset check instead of Seatbelt (macOS default)
|
|
125
|
-
|
|
126
|
-
Every job record (meta.json) carries provenance: provider, harness version,
|
|
127
|
-
the model the backend reported (or unknown), the vendor system prompt's hash
|
|
128
|
-
where the harness exposes it (grok) or known:false, the retrieval tools the
|
|
129
|
-
consultee had and used, and the gaslamp version and git revision.
|
|
66
|
+
GASLAMP_DEBUG print the spawn argv
|
|
67
|
+
CLAUDE_BIN / CODEX_BIN / AGY_BIN / GROK_BIN backend binaries
|
|
130
68
|
`;
|
|
131
69
|
|
|
132
70
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
@@ -140,34 +78,24 @@ switch (cmd) {
|
|
|
140
78
|
runConsult(cmd, rest);
|
|
141
79
|
break;
|
|
142
80
|
}
|
|
143
|
-
case "fleet": {
|
|
144
|
-
const { runFleet } = await import(new URL("../src/fleet.mjs", import.meta.url));
|
|
145
|
-
runFleet(rest[0], rest.slice(1));
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
81
|
case "jobs": {
|
|
149
82
|
const { runJobs } = await import(new URL("../src/jobs.mjs", import.meta.url));
|
|
150
83
|
runJobs(rest);
|
|
151
84
|
break;
|
|
152
85
|
}
|
|
153
|
-
case "threads": {
|
|
154
|
-
const { runThreads } = await import(new URL("../src/threads.mjs", import.meta.url));
|
|
155
|
-
runThreads();
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
case "tail": {
|
|
159
|
-
const { runTail } = await import(new URL("../src/tail.mjs", import.meta.url));
|
|
160
|
-
runTail(rest);
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
86
|
case "poll": {
|
|
164
87
|
const { runPoll } = await import(new URL("../src/jobs.mjs", import.meta.url));
|
|
165
88
|
runPoll(rest);
|
|
166
89
|
break;
|
|
167
90
|
}
|
|
91
|
+
case "threads": {
|
|
92
|
+
const { runThreads } = await import(new URL("../src/threads.mjs", import.meta.url));
|
|
93
|
+
runThreads();
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
168
96
|
case "setup": {
|
|
169
97
|
const { runSetup } = await import(new URL("../src/setup.mjs", import.meta.url));
|
|
170
|
-
runSetup(
|
|
98
|
+
runSetup();
|
|
171
99
|
break;
|
|
172
100
|
}
|
|
173
101
|
case "-v":
|
|
@@ -179,6 +107,11 @@ switch (cmd) {
|
|
|
179
107
|
case "--help":
|
|
180
108
|
process.stdout.write(HELP);
|
|
181
109
|
break;
|
|
110
|
+
case "fleet":
|
|
111
|
+
case "tail":
|
|
112
|
+
process.stderr.write(`gaslamp: ${cmd} is gone in gaslamp 3 — run several consults in the background instead (see gaslamp --help)\n`);
|
|
113
|
+
process.exitCode = 2;
|
|
114
|
+
break;
|
|
182
115
|
default:
|
|
183
116
|
process.stderr.write(`gaslamp: unknown command "${cmd}"\n\n` + HELP);
|
|
184
117
|
process.exitCode = 2;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gaslamp",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Let
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "Let coding agents consult each other from the shell: Claude, Codex, Gemini and Grok.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"gaslamp": "bin/gaslamp.mjs"
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/",
|
|
11
11
|
"src/",
|
|
12
|
+
"skill/",
|
|
12
13
|
"README.md",
|
|
13
14
|
"LICENSE"
|
|
14
15
|
],
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gaslamp
|
|
3
|
+
description: Consult another coding agent (Claude, Codex, Gemini or Grok) from the shell for an independent read or a handoff. Use it to verify a fix or proof, spar on a design, review a diff, search, diagnose with fresh context, or hand off a piece of work. Use it when a claim deserves checking before you rely on it, or when asked to consult Claude / Codex / Gemini / Grok, get a second opinion, or hand work off.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gaslamp
|
|
7
|
+
|
|
8
|
+
gaslamp <claude|codex|gemini|grok> [options] "<prompt>"
|
|
9
|
+
git diff | gaslamp codex "review this for races:" # piped stdin rides along as <stdin> evidence
|
|
10
|
+
gaslamp claude - < notes.md # the whole prompt from a file
|
|
11
|
+
|
|
12
|
+
A consult blocks until the reply arrives, so run it in the background (`run_in_background` in Claude Code, a background terminal in Codex) and keep working. Several can run at once. You get the consultee's final message verbatim, then one `[gaslamp]` line with the job id and the command that continues the conversation. A consult is one hop: the consultee can't consult back, so the synthesis is yours.
|
|
13
|
+
|
|
14
|
+
Send raw evidence, such as errors, diffs and failing output, not just your reading of it.
|
|
15
|
+
|
|
16
|
+
## Choosing a consultee
|
|
17
|
+
|
|
18
|
+
| target | `--model` | omitted `--model` | omitted `--sandbox` |
|
|
19
|
+
|--------|-----------|-------------------|---------------------|
|
|
20
|
+
| claude | fable, opus, sonnet, haiku | its own default | its own config |
|
|
21
|
+
| codex | astra, sol, luna | its own default | its own config |
|
|
22
|
+
| gemini | argon, flash | argon | read-only |
|
|
23
|
+
| grok | an id from `grok models` | the account default | read-only |
|
|
24
|
+
|
|
25
|
+
Roughly, strongest first: fable\* > opus ≈ astra > sonnet ≈ argon ≈ sol > haiku ≈ flash ≈ grok ≈ luna. Match the tier to the stakes, as you would when picking a subagent's model.
|
|
26
|
+
|
|
27
|
+
\* The public fable is a generation behind the others (5.1, against 5.5 for opus, sonnet and haiku), so for now it sits between opus and sonnet.
|
|
28
|
+
|
|
29
|
+
`--effort low|medium|high|xhigh|max` sets reasoning effort. astra and sol also take `ultra`. sol defaults to `low`, so set it when the consult matters.
|
|
30
|
+
|
|
31
|
+
## Conversations
|
|
32
|
+
|
|
33
|
+
`--thread <name>` names a conversation. The first consult with a name starts it, and every later one continues it.
|
|
34
|
+
|
|
35
|
+
- Name it for its subject (`auth-race-review`, not `review`).
|
|
36
|
+
- For an independent second opinion, use a new name, because a reused name carries the earlier context along.
|
|
37
|
+
- A thread lives in the directory it started in. To continue it from anywhere else, pass `--cwd`.
|
|
38
|
+
|
|
39
|
+
Without `--thread` a consult is a one-off, though `--resume <job-id>` can still continue it.
|
|
40
|
+
|
|
41
|
+
## Sandbox
|
|
42
|
+
|
|
43
|
+
`--sandbox read-only | workspace-write | danger-full-access`
|
|
44
|
+
|
|
45
|
+
- **read-only:** the consultee writes nothing outside its harness's own state and temp dirs.
|
|
46
|
+
- **workspace-write:** it writes only in the cwd.
|
|
47
|
+
|
|
48
|
+
The OS sandbox enforces both, and if the sandbox can't start, gaslamp refuses the consult instead of running it unfenced. Gemini's sandbox is macOS-only, so elsewhere only `danger-full-access` runs it.
|
|
49
|
+
|
|
50
|
+
The sandbox fences the filesystem, not search. A read-only consultee can still look things up: Google for Gemini, web and X for Grok. For a search consult, send the question and ask for source URLs, since a summary of search results isn't verification. For a review, pipe in the material.
|
|
51
|
+
|
|
52
|
+
## The rest
|
|
53
|
+
|
|
54
|
+
- `--cwd <dir>` runs the consult in another directory.
|
|
55
|
+
- `--json` prints `{backend, jobId, status, thread, content, error?, data?}`.
|
|
56
|
+
- `--schema <file|json>` with `--json` puts the parsed reply in `.data`. A reply that doesn't parse fails the consult.
|
|
57
|
+
- `gaslamp jobs` lists recent consults, `gaslamp poll <job|--last>` prints one (exit 10 means it's still running), and `gaslamp threads` lists conversations.
|
|
58
|
+
- Each job lives in `~/.gaslamp/jobs/<id>/`, raw events included. A killed consult loses its current turn but keeps its conversation.
|
|
59
|
+
|
|
60
|
+
Exit codes: 0 ok · 1 failed or killed · 2 usage · 3 nested · 4 can't run here (no network, or no sandbox for the mode) · 5 conversation busy.
|
package/src/backends.mjs
CHANGED
|
@@ -1,39 +1,19 @@
|
|
|
1
|
-
// backends.mjs — the four consult targets and the facts every module
|
|
2
|
-
// about them: job-id prefix,
|
|
3
|
-
// --sandbox values are honest, and what an omitted --sandbox means.
|
|
1
|
+
// backends.mjs — the four consult targets, and the two facts every module
|
|
2
|
+
// needs about them: the job-id prefix, and what an omitted --sandbox means.
|
|
4
3
|
//
|
|
5
|
-
// claude and codex
|
|
6
|
-
//
|
|
7
|
-
// and grok (xAI's Grok Build)
|
|
8
|
-
//
|
|
9
|
-
// harness (see consult.mjs), and every job records provenance — the vendor's
|
|
10
|
-
// model or system prompt can change under a stable product name.
|
|
4
|
+
// claude and codex defer to the consulted agent's own config, the way a
|
|
5
|
+
// harness's own subagents inherit theirs. gemini (Google's Antigravity CLI,
|
|
6
|
+
// `agy`) and grok (xAI's Grok Build) start read-only: the narrower trust
|
|
7
|
+
// they joined with.
|
|
11
8
|
|
|
12
9
|
export const BACKENDS = {
|
|
13
|
-
claude: {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
},
|
|
17
|
-
codex: {
|
|
18
|
-
prefix: "cx", provider: "openai", harness: "codex",
|
|
19
|
-
sandboxes: ["read-only", "workspace-write", "danger-full-access"], defaultSandbox: null,
|
|
20
|
-
},
|
|
21
|
-
gemini: {
|
|
22
|
-
// Antigravity has no headless workspace-write: file writes and shell
|
|
23
|
-
// commands are soft-denied under its default request-review preset, and
|
|
24
|
-
// --mode accept-edits did not change that in a print-mode run (probed on
|
|
25
|
-
// 1.4.3). The only escalation is --dangerously-skip-permissions.
|
|
26
|
-
prefix: "gm", provider: "google", harness: "agy",
|
|
27
|
-
sandboxes: ["read-only", "danger-full-access"], defaultSandbox: "read-only",
|
|
28
|
-
},
|
|
29
|
-
grok: {
|
|
30
|
-
prefix: "gk", provider: "xai", harness: "grok",
|
|
31
|
-
sandboxes: ["read-only", "workspace-write", "danger-full-access"], defaultSandbox: "read-only",
|
|
32
|
-
},
|
|
10
|
+
claude: { prefix: "cl", defaultSandbox: null },
|
|
11
|
+
codex: { prefix: "cx", defaultSandbox: null },
|
|
12
|
+
gemini: { prefix: "gm", defaultSandbox: "read-only" },
|
|
13
|
+
grok: { prefix: "gk", defaultSandbox: "read-only" },
|
|
33
14
|
};
|
|
34
15
|
|
|
35
|
-
export const
|
|
36
|
-
export const isBackend = (s) => Object.hasOwn(BACKENDS, s);
|
|
16
|
+
export const SANDBOXES = ["read-only", "workspace-write", "danger-full-access"];
|
|
37
17
|
|
|
38
|
-
const PREFIXES =
|
|
18
|
+
const PREFIXES = Object.values(BACKENDS).map((b) => b.prefix).join("|");
|
|
39
19
|
export const JOB_ID_RE = new RegExp(`^(${PREFIXES})-\\d{8}-\\d{6}-[0-9a-f]{4}$`);
|