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 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 agy — 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.
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 four agents — `claude`, `codex`, `ezio`, and `agy`. ai-whisper drives the *real* Claude, Codex, and Antigravity CLIs, so install and authenticate whichever of those you plan to mount first; `ezio` is protocol-native and ships with ai-whisper.
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` and `codex --dangerously-bypass-approvals-and-sandbox`. 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).
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 agy 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.
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
- Every 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).
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 entirely optional. Turn it off per mount with `--no-duo`, or permanently with `AI_WHISPER_DUO=off` in your environment; an opted-out mount rolls nothing, shows nothing, and falls back to plain vendor names everywhere.
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 extractJsonObjectCandidates(stdout) {
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 = extractJsonObjectCandidates(trimmed);
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 createRequire3 } from "node:module";
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 require5 = createRequire3(import.meta.url);
7741
- return require5.resolve(specifier);
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 spawn5 } from "node:child_process";
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 = spawn5(binary, haxSpawnArgs(options.args), {
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 spawn6 } from "node:child_process";
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 = spawn6) {
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 spawn7 } from "node:child_process";
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 = spawn7(resolveHaxBinary(), ["--list-sessions"], {
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, spawn7),
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 spawn8 } from "node:child_process";
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 = spawn8(config.executable, [...config.execArgs, prompt], {
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 appendFileSync4 } from "node:fs";
10882
- import { createRequire as createRequire4 } from "node:module";
10883
- import { spawn as spawn9 } from "node-pty";
10884
- var require4 = createRequire4(import.meta.url);
10885
- var nodePtyUnixTerminalPath3 = require4.resolve("node-pty/lib/unixTerminal.js");
10886
- var DEFAULT_COLS3 = 120;
10887
- var DEFAULT_ROWS3 = 40;
10888
- function ttySize3(stdout) {
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 : DEFAULT_COLS3;
10891
- const rows = typeof s.rows === "number" && s.rows > 0 ? s.rows : DEFAULT_ROWS3;
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 createNodePty3(input) {
10895
- ensureNodePtySpawnHelperExecutable({ unixTerminalPath: nodePtyUnixTerminalPath3 });
10896
- return spawn9(input.config.executable, input.config.execArgs, buildAntigravityPtySpawnOptions({ cols: input.cols, rows: input.rows, cwd: input.cwd }));
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 } = ttySize3(input.stdout);
10919
- pty = (input.createPty ?? createNodePty3)({
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 = ttySize3(input.stdout);
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
- appendFileSync4(localLog, `${(/* @__PURE__ */ new Date()).toISOString()} agy ${JSON.stringify(message)}
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 = { codex: 0, ezio: 0, claude: 1, agy: 1 };
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
  }