ai-whisper 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/NOTICE +4 -0
- package/README.md +123 -0
- package/dist/bin/companion-agent.js +20 -12
- package/dist/bin/whisper.js +71 -20
- package/dist/skills/ai-whisper-ralph/SKILL.md +1 -1
- package/dist/skills/ai-whisper-sdd/SKILL.md +1 -1
- package/package.json +8 -5
- package/skills/ai-whisper-ralph/SKILL.md +1 -1
- package/skills/ai-whisper-sdd/SKILL.md +1 -1
package/NOTICE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# ai-whisper
|
|
2
|
+
|
|
3
|
+
ai-whisper turns two coding agents — Claude and Codex — into a terminal-native pair that hand work back and forth under a single baton, so one agent implements while the other reviews, and a structured workflow drives the loop to a finished, reviewed deliverable without a human babysitting every round.
|
|
4
|
+
|
|
5
|
+
## Magic moment
|
|
6
|
+
|
|
7
|
+
Mount each agent in its own terminal. Each `mount` claims the current shell, launches the real provider CLI, and binds it to the collab:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# terminal 1
|
|
11
|
+
whisper collab mount claude
|
|
12
|
+
# terminal 2
|
|
13
|
+
whisper collab mount codex
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Then, from inside either agent's session, kick off a structured workflow against a spec — just ask in plain language:
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
Run spec-driven-development using docs/spec.md
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
From there ai-whisper runs the workflow autonomously:
|
|
23
|
+
|
|
24
|
+
- **Implementer / reviewer assignment** — the agent you trigger the workflow from becomes the implementer and the other agent becomes the reviewer; pass `--implementer` / `--reviewer` to choose explicitly. (Started outside a mounted session with no flags, it falls back to a default pairing and warns.) The baton passes between them; only one owns the turn at a time.
|
|
25
|
+
- **Autonomous execution** — the implementer does each step in its real session and hands the result back. An LLM evaluator judges whether the deliverable meets the request.
|
|
26
|
+
- **Review loops** — when work isn't good enough yet, the reviewer's findings are composed into a follow-up handoff and the implementer iterates. The loop repeats until the work is approved or the round budget is exhausted.
|
|
27
|
+
- **Resumability** — workflow and chain state is durable. If the broker restarts or you stop for the day, you recover and reconnect rather than starting over.
|
|
28
|
+
- **Deliverables** — you get committed code plus a review trail (per-step verdicts, round counts), inspectable at any time with `whisper collab dashboard`.
|
|
29
|
+
|
|
30
|
+
## Visual proof
|
|
31
|
+
|
|
32
|
+
A real `spec-driven-development` run: Claude (left) and Codex (middle) work in their own mounted
|
|
33
|
+
sessions while the dashboard (right) tracks the baton handoffs and per-phase verdicts (~20s).
|
|
34
|
+
Click the still to watch it play on the project page.
|
|
35
|
+
|
|
36
|
+
[](https://ai-creed.dev/projects/ai-whisper/)
|
|
37
|
+
|
|
38
|
+
## Who this is for
|
|
39
|
+
|
|
40
|
+
ai-whisper is for engineers who already lean on coding agents and want more structure around them:
|
|
41
|
+
|
|
42
|
+
- you already use coding agents heavily and want two of them to check each other.
|
|
43
|
+
- you work terminal-first and want the agents to live in real terminal sessions, not a web UI.
|
|
44
|
+
- you want multi-agent review — a second model gating the first model's output.
|
|
45
|
+
- you run long, structured workflows (spec → plan → implement → review) rather than one-off prompts.
|
|
46
|
+
|
|
47
|
+
It is **not** for:
|
|
48
|
+
|
|
49
|
+
- one-shot "vibe coding" where you just want a quick answer.
|
|
50
|
+
- invisible background automation you never watch.
|
|
51
|
+
- people new to coding agents looking for a guided, hand-holding experience.
|
|
52
|
+
|
|
53
|
+
## Quickstart
|
|
54
|
+
|
|
55
|
+
Install from npm:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm install -g ai-whisper
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or from a repo checkout:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pnpm install
|
|
65
|
+
pnpm build
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Install the bundled agent skills once (they let the agents verify, kick off, and report on workflows):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
whisper skill install
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Workflows require an LLM evaluator with credentials — set this up before running one. See [Evaluator configuration](docs/evaluator-configuration.md).
|
|
75
|
+
|
|
76
|
+
Then mount both agents and run a workflow:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# terminal 1
|
|
80
|
+
whisper collab mount claude
|
|
81
|
+
# terminal 2
|
|
82
|
+
whisper collab mount codex
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The first `mount` creates the collab and starts the broker daemon for the workspace; the second binds the other agent. From either session, start a workflow against a spec or goal file (`spec-driven-development` for a spec, `ralph-loop` for an open-ended goal). Watch it run with:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
whisper collab dashboard
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
> Running from a repo checkout instead of a packaged install? Build first (`pnpm build`) and invoke the CLI as `node packages/cli/dist/bin/whisper.js ...` wherever these examples say `whisper ...`.
|
|
92
|
+
|
|
93
|
+
## Core concepts
|
|
94
|
+
|
|
95
|
+
ai-whisper is **not a swarm**. The agents never type at once — work moves by a single baton, one owner at a time. Mounted sessions are *real* Claude and Codex sessions in your terminal, and those sessions are the source of truth. Autonomy is supervised: every handoff, verdict, and round is inspectable, and runs are resumable rather than fire-and-forget. Work is organized as structured workflows — explicit loops and state transitions, not a free-form chat.
|
|
96
|
+
|
|
97
|
+
Claude and Codex are supported today; the architecture is provider-agnostic by design, so other coding-agent CLIs can be added behind the same relay.
|
|
98
|
+
|
|
99
|
+
For the full mental model, read [Concepts](docs/concepts.md).
|
|
100
|
+
|
|
101
|
+
## Learn more
|
|
102
|
+
|
|
103
|
+
- [Workflows](docs/workflows.md) — how to use the workflows well: choosing between `spec-driven-development` and `ralph-loop`, and authoring the spec or goal that drives the run.
|
|
104
|
+
- [Concepts](docs/concepts.md) — the mental model: baton handoff, real mounted sessions, supervised autonomy, workflow-first execution.
|
|
105
|
+
- [Relay & handoff flows](docs/relay-handoff-flows.md) — the complete handoff state machine, capture-status table, hotkey reference, per-step verdicts, and troubleshooting.
|
|
106
|
+
- [Evaluator configuration](docs/evaluator-configuration.md) — required credentials and options for the LLM evaluator that gates workflows.
|
|
107
|
+
- [Legacy attach mode](docs/legacy-attach.md) — the shelved `attach` / `adopt` flows, kept for historical reference.
|
|
108
|
+
|
|
109
|
+
## Workspace commands
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pnpm install
|
|
113
|
+
pnpm test
|
|
114
|
+
pnpm typecheck
|
|
115
|
+
pnpm lint
|
|
116
|
+
pnpm format
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
Apache License 2.0 — see [LICENSE](LICENSE) and [NOTICE](NOTICE). Contributions are
|
|
122
|
+
accepted under the [Developer Certificate of Origin](CONTRIBUTING.md) (sign off with
|
|
123
|
+
`git commit -s`).
|
|
@@ -5866,12 +5866,16 @@ function createNodePty(input) {
|
|
|
5866
5866
|
ensureNodePtySpawnHelperExecutable({
|
|
5867
5867
|
unixTerminalPath: nodePtyUnixTerminalPath
|
|
5868
5868
|
});
|
|
5869
|
-
return spawn2(input.config.executable, input.config.execArgs, {
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5869
|
+
return spawn2(input.config.executable, input.config.execArgs, buildClaudePtySpawnOptions({ cols: input.cols, rows: input.rows, cwd: input.cwd }));
|
|
5870
|
+
}
|
|
5871
|
+
function buildClaudePtySpawnOptions(input) {
|
|
5872
|
+
const env = {};
|
|
5873
|
+
for (const [key, value] of Object.entries(input.baseEnv ?? process.env)) {
|
|
5874
|
+
if (typeof value === "string")
|
|
5875
|
+
env[key] = value;
|
|
5876
|
+
}
|
|
5877
|
+
env.AI_WHISPER_AGENT = "claude";
|
|
5878
|
+
return { name: "xterm-256color", cols: input.cols, rows: input.rows, cwd: input.cwd, env };
|
|
5875
5879
|
}
|
|
5876
5880
|
function createClaudeLiveSession(input) {
|
|
5877
5881
|
let pty = null;
|
|
@@ -6169,12 +6173,16 @@ function createNodePty2(input) {
|
|
|
6169
6173
|
ensureNodePtySpawnHelperExecutable({
|
|
6170
6174
|
unixTerminalPath: nodePtyUnixTerminalPath2
|
|
6171
6175
|
});
|
|
6172
|
-
return spawn4(input.config.executable, input.config.execArgs, {
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6176
|
+
return spawn4(input.config.executable, input.config.execArgs, buildCodexPtySpawnOptions({ cols: input.cols, rows: input.rows, cwd: input.cwd }));
|
|
6177
|
+
}
|
|
6178
|
+
function buildCodexPtySpawnOptions(input) {
|
|
6179
|
+
const env = {};
|
|
6180
|
+
for (const [key, value] of Object.entries(input.baseEnv ?? process.env)) {
|
|
6181
|
+
if (typeof value === "string")
|
|
6182
|
+
env[key] = value;
|
|
6183
|
+
}
|
|
6184
|
+
env.AI_WHISPER_AGENT = "codex";
|
|
6185
|
+
return { name: "xterm-256color", cols: input.cols, rows: input.rows, cwd: input.cwd, env };
|
|
6178
6186
|
}
|
|
6179
6187
|
function createCodexLiveSession(input) {
|
|
6180
6188
|
let pty = null;
|
package/dist/bin/whisper.js
CHANGED
|
@@ -6168,12 +6168,16 @@ function createNodePty(input) {
|
|
|
6168
6168
|
ensureNodePtySpawnHelperExecutable({
|
|
6169
6169
|
unixTerminalPath: nodePtyUnixTerminalPath
|
|
6170
6170
|
});
|
|
6171
|
-
return spawn3(input.config.executable, input.config.execArgs, {
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6171
|
+
return spawn3(input.config.executable, input.config.execArgs, buildClaudePtySpawnOptions({ cols: input.cols, rows: input.rows, cwd: input.cwd }));
|
|
6172
|
+
}
|
|
6173
|
+
function buildClaudePtySpawnOptions(input) {
|
|
6174
|
+
const env = {};
|
|
6175
|
+
for (const [key, value] of Object.entries(input.baseEnv ?? process.env)) {
|
|
6176
|
+
if (typeof value === "string")
|
|
6177
|
+
env[key] = value;
|
|
6178
|
+
}
|
|
6179
|
+
env.AI_WHISPER_AGENT = "claude";
|
|
6180
|
+
return { name: "xterm-256color", cols: input.cols, rows: input.rows, cwd: input.cwd, env };
|
|
6177
6181
|
}
|
|
6178
6182
|
function createClaudeLiveSession(input) {
|
|
6179
6183
|
let pty = null;
|
|
@@ -6471,12 +6475,16 @@ function createNodePty2(input) {
|
|
|
6471
6475
|
ensureNodePtySpawnHelperExecutable({
|
|
6472
6476
|
unixTerminalPath: nodePtyUnixTerminalPath2
|
|
6473
6477
|
});
|
|
6474
|
-
return spawn5(input.config.executable, input.config.execArgs, {
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6478
|
+
return spawn5(input.config.executable, input.config.execArgs, buildCodexPtySpawnOptions({ cols: input.cols, rows: input.rows, cwd: input.cwd }));
|
|
6479
|
+
}
|
|
6480
|
+
function buildCodexPtySpawnOptions(input) {
|
|
6481
|
+
const env = {};
|
|
6482
|
+
for (const [key, value] of Object.entries(input.baseEnv ?? process.env)) {
|
|
6483
|
+
if (typeof value === "string")
|
|
6484
|
+
env[key] = value;
|
|
6485
|
+
}
|
|
6486
|
+
env.AI_WHISPER_AGENT = "codex";
|
|
6487
|
+
return { name: "xterm-256color", cols: input.cols, rows: input.rows, cwd: input.cwd, env };
|
|
6480
6488
|
}
|
|
6481
6489
|
function createCodexLiveSession(input) {
|
|
6482
6490
|
let pty = null;
|
|
@@ -10802,21 +10810,62 @@ async function runWorkflowStart(deps) {
|
|
|
10802
10810
|
}
|
|
10803
10811
|
}
|
|
10804
10812
|
const def = getWorkflowDefinition(deps.workflowType);
|
|
10805
|
-
|
|
10806
|
-
|
|
10807
|
-
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
|
|
10813
|
+
let resolved;
|
|
10814
|
+
try {
|
|
10815
|
+
resolved = resolveRoleBindings({
|
|
10816
|
+
explicitImplementer: deps.implementer,
|
|
10817
|
+
explicitReviewer: deps.reviewer,
|
|
10818
|
+
callerAgent: deps.callerAgent ?? null,
|
|
10819
|
+
def
|
|
10820
|
+
});
|
|
10821
|
+
} catch (e) {
|
|
10822
|
+
if (e instanceof Error && /no default role bindings/.test(e.message)) {
|
|
10823
|
+
throw new Error(
|
|
10824
|
+
`Workflow type "${deps.workflowType}" has no default role bindings. Pass --implementer and --reviewer explicitly.`
|
|
10825
|
+
);
|
|
10826
|
+
}
|
|
10827
|
+
throw e;
|
|
10811
10828
|
}
|
|
10812
|
-
|
|
10829
|
+
const { workflowId } = deps.broker.control.createWorkflow({
|
|
10813
10830
|
collabId: deps.collabId,
|
|
10814
10831
|
workflowType: deps.workflowType,
|
|
10815
10832
|
specPath: deps.specPath,
|
|
10816
|
-
roleBindings: { implementer, reviewer },
|
|
10833
|
+
roleBindings: { implementer: resolved.implementer, reviewer: resolved.reviewer },
|
|
10817
10834
|
...deps.name ? { name: deps.name } : {},
|
|
10818
10835
|
now: deps.now
|
|
10819
10836
|
});
|
|
10837
|
+
return { workflowId, ...resolved.warning ? { roleWarning: resolved.warning } : {} };
|
|
10838
|
+
}
|
|
10839
|
+
var otherAgent = (a) => a === "claude" ? "codex" : "claude";
|
|
10840
|
+
function resolveRoleBindings(input) {
|
|
10841
|
+
const { explicitImplementer, explicitReviewer, callerAgent, def } = input;
|
|
10842
|
+
if (explicitImplementer || explicitReviewer) {
|
|
10843
|
+
const implementer2 = explicitImplementer ?? (explicitReviewer ? otherAgent(explicitReviewer) : void 0);
|
|
10844
|
+
const reviewer2 = explicitReviewer ?? (explicitImplementer ? otherAgent(explicitImplementer) : void 0);
|
|
10845
|
+
if (implementer2 && reviewer2) {
|
|
10846
|
+
if (implementer2 === reviewer2) {
|
|
10847
|
+
throw new Error("implementer and reviewer cannot be the same agent");
|
|
10848
|
+
}
|
|
10849
|
+
return { implementer: implementer2, reviewer: reviewer2, source: "explicit" };
|
|
10850
|
+
}
|
|
10851
|
+
}
|
|
10852
|
+
if (callerAgent) {
|
|
10853
|
+
return { implementer: callerAgent, reviewer: otherAgent(callerAgent), source: "caller" };
|
|
10854
|
+
}
|
|
10855
|
+
const implementer = def?.defaultImplementer;
|
|
10856
|
+
const reviewer = def?.defaultReviewer;
|
|
10857
|
+
if (!implementer || !reviewer) {
|
|
10858
|
+
throw new Error("no default role bindings");
|
|
10859
|
+
}
|
|
10860
|
+
return {
|
|
10861
|
+
implementer,
|
|
10862
|
+
reviewer,
|
|
10863
|
+
source: "default",
|
|
10864
|
+
warning: `No triggering agent detected; defaulted to implementer=${implementer} / reviewer=${reviewer}. Pass --implementer / --reviewer to choose explicitly.`
|
|
10865
|
+
};
|
|
10866
|
+
}
|
|
10867
|
+
function parseCallerAgent(raw) {
|
|
10868
|
+
return raw === "claude" || raw === "codex" ? raw : null;
|
|
10820
10869
|
}
|
|
10821
10870
|
|
|
10822
10871
|
// src/commands/workflow/list.ts
|
|
@@ -11184,8 +11233,10 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
11184
11233
|
...opts.implementer ? { implementer: opts.implementer } : {},
|
|
11185
11234
|
...opts.reviewer ? { reviewer: opts.reviewer } : {},
|
|
11186
11235
|
...opts.name ? { name: opts.name } : {},
|
|
11236
|
+
callerAgent: parseCallerAgent(process.env.AI_WHISPER_AGENT),
|
|
11187
11237
|
now: (/* @__PURE__ */ new Date()).toISOString()
|
|
11188
11238
|
});
|
|
11239
|
+
if (result.roleWarning) console.error(result.roleWarning);
|
|
11189
11240
|
console.log(`Workflow started: ${result.workflowId}`);
|
|
11190
11241
|
} finally {
|
|
11191
11242
|
await broker.stop();
|
|
@@ -97,7 +97,7 @@ Run:
|
|
|
97
97
|
whisper workflow start --type=ralph-loop --spec=<resolved-absolute-path>
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
(No `--implementer` / `--reviewer` — the
|
|
100
|
+
(No `--implementer` / `--reviewer` needed — the agent triggering this skill becomes the implementer and the other agent the reviewer. Pass `--implementer <agent> --reviewer <agent>` only to override. `--spec` names the goal file.)
|
|
101
101
|
|
|
102
102
|
Parse the workflowId from stdout (format: `Workflow started: <workflowId>`).
|
|
103
103
|
|
|
@@ -94,7 +94,7 @@ Run:
|
|
|
94
94
|
whisper workflow start --type=spec-driven-development --spec=<resolved-absolute-path>
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
(No `--implementer` / `--reviewer` — the
|
|
97
|
+
(No `--implementer` / `--reviewer` needed — the agent triggering this skill becomes the implementer and the other agent the reviewer. Pass `--implementer <agent> --reviewer <agent>` only to override.)
|
|
98
98
|
|
|
99
99
|
Parse the workflowId from stdout (format: `Workflow started: <workflowId>`).
|
|
100
100
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-whisper",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Terminal-first relay for paired AI coding agents (Claude + Codex), driven by structured workflows.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -30,17 +30,20 @@
|
|
|
30
30
|
"@types/react": "^19.2.14",
|
|
31
31
|
"ink-testing-library": "^4.0.0",
|
|
32
32
|
"@ai-whisper/adapter-claude": "0.0.0",
|
|
33
|
-
"@ai-whisper/
|
|
33
|
+
"@ai-whisper/broker": "0.0.0",
|
|
34
34
|
"@ai-whisper/companion-core": "0.0.0",
|
|
35
35
|
"@ai-whisper/shared": "0.0.0",
|
|
36
|
-
"@ai-whisper/
|
|
36
|
+
"@ai-whisper/adapter-codex": "0.0.0"
|
|
37
37
|
},
|
|
38
38
|
"files": [
|
|
39
39
|
"dist",
|
|
40
|
-
"skills"
|
|
40
|
+
"skills",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE",
|
|
43
|
+
"NOTICE"
|
|
41
44
|
],
|
|
42
45
|
"scripts": {
|
|
43
|
-
"build": "node scripts/bundle.mjs && node scripts/copy-skills.mjs",
|
|
46
|
+
"build": "node scripts/bundle.mjs && node scripts/copy-skills.mjs && node scripts/copy-package-files.mjs",
|
|
44
47
|
"typecheck": "tsc -b --emitDeclarationOnly"
|
|
45
48
|
}
|
|
46
49
|
}
|
|
@@ -97,7 +97,7 @@ Run:
|
|
|
97
97
|
whisper workflow start --type=ralph-loop --spec=<resolved-absolute-path>
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
(No `--implementer` / `--reviewer` — the
|
|
100
|
+
(No `--implementer` / `--reviewer` needed — the agent triggering this skill becomes the implementer and the other agent the reviewer. Pass `--implementer <agent> --reviewer <agent>` only to override. `--spec` names the goal file.)
|
|
101
101
|
|
|
102
102
|
Parse the workflowId from stdout (format: `Workflow started: <workflowId>`).
|
|
103
103
|
|
|
@@ -94,7 +94,7 @@ Run:
|
|
|
94
94
|
whisper workflow start --type=spec-driven-development --spec=<resolved-absolute-path>
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
(No `--implementer` / `--reviewer` — the
|
|
97
|
+
(No `--implementer` / `--reviewer` needed — the agent triggering this skill becomes the implementer and the other agent the reviewer. Pass `--implementer <agent> --reviewer <agent>` only to override.)
|
|
98
98
|
|
|
99
99
|
Parse the workflowId from stdout (format: `Workflow started: <workflowId>`).
|
|
100
100
|
|