ai-whisper 0.13.0 → 0.14.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 +9 -6
- package/dist/bin/broker-daemon.js +2 -2
- package/dist/bin/companion-agent.js +339 -33
- package/dist/bin/relay-monitor.js +9 -3
- package/dist/bin/whisper.js +738 -90
- package/package.json +6 -5
- package/skills/ai-whisper-bugfix/SKILL.md +11 -9
- package/skills/ai-whisper-deliberation/SKILL.md +11 -9
- package/skills/ai-whisper-plan-execution/SKILL.md +1 -1
- package/skills/ai-whisper-quick-task/SKILL.md +12 -10
- package/skills/ai-whisper-ralph/SKILL.md +11 -9
- package/skills/ai-whisper-sdd/SKILL.md +12 -10
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ai-whisper
|
|
2
2
|
|
|
3
|
-
ai-whisper pairs two coding agents — mount any two of Claude, Codex, ezio, and
|
|
3
|
+
ai-whisper pairs two coding agents — mount any two of Claude, Codex, ezio, agy, and Cursor — 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
4
|
|
|
5
5
|
## Magic moment
|
|
6
6
|
|
|
@@ -13,6 +13,8 @@ whisper collab mount claude
|
|
|
13
13
|
whisper collab mount codex
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
Swap in any of the four agents — `whisper collab mount cursor` drives Cursor's `agent` CLI the same way, so e.g. `claude` + `cursor` is a valid pair.
|
|
17
|
+
|
|
16
18
|
Then, from inside either agent's session, kick off a structured workflow against a spec — just ask in plain language:
|
|
17
19
|
|
|
18
20
|
```text
|
|
@@ -52,10 +54,11 @@ It is **not** for:
|
|
|
52
54
|
|
|
53
55
|
## Prerequisites
|
|
54
56
|
|
|
55
|
-
You pair any two of
|
|
57
|
+
You pair any two of five agents — `claude`, `codex`, `ezio`, `agy`, and `cursor`. ai-whisper drives the *real* Claude, Codex, Antigravity, and Cursor CLIs, so install and authenticate whichever of those you plan to mount first; `ezio` is protocol-native and ships with ai-whisper, so it needs no separate CLI.
|
|
56
58
|
|
|
57
59
|
- **[Claude Code CLI](https://claude.com/claude-code)** — the `claude` command, signed in.
|
|
58
60
|
- **[Codex CLI](https://github.com/openai/codex)** — the `codex` command, signed in.
|
|
61
|
+
- **[Cursor CLI](https://cursor.com/cli)** — the `agent` command, signed in. (Override the binary with `AI_WHISPER_CURSOR_CMD` if it is on your PATH under a different name.)
|
|
59
62
|
- **ezio** *(optional)* — bundled with ai-whisper; mount it with `whisper collab mount ezio`, no separate install.
|
|
60
63
|
- **agy** *(optional)* — the `agy` command (Antigravity CLI), signed in. Mount it with `whisper collab mount agy` (manual-mount parity; no auto-launch via `collab start`).
|
|
61
64
|
- **Node.js 22+**.
|
|
@@ -66,7 +69,7 @@ You pair any two of four agents — `claude`, `codex`, `ezio`, and `agy`. ai-whi
|
|
|
66
69
|
|
|
67
70
|
## Safety & permissions
|
|
68
71
|
|
|
69
|
-
ai-whisper launches each agent in **full-autonomy mode** so the relay can drive it unattended — `claude --dangerously-skip-permissions
|
|
72
|
+
ai-whisper launches each agent in **full-autonomy mode** so the relay can drive it unattended — `claude --dangerously-skip-permissions`, `codex --dangerously-bypass-approvals-and-sandbox`, and `agent --force` (Cursor). Inside the mounted workspace the agents read, write, and run commands without prompting. Point it at code you're willing to let two agents change autonomously, watch the run on the dashboard, and remember you are the final gatekeeper — review the result before you ship it. The deeper rationale is in [Concepts](docs/concepts.md).
|
|
70
73
|
|
|
71
74
|
## Quickstart
|
|
72
75
|
|
|
@@ -124,15 +127,15 @@ A run that stops short usually **escalates** — it does not crash. When the eva
|
|
|
124
127
|
|
|
125
128
|
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* agent sessions in your terminal — Claude or Codex CLIs, ezio, or agy — 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.
|
|
126
129
|
|
|
127
|
-
Claude, Codex, ezio, and
|
|
130
|
+
Claude, Codex, ezio, agy, and Cursor are supported today — you mount any two of them; the architecture is provider-agnostic by design, so other coding-agent CLIs can be added behind the same relay.
|
|
128
131
|
|
|
129
132
|
For the full mental model, read [Concepts](docs/concepts.md).
|
|
130
133
|
|
|
131
134
|
## Duo characters
|
|
132
135
|
|
|
133
|
-
|
|
136
|
+
When enabled, a collab casts its two agents as a classic movie duo. The first `whisper collab mount` rolls a pair — Sherlock & Watson, Batman & Robin, Walter White & Jesse Pinkman, and four more — and each mount summons its character with ASCII art and an iconic one-liner before the agent CLI starts. The assignment sticks for the lifetime of the collab: the dashboard and relay chrome show `Batman (claude)` instead of a bare vendor name, and the agents themselves are told who they are (character flavor stays in conversational prose only — never in code, commits, or workflow verdicts).
|
|
134
137
|
|
|
135
|
-
It's cosmetic and
|
|
138
|
+
It's cosmetic and **off by default** — a plain `whisper collab mount` rolls nothing, shows nothing, and uses plain vendor names everywhere. Opt in by setting `AI_WHISPER_DUO=on` (also accepts `1`, `true`, `yes`) in your environment; once enabled, silence a single mount with `--no-duo`, which overrides the environment for that mount.
|
|
136
139
|
|
|
137
140
|
## Learn more
|
|
138
141
|
|
|
@@ -63,7 +63,7 @@ var threadStates = [
|
|
|
63
63
|
"failed"
|
|
64
64
|
];
|
|
65
65
|
var collabStates = ["active", "stopped"];
|
|
66
|
-
var agentTypes = ["codex", "claude", "ezio", "agy"];
|
|
66
|
+
var agentTypes = ["codex", "claude", "ezio", "agy", "cursor"];
|
|
67
67
|
var sessionRegistrationStates = ["registered"];
|
|
68
68
|
var workItemStates = [
|
|
69
69
|
"queued",
|
|
@@ -332,7 +332,7 @@ var mockProviderReplySchema = z14.object({
|
|
|
332
332
|
|
|
333
333
|
// ../shared/dist/relay-host.js
|
|
334
334
|
import { z as z15 } from "zod";
|
|
335
|
-
var relayTargets = ["codex", "claude", "ezio", "agy", "pull"];
|
|
335
|
+
var relayTargets = ["codex", "claude", "ezio", "agy", "cursor", "pull"];
|
|
336
336
|
var relayDirectiveSchema = z15.object({
|
|
337
337
|
raw: z15.string().min(1),
|
|
338
338
|
target: z15.enum(relayTargets),
|
|
@@ -77,7 +77,7 @@ var threadStates = [
|
|
|
77
77
|
"failed"
|
|
78
78
|
];
|
|
79
79
|
var collabStates = ["active", "stopped"];
|
|
80
|
-
var agentTypes = ["codex", "claude", "ezio", "agy"];
|
|
80
|
+
var agentTypes = ["codex", "claude", "ezio", "agy", "cursor"];
|
|
81
81
|
var sessionRegistrationStates = ["registered"];
|
|
82
82
|
var workItemStates = [
|
|
83
83
|
"queued",
|
|
@@ -120,6 +120,54 @@ var attachTargetModes = [
|
|
|
120
120
|
"mount_current_tty"
|
|
121
121
|
];
|
|
122
122
|
|
|
123
|
+
// ../shared/dist/extract-json-object-candidates.js
|
|
124
|
+
function extractJsonObjectCandidates(stdout) {
|
|
125
|
+
const candidates = [];
|
|
126
|
+
let start = -1;
|
|
127
|
+
let depth = 0;
|
|
128
|
+
let inString = false;
|
|
129
|
+
let escaping = false;
|
|
130
|
+
for (let index = 0; index < stdout.length; index += 1) {
|
|
131
|
+
const char = stdout[index];
|
|
132
|
+
if (start !== -1 && inString) {
|
|
133
|
+
if (escaping) {
|
|
134
|
+
escaping = false;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (char === "\\") {
|
|
138
|
+
escaping = true;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (char === '"') {
|
|
142
|
+
inString = false;
|
|
143
|
+
}
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (char === "{") {
|
|
147
|
+
if (depth === 0) {
|
|
148
|
+
start = index;
|
|
149
|
+
}
|
|
150
|
+
depth += 1;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (depth === 0) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (char === '"') {
|
|
157
|
+
inString = true;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (char === "}") {
|
|
161
|
+
depth -= 1;
|
|
162
|
+
if (depth === 0 && start !== -1) {
|
|
163
|
+
candidates.push(stdout.slice(start, index + 1));
|
|
164
|
+
start = -1;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return candidates;
|
|
169
|
+
}
|
|
170
|
+
|
|
123
171
|
// ../shared/dist/repo-relative-path.js
|
|
124
172
|
import { basename, isAbsolute, relative } from "node:path";
|
|
125
173
|
function repoRelativePath(absPath, root) {
|
|
@@ -349,7 +397,7 @@ var mockProviderReplySchema = z14.object({
|
|
|
349
397
|
|
|
350
398
|
// ../shared/dist/relay-host.js
|
|
351
399
|
import { z as z15 } from "zod";
|
|
352
|
-
var relayTargets = ["codex", "claude", "ezio", "agy", "pull"];
|
|
400
|
+
var relayTargets = ["codex", "claude", "ezio", "agy", "cursor", "pull"];
|
|
353
401
|
var relayDirectiveSchema = z15.object({
|
|
354
402
|
raw: z15.string().min(1),
|
|
355
403
|
target: z15.enum(relayTargets),
|
|
@@ -7452,7 +7500,7 @@ function buildCodexFileBackedBrokerPrompt(requestFilePath) {
|
|
|
7452
7500
|
}
|
|
7453
7501
|
|
|
7454
7502
|
// ../adapter-codex/dist/parse-codex-output.js
|
|
7455
|
-
function
|
|
7503
|
+
function extractJsonObjectCandidates2(stdout) {
|
|
7456
7504
|
const candidates = [];
|
|
7457
7505
|
let start = -1;
|
|
7458
7506
|
let depth = 0;
|
|
@@ -7500,7 +7548,7 @@ function extractJsonObjectCandidates(stdout) {
|
|
|
7500
7548
|
}
|
|
7501
7549
|
function parseCodexOutput(stdout) {
|
|
7502
7550
|
const trimmed = stdout.trim();
|
|
7503
|
-
const candidates =
|
|
7551
|
+
const candidates = extractJsonObjectCandidates2(trimmed);
|
|
7504
7552
|
if (candidates.length === 0) {
|
|
7505
7553
|
return {
|
|
7506
7554
|
kind: "failure",
|
|
@@ -7722,9 +7770,244 @@ function createCodexLiveSession(input) {
|
|
|
7722
7770
|
};
|
|
7723
7771
|
}
|
|
7724
7772
|
|
|
7773
|
+
// ../adapter-cursor/dist/create-cursor-provider.js
|
|
7774
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
7775
|
+
|
|
7776
|
+
// ../adapter-cursor/dist/cursor-prompt.js
|
|
7777
|
+
function buildCursorPrompt(request) {
|
|
7778
|
+
return [
|
|
7779
|
+
"Return ONLY valid JSON.",
|
|
7780
|
+
"{",
|
|
7781
|
+
' "kind": "answer" | "review" | "clarification" | "failure",',
|
|
7782
|
+
' "content": "string",',
|
|
7783
|
+
' "transitionIntent": "in_progress" | "awaiting_user" | "completed" | "failed" | null',
|
|
7784
|
+
"}",
|
|
7785
|
+
"",
|
|
7786
|
+
`action: ${request.requestedAction}`,
|
|
7787
|
+
`instruction: ${request.instruction}`,
|
|
7788
|
+
`collabId: ${request.collabId}`,
|
|
7789
|
+
`threadId: ${request.threadId}`,
|
|
7790
|
+
`workItemId: ${request.workItemId}`
|
|
7791
|
+
].join("\n");
|
|
7792
|
+
}
|
|
7793
|
+
function buildCursorFileBackedBrokerPrompt(requestFilePath) {
|
|
7794
|
+
return [
|
|
7795
|
+
"Return ONLY valid JSON matching this schema:",
|
|
7796
|
+
"{",
|
|
7797
|
+
' "kind": "answer" | "review" | "clarification" | "failure",',
|
|
7798
|
+
' "content": "string",',
|
|
7799
|
+
' "transitionIntent": "in_progress" | "awaiting_user" | "completed" | "failed" | null',
|
|
7800
|
+
"}",
|
|
7801
|
+
"",
|
|
7802
|
+
`The work request is in the file: ${requestFilePath}`,
|
|
7803
|
+
"That file is the authoritative source of truth. Read it and respond to the instruction in it."
|
|
7804
|
+
].join("\n");
|
|
7805
|
+
}
|
|
7806
|
+
|
|
7807
|
+
// ../adapter-cursor/dist/parse-cursor-output.js
|
|
7808
|
+
function failure(content) {
|
|
7809
|
+
return { kind: "failure", content, transitionIntent: "failed" };
|
|
7810
|
+
}
|
|
7811
|
+
function parseCursorOutput(stdout) {
|
|
7812
|
+
let envelope;
|
|
7813
|
+
try {
|
|
7814
|
+
envelope = JSON.parse(stdout.trim());
|
|
7815
|
+
} catch {
|
|
7816
|
+
return failure("Cursor output did not contain a JSON result envelope");
|
|
7817
|
+
}
|
|
7818
|
+
if (envelope["is_error"] === true || envelope["subtype"] !== "success") {
|
|
7819
|
+
return failure("Cursor output was not a successful result");
|
|
7820
|
+
}
|
|
7821
|
+
const result = typeof envelope["result"] === "string" ? envelope["result"] : "";
|
|
7822
|
+
if (result.trim().length === 0) {
|
|
7823
|
+
return failure("Cursor result was empty");
|
|
7824
|
+
}
|
|
7825
|
+
const candidates = extractJsonObjectCandidates(result);
|
|
7826
|
+
for (let index = candidates.length - 1; index >= 0; index -= 1) {
|
|
7827
|
+
const candidate = candidates[index];
|
|
7828
|
+
if (candidate === void 0) {
|
|
7829
|
+
continue;
|
|
7830
|
+
}
|
|
7831
|
+
try {
|
|
7832
|
+
return mockProviderReplySchema.parse(JSON.parse(candidate));
|
|
7833
|
+
} catch {
|
|
7834
|
+
}
|
|
7835
|
+
}
|
|
7836
|
+
return { kind: "answer", content: result, transitionIntent: null };
|
|
7837
|
+
}
|
|
7838
|
+
|
|
7839
|
+
// ../adapter-cursor/dist/create-cursor-provider.js
|
|
7840
|
+
function createCursorProvider(config) {
|
|
7841
|
+
return {
|
|
7842
|
+
getIdentity() {
|
|
7843
|
+
return createProviderIdentity({
|
|
7844
|
+
providerId: "cursor-agent-cli",
|
|
7845
|
+
toolFamily: "cursor",
|
|
7846
|
+
providerVersion: "1.0.0"
|
|
7847
|
+
});
|
|
7848
|
+
},
|
|
7849
|
+
getCapabilities() {
|
|
7850
|
+
return {
|
|
7851
|
+
supportsDirectPackets: true,
|
|
7852
|
+
supportsNormalization: true,
|
|
7853
|
+
supportsRelayInterception: true,
|
|
7854
|
+
supportsLocalBuffering: false,
|
|
7855
|
+
supportsLaunchHooks: true,
|
|
7856
|
+
extensions: {}
|
|
7857
|
+
};
|
|
7858
|
+
},
|
|
7859
|
+
getHealthState() {
|
|
7860
|
+
return "healthy";
|
|
7861
|
+
},
|
|
7862
|
+
attachInteractiveSession(session) {
|
|
7863
|
+
void session;
|
|
7864
|
+
},
|
|
7865
|
+
handleWork(request, context) {
|
|
7866
|
+
const prompt = context?.artifactHandle ? buildCursorFileBackedBrokerPrompt(context.artifactHandle.requestFilePath) : buildCursorPrompt(request);
|
|
7867
|
+
return new Promise((resolve2) => {
|
|
7868
|
+
const child = spawn5(config.executable, [...config.execArgs, prompt], {
|
|
7869
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
7870
|
+
});
|
|
7871
|
+
let stdout = "";
|
|
7872
|
+
let stderr = "";
|
|
7873
|
+
let settled = false;
|
|
7874
|
+
child.stdout.on("data", (chunk) => {
|
|
7875
|
+
stdout += String(chunk);
|
|
7876
|
+
});
|
|
7877
|
+
child.stderr.on("data", (chunk) => {
|
|
7878
|
+
stderr += String(chunk);
|
|
7879
|
+
});
|
|
7880
|
+
child.on("error", (err) => {
|
|
7881
|
+
if (settled)
|
|
7882
|
+
return;
|
|
7883
|
+
settled = true;
|
|
7884
|
+
resolve2({
|
|
7885
|
+
kind: "failure",
|
|
7886
|
+
content: `Failed to spawn ${config.executable}: ${err.message}`,
|
|
7887
|
+
transitionIntent: "failed"
|
|
7888
|
+
});
|
|
7889
|
+
});
|
|
7890
|
+
child.on("close", (code) => {
|
|
7891
|
+
if (settled)
|
|
7892
|
+
return;
|
|
7893
|
+
settled = true;
|
|
7894
|
+
if (code !== 0) {
|
|
7895
|
+
resolve2({
|
|
7896
|
+
kind: "failure",
|
|
7897
|
+
content: `Cursor exited with code ${code}: ${stderr.trim()}`,
|
|
7898
|
+
transitionIntent: "failed"
|
|
7899
|
+
});
|
|
7900
|
+
return;
|
|
7901
|
+
}
|
|
7902
|
+
resolve2(parseCursorOutput(stdout));
|
|
7903
|
+
});
|
|
7904
|
+
});
|
|
7905
|
+
}
|
|
7906
|
+
};
|
|
7907
|
+
}
|
|
7908
|
+
|
|
7909
|
+
// ../adapter-cursor/dist/create-cursor-live-session.js
|
|
7910
|
+
import { appendFileSync as appendFileSync4 } from "node:fs";
|
|
7911
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
7912
|
+
import { spawn as spawn6 } from "node-pty";
|
|
7913
|
+
var require4 = createRequire3(import.meta.url);
|
|
7914
|
+
var nodePtyUnixTerminalPath3 = require4.resolve("node-pty/lib/unixTerminal.js");
|
|
7915
|
+
var DEFAULT_COLS3 = 120;
|
|
7916
|
+
var DEFAULT_ROWS3 = 40;
|
|
7917
|
+
function ttySize3(stdout) {
|
|
7918
|
+
const s = stdout;
|
|
7919
|
+
const cols = typeof s.columns === "number" && s.columns > 0 ? s.columns : DEFAULT_COLS3;
|
|
7920
|
+
const rows = typeof s.rows === "number" && s.rows > 0 ? s.rows : DEFAULT_ROWS3;
|
|
7921
|
+
return { cols, rows };
|
|
7922
|
+
}
|
|
7923
|
+
function createNodePty3(input) {
|
|
7924
|
+
ensureNodePtySpawnHelperExecutable({
|
|
7925
|
+
unixTerminalPath: nodePtyUnixTerminalPath3
|
|
7926
|
+
});
|
|
7927
|
+
return spawn6(input.config.executable, input.config.execArgs, buildCursorPtySpawnOptions({ cols: input.cols, rows: input.rows, cwd: input.cwd }));
|
|
7928
|
+
}
|
|
7929
|
+
function buildCursorPtySpawnOptions(input) {
|
|
7930
|
+
const env = {};
|
|
7931
|
+
for (const [key, value] of Object.entries(input.baseEnv ?? process.env)) {
|
|
7932
|
+
if (typeof value === "string")
|
|
7933
|
+
env[key] = value;
|
|
7934
|
+
}
|
|
7935
|
+
env.AI_WHISPER_AGENT = "cursor";
|
|
7936
|
+
return { name: "xterm-256color", cols: input.cols, rows: input.rows, cwd: input.cwd, env };
|
|
7937
|
+
}
|
|
7938
|
+
function createCursorLiveSession(input) {
|
|
7939
|
+
let pty = null;
|
|
7940
|
+
let exitHandler = null;
|
|
7941
|
+
let outputHandler = null;
|
|
7942
|
+
let onStdoutResize = null;
|
|
7943
|
+
function handleData(data) {
|
|
7944
|
+
outputHandler?.(data);
|
|
7945
|
+
input.stdout.write(data);
|
|
7946
|
+
}
|
|
7947
|
+
return {
|
|
7948
|
+
start() {
|
|
7949
|
+
const { cols, rows } = ttySize3(input.stdout);
|
|
7950
|
+
pty = (input.createPty ?? createNodePty3)({
|
|
7951
|
+
config: input.config,
|
|
7952
|
+
cwd: input.cwd,
|
|
7953
|
+
cols,
|
|
7954
|
+
rows
|
|
7955
|
+
});
|
|
7956
|
+
pty.onData(handleData);
|
|
7957
|
+
pty.onExit(() => {
|
|
7958
|
+
exitHandler?.();
|
|
7959
|
+
});
|
|
7960
|
+
const s = input.stdout;
|
|
7961
|
+
if (typeof s.on === "function") {
|
|
7962
|
+
onStdoutResize = () => {
|
|
7963
|
+
const next = ttySize3(input.stdout);
|
|
7964
|
+
pty?.resize(next.cols, next.rows);
|
|
7965
|
+
};
|
|
7966
|
+
s.on("resize", onStdoutResize);
|
|
7967
|
+
}
|
|
7968
|
+
return Promise.resolve();
|
|
7969
|
+
},
|
|
7970
|
+
stop() {
|
|
7971
|
+
const s = input.stdout;
|
|
7972
|
+
if (onStdoutResize) {
|
|
7973
|
+
(s.off ?? s.removeListener)?.call(s, "resize", onStdoutResize);
|
|
7974
|
+
onStdoutResize = null;
|
|
7975
|
+
}
|
|
7976
|
+
if (pty) {
|
|
7977
|
+
pty.kill();
|
|
7978
|
+
pty = null;
|
|
7979
|
+
}
|
|
7980
|
+
return Promise.resolve();
|
|
7981
|
+
},
|
|
7982
|
+
writeUserInput(data) {
|
|
7983
|
+
pty?.write(data);
|
|
7984
|
+
},
|
|
7985
|
+
resize(cols, rows) {
|
|
7986
|
+
pty?.resize(cols, rows);
|
|
7987
|
+
},
|
|
7988
|
+
sendLocalMessage(message) {
|
|
7989
|
+
const localLog = process.env.AI_WHISPER_DEBUG_LOCAL_LOG;
|
|
7990
|
+
if (localLog) {
|
|
7991
|
+
try {
|
|
7992
|
+
appendFileSync4(localLog, `${(/* @__PURE__ */ new Date()).toISOString()} cursor ${JSON.stringify(message)}
|
|
7993
|
+
`);
|
|
7994
|
+
} catch {
|
|
7995
|
+
}
|
|
7996
|
+
}
|
|
7997
|
+
input.stdout.write(message);
|
|
7998
|
+
},
|
|
7999
|
+
onExit(handler) {
|
|
8000
|
+
exitHandler = handler;
|
|
8001
|
+
},
|
|
8002
|
+
onProviderOutput(handler) {
|
|
8003
|
+
outputHandler = handler;
|
|
8004
|
+
}
|
|
8005
|
+
};
|
|
8006
|
+
}
|
|
8007
|
+
|
|
7725
8008
|
// ../../node_modules/.pnpm/@ai-ezio+harness@file+..+ai-ezio+packages+harness/node_modules/@ai-ezio/harness/dist/resolve-hax.js
|
|
7726
8009
|
import { existsSync as existsSync7 } from "node:fs";
|
|
7727
|
-
import { createRequire as
|
|
8010
|
+
import { createRequire as createRequire4 } from "node:module";
|
|
7728
8011
|
import { dirname as dirname3, join as join9 } from "node:path";
|
|
7729
8012
|
import { fileURLToPath } from "node:url";
|
|
7730
8013
|
var HaxBinaryNotFoundError = class extends Error {
|
|
@@ -7737,8 +8020,8 @@ function platformPackageName(platform, arch) {
|
|
|
7737
8020
|
return `@ai-creed/hax-${platform}-${arch}`;
|
|
7738
8021
|
}
|
|
7739
8022
|
function defaultResolvePackageJson(specifier) {
|
|
7740
|
-
const
|
|
7741
|
-
return
|
|
8023
|
+
const require6 = createRequire4(import.meta.url);
|
|
8024
|
+
return require6.resolve(specifier);
|
|
7742
8025
|
}
|
|
7743
8026
|
function findDevRoot() {
|
|
7744
8027
|
let dir = dirname3(fileURLToPath(import.meta.url));
|
|
@@ -7811,7 +8094,7 @@ function resolveHaxBinary(options = {}) {
|
|
|
7811
8094
|
}
|
|
7812
8095
|
|
|
7813
8096
|
// ../../node_modules/.pnpm/@ai-ezio+harness@file+..+ai-ezio+packages+harness/node_modules/@ai-ezio/harness/dist/spawn.js
|
|
7814
|
-
import { spawn as
|
|
8097
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
7815
8098
|
|
|
7816
8099
|
// ../../node_modules/.pnpm/@ai-ezio+harness@file+..+ai-ezio+packages+harness/node_modules/@ai-ezio/harness/dist/skills-dir.js
|
|
7817
8100
|
import { homedir } from "node:os";
|
|
@@ -7835,7 +8118,7 @@ function haxSpawnEnv(base = process.env, transcriptPath) {
|
|
|
7835
8118
|
}
|
|
7836
8119
|
function spawnHax(options = {}) {
|
|
7837
8120
|
const binary = options.binary ?? resolveHaxBinary();
|
|
7838
|
-
const child =
|
|
8121
|
+
const child = spawn7(binary, haxSpawnArgs(options.args), {
|
|
7839
8122
|
// 0,1,2 ignored; 3 = events (hax writes), 4 = controls (hax reads).
|
|
7840
8123
|
stdio: ["ignore", "ignore", "ignore", "pipe", "pipe"],
|
|
7841
8124
|
env: haxSpawnEnv(options.env ?? process.env, options.transcriptPath)
|
|
@@ -9952,7 +10235,7 @@ function nodeSkillFs() {
|
|
|
9952
10235
|
}
|
|
9953
10236
|
|
|
9954
10237
|
// ../../node_modules/.pnpm/@ai-ezio+surface@file+..+ai-ezio+packages+surface/node_modules/@ai-ezio/surface/dist/clipboard.js
|
|
9955
|
-
import { spawn as
|
|
10238
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
9956
10239
|
function tryCopy(argv, text, spawnFn) {
|
|
9957
10240
|
return new Promise((resolve2, reject) => {
|
|
9958
10241
|
const child = spawnFn(argv[0], argv.slice(1), { stdio: ["pipe", "ignore", "ignore"] });
|
|
@@ -9961,7 +10244,7 @@ function tryCopy(argv, text, spawnFn) {
|
|
|
9961
10244
|
child.stdin?.end(text);
|
|
9962
10245
|
});
|
|
9963
10246
|
}
|
|
9964
|
-
function makeClipboard(platform, spawnFn =
|
|
10247
|
+
function makeClipboard(platform, spawnFn = spawn8) {
|
|
9965
10248
|
const candidates = platform === "darwin" ? [["pbcopy"]] : [["wl-copy"], ["xclip", "-selection", "clipboard"]];
|
|
9966
10249
|
return async (text) => {
|
|
9967
10250
|
let lastErr = new Error("no clipboard tool available");
|
|
@@ -10480,7 +10763,7 @@ function isMidCompositionShape(message) {
|
|
|
10480
10763
|
}
|
|
10481
10764
|
|
|
10482
10765
|
// ../adapter-ai-ezio/dist/create-ai-ezio-live-session.js
|
|
10483
|
-
import { spawn as
|
|
10766
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
10484
10767
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
10485
10768
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
10486
10769
|
import { tmpdir } from "node:os";
|
|
@@ -10510,7 +10793,7 @@ function createAiEzioLiveSession(input) {
|
|
|
10510
10793
|
const titleStore = input.titleStore ?? createSessionTitleStore();
|
|
10511
10794
|
const listSessions = input.listSessions ?? (() => new Promise((resolve2) => {
|
|
10512
10795
|
let out = "";
|
|
10513
|
-
const child =
|
|
10796
|
+
const child = spawn9(resolveHaxBinary(), ["--list-sessions"], {
|
|
10514
10797
|
cwd: process.cwd(),
|
|
10515
10798
|
stdio: ["ignore", "pipe", "ignore"]
|
|
10516
10799
|
});
|
|
@@ -10667,7 +10950,7 @@ function createAiEzioLiveSession(input) {
|
|
|
10667
10950
|
source: s.source,
|
|
10668
10951
|
description: s.description
|
|
10669
10952
|
})),
|
|
10670
|
-
clipboard: input.clipboard ?? makeClipboard(process.platform,
|
|
10953
|
+
clipboard: input.clipboard ?? makeClipboard(process.platform, spawn9),
|
|
10671
10954
|
showTranscript: () => showTranscript({
|
|
10672
10955
|
path: sessionFacet.transcriptPath ?? "",
|
|
10673
10956
|
readText: (p) => {
|
|
@@ -10732,7 +11015,7 @@ function createAiEzioLiveSession(input) {
|
|
|
10732
11015
|
}
|
|
10733
11016
|
|
|
10734
11017
|
// ../adapter-antigravity/dist/create-antigravity-provider.js
|
|
10735
|
-
import { spawn as
|
|
11018
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
10736
11019
|
|
|
10737
11020
|
// ../adapter-antigravity/dist/antigravity-prompt.js
|
|
10738
11021
|
function buildAntigravityPrompt(request) {
|
|
@@ -10819,7 +11102,7 @@ function createAntigravityProvider(config) {
|
|
|
10819
11102
|
return new Promise((resolve2) => {
|
|
10820
11103
|
let child;
|
|
10821
11104
|
try {
|
|
10822
|
-
child =
|
|
11105
|
+
child = spawn10(config.executable, [...config.execArgs, prompt], {
|
|
10823
11106
|
stdio: ["ignore", "pipe", "pipe"]
|
|
10824
11107
|
});
|
|
10825
11108
|
} catch (err) {
|
|
@@ -10878,22 +11161,22 @@ function createAntigravityProvider(config) {
|
|
|
10878
11161
|
}
|
|
10879
11162
|
|
|
10880
11163
|
// ../adapter-antigravity/dist/create-antigravity-live-session.js
|
|
10881
|
-
import { appendFileSync as
|
|
10882
|
-
import { createRequire as
|
|
10883
|
-
import { spawn as
|
|
10884
|
-
var
|
|
10885
|
-
var
|
|
10886
|
-
var
|
|
10887
|
-
var
|
|
10888
|
-
function
|
|
11164
|
+
import { appendFileSync as appendFileSync5 } from "node:fs";
|
|
11165
|
+
import { createRequire as createRequire5 } from "node:module";
|
|
11166
|
+
import { spawn as spawn11 } from "node-pty";
|
|
11167
|
+
var require5 = createRequire5(import.meta.url);
|
|
11168
|
+
var nodePtyUnixTerminalPath4 = require5.resolve("node-pty/lib/unixTerminal.js");
|
|
11169
|
+
var DEFAULT_COLS4 = 120;
|
|
11170
|
+
var DEFAULT_ROWS4 = 40;
|
|
11171
|
+
function ttySize4(stdout) {
|
|
10889
11172
|
const s = stdout;
|
|
10890
|
-
const cols = typeof s.columns === "number" && s.columns > 0 ? s.columns :
|
|
10891
|
-
const rows = typeof s.rows === "number" && s.rows > 0 ? s.rows :
|
|
11173
|
+
const cols = typeof s.columns === "number" && s.columns > 0 ? s.columns : DEFAULT_COLS4;
|
|
11174
|
+
const rows = typeof s.rows === "number" && s.rows > 0 ? s.rows : DEFAULT_ROWS4;
|
|
10892
11175
|
return { cols, rows };
|
|
10893
11176
|
}
|
|
10894
|
-
function
|
|
10895
|
-
ensureNodePtySpawnHelperExecutable({ unixTerminalPath:
|
|
10896
|
-
return
|
|
11177
|
+
function createNodePty4(input) {
|
|
11178
|
+
ensureNodePtySpawnHelperExecutable({ unixTerminalPath: nodePtyUnixTerminalPath4 });
|
|
11179
|
+
return spawn11(input.config.executable, input.config.execArgs, buildAntigravityPtySpawnOptions({ cols: input.cols, rows: input.rows, cwd: input.cwd }));
|
|
10897
11180
|
}
|
|
10898
11181
|
function buildAntigravityPtySpawnOptions(input) {
|
|
10899
11182
|
const env = {};
|
|
@@ -10915,8 +11198,8 @@ function createAntigravityLiveSession(input) {
|
|
|
10915
11198
|
}
|
|
10916
11199
|
return {
|
|
10917
11200
|
start() {
|
|
10918
|
-
const { cols, rows } =
|
|
10919
|
-
pty = (input.createPty ??
|
|
11201
|
+
const { cols, rows } = ttySize4(input.stdout);
|
|
11202
|
+
pty = (input.createPty ?? createNodePty4)({
|
|
10920
11203
|
config: input.config,
|
|
10921
11204
|
cwd: input.cwd,
|
|
10922
11205
|
cols,
|
|
@@ -10929,7 +11212,7 @@ function createAntigravityLiveSession(input) {
|
|
|
10929
11212
|
const s = input.stdout;
|
|
10930
11213
|
if (typeof s.on === "function") {
|
|
10931
11214
|
onStdoutResize = () => {
|
|
10932
|
-
const next =
|
|
11215
|
+
const next = ttySize4(input.stdout);
|
|
10933
11216
|
pty?.resize(next.cols, next.rows);
|
|
10934
11217
|
};
|
|
10935
11218
|
s.on("resize", onStdoutResize);
|
|
@@ -10958,7 +11241,7 @@ function createAntigravityLiveSession(input) {
|
|
|
10958
11241
|
const localLog = process.env.AI_WHISPER_DEBUG_LOCAL_LOG;
|
|
10959
11242
|
if (localLog) {
|
|
10960
11243
|
try {
|
|
10961
|
-
|
|
11244
|
+
appendFileSync5(localLog, `${(/* @__PURE__ */ new Date()).toISOString()} agy ${JSON.stringify(message)}
|
|
10962
11245
|
`);
|
|
10963
11246
|
} catch {
|
|
10964
11247
|
}
|
|
@@ -10987,6 +11270,9 @@ function getInteractiveSessionExecArgsForTarget(target, turnEvents) {
|
|
|
10987
11270
|
if (target === "agy") {
|
|
10988
11271
|
return ["--add-dir", tempRoot, "--dangerously-skip-permissions"];
|
|
10989
11272
|
}
|
|
11273
|
+
if (target === "cursor") {
|
|
11274
|
+
return ["--force"];
|
|
11275
|
+
}
|
|
10990
11276
|
const base = ["--add-dir", tempRoot, "--dangerously-skip-permissions"];
|
|
10991
11277
|
return turnEvents?.claudeSettingsFile ? [...base, "--settings", turnEvents.claudeSettingsFile] : base;
|
|
10992
11278
|
}
|
|
@@ -11006,6 +11292,9 @@ function getProviderExecArgsForTarget(target) {
|
|
|
11006
11292
|
if (target === "agy") {
|
|
11007
11293
|
return ["-p", "--add-dir", tempRoot, "--dangerously-skip-permissions"];
|
|
11008
11294
|
}
|
|
11295
|
+
if (target === "cursor") {
|
|
11296
|
+
return ["-p", "--force", "--output-format", "json"];
|
|
11297
|
+
}
|
|
11009
11298
|
return ["-p", "--add-dir", tempRoot, "--dangerously-skip-permissions"];
|
|
11010
11299
|
}
|
|
11011
11300
|
function createProviderForTarget(target) {
|
|
@@ -11024,6 +11313,12 @@ function createProviderForTarget(target) {
|
|
|
11024
11313
|
execArgs: getProviderExecArgsForTarget("agy")
|
|
11025
11314
|
});
|
|
11026
11315
|
}
|
|
11316
|
+
if (target === "cursor") {
|
|
11317
|
+
return createCursorProvider({
|
|
11318
|
+
executable: process.env.AI_WHISPER_CURSOR_CMD ?? "agent",
|
|
11319
|
+
execArgs: getProviderExecArgsForTarget("cursor")
|
|
11320
|
+
});
|
|
11321
|
+
}
|
|
11027
11322
|
return createClaudeProvider({
|
|
11028
11323
|
executable: process.env.AI_WHISPER_CLAUDE_CMD ?? "claude",
|
|
11029
11324
|
execArgs: getProviderExecArgsForTarget("claude")
|
|
@@ -11060,6 +11355,17 @@ function createInteractiveSessionForTarget(input) {
|
|
|
11060
11355
|
...input.replyTimeoutMs !== void 0 ? { replyTimeoutMs: input.replyTimeoutMs } : {}
|
|
11061
11356
|
});
|
|
11062
11357
|
}
|
|
11358
|
+
if (input.target === "cursor") {
|
|
11359
|
+
return createCursorLiveSession({
|
|
11360
|
+
config: {
|
|
11361
|
+
executable: process.env.AI_WHISPER_CURSOR_CMD ?? "agent",
|
|
11362
|
+
execArgs
|
|
11363
|
+
},
|
|
11364
|
+
cwd: input.cwd,
|
|
11365
|
+
stdout: input.stdout,
|
|
11366
|
+
...input.replyTimeoutMs !== void 0 ? { replyTimeoutMs: input.replyTimeoutMs } : {}
|
|
11367
|
+
});
|
|
11368
|
+
}
|
|
11063
11369
|
return createClaudeLiveSession({
|
|
11064
11370
|
config: {
|
|
11065
11371
|
executable: process.env.AI_WHISPER_CLAUDE_CMD ?? "claude",
|
|
@@ -66,7 +66,7 @@ var threadStates = [
|
|
|
66
66
|
"failed"
|
|
67
67
|
];
|
|
68
68
|
var collabStates = ["active", "stopped"];
|
|
69
|
-
var agentTypes = ["codex", "claude", "ezio", "agy"];
|
|
69
|
+
var agentTypes = ["codex", "claude", "ezio", "agy", "cursor"];
|
|
70
70
|
var sessionRegistrationStates = ["registered"];
|
|
71
71
|
var workItemStates = [
|
|
72
72
|
"queued",
|
|
@@ -335,7 +335,7 @@ var mockProviderReplySchema = z14.object({
|
|
|
335
335
|
|
|
336
336
|
// ../shared/dist/relay-host.js
|
|
337
337
|
import { z as z15 } from "zod";
|
|
338
|
-
var relayTargets = ["codex", "claude", "ezio", "agy", "pull"];
|
|
338
|
+
var relayTargets = ["codex", "claude", "ezio", "agy", "cursor", "pull"];
|
|
339
339
|
var relayDirectiveSchema = z15.object({
|
|
340
340
|
raw: z15.string().min(1),
|
|
341
341
|
target: z15.enum(relayTargets),
|
|
@@ -6412,7 +6412,13 @@ function stuckBudgetMs(step, baseMs) {
|
|
|
6412
6412
|
if (step === "execute" || step === "review") return Math.max(baseMs, STUCK_STEP_MS);
|
|
6413
6413
|
return baseMs;
|
|
6414
6414
|
}
|
|
6415
|
-
var AGENT_DISPLAY_RANK = {
|
|
6415
|
+
var AGENT_DISPLAY_RANK = {
|
|
6416
|
+
codex: 0,
|
|
6417
|
+
ezio: 0,
|
|
6418
|
+
claude: 1,
|
|
6419
|
+
agy: 1,
|
|
6420
|
+
cursor: 1
|
|
6421
|
+
};
|
|
6416
6422
|
function isAgentType(s) {
|
|
6417
6423
|
return agentTypes.includes(s);
|
|
6418
6424
|
}
|