@synkro-sh/cli 1.10.6 → 1.10.8

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/dist/bootstrap.js CHANGED
@@ -147,7 +147,7 @@ function getIdentity() {
147
147
  if (cached2) return cached2;
148
148
  let cliVersion2 = "0.0.0";
149
149
  try {
150
- cliVersion2 = "1.10.6";
150
+ cliVersion2 = "1.10.8";
151
151
  } catch {
152
152
  }
153
153
  const creds = loadCredentialsIdentity();
@@ -2351,7 +2351,7 @@ function buildCodexHookTrustEdits(hooks) {
2351
2351
  return [...edits.values()];
2352
2352
  }
2353
2353
  function queryCodexHookTrust(codexBinary = "codex", cwd = process.cwd(), autoTrust = false) {
2354
- return new Promise((resolve7) => {
2354
+ return new Promise((resolve8) => {
2355
2355
  let settled = false;
2356
2356
  let stdout = "";
2357
2357
  let pending = "";
@@ -2369,7 +2369,7 @@ function queryCodexHookTrust(codexBinary = "codex", cwd = process.cwd(), autoTru
2369
2369
  child.kill();
2370
2370
  } catch {
2371
2371
  }
2372
- resolve7(summary);
2372
+ resolve8(summary);
2373
2373
  };
2374
2374
  const timer = setTimeout(() => finish(null), 1e4);
2375
2375
  try {
@@ -5181,7 +5181,7 @@ function createCallbackServer() {
5181
5181
  "Access-Control-Allow-Headers": "Content-Type",
5182
5182
  "Vary": "Origin"
5183
5183
  };
5184
- return new Promise((resolve7, reject) => {
5184
+ return new Promise((resolve8, reject) => {
5185
5185
  const server = createServer((req, res) => {
5186
5186
  if (req.method === "OPTIONS") {
5187
5187
  const origin = req.headers.origin;
@@ -5270,7 +5270,7 @@ function createCallbackServer() {
5270
5270
  res.end(JSON.stringify({ ok: true }));
5271
5271
  setTimeout(() => {
5272
5272
  server.close();
5273
- resolve7(authData);
5273
+ resolve8(authData);
5274
5274
  }, 200);
5275
5275
  });
5276
5276
  req.on("error", (e) => {
@@ -5606,7 +5606,7 @@ function detectSubdirRepos() {
5606
5606
  }
5607
5607
  }
5608
5608
  function ask(rl, question) {
5609
- return new Promise((resolve7) => rl.question(question, resolve7));
5609
+ return new Promise((resolve8) => rl.question(question, resolve8));
5610
5610
  }
5611
5611
  async function linkRepo(repo, linkedNames) {
5612
5612
  try {
@@ -5845,7 +5845,7 @@ async function runClaudeDesktopTap(opts = {}) {
5845
5845
  writeFileSync12(join13(sessionDir, "mcp_patch.py"), MCP_PATCH_PY, "utf-8");
5846
5846
  const runnerPath = join13(sessionDir, "run.sh");
5847
5847
  writeFileSync12(runnerPath, buildRunner(sessionDir), { mode: 493 });
5848
- await new Promise((resolve7) => {
5848
+ await new Promise((resolve8) => {
5849
5849
  const child = spawn3("bash", [runnerPath], {
5850
5850
  stdio: "inherit",
5851
5851
  env: { ...process.env, SYNKRO_CAPTURE_URL: CAPTURE_URL, SYNKRO_SCAN_URL: SCAN_URL, SYNKRO_SCAN_TURN_URL: SCAN_TURN_URL, SYNKRO_DLP_POLICY_URL: DLP_POLICY_URL, SYNKRO_TURN_VERDICTS_URL: TURN_VERDICTS_URL, SYNKRO_TURN_VERDICT_URL: TURN_VERDICT_URL, SYNKRO_MCP_EVENT_URL: MCP_EVENT_URL, SYNKRO_TAP_TOKEN: token, SYNKRO_TAP_TOKEN_FILE: JWT_PATH, SYNKRO_CD_BACKFILL: opts.backfill ? "1" : "" }
@@ -5861,7 +5861,7 @@ async function runClaudeDesktopTap(opts = {}) {
5861
5861
  child.on("exit", () => {
5862
5862
  process.off("SIGINT", forward);
5863
5863
  process.off("SIGTERM", forward);
5864
- resolve7();
5864
+ resolve8();
5865
5865
  });
5866
5866
  });
5867
5867
  }
@@ -7934,7 +7934,7 @@ var init_dockerInstall = __esm({
7934
7934
  HOST_PGLITE_PORT = parseInt(process.env.SYNKRO_HOST_PGLITE_PORT || "15433", 10);
7935
7935
  CONTAINER_NAME = resolveContainerName();
7936
7936
  defaultImageVersion = () => {
7937
- if (true) return "1.10.6";
7937
+ if (true) return "1.10.8";
7938
7938
  try {
7939
7939
  const pkg = JSON.parse(readFileSync17(new URL("../../package.json", import.meta.url), "utf8"));
7940
7940
  if (pkg.version) return pkg.version;
@@ -7966,7 +7966,7 @@ function captureClaudeSetupToken() {
7966
7966
  const bin = "script";
7967
7967
  const args2 = isMac ? ["-q", tmpFile, "claude", "setup-token"] : ["-qec", "claude setup-token", tmpFile];
7968
7968
  const OAUTH_HINT = 'The browser approval did not return a token. This usually means claude.ai rejected the OAuth request (its "Authorization failed \u2014 Unsupported media type" page), most often because a browser extension (Grammarly, ad/script blockers, AI-assistant toolbars) stripped the request headers, or you approved on the wrong Claude account. Fix: retry in a clean incognito window with extensions disabled, and approve on the Claude account you want the cloud workers to use.';
7969
- return new Promise((resolve7, reject) => {
7969
+ return new Promise((resolve8, reject) => {
7970
7970
  const proc = nodeSpawn(bin, args2, {
7971
7971
  stdio: "inherit",
7972
7972
  env: { ...process.env, FORCE_COLOR: "3", COLORTERM: "truecolor", TERM: "xterm-256color" }
@@ -8033,7 +8033,7 @@ function captureClaudeSetupToken() {
8033
8033
  reject(new Error(`Captured no setup token from claude setup-token output. ${reason}`));
8034
8034
  return;
8035
8035
  }
8036
- resolve7(token);
8036
+ resolve8(token);
8037
8037
  });
8038
8038
  });
8039
8039
  }
@@ -8059,13 +8059,13 @@ function findCodexBinary() {
8059
8059
  function runCodexLogin(codexBin, codexHome) {
8060
8060
  mkdirSync13(codexHome, { recursive: true, mode: 448 });
8061
8061
  writeFileSync15(join17(codexHome, "config.toml"), 'cli_auth_credentials_store = "file"\n', { mode: 384 });
8062
- return new Promise((resolve7, reject) => {
8062
+ return new Promise((resolve8, reject) => {
8063
8063
  const proc = nodeSpawn2(codexBin, ["login"], {
8064
8064
  stdio: "inherit",
8065
8065
  env: { ...process.env, CODEX_HOME: codexHome }
8066
8066
  });
8067
8067
  proc.on("error", (err) => reject(new Error(`failed to spawn codex login: ${err.message}`)));
8068
- proc.on("close", (code) => code === 0 ? resolve7() : reject(new Error(`codex login exited with code ${code}`)));
8068
+ proc.on("close", (code) => code === 0 ? resolve8() : reject(new Error(`codex login exited with code ${code}`)));
8069
8069
  });
8070
8070
  }
8071
8071
  async function setupCodexCloud(gatewayUrl, bearerToken, onStatus) {
@@ -8767,20 +8767,20 @@ async function promptAgentSelection(detected) {
8767
8767
  detected.forEach((a, i) => console.log(` ${i + 1}. ${a.name}`));
8768
8768
  console.log(` ${detected.length + 1}. Both / all (default)`);
8769
8769
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
8770
- const ask3 = () => new Promise((resolve7) => {
8770
+ const ask3 = () => new Promise((resolve8) => {
8771
8771
  rl.question(`Pick [1-${detected.length + 1}] (default: all): `, (answer) => {
8772
8772
  const t = answer.trim().toLowerCase();
8773
8773
  if (t === "" || t === String(detected.length + 1) || t === "both" || t === "all") {
8774
8774
  rl.close();
8775
- return resolve7(detected);
8775
+ return resolve8(detected);
8776
8776
  }
8777
8777
  const n = parseInt(t, 10);
8778
8778
  if (Number.isInteger(n) && n >= 1 && n <= detected.length) {
8779
8779
  rl.close();
8780
- return resolve7([detected[n - 1]]);
8780
+ return resolve8([detected[n - 1]]);
8781
8781
  }
8782
8782
  console.log("Invalid choice. Try again.");
8783
- resolve7(ask3());
8783
+ resolve8(ask3());
8784
8784
  });
8785
8785
  });
8786
8786
  return ask3();
@@ -8803,12 +8803,12 @@ async function promptCursorApiKey(opts) {
8803
8803
  return;
8804
8804
  }
8805
8805
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
8806
- const key = await new Promise((resolve7) => {
8806
+ const key = await new Promise((resolve8) => {
8807
8807
  rl.question(
8808
8808
  "Cursor grading needs a Cursor API key (cursor.com \u2192 Settings \u2192 API Keys).\nPaste it now, or press Enter to skip (Cursor workers stay idle until set): ",
8809
8809
  (answer) => {
8810
8810
  rl.close();
8811
- resolve7(answer.trim());
8811
+ resolve8(answer.trim());
8812
8812
  }
8813
8813
  );
8814
8814
  });
@@ -8823,7 +8823,7 @@ async function promptDeployLocation(current = "local") {
8823
8823
  if (!process.stdin.isTTY) return current;
8824
8824
  const other = current === "cloud" ? "local" : "cloud";
8825
8825
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
8826
- return new Promise((resolve7) => {
8826
+ return new Promise((resolve8) => {
8827
8827
  rl.question(
8828
8828
  `Where should Synkro run?
8829
8829
  local \u2014 a grading container on this machine (Docker)
@@ -8832,7 +8832,7 @@ Each worker uses the account credentials you authorize. Choose [${current}] / ${
8832
8832
  (answer) => {
8833
8833
  rl.close();
8834
8834
  const a = answer.trim().toLowerCase();
8835
- resolve7(a === "cloud" ? "cloud" : a === "local" ? "local" : current);
8835
+ resolve8(a === "cloud" ? "cloud" : a === "local" ? "local" : current);
8836
8836
  }
8837
8837
  );
8838
8838
  });
@@ -8994,7 +8994,7 @@ function writeConfigEnv(opts) {
8994
8994
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
8995
8995
  `SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
8996
8996
  `SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
8997
- `SYNKRO_VERSION=${shellQuoteSingle2("1.10.6")}`
8997
+ `SYNKRO_VERSION=${shellQuoteSingle2("1.10.8")}`
8998
8998
  ];
8999
8999
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
9000
9000
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
@@ -9741,7 +9741,7 @@ async function installCommand(opts = {}) {
9741
9741
  await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
9742
9742
  emit("install", {
9743
9743
  phase: "started",
9744
- cli_version_to: "1.10.6",
9744
+ cli_version_to: "1.10.8",
9745
9745
  agents_detected: agents.map((a) => a.kind),
9746
9746
  with_github: false,
9747
9747
  with_local_cc: false,
@@ -10809,8 +10809,8 @@ function ensureReachabilityGitHook() {
10809
10809
  }
10810
10810
  return "updated";
10811
10811
  }
10812
- const sep3 = cur.endsWith("\n") ? "" : "\n";
10813
- writeFileSync17(hookPath, cur + sep3 + "\n" + block + "\n");
10812
+ const sep4 = cur.endsWith("\n") ? "" : "\n";
10813
+ writeFileSync17(hookPath, cur + sep4 + "\n" + block + "\n");
10814
10814
  try {
10815
10815
  chmodSync5(hookPath, 493);
10816
10816
  } catch {
@@ -12030,10 +12030,10 @@ function confirmPurge() {
12030
12030
  return Promise.resolve(false);
12031
12031
  }
12032
12032
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
12033
- return new Promise((resolve7) => {
12033
+ return new Promise((resolve8) => {
12034
12034
  rl.question(" Type 'yes' to wipe everything (anything else cancels): ", (answer) => {
12035
12035
  rl.close();
12036
- resolve7(answer.trim().toLowerCase() === "yes");
12036
+ resolve8(answer.trim().toLowerCase() === "yes");
12037
12037
  });
12038
12038
  });
12039
12039
  }
@@ -12286,7 +12286,7 @@ async function submitToChannel(role, payload, opts = {}) {
12286
12286
  const port = opts.port ?? CHANNEL_PORT;
12287
12287
  const startedAt = Date.now();
12288
12288
  try {
12289
- const result = await new Promise((resolve7, reject) => {
12289
+ const result = await new Promise((resolve8, reject) => {
12290
12290
  const req = httpRequest({
12291
12291
  host: CHANNEL_HOST,
12292
12292
  port,
@@ -12312,7 +12312,7 @@ async function submitToChannel(role, payload, opts = {}) {
12312
12312
  reject(new LocalCCError(parsed.error));
12313
12313
  return;
12314
12314
  }
12315
- resolve7(String(parsed.result ?? ""));
12315
+ resolve8(String(parsed.result ?? ""));
12316
12316
  } catch (err) {
12317
12317
  reject(new LocalCCError(`malformed channel response: ${text.slice(0, 200)}`, err));
12318
12318
  }
@@ -12338,14 +12338,14 @@ async function submitToChannel(role, payload, opts = {}) {
12338
12338
  }
12339
12339
  }
12340
12340
  function isChannelAvailable(port = CHANNEL_PORT, timeoutMs = 500) {
12341
- return new Promise((resolve7) => {
12341
+ return new Promise((resolve8) => {
12342
12342
  const sock = connect(port, CHANNEL_HOST);
12343
12343
  const done = (ok) => {
12344
12344
  try {
12345
12345
  sock.destroy();
12346
12346
  } catch {
12347
12347
  }
12348
- resolve7(ok);
12348
+ resolve8(ok);
12349
12349
  };
12350
12350
  sock.once("connect", () => done(true));
12351
12351
  sock.once("error", () => done(false));
@@ -12377,10 +12377,10 @@ __export(grade_exports, {
12377
12377
  gradeCommand: () => gradeCommand
12378
12378
  });
12379
12379
  async function readStdin() {
12380
- return new Promise((resolve7, reject) => {
12380
+ return new Promise((resolve8, reject) => {
12381
12381
  const chunks = [];
12382
12382
  process.stdin.on("data", (c) => chunks.push(c));
12383
- process.stdin.on("end", () => resolve7(Buffer.concat(chunks).toString("utf-8")));
12383
+ process.stdin.on("end", () => resolve8(Buffer.concat(chunks).toString("utf-8")));
12384
12384
  process.stdin.on("error", reject);
12385
12385
  });
12386
12386
  }
@@ -12634,7 +12634,7 @@ function spawnClaudeJudge(file, claudeToken, promptHeader) {
12634
12634
  Diff:
12635
12635
  ${hunks}`;
12636
12636
  const fullPrompt = promptHeader + userMessage;
12637
- return new Promise((resolve7) => {
12637
+ return new Promise((resolve8) => {
12638
12638
  const t0 = Date.now();
12639
12639
  const proc = spawn6(
12640
12640
  "claude",
@@ -12662,7 +12662,7 @@ ${hunks}`;
12662
12662
  const latencyMs = Date.now() - t0;
12663
12663
  if (code !== 0) {
12664
12664
  console.warn(` claude exited ${code}: ${(stderr || stdout).slice(0, 500)}`);
12665
- resolve7({ findings: [], latencyMs });
12665
+ resolve8({ findings: [], latencyMs });
12666
12666
  return;
12667
12667
  }
12668
12668
  try {
@@ -12681,10 +12681,10 @@ ${hunks}`;
12681
12681
  description: f.description,
12682
12682
  fix: f.fix
12683
12683
  }));
12684
- resolve7({ findings, latencyMs });
12684
+ resolve8({ findings, latencyMs });
12685
12685
  } catch (parseErr) {
12686
12686
  console.warn(` failed to parse claude response: ${stdout.slice(0, 300)}`);
12687
- resolve7({ findings: [], latencyMs });
12687
+ resolve8({ findings: [], latencyMs });
12688
12688
  }
12689
12689
  });
12690
12690
  });
@@ -12733,7 +12733,7 @@ ${JSON.stringify(findings, null, 2)}
12733
12733
  `;
12734
12734
  }
12735
12735
  function spawnOpusConsolidator(findings, claudeToken) {
12736
- return new Promise((resolve7) => {
12736
+ return new Promise((resolve8) => {
12737
12737
  const prompt = buildConsolidationPrompt(findings);
12738
12738
  const proc = spawn6(
12739
12739
  "claude",
@@ -12760,7 +12760,7 @@ function spawnOpusConsolidator(findings, claudeToken) {
12760
12760
  proc.on("close", (code) => {
12761
12761
  if (code !== 0) {
12762
12762
  console.warn(` opus consolidation exited ${code}: ${(stderr || stdout).slice(0, 300)}`);
12763
- resolve7(fallbackReview(findings));
12763
+ resolve8(fallbackReview(findings));
12764
12764
  return;
12765
12765
  }
12766
12766
  try {
@@ -12781,10 +12781,10 @@ function spawnOpusConsolidator(findings, claudeToken) {
12781
12781
  const order = ["low", "medium", "high", "critical"];
12782
12782
  return order.indexOf(f.severity) > order.indexOf(max) ? f.severity : max;
12783
12783
  }, "low");
12784
- resolve7({ summary: review.summary || "", comments, severity: maxSeverity });
12784
+ resolve8({ summary: review.summary || "", comments, severity: maxSeverity });
12785
12785
  } catch {
12786
12786
  console.warn(` failed to parse opus response, using fallback`);
12787
- resolve7(fallbackReview(findings));
12787
+ resolve8(fallbackReview(findings));
12788
12788
  }
12789
12789
  });
12790
12790
  });
@@ -13267,14 +13267,14 @@ function ensureRunning(opts = {}) {
13267
13267
  return startTask(opts);
13268
13268
  }
13269
13269
  function probePort(host, port, timeoutMs = 500) {
13270
- return new Promise((resolve7) => {
13270
+ return new Promise((resolve8) => {
13271
13271
  const sock = connect2(port, host);
13272
13272
  const done = (ok) => {
13273
13273
  try {
13274
13274
  sock.destroy();
13275
13275
  } catch {
13276
13276
  }
13277
- resolve7(ok);
13277
+ resolve8(ok);
13278
13278
  };
13279
13279
  sock.once("connect", () => done(true));
13280
13280
  sock.once("error", () => done(false));
@@ -13824,7 +13824,7 @@ function cmdLogs(rest) {
13824
13824
  if (!raw) console.log(" " + colorize("(use --raw / -r to see full payloads, --live / -f to follow)", 90));
13825
13825
  return;
13826
13826
  }
13827
- return new Promise((resolve7) => {
13827
+ return new Promise((resolve8) => {
13828
13828
  console.log(" " + colorize("\u2014 following new turns (Ctrl-C to exit) \u2014", 90));
13829
13829
  const stop = followTurns((t) => {
13830
13830
  console.log(" " + formatTurn(t, raw));
@@ -13832,7 +13832,7 @@ function cmdLogs(rest) {
13832
13832
  const onSigint = () => {
13833
13833
  stop();
13834
13834
  process.removeListener("SIGINT", onSigint);
13835
- resolve7();
13835
+ resolve8();
13836
13836
  };
13837
13837
  process.on("SIGINT", onSigint);
13838
13838
  });
@@ -14121,9 +14121,9 @@ function parseSession(file, seenStableIds) {
14121
14121
  }
14122
14122
  function ask2(q) {
14123
14123
  const rl = createInterface4({ input: process.stdin, output: process.stdout });
14124
- return new Promise((resolve7) => rl.question(q, (a) => {
14124
+ return new Promise((resolve8) => rl.question(q, (a) => {
14125
14125
  rl.close();
14126
- resolve7(/^y(es)?$/i.test(a.trim()));
14126
+ resolve8(/^y(es)?$/i.test(a.trim()));
14127
14127
  }));
14128
14128
  }
14129
14129
  async function importCommand() {
@@ -14650,6 +14650,7 @@ function buildSpawnAgent(opts) {
14650
14650
  // and its own history scrolls — without it only the last screen is
14651
14651
  // reachable once the session is nested inside the layout.
14652
14652
  ["tmux", "set-option", "-t", session, "mouse", "on"],
14653
+ ...buildClipboardBindings(session),
14653
14654
  ["tmux", "set-option", "-t", session, "-q", "@synkro_harness", opts.harness],
14654
14655
  ["tmux", "set-option", "-t", session, "-q", "@synkro_space", opts.space],
14655
14656
  ["tmux", "set-option", "-t", session, "-q", "@synkro_backend", opts.backend],
@@ -14658,6 +14659,13 @@ function buildSpawnAgent(opts) {
14658
14659
  ["tmux", "set-option", "-t", session, "remain-on-exit", "on"]
14659
14660
  ];
14660
14661
  }
14662
+ function buildClipboardBindings(session) {
14663
+ return [
14664
+ ["tmux", "set-option", "-t", session, "set-clipboard", "on"],
14665
+ ["tmux", "bind-key", "-T", "copy-mode", "MouseDragEnd1Pane", "send-keys", "-X", "copy-pipe-and-cancel", SYSTEM_CLIPBOARD],
14666
+ ["tmux", "bind-key", "-T", "copy-mode-vi", "MouseDragEnd1Pane", "send-keys", "-X", "copy-pipe-and-cancel", SYSTEM_CLIPBOARD]
14667
+ ];
14668
+ }
14661
14669
  function buildListAgents() {
14662
14670
  return ["tmux", "list-sessions", "-F", ["#{session_name}", "#{?pane_dead,dead,alive}", "#{@synkro_harness}", "#{@synkro_space}", "#{@synkro_backend}", "#{@synkro_pueue}", "#{pane_current_path}"].join(FIELD_SEP)];
14663
14671
  }
@@ -14701,6 +14709,9 @@ function buildSendText(session, text) {
14701
14709
  function buildEnableMouse(session) {
14702
14710
  return ["tmux", "set-option", "-t", session, "mouse", "on"];
14703
14711
  }
14712
+ function buildKeepPaneAfterExit(pane) {
14713
+ return ["tmux", "set-option", "-p", "-t", pane, "remain-on-exit", "on"];
14714
+ }
14704
14715
  function buildInterrupt(session) {
14705
14716
  return ["tmux", "send-keys", "-t", session, "Escape"];
14706
14717
  }
@@ -14767,7 +14778,7 @@ function parseWorktrees(porcelain) {
14767
14778
  }
14768
14779
  return rows;
14769
14780
  }
14770
- var execFileAsync, AGENT_PREFIX, UI_SESSION, CONTAINER_USER, FIELD_SEP;
14781
+ var execFileAsync, AGENT_PREFIX, UI_SESSION, CONTAINER_USER, SYSTEM_CLIPBOARD, FIELD_SEP;
14771
14782
  var init_tmux = __esm({
14772
14783
  "cli/ui/tmux.ts"() {
14773
14784
  "use strict";
@@ -14775,6 +14786,7 @@ var init_tmux = __esm({
14775
14786
  AGENT_PREFIX = "synkro-agent-";
14776
14787
  UI_SESSION = "synkro-ui";
14777
14788
  CONTAINER_USER = "synkro";
14789
+ SYSTEM_CLIPBOARD = "if command -v pbcopy >/dev/null 2>&1; then pbcopy; elif command -v wl-copy >/dev/null 2>&1; then wl-copy; elif command -v xclip >/dev/null 2>&1; then xclip -selection clipboard; else cat >/dev/null; fi";
14778
14790
  FIELD_SEP = "|";
14779
14791
  }
14780
14792
  });
@@ -14907,6 +14919,10 @@ function visibleAgents(all, space, filter, grouped) {
14907
14919
  if (grouped || filter === "all" || !space) return all;
14908
14920
  return all.filter((agent) => agent.repo === space.path);
14909
14921
  }
14922
+ function nextLiveAgent(all, preferredRepo = "") {
14923
+ const live = all.filter((agent) => agent.status !== "offline" && agent.status !== "done");
14924
+ return live.find((agent) => Boolean(preferredRepo) && agent.repo === preferredRepo) || live[0];
14925
+ }
14910
14926
  async function discoverAgents(runner, backend, memory) {
14911
14927
  const snapshot = await run(runner, buildAgentSnapshot());
14912
14928
  const { list, captures } = parseAgentSnapshot(snapshot.ok ? snapshot.stdout : "");
@@ -15172,6 +15188,7 @@ async function styleOuterSession(bootPath, repoCwd, sidebarWidth) {
15172
15188
  ["bind-key", "-n", "M-s", "select-pane", "-L"]
15173
15189
  ];
15174
15190
  for (const argv of style) await run(HOST, ["tmux", ...argv]);
15191
+ for (const argv of buildClipboardBindings(UI_SESSION)) await run(HOST, argv);
15175
15192
  }
15176
15193
  async function buildTab(bootPath, repoCwd, spec) {
15177
15194
  await run(HOST, ["tmux", "set-option", "-g", "history-limit", "50000"]);
@@ -15214,6 +15231,7 @@ async function buildTab(bootPath, repoCwd, spec) {
15214
15231
  const sidebarPane = split.stdout.trim();
15215
15232
  const panes = await run(HOST, ["tmux", "list-panes", "-t", windowTarget, "-F", "#{pane_id}"]);
15216
15233
  const centerPane = panes.stdout.split("\n").map((line) => line.trim()).filter(Boolean).find((id) => id !== sidebarPane) || "";
15234
+ await run(HOST, buildKeepPaneAfterExit(centerPane));
15217
15235
  if (spec.agentSession) {
15218
15236
  await run(HOST, ["tmux", "set-option", "-t", windowTarget, "-w", "-q", "@synkro_agent", spec.agentSession]);
15219
15237
  }
@@ -15593,6 +15611,9 @@ async function detectContainerBackend() {
15593
15611
  }
15594
15612
  return { runner: { kind: "host" }, backend: "host", note: "runtime: host (container unavailable)" };
15595
15613
  }
15614
+ function harnessCommand(request) {
15615
+ return request.resume ? resumeCommand(request.harness) : HARNESS_COMMANDS[request.harness] || "claude";
15616
+ }
15596
15617
  async function provisionContainerWorkspace(runner, slug) {
15597
15618
  const dir = CONTAINER_WORK + "/ui-" + slug;
15598
15619
  await run(runner, [
@@ -15610,7 +15631,7 @@ async function spawnAgent(info, request) {
15610
15631
  if (request.backend === "container") {
15611
15632
  cwd = request.cwd.startsWith(CONTAINER_WORK) ? request.cwd : await provisionContainerWorkspace(runner, slug);
15612
15633
  }
15613
- const command = request.resume ? resumeCommand(request.harness) : HARNESS_COMMANDS[request.harness] || "claude";
15634
+ const command = harnessCommand(request);
15614
15635
  for (const argv of buildSpawnAgent({ name: slug, cwd, command, harness: request.harness, space: cwd, backend: request.backend })) {
15615
15636
  const result = await run(runner, argv);
15616
15637
  if (!result.ok && argv[1] === "new-session") {
@@ -15901,6 +15922,24 @@ async function runSidebar() {
15901
15922
  const agent = state.agents[state.agentIndex];
15902
15923
  if (agent) await showAgent(agent);
15903
15924
  }
15925
+ async function showWorkspaceTerminal(cwd) {
15926
+ if (!centerPane || !cwd) return;
15927
+ const command = makeTerminalCommand(bootPath);
15928
+ const argv = ["tmux", "respawn-pane", "-k", "-t", centerPane, "-c", cwd];
15929
+ if (command) argv.push(command);
15930
+ const respawned = await run(host, argv);
15931
+ if (!respawned.ok) {
15932
+ state.message = "could not restore workspace terminal";
15933
+ return;
15934
+ }
15935
+ const spaceName = cwd.split("/").filter(Boolean).pop() || "space";
15936
+ await run(host, ["tmux", "set-option", "-t", centerPane, "-w", "-u", "@synkro_agent"]);
15937
+ await run(host, ["tmux", "set-option", "-t", centerPane, "-w", "-q", "@synkro_cwd", cwd]);
15938
+ await run(host, ["tmux", "set-option", "-t", centerPane, "-w", "-q", "@synkro_kind", "terminal"]);
15939
+ await run(host, ["tmux", "rename-window", "-t", centerPane, tabTitle("terminal", spaceName)]);
15940
+ state.viewing = "";
15941
+ await snapshotTabs();
15942
+ }
15904
15943
  async function showAgent(agent) {
15905
15944
  if (!centerPane) return;
15906
15945
  if (agent.status === "offline") {
@@ -15919,6 +15958,9 @@ async function runSidebar() {
15919
15958
  return;
15920
15959
  }
15921
15960
  await run(runnerFor(agent.backend), buildEnableMouse(agent.session));
15961
+ for (const argv of buildClipboardBindings(agent.session)) {
15962
+ await run(runnerFor(agent.backend), argv);
15963
+ }
15922
15964
  const command = buildCenterAttachCommand(runnerFor(agent.backend), agent.session);
15923
15965
  await run(host, ["tmux", "respawn-pane", "-k", "-t", centerPane, command]);
15924
15966
  const where = agent.space ? agent.space.split("/").filter(Boolean).pop() || "" : "";
@@ -15931,7 +15973,15 @@ async function runSidebar() {
15931
15973
  async function confirmKillAgent() {
15932
15974
  const agent = state.agents[state.agentIndex];
15933
15975
  if (!agent) return;
15976
+ const wasViewing = state.viewing === agent.session;
15934
15977
  await openDialog("kill", [agent.backend, agent.session]);
15978
+ await refresh();
15979
+ if (allAgents.some((candidate) => candidate.session === agent.session && candidate.status !== "offline")) return;
15980
+ state.message = "closed " + agent.name;
15981
+ if (!wasViewing) return;
15982
+ await showWorkspaceTerminal(agent.space || selectedSpace()?.path || repoCwd);
15983
+ const successor = nextLiveAgent(allAgents, agent.repo || "");
15984
+ if (successor) await showAgent(successor);
15935
15985
  }
15936
15986
  async function consent(action) {
15937
15987
  const agent = state.agents[state.agentIndex];
@@ -15954,6 +16004,7 @@ async function runSidebar() {
15954
16004
  const remembered = space ? lastAgentFor(space.path) : "";
15955
16005
  const agent = allAgents.find((row2) => row2.session === remembered && row2.status !== "offline");
15956
16006
  if (agent) await showAgent(agent);
16007
+ else if (space) await showWorkspaceTerminal(space.path);
15957
16008
  } else if (target.kind === "agent") {
15958
16009
  state.section = "agents";
15959
16010
  state.agentIndex = target.index;
@@ -16155,6 +16206,9 @@ var init_repos = __esm({
16155
16206
 
16156
16207
  // cli/ui/tabs.ts
16157
16208
  import { execSync as execSync7 } from "child_process";
16209
+ function isLegacyCodexRenderer(command) {
16210
+ return /(?:^|\s)ui\s+--run\s+codex(?:\s|$)/.test(String(command || "").trim());
16211
+ }
16158
16212
  function repoRoot() {
16159
16213
  try {
16160
16214
  return execSync7("git rev-parse --show-toplevel", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim() || process.cwd();
@@ -16175,16 +16229,6 @@ async function createTab(bootPath, kind, spacePath) {
16175
16229
  });
16176
16230
  return;
16177
16231
  }
16178
- if (kind === "cursor-synkro") {
16179
- await buildTab(bootPath, repo, {
16180
- cwd: spacePath,
16181
- center: ["node", bootPath, "ui", "--run", "cursor", spacePath].map(shellQuote3).join(" "),
16182
- title: tabTitle("cursor", space),
16183
- kind: "cursor-synkro",
16184
- focus: "center"
16185
- });
16186
- return;
16187
- }
16188
16232
  if (kind === "settings") {
16189
16233
  await buildTab(bootPath, repo, {
16190
16234
  cwd: spacePath,
@@ -16195,12 +16239,13 @@ async function createTab(bootPath, kind, spacePath) {
16195
16239
  });
16196
16240
  return;
16197
16241
  }
16242
+ if (!["claude", "codex", "cursor"].includes(kind)) return;
16198
16243
  const info = await detectContainerBackend();
16199
16244
  const spaceName = space;
16200
16245
  const stamp = String(process.pid % 1e4);
16201
- const harness = ["claude", "codex", "cursor"].includes(kind) ? kind : "claude";
16246
+ const harness = kind;
16202
16247
  const reachable = info.backend === "container" && (await run(info.runner, ["test", "-d", spacePath])).ok;
16203
- const backend = reachable ? "container" : "host";
16248
+ const backend = harness === "codex" ? "host" : reachable ? "container" : "host";
16204
16249
  const spawned = await spawnAgent(info, {
16205
16250
  name: spaceName + "-" + harness + "-" + stamp,
16206
16251
  harness,
@@ -16231,16 +16276,34 @@ async function openAgentTab(bootPath, session) {
16231
16276
  ]);
16232
16277
  const [harness, space, backend] = (meta.stdout.trim() || "||").split("|");
16233
16278
  const containerHosted = backend === "container";
16234
- const runner = containerHosted ? info.runner : HOST2;
16235
- const alive = (await run(runner, ["tmux", "has-session", "-t", session])).ok;
16236
- if (!alive) return;
16279
+ let runner = containerHosted ? info.runner : HOST2;
16280
+ let activeSession = session;
16237
16281
  const spaceName = (space || "").split("/").filter(Boolean).pop() || "space";
16282
+ if (harness === "codex") {
16283
+ const command = await run(HOST2, ["tmux", "display-message", "-p", "-t", session, "#{pane_start_command}"]);
16284
+ if (command.ok && isLegacyCodexRenderer(command.stdout)) {
16285
+ await run(HOST2, buildKillSession(session));
16286
+ const restored = await spawnAgent(info, {
16287
+ name: session.replace(/^synkro-agent-/, ""),
16288
+ harness: "codex",
16289
+ spaceName,
16290
+ cwd: space || repoRoot(),
16291
+ backend: "host",
16292
+ resume: true
16293
+ });
16294
+ if (!restored.ok) return;
16295
+ activeSession = restored.session;
16296
+ runner = HOST2;
16297
+ }
16298
+ }
16299
+ const alive = (await run(runner, ["tmux", "has-session", "-t", activeSession])).ok;
16300
+ if (!alive) return;
16238
16301
  await buildTab(bootPath, repoRoot(), {
16239
16302
  cwd: space || repoRoot(),
16240
- center: buildCenterAttachCommand(runner, session),
16303
+ center: buildCenterAttachCommand(runner, activeSession),
16241
16304
  title: tabTitle(harness || "claude", spaceName),
16242
16305
  kind: harness || "claude",
16243
- agentSession: session,
16306
+ agentSession: activeSession,
16244
16307
  focus: "center"
16245
16308
  });
16246
16309
  }
@@ -16256,11 +16319,23 @@ async function restoreTabs(bootPath, repoCwd) {
16256
16319
  const space = tab.cwd.split("/").filter(Boolean).pop() || "space";
16257
16320
  const containerHosted = info.backend === "container" && (await run(info.runner, ["test", "-d", tab.cwd])).ok;
16258
16321
  const harness = ["claude", "codex", "cursor"].includes(tab.kind) ? tab.kind : "";
16259
- if (tab.agentSession && alive.has(tab.agentSession)) {
16260
- await run(containerHosted ? info.runner : HOST2, buildEnableMouse(tab.agentSession));
16322
+ const sessionRunner = harness === "codex" ? HOST2 : containerHosted ? info.runner : HOST2;
16323
+ let sessionAlive = Boolean(tab.agentSession && alive.has(tab.agentSession));
16324
+ if (sessionAlive && harness === "codex") {
16325
+ const command = await run(HOST2, ["tmux", "display-message", "-p", "-t", tab.agentSession, "#{pane_start_command}"]);
16326
+ if (command.ok && isLegacyCodexRenderer(command.stdout)) {
16327
+ await run(HOST2, buildKillSession(tab.agentSession));
16328
+ sessionAlive = false;
16329
+ }
16330
+ }
16331
+ if (tab.agentSession && sessionAlive) {
16332
+ await run(sessionRunner, buildEnableMouse(tab.agentSession));
16333
+ for (const argv of buildClipboardBindings(tab.agentSession)) {
16334
+ await run(sessionRunner, argv);
16335
+ }
16261
16336
  await buildTab(bootPath, repoCwd, {
16262
16337
  cwd: tab.cwd,
16263
- center: buildCenterAttachCommand(containerHosted ? info.runner : HOST2, tab.agentSession),
16338
+ center: buildCenterAttachCommand(sessionRunner, tab.agentSession),
16264
16339
  title: tab.title,
16265
16340
  kind: tab.kind,
16266
16341
  agentSession: tab.agentSession,
@@ -16272,12 +16347,13 @@ async function restoreTabs(bootPath, repoCwd) {
16272
16347
  harness,
16273
16348
  spaceName: space,
16274
16349
  cwd: tab.cwd,
16275
- backend: containerHosted ? "container" : "host",
16350
+ backend: harness === "codex" ? "host" : containerHosted ? "container" : "host",
16276
16351
  resume: true
16277
16352
  });
16353
+ const restoredInContainer = harness !== "codex" && containerHosted;
16278
16354
  await buildTab(bootPath, repoCwd, spawned.ok ? {
16279
16355
  cwd: tab.cwd,
16280
- center: buildCenterAttachCommand(containerHosted ? info.runner : HOST2, spawned.session),
16356
+ center: buildCenterAttachCommand(restoredInContainer ? info.runner : HOST2, spawned.session),
16281
16357
  title: tabTitle(harness, space),
16282
16358
  kind: harness,
16283
16359
  agentSession: spawned.session,
@@ -16370,7 +16446,7 @@ async function pick(opts) {
16370
16446
  const perItem = opts.choices.some((choice) => choice.detail) ? 2 : 1;
16371
16447
  const visibleItems = Math.max(1, Math.floor(Math.max(2, height - 5) / perItem));
16372
16448
  const firstRow = 4;
16373
- return new Promise((resolve7) => {
16449
+ return new Promise((resolve8) => {
16374
16450
  const view = () => opts.choices.filter((choice) => matches(choice, filter));
16375
16451
  const draw = () => {
16376
16452
  const shown = view();
@@ -16418,7 +16494,7 @@ async function pick(opts) {
16418
16494
  const hit = shown.findIndex((_, index) => y >= rowOfItem(index) && y < rowOfItem(index) + perItem);
16419
16495
  if (hit >= 0) {
16420
16496
  process.stdin.off("data", onData);
16421
- resolve7(shown[hit].value);
16497
+ resolve8(shown[hit].value);
16422
16498
  return;
16423
16499
  }
16424
16500
  }
@@ -16426,13 +16502,13 @@ async function pick(opts) {
16426
16502
  if (!sawMouse) {
16427
16503
  if (input === KEY_ESC && !input.includes("[<") || input === KEY_CTRL_C2) {
16428
16504
  process.stdin.off("data", onData);
16429
- resolve7(null);
16505
+ resolve8(null);
16430
16506
  return;
16431
16507
  }
16432
16508
  if (input === "\r") {
16433
16509
  if (shown.length === 0) return;
16434
16510
  process.stdin.off("data", onData);
16435
- resolve7(shown[selected].value);
16511
+ resolve8(shown[selected].value);
16436
16512
  return;
16437
16513
  }
16438
16514
  const down = input === CSI2 + "B" || opts.menu && (input === "j" || input === CSI2 + "C");
@@ -16456,7 +16532,7 @@ async function readLine(opts) {
16456
16532
  let value = "";
16457
16533
  let error = "";
16458
16534
  const width = Math.max(20, Number(process.stdout.columns || 80));
16459
- return new Promise((resolve7) => {
16535
+ return new Promise((resolve8) => {
16460
16536
  const draw = () => {
16461
16537
  const lines = [
16462
16538
  " " + S.title + opts.title + S.reset,
@@ -16475,7 +16551,7 @@ async function readLine(opts) {
16475
16551
  const input = chunk.toString("utf8");
16476
16552
  if (input === KEY_ESC || input === KEY_CTRL_C2) {
16477
16553
  process.stdin.off("data", onData);
16478
- resolve7(null);
16554
+ resolve8(null);
16479
16555
  return;
16480
16556
  }
16481
16557
  if (input === "\r") {
@@ -16487,7 +16563,7 @@ async function readLine(opts) {
16487
16563
  return;
16488
16564
  }
16489
16565
  process.stdin.off("data", onData);
16490
- resolve7(value.trim());
16566
+ resolve8(value.trim());
16491
16567
  })();
16492
16568
  return;
16493
16569
  }
@@ -16754,9 +16830,6 @@ async function runDialog(kind, repoCwd, argA = "", argB = "") {
16754
16830
  label: (TAB_GLYPHS[harness] || "") + " " + (HARNESS_LABELS[harness] || harness)
16755
16831
  }))
16756
16832
  ];
16757
- if (harnesses.includes("cursor")) {
16758
- sessions.push({ value: "cursor-synkro", label: TAB_GLYPHS.cursor + " Cursor in Synkro UX" });
16759
- }
16760
16833
  for (; ; ) {
16761
16834
  const session = await pick({
16762
16835
  menu: true,
@@ -16815,21 +16888,418 @@ var init_dialog = __esm({
16815
16888
  }
16816
16889
  });
16817
16890
 
16891
+ // cli/harness/render.ts
16892
+ function spinnerFrame(tick) {
16893
+ return SPINNER[Math.abs(tick) % SPINNER.length];
16894
+ }
16895
+ function terminalText(text) {
16896
+ return String(text).replace(/[\u0000-\u001F\u007F-\u009F]/g, "");
16897
+ }
16898
+ function clip3(text, max) {
16899
+ const value = String(text || "").replace(/\s+/g, " ").trim();
16900
+ if (max <= 1) return value;
16901
+ return value.length <= max ? value : value.slice(0, max - 1) + "\u2026";
16902
+ }
16903
+ function seconds(ms) {
16904
+ const total = Math.max(0, Math.round(ms / 1e3));
16905
+ if (total < 60) return total + "s";
16906
+ return Math.floor(total / 60) + "m" + String(total % 60).padStart(2, "0") + "s";
16907
+ }
16908
+ function statusLine(opts) {
16909
+ const hint = opts.hint ? " \xB7 " + opts.hint : "";
16910
+ const body = opts.text + " (" + seconds(opts.elapsedMs) + hint + ")";
16911
+ const width = Math.max(20, (opts.width || 100) - 4);
16912
+ return S2.think + " " + spinnerFrame(opts.tick) + " " + clip3(body, width) + S2.reset;
16913
+ }
16914
+ function renderEvent(event, width = 100) {
16915
+ const body = Math.max(30, width - 4);
16916
+ switch (event.type) {
16917
+ // The workspace and both accounts are already in the session header, so
16918
+ // this line carries only what the header could not know before the harness
16919
+ // started: which model answered, and whether a subscription or a key paid.
16920
+ case "session-start":
16921
+ return [
16922
+ "",
16923
+ S2.dim + " " + clip3(event.model, body - 20) + (event.authSource === "login" ? " \xB7 subscription" : " \xB7 api key") + S2.reset,
16924
+ ""
16925
+ ];
16926
+ case "user-message":
16927
+ return ["", S2.user + " \u276F " + S2.reset + S2.bold + clip3(event.text, body) + S2.reset, ""];
16928
+ // Live state, not transcript. The runner promotes these to the status line.
16929
+ case "thinking":
16930
+ return [];
16931
+ case "assistant-message": {
16932
+ const lines = wrap(event.text, body - 2);
16933
+ return [
16934
+ "",
16935
+ ...lines.map((line, index) => index === 0 ? S2.agent + " " + BULLET + " " + line + S2.reset : S2.agent + " " + line + S2.reset),
16936
+ ""
16937
+ ];
16938
+ }
16939
+ case "tool-start": {
16940
+ const label = TOOL_LABEL[event.kind] || TOOL_LABEL.other;
16941
+ return [
16942
+ S2.tool + " " + BULLET + " " + label + S2.reset + S2.dim + "(" + clip3(event.target, body - label.length - 8) + ")" + S2.reset
16943
+ ];
16944
+ }
16945
+ case "tool-end": {
16946
+ if (event.blocked) {
16947
+ return [
16948
+ S2.blocked + " " + BULLET + " Blocked" + S2.reset + S2.dim + " " + clip3(terminalText(event.target), body - 14) + S2.reset,
16949
+ ...wrap(terminalText(event.reason), body - 6).map((line, index) => index === 0 ? S2.blocked + " " + ELBOW + " " + S2.reset + S2.rule + line + S2.reset : S2.rule + " " + line + S2.reset)
16950
+ ];
16951
+ }
16952
+ const detail = event.ok ? event.output.trim() ? clip3(terminalText(event.output), body - 10) : "done" : "failed" + (event.exitCode === null ? "" : " (exit " + event.exitCode + ")");
16953
+ const tint = event.ok ? S2.ok : S2.blocked;
16954
+ return [tint + " " + ELBOW + " " + S2.reset + S2.dim + detail + S2.reset];
16955
+ }
16956
+ // A clean finish needs no announcement: the prompt returning IS the signal.
16957
+ case "turn-end":
16958
+ return event.ok ? [] : ["", S2.blocked + " " + BULLET + " turn ended with an error" + S2.reset, ""];
16959
+ case "notice":
16960
+ return [S2.dim + " " + clip3(event.text, body) + S2.reset];
16961
+ default:
16962
+ return [];
16963
+ }
16964
+ }
16965
+ function wrap(text, width) {
16966
+ const words = String(text || "").replace(/\s+/g, " ").trim().split(" ").filter(Boolean);
16967
+ if (words.length === 0) return [];
16968
+ const lines = [];
16969
+ let line = "";
16970
+ for (const word of words) {
16971
+ if (!line) line = word;
16972
+ else if ((line + " " + word).length <= width) line += " " + word;
16973
+ else {
16974
+ lines.push(line);
16975
+ line = word;
16976
+ }
16977
+ }
16978
+ if (line) lines.push(line);
16979
+ return lines;
16980
+ }
16981
+ var ESC2, S2, CLEAR_LINE, BULLET, ELBOW, SPINNER, TOOL_LABEL;
16982
+ var init_render2 = __esm({
16983
+ "cli/harness/render.ts"() {
16984
+ "use strict";
16985
+ ESC2 = "\x1B[";
16986
+ S2 = {
16987
+ reset: ESC2 + "0m",
16988
+ dim: ESC2 + "2m",
16989
+ bold: ESC2 + "1m",
16990
+ user: ESC2 + "38;5;111m",
16991
+ agent: ESC2 + "38;5;252m",
16992
+ think: ESC2 + "38;5;244m",
16993
+ tool: ESC2 + "38;5;180m",
16994
+ ok: ESC2 + "38;5;114m",
16995
+ blocked: ESC2 + "38;5;203m",
16996
+ rule: ESC2 + "38;5;211m"
16997
+ };
16998
+ CLEAR_LINE = "\r" + ESC2 + "2K";
16999
+ BULLET = "\u23FA";
17000
+ ELBOW = "\u23BF";
17001
+ SPINNER = ["\xB7", "\u2722", "\u2733", "\u2217", "\u273B", "\u273D"];
17002
+ TOOL_LABEL = {
17003
+ shell: "Shell",
17004
+ read: "Read",
17005
+ edit: "Edit",
17006
+ write: "Write",
17007
+ delete: "Delete",
17008
+ search: "Search",
17009
+ list: "List",
17010
+ todo: "Todo",
17011
+ other: "Tool"
17012
+ };
17013
+ }
17014
+ });
17015
+
17016
+ // cli/harness/composer.ts
17017
+ import { execFileSync as execFileSync5, spawnSync as spawnSync12 } from "child_process";
17018
+ import { existsSync as existsSync38, mkdtempSync as mkdtempSync2, rmSync as rmSync7, statSync as statSync5, writeFileSync as writeFileSync27 } from "fs";
17019
+ import { homedir as homedir38, tmpdir } from "os";
17020
+ import { basename as basename3, dirname as dirname12, extname, isAbsolute as isAbsolute2, join as join37, resolve as resolve5, sep as sep3 } from "path";
17021
+ import { createInterface as createInterface5 } from "readline";
17022
+ function markerCarryLength(input, marker) {
17023
+ for (let length = Math.min(input.length, marker.length - 1); length > 0; length -= 1) {
17024
+ if (marker.startsWith(input.slice(-length))) return length;
17025
+ }
17026
+ return 0;
17027
+ }
17028
+ function normalizePathToken(raw) {
17029
+ let value = raw.trim();
17030
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
17031
+ value = value.slice(1, -1);
17032
+ }
17033
+ value = value.replace(/\\([\\ "'()])/g, "$1");
17034
+ if (value === "~") return homedir38();
17035
+ if (value.startsWith("~/")) return join37(homedir38(), value.slice(2));
17036
+ return value;
17037
+ }
17038
+ function imagePathsInText(text, cwd) {
17039
+ const candidates = String(text || "").match(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|(?:\\.|[^\s])+/g) || [];
17040
+ const images = [];
17041
+ for (const candidate of candidates) {
17042
+ const token = normalizePathToken(candidate);
17043
+ if (!IMAGE_EXTENSIONS.has(extname(token).toLowerCase())) continue;
17044
+ const path = isAbsolute2(token) ? token : resolve5(cwd, token);
17045
+ try {
17046
+ if (statSync5(path).isFile() && !images.includes(path)) images.push(path);
17047
+ } catch {
17048
+ }
17049
+ }
17050
+ return images;
17051
+ }
17052
+ function codexUserInput(draft) {
17053
+ const input = [];
17054
+ if (draft.text.trim()) input.push({ type: "text", text: draft.text, text_elements: [] });
17055
+ for (const path of draft.images) input.push({ type: "localImage", path });
17056
+ return input;
17057
+ }
17058
+ function clipboardText() {
17059
+ try {
17060
+ if (process.platform === "darwin") return execFileSync5("pbpaste", [], { encoding: "utf8", timeout: 1500 });
17061
+ const wayland = spawnSync12("wl-paste", ["--no-newline"], { encoding: "utf8", timeout: 1500 });
17062
+ if (wayland.status === 0) return String(wayland.stdout || "");
17063
+ const x11 = spawnSync12("xclip", ["-selection", "clipboard", "-o"], { encoding: "utf8", timeout: 1500 });
17064
+ return x11.status === 0 ? String(x11.stdout || "") : "";
17065
+ } catch {
17066
+ return "";
17067
+ }
17068
+ }
17069
+ function macClipboardPng(target) {
17070
+ const script = [
17071
+ "on run argv",
17072
+ "set outputPath to item 1 of argv",
17073
+ "set imageData to the clipboard as \xABclass PNGf\xBB",
17074
+ "set outputFile to open for access POSIX file outputPath with write permission",
17075
+ "set eof outputFile to 0",
17076
+ "write imageData to outputFile",
17077
+ "close access outputFile",
17078
+ "end run"
17079
+ ].join("\n");
17080
+ const result = spawnSync12("osascript", ["-e", script, target], { encoding: "utf8", timeout: 3e3 });
17081
+ return result.status === 0 && existsSync38(target);
17082
+ }
17083
+ function linuxClipboardPng(target) {
17084
+ const wayland = spawnSync12("wl-paste", ["--type", "image/png"], { encoding: null, timeout: 3e3 });
17085
+ if (wayland.status === 0 && Buffer.isBuffer(wayland.stdout) && wayland.stdout.length > 0) {
17086
+ writeFileSync27(target, wayland.stdout);
17087
+ return true;
17088
+ }
17089
+ const x11 = spawnSync12("xclip", ["-selection", "clipboard", "-t", "image/png", "-o"], { encoding: null, timeout: 3e3 });
17090
+ if (x11.status === 0 && Buffer.isBuffer(x11.stdout) && x11.stdout.length > 0) {
17091
+ writeFileSync27(target, x11.stdout);
17092
+ return true;
17093
+ }
17094
+ return false;
17095
+ }
17096
+ function clipboardImage() {
17097
+ const directory = mkdtempSync2(join37(tmpdir(), "synkro-paste-"));
17098
+ const target = join37(directory, "clipboard.png");
17099
+ try {
17100
+ const copied = process.platform === "darwin" ? macClipboardPng(target) : process.platform === "linux" && linuxClipboardPng(target);
17101
+ if (copied && statSync5(target).size > 0) return target;
17102
+ } catch {
17103
+ }
17104
+ rmSync7(directory, { recursive: true, force: true });
17105
+ return "";
17106
+ }
17107
+ function cleanupPromptDraft(draft) {
17108
+ for (const path of draft.temporaryImages) {
17109
+ const directory = dirname12(resolve5(path));
17110
+ const temporaryRoot = resolve5(tmpdir()) + sep3;
17111
+ if (!directory.startsWith(temporaryRoot) || !basename3(directory).startsWith("synkro-paste-")) continue;
17112
+ try {
17113
+ rmSync7(directory, { recursive: true, force: true });
17114
+ } catch {
17115
+ }
17116
+ }
17117
+ }
17118
+ function fallbackPrompt(cwd) {
17119
+ return new Promise((resolveDraft) => {
17120
+ const rl = createInterface5({ input: process.stdin, output: process.stdout });
17121
+ let settled = false;
17122
+ const finish = (value) => {
17123
+ if (settled) return;
17124
+ settled = true;
17125
+ rl.close();
17126
+ resolveDraft(value);
17127
+ };
17128
+ rl.once("close", () => finish(null));
17129
+ rl.once("SIGINT", () => finish(null));
17130
+ rl.question(S2.user + " \u276F " + S2.reset, (text) => {
17131
+ finish({ text, images: imagePathsInText(text, cwd), temporaryImages: [] });
17132
+ });
17133
+ });
17134
+ }
17135
+ function readPrompt(cwd) {
17136
+ const input = process.stdin;
17137
+ const output = process.stdout;
17138
+ if (!input.isTTY || !output.isTTY || !input.setRawMode) return fallbackPrompt(cwd);
17139
+ return new Promise((resolveDraft) => {
17140
+ let text = "";
17141
+ let cursor = 0;
17142
+ let stream = "";
17143
+ let pasting = false;
17144
+ let settled = false;
17145
+ const images = [];
17146
+ const temporaryImages = [];
17147
+ const wasRaw = Boolean(input.isRaw);
17148
+ const prompt = S2.user + " \u276F " + S2.reset;
17149
+ const redraw = () => {
17150
+ const safe = text.replace(/[\u0000-\u0008\u000B-\u001F\u007F-\u009F]/g, "").replace(/[\r\n]+/g, " / ");
17151
+ const attachmentLabel = images.length ? " [" + images.length + " image" + (images.length === 1 ? "" : "s") + "]" : "";
17152
+ const attachments = attachmentLabel ? S2.dim + attachmentLabel + S2.reset : "";
17153
+ output.write("\r\x1B[2K" + prompt + safe + attachments);
17154
+ const after = text.slice(cursor).replace(/[\r\n]+/g, " / ").length + attachmentLabel.length;
17155
+ if (after > 0) output.write("\x1B[" + after + "D");
17156
+ };
17157
+ const finish = (draft) => {
17158
+ if (settled) return;
17159
+ settled = true;
17160
+ input.off("data", onData);
17161
+ input.setRawMode?.(wasRaw);
17162
+ output.write("\x1B[?2004l\n");
17163
+ if (!draft) cleanupPromptDraft({ temporaryImages });
17164
+ resolveDraft(draft);
17165
+ };
17166
+ const insert = (value) => {
17167
+ text = text.slice(0, cursor) + value + text.slice(cursor);
17168
+ cursor += value.length;
17169
+ };
17170
+ const attachClipboard = () => {
17171
+ const image = clipboardImage();
17172
+ if (image) {
17173
+ images.push(image);
17174
+ temporaryImages.push(image);
17175
+ return;
17176
+ }
17177
+ insert(clipboardText());
17178
+ };
17179
+ const processStream = () => {
17180
+ while (stream && !settled) {
17181
+ if (pasting) {
17182
+ const end = stream.indexOf(PASTE_END);
17183
+ if (end < 0) {
17184
+ const keep = markerCarryLength(stream, PASTE_END);
17185
+ insert(stream.slice(0, stream.length - keep));
17186
+ stream = stream.slice(stream.length - keep);
17187
+ break;
17188
+ }
17189
+ insert(stream.slice(0, end));
17190
+ stream = stream.slice(end + PASTE_END.length);
17191
+ pasting = false;
17192
+ continue;
17193
+ }
17194
+ if (stream.startsWith(PASTE_START)) {
17195
+ stream = stream.slice(PASTE_START.length);
17196
+ pasting = true;
17197
+ continue;
17198
+ }
17199
+ if (PASTE_START.startsWith(stream)) break;
17200
+ if (stream.startsWith("\x1B[D")) {
17201
+ cursor = Math.max(0, cursor - 1);
17202
+ stream = stream.slice(3);
17203
+ continue;
17204
+ }
17205
+ if (stream.startsWith("\x1B[C")) {
17206
+ cursor = Math.min(text.length, cursor + 1);
17207
+ stream = stream.slice(3);
17208
+ continue;
17209
+ }
17210
+ if (stream.startsWith("\x1B[H")) {
17211
+ cursor = 0;
17212
+ stream = stream.slice(3);
17213
+ continue;
17214
+ }
17215
+ if (stream.startsWith("\x1B[F")) {
17216
+ cursor = text.length;
17217
+ stream = stream.slice(3);
17218
+ continue;
17219
+ }
17220
+ if (stream.startsWith("\x1B[A") || stream.startsWith("\x1B[B")) {
17221
+ stream = stream.slice(3);
17222
+ continue;
17223
+ }
17224
+ if (stream.startsWith("\x1B") && stream.length < 3) break;
17225
+ const char = stream[0];
17226
+ stream = stream.slice(1);
17227
+ if (char === "\r" || char === "\n") {
17228
+ const found = imagePathsInText(text, cwd);
17229
+ for (const path of found) if (!images.includes(path)) images.push(path);
17230
+ finish({ text, images, temporaryImages });
17231
+ } else if (char === "" || char === "" && !text && images.length === 0) {
17232
+ finish(null);
17233
+ } else if (char === "\x7F" || char === "\b") {
17234
+ if (cursor > 0) {
17235
+ text = text.slice(0, cursor - 1) + text.slice(cursor);
17236
+ cursor -= 1;
17237
+ }
17238
+ } else if (char === "") {
17239
+ text = text.slice(cursor);
17240
+ cursor = 0;
17241
+ } else if (char === "") {
17242
+ const before = text.slice(0, cursor).replace(/\s*\S+\s*$/, "");
17243
+ text = before + text.slice(cursor);
17244
+ cursor = before.length;
17245
+ } else if (char === "") {
17246
+ attachClipboard();
17247
+ } else if (char >= " " || char === " ") {
17248
+ insert(char);
17249
+ }
17250
+ }
17251
+ if (!settled) redraw();
17252
+ };
17253
+ let queue = Promise.resolve();
17254
+ const onData = (chunk) => {
17255
+ queue = queue.then(() => {
17256
+ stream += chunk.toString("utf8");
17257
+ processStream();
17258
+ });
17259
+ };
17260
+ input.setRawMode(true);
17261
+ input.resume();
17262
+ input.on("data", onData);
17263
+ output.write("\x1B[?2004h" + prompt);
17264
+ });
17265
+ }
17266
+ var PASTE_START, PASTE_END, IMAGE_EXTENSIONS;
17267
+ var init_composer = __esm({
17268
+ "cli/harness/composer.ts"() {
17269
+ "use strict";
17270
+ init_render2();
17271
+ PASTE_START = "\x1B[200~";
17272
+ PASTE_END = "\x1B[201~";
17273
+ IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp"]);
17274
+ }
17275
+ });
17276
+
16818
17277
  // cli/harness/events.ts
17278
+ function fixPollId(raw) {
17279
+ const text = String(raw || "");
17280
+ return FIX_POLL_MARKER.exec(text)?.[1] || LEGACY_FIX_POLL_MARKER.exec(text)?.[1] || "";
17281
+ }
17282
+ function cleanGuardText(raw) {
17283
+ return String(raw || "").replace(FIX_POLL_MARKER, "").replace(/\n*\s*SYNKRO FIX POLL[\s\S]*$/i, "").replace(/\n?\d{4}-\d\d-\d\dT[^\n]*\sERROR\s+codex_core::tools::router:[^\n]*/gi, "").replace(/\s*Checking command\s*$/i, "").trim();
17284
+ }
16819
17285
  function blockReason(raw) {
16820
- const text = String(raw || "").trim();
17286
+ const text = cleanGuardText(raw);
16821
17287
  if (!text) return "blocked by policy";
17288
+ const guardAt = text.lastIndexOf("Guard:");
17289
+ if (guardAt >= 0) return text.slice(guardAt + "Guard:".length).trim();
16822
17290
  const afterTag = text.match(/\[synkro:[^\]]*\]\s*(.+)/is);
16823
17291
  if (afterTag) return afterTag[1].trim();
16824
17292
  const afterHook = text.match(/blocked by a hook:\s*(.+)/is);
16825
17293
  if (afterHook) return afterHook[1].trim();
16826
17294
  return text;
16827
17295
  }
16828
- var BLOCK_MARKER;
17296
+ var BLOCK_MARKER, FIX_POLL_MARKER, LEGACY_FIX_POLL_MARKER;
16829
17297
  var init_events = __esm({
16830
17298
  "cli/harness/events.ts"() {
16831
17299
  "use strict";
16832
17300
  BLOCK_MARKER = /blocked by a hook|\[synkro:/i;
17301
+ FIX_POLL_MARKER = /\[synkro:fix-poll\s+item_id=\\?["']?([A-Za-z0-9_-]+)\\?["']?\]/i;
17302
+ LEGACY_FIX_POLL_MARKER = /SYNKRO FIX POLL\s*\(item_id=([A-Za-z0-9_-]+)\)/i;
16833
17303
  }
16834
17304
  });
16835
17305
 
@@ -16910,6 +17380,7 @@ function parseCursorLine(line) {
16910
17380
  ok: Boolean(success) && Number(success?.exitCode ?? 0) === 0,
16911
17381
  blocked,
16912
17382
  reason: rejected ? blocked ? blockReason(rawReason) : rawReason || "rejected" : "",
17383
+ pollId: blocked ? fixPollId(rawReason) : "",
16913
17384
  exitCode: success ? Number(success.exitCode ?? 0) : null,
16914
17385
  output: String(success?.stdout || success?.stderr || "")
16915
17386
  }];
@@ -16981,128 +17452,6 @@ var init_cursor = __esm({
16981
17452
  }
16982
17453
  });
16983
17454
 
16984
- // cli/harness/render.ts
16985
- function spinnerFrame(tick) {
16986
- return SPINNER[Math.abs(tick) % SPINNER.length];
16987
- }
16988
- function clip3(text, max) {
16989
- const value = String(text || "").replace(/\s+/g, " ").trim();
16990
- if (max <= 1) return value;
16991
- return value.length <= max ? value : value.slice(0, max - 1) + "\u2026";
16992
- }
16993
- function seconds(ms) {
16994
- const total = Math.max(0, Math.round(ms / 1e3));
16995
- if (total < 60) return total + "s";
16996
- return Math.floor(total / 60) + "m" + String(total % 60).padStart(2, "0") + "s";
16997
- }
16998
- function statusLine(opts) {
16999
- const hint = opts.hint ? " \xB7 " + opts.hint : "";
17000
- const body = opts.text + " (" + seconds(opts.elapsedMs) + hint + ")";
17001
- const width = Math.max(20, (opts.width || 100) - 4);
17002
- return S2.think + " " + spinnerFrame(opts.tick) + " " + clip3(body, width) + S2.reset;
17003
- }
17004
- function renderEvent(event, width = 100) {
17005
- const body = Math.max(30, width - 4);
17006
- switch (event.type) {
17007
- // The workspace and both accounts are already in the session header, so
17008
- // this line carries only what the header could not know before the harness
17009
- // started: which model answered, and whether a subscription or a key paid.
17010
- case "session-start":
17011
- return [
17012
- "",
17013
- S2.dim + " " + clip3(event.model, body - 20) + (event.authSource === "login" ? " \xB7 subscription" : " \xB7 api key") + S2.reset,
17014
- ""
17015
- ];
17016
- case "user-message":
17017
- return ["", S2.user + " \u276F " + S2.reset + S2.bold + clip3(event.text, body) + S2.reset, ""];
17018
- // Live state, not transcript. The runner promotes these to the status line.
17019
- case "thinking":
17020
- return [];
17021
- case "assistant-message": {
17022
- const lines = wrap(event.text, body - 2);
17023
- return [
17024
- "",
17025
- ...lines.map((line, index) => index === 0 ? S2.agent + " " + BULLET + " " + line + S2.reset : S2.agent + " " + line + S2.reset),
17026
- ""
17027
- ];
17028
- }
17029
- case "tool-start": {
17030
- const label = TOOL_LABEL[event.kind] || TOOL_LABEL.other;
17031
- return [
17032
- S2.tool + " " + BULLET + " " + label + S2.reset + S2.dim + "(" + clip3(event.target, body - label.length - 8) + ")" + S2.reset
17033
- ];
17034
- }
17035
- case "tool-end": {
17036
- if (event.blocked) {
17037
- return [
17038
- S2.blocked + " " + BULLET + " Blocked" + S2.reset + S2.dim + " " + clip3(event.target, body - 14) + S2.reset,
17039
- ...wrap(event.reason, body - 6).map((line, index) => index === 0 ? S2.blocked + " " + ELBOW + " " + S2.reset + S2.rule + line + S2.reset : S2.rule + " " + line + S2.reset)
17040
- ];
17041
- }
17042
- const detail = event.ok ? event.output.trim() ? clip3(event.output, body - 10) : "done" : "failed" + (event.exitCode === null ? "" : " (exit " + event.exitCode + ")");
17043
- const tint = event.ok ? S2.ok : S2.blocked;
17044
- return [tint + " " + ELBOW + " " + S2.reset + S2.dim + detail + S2.reset];
17045
- }
17046
- // A clean finish needs no announcement: the prompt returning IS the signal.
17047
- case "turn-end":
17048
- return event.ok ? [] : ["", S2.blocked + " " + BULLET + " turn ended with an error" + S2.reset, ""];
17049
- case "notice":
17050
- return [S2.dim + " " + clip3(event.text, body) + S2.reset];
17051
- default:
17052
- return [];
17053
- }
17054
- }
17055
- function wrap(text, width) {
17056
- const words = String(text || "").replace(/\s+/g, " ").trim().split(" ").filter(Boolean);
17057
- if (words.length === 0) return [];
17058
- const lines = [];
17059
- let line = "";
17060
- for (const word of words) {
17061
- if (!line) line = word;
17062
- else if ((line + " " + word).length <= width) line += " " + word;
17063
- else {
17064
- lines.push(line);
17065
- line = word;
17066
- }
17067
- }
17068
- if (line) lines.push(line);
17069
- return lines;
17070
- }
17071
- var ESC2, S2, CLEAR_LINE, BULLET, ELBOW, SPINNER, TOOL_LABEL;
17072
- var init_render2 = __esm({
17073
- "cli/harness/render.ts"() {
17074
- "use strict";
17075
- ESC2 = "\x1B[";
17076
- S2 = {
17077
- reset: ESC2 + "0m",
17078
- dim: ESC2 + "2m",
17079
- bold: ESC2 + "1m",
17080
- user: ESC2 + "38;5;111m",
17081
- agent: ESC2 + "38;5;252m",
17082
- think: ESC2 + "38;5;244m",
17083
- tool: ESC2 + "38;5;180m",
17084
- ok: ESC2 + "38;5;114m",
17085
- blocked: ESC2 + "38;5;203m",
17086
- rule: ESC2 + "38;5;211m"
17087
- };
17088
- CLEAR_LINE = "\r" + ESC2 + "2K";
17089
- BULLET = "\u23FA";
17090
- ELBOW = "\u23BF";
17091
- SPINNER = ["\xB7", "\u2722", "\u2733", "\u2217", "\u273B", "\u273D"];
17092
- TOOL_LABEL = {
17093
- shell: "Shell",
17094
- read: "Read",
17095
- edit: "Edit",
17096
- write: "Write",
17097
- delete: "Delete",
17098
- search: "Search",
17099
- list: "List",
17100
- todo: "Todo",
17101
- other: "Tool"
17102
- };
17103
- }
17104
- });
17105
-
17106
17455
  // cli/harness/run.ts
17107
17456
  import { spawn as spawn9 } from "child_process";
17108
17457
  function replayKey(event) {
@@ -17131,6 +17480,7 @@ function createTurnSink(opts) {
17131
17480
  let buffer = "";
17132
17481
  let reconnecting = false;
17133
17482
  let lastWasAnswer = false;
17483
+ const toolStartedAt = /* @__PURE__ */ new Map();
17134
17484
  let statusText = "";
17135
17485
  let statusKind = "progress";
17136
17486
  let statusShown = false;
@@ -17233,7 +17583,17 @@ function createTurnSink(opts) {
17233
17583
  opts.write(rendered.join("\n") + "\n");
17234
17584
  drawStatus();
17235
17585
  };
17236
- const take = (event) => {
17586
+ const take = (incoming) => {
17587
+ let event = incoming;
17588
+ if (event.type === "tool-start" && event.id && !toolStartedAt.has(event.id)) {
17589
+ toolStartedAt.set(event.id, now());
17590
+ } else if (event.type === "tool-end") {
17591
+ const started = toolStartedAt.get(event.id);
17592
+ if ((event.durationMs === null || event.durationMs === void 0) && started !== void 0) {
17593
+ event = { ...event, durationMs: Math.max(0, now() - started) };
17594
+ }
17595
+ toolStartedAt.delete(event.id);
17596
+ }
17237
17597
  const isRetry = event.type === "notice" && event.kind === "retry";
17238
17598
  if (!isRetry) disarmStall();
17239
17599
  if (isRetry) {
@@ -17266,6 +17626,9 @@ function createTurnSink(opts) {
17266
17626
  notice(text) {
17267
17627
  emit2({ type: "notice", text });
17268
17628
  },
17629
+ event(event) {
17630
+ take(event);
17631
+ },
17269
17632
  finish(exit, interrupted = false) {
17270
17633
  disarmStall();
17271
17634
  stopStatus();
@@ -17299,7 +17662,7 @@ async function runCursorTurn(opts) {
17299
17662
  showHeader: opts.showHeader,
17300
17663
  onGiveUp: () => stopHarness()
17301
17664
  });
17302
- return new Promise((resolve7) => {
17665
+ return new Promise((resolve8) => {
17303
17666
  const child = spawn9("cursor-agent", cursorArgs(opts.prompt), {
17304
17667
  cwd: opts.cwd,
17305
17668
  stdio: ["ignore", "pipe", "pipe"]
@@ -17328,11 +17691,11 @@ async function runCursorTurn(opts) {
17328
17691
  });
17329
17692
  child.on("close", (code) => {
17330
17693
  opts.signal?.removeEventListener("abort", onAbort);
17331
- resolve7(sink.finish(code ?? 0, interrupted));
17694
+ resolve8(sink.finish(code ?? 0, interrupted));
17332
17695
  });
17333
17696
  child.on("error", (error) => {
17334
17697
  sink.notice("failed to start cursor-agent: " + String(error));
17335
- resolve7(sink.finish(1, interrupted));
17698
+ resolve8(sink.finish(1, interrupted));
17336
17699
  });
17337
17700
  });
17338
17701
  }
@@ -17346,10 +17709,474 @@ var init_run = __esm({
17346
17709
  }
17347
17710
  });
17348
17711
 
17712
+ // cli/harness/codex.ts
17713
+ import { spawn as spawn10 } from "child_process";
17714
+ function itemTarget(item) {
17715
+ if (item?.type === "commandExecution") {
17716
+ return { kind: "shell", target: String(item.command || ""), description: "" };
17717
+ }
17718
+ if (item?.type === "fileChange") {
17719
+ const paths = Array.isArray(item.changes) ? item.changes.map((change) => String(change?.path || "")).filter(Boolean) : [];
17720
+ return { kind: "edit", target: paths.join(", ") || "files", description: "" };
17721
+ }
17722
+ if (item?.type === "mcpToolCall") {
17723
+ return { kind: "other", target: String(item.server || "") + "::" + String(item.tool || ""), description: "MCP tool" };
17724
+ }
17725
+ if (item?.type === "dynamicToolCall") {
17726
+ return { kind: "other", target: [item.namespace, item.tool].filter(Boolean).join("::"), description: "tool" };
17727
+ }
17728
+ return { kind: "other", target: String(item?.type || "tool"), description: "" };
17729
+ }
17730
+ function itemOutput(item) {
17731
+ if (item?.type === "commandExecution") return String(item.aggregatedOutput || "");
17732
+ if (item?.type === "mcpToolCall") return item.error ? String(item.error?.message || JSON.stringify(item.error)) : item.result ? JSON.stringify(item.result) : "";
17733
+ if (item?.type === "dynamicToolCall") return item.contentItems ? JSON.stringify(item.contentItems) : "";
17734
+ return "";
17735
+ }
17736
+ function parseCodexNotification(method, params, startedAt = /* @__PURE__ */ new Map(), approvalReasons = /* @__PURE__ */ new Map()) {
17737
+ if (method === "hook/started" || method === "hook/completed") {
17738
+ const run2 = params?.run || {};
17739
+ if (run2.eventName !== "preToolUse") return [];
17740
+ const asMs = (value) => {
17741
+ const number = Number(value || 0);
17742
+ return number > 0 && number < 1e11 ? number * 1e3 : number;
17743
+ };
17744
+ const started = asMs(run2.startedAt);
17745
+ const priorStart = Number(approvalReasons.get("__pretool_started__") || 0);
17746
+ if (started && (!priorStart || started < priorStart)) approvalReasons.set("__pretool_started__", String(started));
17747
+ if (method === "hook/completed") {
17748
+ const completed = asMs(run2.completedAt) || started + Number(run2.durationMs || 0);
17749
+ const priorCompleted = Number(approvalReasons.get("__pretool_completed__") || 0);
17750
+ if (completed > priorCompleted) approvalReasons.set("__pretool_completed__", String(completed));
17751
+ const entries = Array.isArray(run2.entries) ? run2.entries : [];
17752
+ const pollEntry = entries.find((entry) => fixPollId(String(entry?.text || "")));
17753
+ const blockingEntry = entries.find((entry) => entry?.kind === "stop" || entry?.kind === "error");
17754
+ const text = String(pollEntry?.text || blockingEntry?.text || run2.statusMessage || "");
17755
+ if (text && (run2.status === "blocked" || run2.status === "failed" || BLOCK_MARKER.test(text))) {
17756
+ approvalReasons.set("__latest_hook__", text);
17757
+ approvalReasons.set("__latest_hook_id__", String(run2.id || "synkro-policy-block"));
17758
+ approvalReasons.set("__latest_hook_duration__", String(Number(run2.durationMs || Math.max(0, completed - started))));
17759
+ }
17760
+ }
17761
+ return [];
17762
+ }
17763
+ if (method === "item/started") {
17764
+ const item = params?.item;
17765
+ if (!item?.id) return [];
17766
+ startedAt.set(String(item.id), Number(params.startedAtMs || Date.now()));
17767
+ if (!["commandExecution", "fileChange", "mcpToolCall", "dynamicToolCall"].includes(item.type)) return [];
17768
+ return [{ type: "tool-start", id: String(item.id), ...itemTarget(item) }];
17769
+ }
17770
+ if (method === "item/completed") {
17771
+ const item = params?.item;
17772
+ if (!item?.id) return [];
17773
+ if (item.type === "hookPrompt") {
17774
+ const text = Array.isArray(item.fragments) ? item.fragments.map((fragment) => String(fragment?.text || "")).filter(Boolean).join("\n") : "";
17775
+ if (text) {
17776
+ const start2 = startedAt.get(String(item.id));
17777
+ const completed2 = Number(params.completedAtMs || Date.now());
17778
+ approvalReasons.set("__latest_hook__", text);
17779
+ approvalReasons.set("__latest_hook_id__", String(item.id));
17780
+ approvalReasons.set("__latest_hook_duration__", String(start2 === void 0 ? 0 : Math.max(0, completed2 - start2)));
17781
+ }
17782
+ startedAt.delete(String(item.id));
17783
+ return [];
17784
+ }
17785
+ if (item.type === "agentMessage") {
17786
+ return item.text ? [{ type: "assistant-message", text: String(item.text) }] : [];
17787
+ }
17788
+ if (item.type === "reasoning") {
17789
+ const text = [...item.summary || [], ...item.content || []].join(" ").trim();
17790
+ return text ? [{ type: "thinking", text }] : [];
17791
+ }
17792
+ if (!["commandExecution", "fileChange", "mcpToolCall", "dynamicToolCall"].includes(item.type)) return [];
17793
+ const output = itemOutput(item);
17794
+ const approvalReason = approvalReasons.get(String(item.id)) || "";
17795
+ const hookReason = approvalReasons.get("__latest_hook__") || "";
17796
+ const rawReason = [approvalReason, hookReason, output].filter(Boolean).join("\n");
17797
+ const status = String(item.status || "");
17798
+ const blocked = BLOCK_MARKER.test(rawReason) || /blocked by PreToolUse hook/i.test(rawReason) || Boolean(hookReason) && (status === "failed" || status === "declined");
17799
+ const itemStart = startedAt.get(String(item.id));
17800
+ const hookStart = Number(approvalReasons.get("__pretool_started__") || 0);
17801
+ const start = itemStart === void 0 ? hookStart || void 0 : hookStart ? Math.min(itemStart, hookStart) : itemStart;
17802
+ const completed = Number(params.completedAtMs || Date.now());
17803
+ const durationMs = start === void 0 ? item.durationMs ?? null : Math.max(0, completed - start);
17804
+ startedAt.delete(String(item.id));
17805
+ approvalReasons.delete(String(item.id));
17806
+ approvalReasons.delete("__latest_hook__");
17807
+ approvalReasons.delete("__latest_hook_id__");
17808
+ approvalReasons.delete("__latest_hook_duration__");
17809
+ approvalReasons.delete("__pretool_started__");
17810
+ approvalReasons.delete("__pretool_completed__");
17811
+ return [{
17812
+ type: "tool-end",
17813
+ id: String(item.id),
17814
+ ...itemTarget(item),
17815
+ ok: status === "completed" && Number(item.exitCode ?? 0) === 0,
17816
+ blocked,
17817
+ reason: blocked ? blockReason(rawReason) : "",
17818
+ pollId: blocked ? fixPollId(rawReason) : "",
17819
+ exitCode: item.exitCode === null || item.exitCode === void 0 ? null : Number(item.exitCode),
17820
+ output: blocked ? "" : output,
17821
+ durationMs
17822
+ }];
17823
+ }
17824
+ if (method === "turn/completed") {
17825
+ const status = String(params?.turn?.status || "failed");
17826
+ const hookReason = approvalReasons.get("__latest_hook__") || "";
17827
+ const events = [];
17828
+ if (hookReason) {
17829
+ events.push({
17830
+ type: "tool-end",
17831
+ id: approvalReasons.get("__latest_hook_id__") || "synkro-policy-block",
17832
+ kind: "other",
17833
+ target: "policy check",
17834
+ ok: false,
17835
+ blocked: true,
17836
+ reason: blockReason(hookReason),
17837
+ pollId: fixPollId(hookReason),
17838
+ exitCode: null,
17839
+ output: "",
17840
+ durationMs: Number(approvalReasons.get("__latest_hook_duration__") || 0)
17841
+ });
17842
+ approvalReasons.delete("__latest_hook__");
17843
+ approvalReasons.delete("__latest_hook_id__");
17844
+ approvalReasons.delete("__latest_hook_duration__");
17845
+ }
17846
+ approvalReasons.delete("__pretool_started__");
17847
+ approvalReasons.delete("__pretool_completed__");
17848
+ events.push({ type: "turn-end", ok: status === "completed", text: String(params?.turn?.error?.message || "") });
17849
+ return events;
17850
+ }
17851
+ return [];
17852
+ }
17853
+ function codexStderrNotice(raw, initialized) {
17854
+ const text = String(raw || "").trim();
17855
+ if (!text || initialized) return "";
17856
+ return /fatal|panic|authentication failed|not logged in/i.test(text) ? text.split("\n")[0].slice(0, 300) : "";
17857
+ }
17858
+ async function askApproval(text) {
17859
+ const input = process.stdin;
17860
+ const output = process.stdout;
17861
+ if (!input.isTTY || !output.isTTY) return false;
17862
+ output.write("\n" + S2.rule + " Synkro approval required" + S2.reset + "\n " + text.replace(/[\u0000-\u001F\u007F-\u009F]/g, " ").slice(0, 600) + "\n");
17863
+ output.write(S2.dim + " Press y to allow once; any other key denies." + S2.reset + "\n");
17864
+ return new Promise((resolve8) => {
17865
+ const wasRaw = Boolean(input.isRaw);
17866
+ const onData = (chunk) => {
17867
+ input.off("data", onData);
17868
+ if (input.setRawMode) input.setRawMode(wasRaw);
17869
+ resolve8(chunk.toString("utf8").toLowerCase() === "y");
17870
+ };
17871
+ if (input.setRawMode) input.setRawMode(true);
17872
+ input.resume();
17873
+ input.on("data", onData);
17874
+ });
17875
+ }
17876
+ async function runCodexTurn(opts) {
17877
+ const session = new CodexAppSession();
17878
+ try {
17879
+ return await session.turn(opts);
17880
+ } finally {
17881
+ session.close();
17882
+ }
17883
+ }
17884
+ var CodexAppSession;
17885
+ var init_codex = __esm({
17886
+ "cli/harness/codex.ts"() {
17887
+ "use strict";
17888
+ init_composer();
17889
+ init_events();
17890
+ init_run();
17891
+ init_render2();
17892
+ CodexAppSession = class {
17893
+ child = null;
17894
+ buffer = "";
17895
+ nextId = 1;
17896
+ pending = /* @__PURE__ */ new Map();
17897
+ initialized = false;
17898
+ threadId = "";
17899
+ model = "Codex";
17900
+ cwd = "";
17901
+ active = null;
17902
+ send(message) {
17903
+ this.child?.stdin.write(JSON.stringify({ jsonrpc: "2.0", ...message }) + "\n");
17904
+ }
17905
+ request(method, params) {
17906
+ const id = this.nextId++;
17907
+ return new Promise((resolve8, reject) => {
17908
+ this.pending.set(id, { resolve: resolve8, reject });
17909
+ this.send({ id, method, params });
17910
+ });
17911
+ }
17912
+ respond(id, result) {
17913
+ this.send({ id, result });
17914
+ }
17915
+ async handleServerRequest(message) {
17916
+ const method = String(message.method || "");
17917
+ const params = message.params || {};
17918
+ if (params.itemId && params.reason) this.active?.approvalReasons.set(String(params.itemId), String(params.reason));
17919
+ if (method === "item/commandExecution/requestApproval" || method === "item/fileChange/requestApproval") {
17920
+ const summary = String(params.reason || params.command || params.grantRoot || "Allow this action?");
17921
+ const accepted = await askApproval(summary);
17922
+ this.respond(message.id, { decision: accepted ? "accept" : "decline" });
17923
+ return;
17924
+ }
17925
+ if (method === "item/tool/requestUserInput") {
17926
+ const answers = {};
17927
+ for (const question of params.questions || []) answers[String(question.id)] = { answers: [] };
17928
+ this.respond(message.id, { answers });
17929
+ return;
17930
+ }
17931
+ if (method === "mcpServer/elicitation/request") {
17932
+ this.respond(message.id, { action: "decline" });
17933
+ return;
17934
+ }
17935
+ this.respond(message.id, {});
17936
+ }
17937
+ handleLine(line) {
17938
+ let message;
17939
+ try {
17940
+ message = JSON.parse(line);
17941
+ } catch {
17942
+ return;
17943
+ }
17944
+ if (message.method && message.id !== void 0) {
17945
+ void this.handleServerRequest(message);
17946
+ return;
17947
+ }
17948
+ if (message.method) {
17949
+ const active = this.active;
17950
+ if (!active) return;
17951
+ const events = parseCodexNotification(message.method, message.params, active.startedAt, active.approvalReasons);
17952
+ for (const event of events) active.sink.event(event);
17953
+ if (message.method === "turn/started") active.turnId = String(message.params?.turn?.id || "");
17954
+ if (message.method === "turn/completed") {
17955
+ const result = active.sink.finish(message.params?.turn?.status === "completed" ? 0 : 1, active.interrupted);
17956
+ this.active = null;
17957
+ active.resolve(result);
17958
+ }
17959
+ return;
17960
+ }
17961
+ if (message.id !== void 0) {
17962
+ const pending = this.pending.get(Number(message.id));
17963
+ if (!pending) return;
17964
+ this.pending.delete(Number(message.id));
17965
+ if (message.error) pending.reject(new Error(String(message.error?.message || JSON.stringify(message.error))));
17966
+ else pending.resolve(message.result);
17967
+ }
17968
+ }
17969
+ async start(cwd) {
17970
+ if (this.child) return;
17971
+ this.cwd = cwd;
17972
+ this.child = spawn10("codex", ["app-server"], { cwd, stdio: ["pipe", "pipe", "pipe"] });
17973
+ this.child.stdout.on("data", (chunk) => {
17974
+ this.buffer += chunk.toString("utf8");
17975
+ let newline = this.buffer.indexOf("\n");
17976
+ while (newline !== -1) {
17977
+ const line = this.buffer.slice(0, newline).trim();
17978
+ this.buffer = this.buffer.slice(newline + 1);
17979
+ if (line) this.handleLine(line);
17980
+ newline = this.buffer.indexOf("\n");
17981
+ }
17982
+ });
17983
+ this.child.stderr.on("data", (chunk) => {
17984
+ const note = codexStderrNotice(chunk.toString("utf8"), this.initialized);
17985
+ if (note) this.active?.sink.notice(note);
17986
+ });
17987
+ this.child.on("close", (code) => {
17988
+ const active = this.active;
17989
+ if (active) {
17990
+ this.active = null;
17991
+ active.resolve(active.sink.finish(code ?? 1, active.interrupted));
17992
+ }
17993
+ for (const pending of this.pending.values()) pending.reject(new Error("codex app-server exited"));
17994
+ this.pending.clear();
17995
+ this.child = null;
17996
+ });
17997
+ this.child.on("error", (error) => {
17998
+ const failure = new Error("failed to start codex app-server: " + String(error));
17999
+ const active = this.active;
18000
+ if (active) {
18001
+ active.sink.notice(failure.message);
18002
+ this.active = null;
18003
+ active.resolve(active.sink.finish(1, active.interrupted));
18004
+ }
18005
+ for (const pending of this.pending.values()) pending.reject(failure);
18006
+ this.pending.clear();
18007
+ });
18008
+ const initialized = await this.request("initialize", { clientInfo: { name: "synkro-governed", version: "1.0.0" } });
18009
+ this.initialized = true;
18010
+ this.send({ method: "initialized", params: {} });
18011
+ const started = await this.request("thread/start", {
18012
+ cwd,
18013
+ approvalPolicy: "on-request",
18014
+ approvalsReviewer: "user",
18015
+ sandbox: "workspace-write",
18016
+ ephemeral: false
18017
+ });
18018
+ this.threadId = String(started?.thread?.id || "");
18019
+ this.model = String(started?.model || initialized?.userAgent || "Codex");
18020
+ if (!this.threadId) throw new Error("codex app-server did not return a thread id");
18021
+ }
18022
+ async turn(opts) {
18023
+ await this.start(opts.cwd);
18024
+ if (this.active) throw new Error("a Codex turn is already running");
18025
+ const sink = createTurnSink({
18026
+ write: opts.write || ((text) => process.stdout.write(text)),
18027
+ onEvent: opts.onEvent,
18028
+ width: opts.width || Number(process.stdout.columns || 100),
18029
+ animate: false,
18030
+ showPrompt: opts.showPrompt,
18031
+ showHeader: opts.showHeader
18032
+ });
18033
+ sink.event({ type: "session-start", sessionId: this.threadId, model: this.model, cwd: this.cwd, authSource: "login" });
18034
+ sink.event({
18035
+ type: "user-message",
18036
+ text: opts.prompt || (opts.images?.length ? "[" + opts.images.length + " image attached]" : "")
18037
+ });
18038
+ const result = new Promise((resolve8) => {
18039
+ this.active = { sink, resolve: resolve8, startedAt: /* @__PURE__ */ new Map(), approvalReasons: /* @__PURE__ */ new Map(), turnId: "", interrupted: false };
18040
+ });
18041
+ const onAbort = () => {
18042
+ if (!this.active) return;
18043
+ this.active.interrupted = true;
18044
+ if (this.active.turnId) void this.request("turn/interrupt", { threadId: this.threadId, turnId: this.active.turnId }).catch(() => {
18045
+ });
18046
+ };
18047
+ if (opts.signal?.aborted) onAbort();
18048
+ else opts.signal?.addEventListener("abort", onAbort, { once: true });
18049
+ try {
18050
+ await this.request("turn/start", {
18051
+ threadId: this.threadId,
18052
+ cwd: opts.cwd,
18053
+ input: codexUserInput({ text: opts.prompt, images: opts.images || [] })
18054
+ });
18055
+ return await result;
18056
+ } finally {
18057
+ opts.signal?.removeEventListener("abort", onAbort);
18058
+ }
18059
+ }
18060
+ close() {
18061
+ this.child?.kill();
18062
+ this.child = null;
18063
+ }
18064
+ };
18065
+ }
18066
+ });
18067
+
18068
+ // cli/harness/fixPoll.ts
18069
+ function pollBaseUrl() {
18070
+ const port = String(process.env.SYNKRO_MCP_PORT || "18931");
18071
+ return "http://127.0.0.1:" + port + "/api/local/fix-poll";
18072
+ }
18073
+ function neutralizeTerminalControls(value) {
18074
+ return value.replace(/[\u0000-\u0008\u000B-\u001F\u007F-\u009F\u001B]/g, "");
18075
+ }
18076
+ async function loadFixPoll(itemId, fetchImpl = fetch) {
18077
+ const response = await fetchImpl(pollBaseUrl() + "?item_id=" + encodeURIComponent(itemId));
18078
+ if (!response.ok) return null;
18079
+ const body = await response.json();
18080
+ const candidates = Array.isArray(body.candidates) ? body.candidates.filter((candidate) => typeof candidate === "string").slice(0, 8) : [];
18081
+ if (!candidates.length || body.status !== "pending") return null;
18082
+ return {
18083
+ itemId,
18084
+ filePath: String(body.file_path || ""),
18085
+ ruleId: String(body.rule_id || ""),
18086
+ candidates
18087
+ };
18088
+ }
18089
+ async function recordFixPoll(itemId, chosenIdx, fetchImpl = fetch) {
18090
+ const response = await fetchImpl(pollBaseUrl() + "/record", {
18091
+ method: "POST",
18092
+ headers: { "content-type": "application/json" },
18093
+ body: JSON.stringify({ item_id: itemId, chosen_idx: chosenIdx })
18094
+ });
18095
+ return response.ok;
18096
+ }
18097
+ function fixPollLines(poll, selected, width = 100) {
18098
+ const max = Math.max(32, width - 10);
18099
+ const choices = [...poll.candidates, "None of the above"];
18100
+ const scope = [poll.ruleId, poll.filePath].filter(Boolean).join(" \xB7 ");
18101
+ const lines = [
18102
+ "",
18103
+ S2.bold + S2.rule + " Synkro needs your decision" + S2.reset,
18104
+ ...scope ? [S2.dim + " " + neutralizeTerminalControls(scope) + S2.reset] : [],
18105
+ ""
18106
+ ];
18107
+ choices.forEach((choice, index) => {
18108
+ const prefix = index === selected ? S2.user + " \u276F " : S2.dim + " ";
18109
+ const text = wrap(index + 1 + ". " + neutralizeTerminalControls(choice), max);
18110
+ lines.push(prefix + (text[0] || "") + S2.reset);
18111
+ for (const continuation of text.slice(1)) lines.push(" " + continuation);
18112
+ });
18113
+ lines.push("", S2.dim + " \u2191/\u2193 select \xB7 Enter confirm \xB7 1-" + choices.length + " choose" + S2.reset);
18114
+ return lines;
18115
+ }
18116
+ async function pickFixPoll(poll, io = {}) {
18117
+ const input = io.input || process.stdin;
18118
+ const output = io.output || process.stdout;
18119
+ if (!input.isTTY || !output.isTTY) return -1;
18120
+ let selected = 0;
18121
+ let rendered = 0;
18122
+ const draw = () => {
18123
+ if (rendered) output.write("\x1B[" + rendered + "A\x1B[J");
18124
+ const lines = fixPollLines(poll, selected, Number(output.columns || 100));
18125
+ output.write(lines.join("\n") + "\n");
18126
+ rendered = lines.length;
18127
+ };
18128
+ const choice = await new Promise((resolve8) => {
18129
+ const choices = poll.candidates.length + 1;
18130
+ const wasRaw = Boolean(input.isRaw);
18131
+ const done = (index) => {
18132
+ input.off("data", onData);
18133
+ if (input.setRawMode) input.setRawMode(wasRaw);
18134
+ resolve8(index === poll.candidates.length ? -1 : index);
18135
+ };
18136
+ const onData = (chunk) => {
18137
+ const key = chunk.toString("utf8");
18138
+ if (key === "") return done(poll.candidates.length);
18139
+ if (key === "\x1B" || key.toLowerCase() === "n") return done(poll.candidates.length);
18140
+ if (key === "\r" || key === "\n") return done(selected);
18141
+ if (key === "\x1B[A" || key === "k") selected = (selected - 1 + choices) % choices;
18142
+ else if (key === "\x1B[B" || key === "j") selected = (selected + 1) % choices;
18143
+ else if (/^[1-9]$/.test(key)) {
18144
+ const index = Number(key) - 1;
18145
+ if (index < choices) return done(index);
18146
+ } else return;
18147
+ draw();
18148
+ };
18149
+ if (input.setRawMode) input.setRawMode(true);
18150
+ input.resume();
18151
+ input.on("data", onData);
18152
+ draw();
18153
+ });
18154
+ return choice;
18155
+ }
18156
+ async function resolveFixPolls(events, io = {}) {
18157
+ const ids = Array.from(new Set(events.filter((event) => event.type === "tool-end").map((event) => event.pollId || "").filter(Boolean)));
18158
+ const recorded = [];
18159
+ for (const itemId of ids) {
18160
+ const poll = await loadFixPoll(itemId, io.fetchImpl).catch(() => null);
18161
+ if (!poll) continue;
18162
+ const chosen = await pickFixPoll(poll, io);
18163
+ const ok = await recordFixPoll(itemId, chosen, io.fetchImpl).catch(() => false);
18164
+ if (ok) recorded.push(chosen);
18165
+ else (io.output || process.stdout).write(S2.blocked + " Could not record that Synkro decision.\n" + S2.reset);
18166
+ }
18167
+ return recorded;
18168
+ }
18169
+ var init_fixPoll = __esm({
18170
+ "cli/harness/fixPoll.ts"() {
18171
+ "use strict";
18172
+ init_render2();
18173
+ }
18174
+ });
18175
+
17349
18176
  // cli/harness/identity.ts
17350
- import { execFileSync as execFileSync5 } from "child_process";
17351
- import { homedir as homedir38 } from "os";
17352
- function shortenPath(path, home = homedir38()) {
18177
+ import { execFileSync as execFileSync6 } from "child_process";
18178
+ import { homedir as homedir39 } from "os";
18179
+ function shortenPath(path, home = homedir39()) {
17353
18180
  const value = String(path || "");
17354
18181
  if (home && value === home) return "~";
17355
18182
  if (home && value.startsWith(home + "/")) return "~" + value.slice(home.length);
@@ -17361,7 +18188,7 @@ function parseCursorAccount(output) {
17361
18188
  }
17362
18189
  function cursorAccount() {
17363
18190
  try {
17364
- const out = execFileSync5("cursor-agent", ["status"], {
18191
+ const out = execFileSync6("cursor-agent", ["status"], {
17365
18192
  encoding: "utf8",
17366
18193
  timeout: 5e3,
17367
18194
  stdio: ["ignore", "pipe", "pipe"]
@@ -17412,13 +18239,13 @@ var init_identity2 = __esm({
17412
18239
  });
17413
18240
 
17414
18241
  // cli/harness/session.ts
17415
- import { createInterface as createInterface5 } from "readline";
17416
18242
  async function runOnce(harness, cwd, prompt, echoPrompt = true, showHeader = true, signal) {
17417
- if (harness !== "cursor") {
18243
+ if (harness !== "cursor" && harness !== "codex") {
17418
18244
  process.stdout.write(S2.dim + " " + harness + " sessions are not embedded yet\n" + S2.reset);
17419
18245
  return 1;
17420
18246
  }
17421
- const result = await runCursorTurn({ prompt, cwd, showPrompt: echoPrompt, showHeader, signal });
18247
+ const result = harness === "codex" ? await runCodexTurn({ prompt, cwd, showPrompt: echoPrompt, showHeader, signal }) : await runCursorTurn({ prompt, cwd, showPrompt: echoPrompt, showHeader, signal });
18248
+ await resolveFixPolls(result.events);
17422
18249
  if (result.blocked > 0) {
17423
18250
  process.stdout.write(
17424
18251
  S2.blocked + " " + result.blocked + " action" + (result.blocked === 1 ? "" : "s") + " blocked by policy" + S2.reset + "\n"
@@ -17429,43 +18256,49 @@ async function runOnce(harness, cwd, prompt, echoPrompt = true, showHeader = tru
17429
18256
  async function runGovernedSession(harness, cwd, prompt) {
17430
18257
  if (prompt) return runOnce(harness, cwd, prompt);
17431
18258
  process.stdout.write(identityHeader({ cwd, harness, identity: readIdentity(harness) }).join("\n") + "\n");
17432
- const rl = createInterface5({ input: process.stdin, output: process.stdout });
17433
- const ask3 = () => new Promise((resolve7) => {
17434
- const onClose = () => resolve7(null);
17435
- rl.once("close", onClose);
17436
- rl.question(S2.user + " \u276F " + S2.reset, (answer) => {
17437
- rl.removeListener("close", onClose);
17438
- resolve7(answer);
17439
- });
17440
- });
18259
+ const codex = harness === "codex" ? new CodexAppSession() : null;
17441
18260
  let turn = null;
17442
- rl.on("SIGINT", () => {
17443
- if (turn) {
17444
- turn.abort();
17445
- return;
17446
- }
17447
- process.stdout.write("\n");
17448
- rl.close();
17449
- });
18261
+ const interruptTurn = () => {
18262
+ turn?.abort();
18263
+ };
18264
+ process.on("SIGINT", interruptTurn);
17450
18265
  let first = true;
17451
- for (; ; ) {
17452
- const answer = await ask3();
17453
- if (answer === null) break;
17454
- const line = answer.trim();
17455
- if (!line) continue;
17456
- if (line === "exit" || line === "quit") break;
17457
- turn = new AbortController();
17458
- await runOnce(harness, cwd, line, false, first, turn.signal);
17459
- turn = null;
17460
- first = false;
18266
+ try {
18267
+ for (; ; ) {
18268
+ const draft = await readPrompt(cwd);
18269
+ if (draft === null) break;
18270
+ const line = draft.text.trim();
18271
+ if (!line && draft.images.length === 0) continue;
18272
+ if (draft.images.length === 0 && (line === "exit" || line === "quit")) break;
18273
+ try {
18274
+ turn = new AbortController();
18275
+ const cursorPrompt = draft.images.length ? [draft.text, "", "Attached image files:", ...draft.images].join("\n") : draft.text;
18276
+ const result = codex ? await codex.turn({ prompt: draft.text, images: draft.images, cwd, showPrompt: false, showHeader: first, signal: turn.signal }) : await runCursorTurn({ prompt: cursorPrompt, cwd, showPrompt: false, showHeader: first, signal: turn.signal });
18277
+ await resolveFixPolls(result.events);
18278
+ if (result.blocked > 0) {
18279
+ process.stdout.write(
18280
+ S2.blocked + " " + result.blocked + " action" + (result.blocked === 1 ? "" : "s") + " blocked by policy" + S2.reset + "\n"
18281
+ );
18282
+ }
18283
+ first = false;
18284
+ } finally {
18285
+ cleanupPromptDraft(draft);
18286
+ turn = null;
18287
+ }
18288
+ }
18289
+ } finally {
18290
+ process.off("SIGINT", interruptTurn);
18291
+ codex?.close();
17461
18292
  }
17462
- rl.close();
17463
18293
  return 0;
17464
18294
  }
17465
18295
  var init_session = __esm({
17466
18296
  "cli/harness/session.ts"() {
17467
18297
  "use strict";
18298
+ init_composer();
17468
18299
  init_run();
18300
+ init_codex();
18301
+ init_fixPoll();
17469
18302
  init_identity2();
17470
18303
  init_render2();
17471
18304
  }
@@ -17489,7 +18322,7 @@ async function takeover(kind, cwd) {
17489
18322
  const harness = ["claude", "codex", "cursor"].includes(kind) ? kind : "claude";
17490
18323
  const info = await detectContainerBackend();
17491
18324
  const reachable = info.backend === "container" ? (await run(info.runner, ["test", "-d", cwd])).ok : false;
17492
- const backend = reachable ? "container" : "host";
18325
+ const backend = harness === "codex" ? "host" : reachable ? "container" : "host";
17493
18326
  const spaceName = cwd.split("/").filter(Boolean).pop() || "space";
17494
18327
  const spawned = await spawnAgent(info, {
17495
18328
  name: spaceName + "-" + harness + "-" + String(process.pid % 1e4),
@@ -17517,7 +18350,7 @@ async function restoreSession(session) {
17517
18350
  harness: record.harness,
17518
18351
  spaceName: record.spaceName,
17519
18352
  cwd: record.space,
17520
- backend: record.backend,
18353
+ backend: record.harness === "codex" ? "host" : record.backend,
17521
18354
  resume: true
17522
18355
  });
17523
18356
  }
@@ -17660,11 +18493,11 @@ __export(linear_exports, {
17660
18493
  linearCommand: () => linearCommand
17661
18494
  });
17662
18495
  import { readFileSync as readFileSync33 } from "fs";
17663
- import { homedir as homedir39 } from "os";
17664
- import { join as join37 } from "path";
18496
+ import { homedir as homedir40 } from "os";
18497
+ import { join as join38 } from "path";
17665
18498
  function mcpJwt() {
17666
18499
  try {
17667
- return readFileSync33(join37(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
18500
+ return readFileSync33(join38(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
17668
18501
  } catch {
17669
18502
  return "";
17670
18503
  }
@@ -17703,7 +18536,7 @@ var SYNKRO_DIR14, PORT2, BASE;
17703
18536
  var init_linear = __esm({
17704
18537
  "cli/commands/linear.ts"() {
17705
18538
  "use strict";
17706
- SYNKRO_DIR14 = join37(homedir39(), ".synkro");
18539
+ SYNKRO_DIR14 = join38(homedir40(), ".synkro");
17707
18540
  PORT2 = process.env.SYNKRO_MCP_PORT || "18931";
17708
18541
  BASE = `http://127.0.0.1:${PORT2}`;
17709
18542
  }
@@ -17852,10 +18685,10 @@ var init_cveReachability = __esm({
17852
18685
  });
17853
18686
 
17854
18687
  // cli/reachability/reachabilityScan.ts
17855
- import { spawnSync as spawnSync12, execFileSync as execFileSync6 } from "child_process";
17856
- import { readFileSync as readFileSync35, writeFileSync as writeFileSync27, existsSync as existsSync38, readdirSync as readdirSync10 } from "fs";
17857
- import { join as join38 } from "path";
17858
- import { homedir as homedir40 } from "os";
18688
+ import { spawnSync as spawnSync13, execFileSync as execFileSync7 } from "child_process";
18689
+ import { readFileSync as readFileSync35, writeFileSync as writeFileSync28, existsSync as existsSync39, readdirSync as readdirSync10 } from "fs";
18690
+ import { join as join39 } from "path";
18691
+ import { homedir as homedir41 } from "os";
17859
18692
  import { createRequire } from "module";
17860
18693
  function walkSourceFiles(repoRoot3, maxFiles = 4e3, maxBytes = 5e5) {
17861
18694
  const SKIP2 = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "coverage", ".next", ".turbo", "out", ".cache", ".synkro", ".claude", "vendor", "__tests__", "test-results"]);
@@ -17872,7 +18705,7 @@ function walkSourceFiles(repoRoot3, maxFiles = 4e3, maxBytes = 5e5) {
17872
18705
  }
17873
18706
  for (const e of ents) {
17874
18707
  if (files.length >= maxFiles) break;
17875
- const full = join38(dir, e.name);
18708
+ const full = join39(dir, e.name);
17876
18709
  if (e.isDirectory()) {
17877
18710
  if (!SKIP2.has(e.name) && !e.name.startsWith(".")) stack.push(full);
17878
18711
  continue;
@@ -17899,12 +18732,12 @@ function cleanVersion(spec) {
17899
18732
  function gatherManifestVersions(repoRoot3) {
17900
18733
  const out = {};
17901
18734
  const dirs = [repoRoot3];
17902
- const pkgsDir = join38(repoRoot3, "packages");
17903
- if (existsSync38(pkgsDir)) {
18735
+ const pkgsDir = join39(repoRoot3, "packages");
18736
+ if (existsSync39(pkgsDir)) {
17904
18737
  try {
17905
18738
  for (const d of readdirSync10(pkgsDir)) {
17906
- const pd = join38(pkgsDir, d);
17907
- if (existsSync38(join38(pd, "package.json"))) dirs.push(pd);
18739
+ const pd = join39(pkgsDir, d);
18740
+ if (existsSync39(join39(pd, "package.json"))) dirs.push(pd);
17908
18741
  }
17909
18742
  } catch {
17910
18743
  }
@@ -17913,7 +18746,7 @@ function gatherManifestVersions(repoRoot3) {
17913
18746
  for (const dir of dirs) {
17914
18747
  let pkg;
17915
18748
  try {
17916
- pkg = JSON.parse(readFileSync35(join38(dir, "package.json"), "utf8"));
18749
+ pkg = JSON.parse(readFileSync35(join39(dir, "package.json"), "utf8"));
17917
18750
  } catch {
17918
18751
  continue;
17919
18752
  }
@@ -17936,25 +18769,25 @@ function findJelly(repoRoot3) {
17936
18769
  const pkg = JSON.parse(readFileSync35(pkgJson, "utf8"));
17937
18770
  const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin && (pkg.bin.jelly || pkg.bin[Object.keys(pkg.bin)[0]]);
17938
18771
  if (bin) {
17939
- const p = join38(dir, bin);
17940
- if (existsSync38(p)) return p;
18772
+ const p = join39(dir, bin);
18773
+ if (existsSync39(p)) return p;
17941
18774
  }
17942
18775
  } catch {
17943
18776
  }
17944
18777
  for (const base of [repoRoot3, process.cwd()]) {
17945
- const b = join38(base, "node_modules", ".bin", "jelly");
17946
- if (existsSync38(b)) return b;
18778
+ const b = join39(base, "node_modules", ".bin", "jelly");
18779
+ if (existsSync39(b)) return b;
17947
18780
  }
17948
18781
  return null;
17949
18782
  }
17950
18783
  function findEntries(repoRoot3) {
17951
18784
  const dirs = [repoRoot3];
17952
- const pkgsDir = join38(repoRoot3, "packages");
17953
- if (existsSync38(pkgsDir)) {
18785
+ const pkgsDir = join39(repoRoot3, "packages");
18786
+ if (existsSync39(pkgsDir)) {
17954
18787
  try {
17955
18788
  for (const d of readdirSync10(pkgsDir)) {
17956
- const pd = join38(pkgsDir, d);
17957
- if (existsSync38(join38(pd, "package.json"))) dirs.push(pd);
18789
+ const pd = join39(pkgsDir, d);
18790
+ if (existsSync39(join39(pd, "package.json"))) dirs.push(pd);
17958
18791
  }
17959
18792
  } catch {
17960
18793
  }
@@ -17962,12 +18795,12 @@ function findEntries(repoRoot3) {
17962
18795
  const entries = [];
17963
18796
  for (const dir of dirs) {
17964
18797
  try {
17965
- const pkg = JSON.parse(readFileSync35(join38(dir, "package.json"), "utf8"));
18798
+ const pkg = JSON.parse(readFileSync35(join39(dir, "package.json"), "utf8"));
17966
18799
  const cands = [pkg.source, pkg.module, pkg.main, "src/index.ts", "src/index.js", "src/main.ts", "src/server.ts", "index.ts", "index.js"];
17967
18800
  for (const c of cands) {
17968
18801
  if (typeof c !== "string") continue;
17969
- const f = join38(dir, c);
17970
- if (existsSync38(f)) {
18802
+ const f = join39(dir, c);
18803
+ if (existsSync39(f)) {
17971
18804
  entries.push(f);
17972
18805
  break;
17973
18806
  }
@@ -17979,7 +18812,7 @@ function findEntries(repoRoot3) {
17979
18812
  }
17980
18813
  function currentCommit(repoRoot3) {
17981
18814
  try {
17982
- return execFileSync6("git", ["rev-parse", "HEAD"], { cwd: repoRoot3, encoding: "utf8" }).trim();
18815
+ return execFileSync7("git", ["rev-parse", "HEAD"], { cwd: repoRoot3, encoding: "utf8" }).trim();
17983
18816
  } catch {
17984
18817
  return "";
17985
18818
  }
@@ -18000,7 +18833,7 @@ function parseApiUsage(log) {
18000
18833
  }
18001
18834
  function runReachabilityScan(repoRoot3, opts = {}) {
18002
18835
  const commit = currentCommit(repoRoot3);
18003
- if (!opts.force && commit && existsSync38(REACHABILITY_PATH)) {
18836
+ if (!opts.force && commit && existsSync39(REACHABILITY_PATH)) {
18004
18837
  try {
18005
18838
  const prev = JSON.parse(readFileSync35(REACHABILITY_PATH, "utf8"));
18006
18839
  if (prev.commit === commit) return { ok: true, cached: true, packages: Object.keys(prev.packages || {}).length };
@@ -18053,7 +18886,7 @@ function runReachabilityScan(repoRoot3, opts = {}) {
18053
18886
  if (jelly) {
18054
18887
  const entries = findEntries(repoRoot3);
18055
18888
  if (entries.length > 0) {
18056
- const r = spawnSync12(
18889
+ const r = spawnSync13(
18057
18890
  process.execPath,
18058
18891
  [jelly, "-b", repoRoot3, "--api-usage", ...entries],
18059
18892
  { encoding: "utf8", timeout: opts.timeoutMs ?? 18e4, maxBuffer: 2e8 }
@@ -18091,7 +18924,7 @@ function runReachabilityScan(repoRoot3, opts = {}) {
18091
18924
  if (Object.keys(packages).length === 0) return { ok: false, reason: "no package usage found (no jelly output, no AST imports)" };
18092
18925
  const file = { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), commit, tool, packages, versions: gatherManifestVersions(repoRoot3) };
18093
18926
  try {
18094
- writeFileSync27(REACHABILITY_PATH, JSON.stringify(file, null, 2));
18927
+ writeFileSync28(REACHABILITY_PATH, JSON.stringify(file, null, 2));
18095
18928
  } catch (e) {
18096
18929
  return { ok: false, reason: "write failed: " + String(e.message || e) };
18097
18930
  }
@@ -18103,7 +18936,7 @@ var init_reachabilityScan = __esm({
18103
18936
  "use strict";
18104
18937
  init_cveReachability();
18105
18938
  require2 = createRequire(import.meta.url);
18106
- REACHABILITY_PATH = join38(homedir40(), ".synkro", "reachability.json");
18939
+ REACHABILITY_PATH = join39(homedir41(), ".synkro", "reachability.json");
18107
18940
  }
18108
18941
  });
18109
18942
 
@@ -18112,13 +18945,13 @@ var reachabilityScan_exports = {};
18112
18945
  __export(reachabilityScan_exports, {
18113
18946
  reachabilityScanCommand: () => reachabilityScanCommand
18114
18947
  });
18115
- import { readFileSync as readFileSync36, existsSync as existsSync39 } from "fs";
18116
- import { join as join39 } from "path";
18117
- import { homedir as homedir41 } from "os";
18118
- import { execFileSync as execFileSync7 } from "child_process";
18948
+ import { readFileSync as readFileSync36, existsSync as existsSync40 } from "fs";
18949
+ import { join as join40 } from "path";
18950
+ import { homedir as homedir42 } from "os";
18951
+ import { execFileSync as execFileSync8 } from "child_process";
18119
18952
  function readConfigEnv4() {
18120
- const p = join39(SYNKRO_DIR15, "config.env");
18121
- if (!existsSync39(p)) return {};
18953
+ const p = join40(SYNKRO_DIR15, "config.env");
18954
+ if (!existsSync40(p)) return {};
18122
18955
  const out = {};
18123
18956
  for (const line of readFileSync36(p, "utf-8").split("\n")) {
18124
18957
  const t = line.trim();
@@ -18130,7 +18963,7 @@ function readConfigEnv4() {
18130
18963
  }
18131
18964
  function repoRoot2() {
18132
18965
  try {
18133
- return execFileSync7("git", ["rev-parse", "--show-toplevel"], { encoding: "utf-8" }).trim();
18966
+ return execFileSync8("git", ["rev-parse", "--show-toplevel"], { encoding: "utf-8" }).trim();
18134
18967
  } catch {
18135
18968
  return process.cwd();
18136
18969
  }
@@ -18138,7 +18971,7 @@ function repoRoot2() {
18138
18971
  function repoSlug(root) {
18139
18972
  const run2 = (a) => {
18140
18973
  try {
18141
- return execFileSync7("git", a, { encoding: "utf-8" }).trim();
18974
+ return execFileSync8("git", a, { encoding: "utf-8" }).trim();
18142
18975
  } catch {
18143
18976
  return "";
18144
18977
  }
@@ -18152,10 +18985,10 @@ async function pushToCloud(cfg, repo) {
18152
18985
  while (gwBase.endsWith("/")) gwBase = gwBase.slice(0, -1);
18153
18986
  let jwt2 = "";
18154
18987
  try {
18155
- jwt2 = readFileSync36(join39(SYNKRO_DIR15, ".mcp-jwt"), "utf-8").trim();
18988
+ jwt2 = readFileSync36(join40(SYNKRO_DIR15, ".mcp-jwt"), "utf-8").trim();
18156
18989
  } catch {
18157
18990
  }
18158
- if (!jwt2 || !existsSync39(REACHABILITY_PATH)) return;
18991
+ if (!jwt2 || !existsSync40(REACHABILITY_PATH)) return;
18159
18992
  const body = readFileSync36(REACHABILITY_PATH, "utf-8");
18160
18993
  try {
18161
18994
  const resp = await fetch(gwBase + "/api/v1/reachability?repo=" + encodeURIComponent(repo), {
@@ -18188,7 +19021,7 @@ var init_reachabilityScan2 = __esm({
18188
19021
  "cli/commands/reachabilityScan.ts"() {
18189
19022
  "use strict";
18190
19023
  init_reachabilityScan();
18191
- SYNKRO_DIR15 = join39(homedir41(), ".synkro");
19024
+ SYNKRO_DIR15 = join40(homedir42(), ".synkro");
18192
19025
  }
18193
19026
  });
18194
19027
 
@@ -18318,11 +19151,11 @@ var config_exports = {};
18318
19151
  __export(config_exports, {
18319
19152
  configCommand: () => configCommand
18320
19153
  });
18321
- import { readFileSync as readFileSync37, writeFileSync as writeFileSync28, existsSync as existsSync40 } from "fs";
18322
- import { join as join40 } from "path";
18323
- import { homedir as homedir42 } from "os";
19154
+ import { readFileSync as readFileSync37, writeFileSync as writeFileSync29, existsSync as existsSync41 } from "fs";
19155
+ import { join as join41 } from "path";
19156
+ import { homedir as homedir43 } from "os";
18324
19157
  function readConfigEnv5() {
18325
- if (!existsSync40(CONFIG_PATH9)) return {};
19158
+ if (!existsSync41(CONFIG_PATH9)) return {};
18326
19159
  const out = {};
18327
19160
  for (const line of readFileSync37(CONFIG_PATH9, "utf-8").split("\n")) {
18328
19161
  const t = line.trim();
@@ -18333,7 +19166,7 @@ function readConfigEnv5() {
18333
19166
  return out;
18334
19167
  }
18335
19168
  function updateConfigValue(key, value) {
18336
- if (!existsSync40(CONFIG_PATH9)) {
19169
+ if (!existsSync41(CONFIG_PATH9)) {
18337
19170
  console.error("No config found. Run `synkro install` first.");
18338
19171
  process.exit(1);
18339
19172
  }
@@ -18348,7 +19181,7 @@ function updateConfigValue(key, value) {
18348
19181
  return line;
18349
19182
  });
18350
19183
  if (!found) updated.splice(updated.length - 1, 0, `${key}='${value}'`);
18351
- writeFileSync28(CONFIG_PATH9, updated.join("\n"), "utf-8");
19184
+ writeFileSync29(CONFIG_PATH9, updated.join("\n"), "utf-8");
18352
19185
  }
18353
19186
  function resolveInferenceMode(cfg) {
18354
19187
  if ((cfg.SYNKRO_GRADING_MODE || "local") === "byok") return "byok";
@@ -18506,8 +19339,8 @@ var init_config = __esm({
18506
19339
  "use strict";
18507
19340
  init_stub();
18508
19341
  init_optout();
18509
- SYNKRO_DIR16 = join40(homedir42(), ".synkro");
18510
- CONFIG_PATH9 = join40(SYNKRO_DIR16, "config.env");
19342
+ SYNKRO_DIR16 = join41(homedir43(), ".synkro");
19343
+ CONFIG_PATH9 = join41(SYNKRO_DIR16, "config.env");
18511
19344
  }
18512
19345
  });
18513
19346
 
@@ -18597,12 +19430,12 @@ async function runExport(args2) {
18597
19430
  }
18598
19431
  function confirmYesNo(question) {
18599
19432
  if (!process.stdin.isTTY) return Promise.resolve(false);
18600
- return new Promise((resolve7) => {
19433
+ return new Promise((resolve8) => {
18601
19434
  const rl = createInterface6({ input: process.stdin, output: process.stdout });
18602
19435
  rl.question(`${question} (y/N): `, (answer) => {
18603
19436
  rl.close();
18604
19437
  const t = answer.trim().toLowerCase();
18605
- resolve7(t === "y" || t === "yes");
19438
+ resolve8(t === "y" || t === "yes");
18606
19439
  });
18607
19440
  });
18608
19441
  }
@@ -18697,11 +19530,11 @@ Usage:
18697
19530
 
18698
19531
  // cli/inventory/identity.ts
18699
19532
  import { randomUUID as randomUUID5 } from "crypto";
18700
- import { existsSync as existsSync41, mkdirSync as mkdirSync24, readFileSync as readFileSync38, renameSync as renameSync9, writeFileSync as writeFileSync29 } from "fs";
18701
- import { homedir as homedir43 } from "os";
18702
- import { dirname as dirname12, join as join41 } from "path";
19533
+ import { existsSync as existsSync42, mkdirSync as mkdirSync24, readFileSync as readFileSync38, renameSync as renameSync9, writeFileSync as writeFileSync30 } from "fs";
19534
+ import { homedir as homedir44 } from "os";
19535
+ import { dirname as dirname13, join as join42 } from "path";
18703
19536
  function operationalIdentityPath() {
18704
- return process.env.SYNKRO_OPERATIONAL_IDENTITY_PATH || join41(homedir43(), ".synkro", "installation.json");
19537
+ return process.env.SYNKRO_OPERATIONAL_IDENTITY_PATH || join42(homedir44(), ".synkro", "installation.json");
18705
19538
  }
18706
19539
  function validIdentity(value) {
18707
19540
  if (!value || typeof value !== "object") return false;
@@ -18709,15 +19542,15 @@ function validIdentity(value) {
18709
19542
  return typeof row2.installation_id === "string" && UUID_RE.test(row2.installation_id) && typeof row2.created_at === "string" && Number.isFinite(Date.parse(row2.created_at));
18710
19543
  }
18711
19544
  function writeIdentity(path, identity) {
18712
- mkdirSync24(dirname12(path), { recursive: true, mode: 448 });
19545
+ mkdirSync24(dirname13(path), { recursive: true, mode: 448 });
18713
19546
  const temp = `${path}.${process.pid}.${randomUUID5()}.tmp`;
18714
- writeFileSync29(temp, JSON.stringify(identity, null, 2) + "\n", { encoding: "utf8", mode: 384 });
19547
+ writeFileSync30(temp, JSON.stringify(identity, null, 2) + "\n", { encoding: "utf8", mode: 384 });
18715
19548
  renameSync9(temp, path);
18716
19549
  }
18717
19550
  function getOperationalInstallationIdentity(path = operationalIdentityPath()) {
18718
19551
  const prior = cached4.get(path);
18719
19552
  if (prior) return prior;
18720
- if (existsSync41(path)) {
19553
+ if (existsSync42(path)) {
18721
19554
  try {
18722
19555
  const parsed = JSON.parse(readFileSync38(path, "utf8"));
18723
19556
  if (validIdentity(parsed)) {
@@ -18744,13 +19577,13 @@ var init_identity3 = __esm({
18744
19577
  // cli/inventory/collector.ts
18745
19578
  import { createHash as createHash5 } from "crypto";
18746
19579
  import {
18747
- existsSync as existsSync42,
19580
+ existsSync as existsSync43,
18748
19581
  readFileSync as readFileSync39,
18749
19582
  readdirSync as readdirSync11,
18750
- statSync as statSync5
19583
+ statSync as statSync6
18751
19584
  } from "fs";
18752
- import { arch, homedir as homedir44, hostname as hostname2, platform as platform6, release as release2 } from "os";
18753
- import { basename as basename3, join as join42, relative, resolve as resolve5 } from "path";
19585
+ import { arch, homedir as homedir45, hostname as hostname2, platform as platform6, release as release2 } from "os";
19586
+ import { basename as basename4, join as join43, relative, resolve as resolve6 } from "path";
18754
19587
  import { fileURLToPath } from "url";
18755
19588
  function sha256(value) {
18756
19589
  return createHash5("sha256").update(value).digest("hex");
@@ -18760,14 +19593,14 @@ function pseudonymousHostnameHash(installationId, host) {
18760
19593
  }
18761
19594
  function cliVersion() {
18762
19595
  try {
18763
- return "1.10.6";
19596
+ return "1.10.8";
18764
19597
  } catch {
18765
19598
  return "0.0.0";
18766
19599
  }
18767
19600
  }
18768
19601
  function readJson(path) {
18769
19602
  try {
18770
- if (!existsSync42(path)) return null;
19603
+ if (!existsSync43(path)) return null;
18771
19604
  const parsed = JSON.parse(readFileSync39(path, "utf8"));
18772
19605
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
18773
19606
  } catch {
@@ -18776,7 +19609,7 @@ function readJson(path) {
18776
19609
  }
18777
19610
  function readText(path) {
18778
19611
  try {
18779
- if (!existsSync42(path)) return "";
19612
+ if (!existsSync43(path)) return "";
18780
19613
  return readFileSync39(path, "utf8");
18781
19614
  } catch {
18782
19615
  return "";
@@ -18797,7 +19630,7 @@ function canonical(raw) {
18797
19630
  }
18798
19631
  function safePackageName(command, args2) {
18799
19632
  if (typeof command !== "string" || !command.trim()) return void 0;
18800
- const runner = basename3(command.trim()).replace(/\.exe$/i, "");
19633
+ const runner = basename4(command.trim()).replace(/\.exe$/i, "");
18801
19634
  if (Array.isArray(args2) && ["npx", "bunx", "uvx"].includes(runner)) {
18802
19635
  const pkg = args2.find((arg) => typeof arg === "string" && !arg.startsWith("-"));
18803
19636
  if (typeof pkg === "string") {
@@ -18805,7 +19638,7 @@ function safePackageName(command, args2) {
18805
19638
  if (/^(?:@[a-z0-9_.-]+\/)?[a-z0-9_.-]+(?:@[a-z0-9_.+~-]+)?$/i.test(candidate)) {
18806
19639
  return candidate;
18807
19640
  }
18808
- return basename3(candidate);
19641
+ return basename4(candidate);
18809
19642
  }
18810
19643
  }
18811
19644
  return runner;
@@ -18863,16 +19696,16 @@ function mcpArtifactsFromJson(harness, config, configScope = "user") {
18863
19696
  }
18864
19697
  function claudeDesktopConfigCandidates(home, targetPlatform) {
18865
19698
  if (targetPlatform === "darwin") {
18866
- return [join42(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")];
19699
+ return [join43(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")];
18867
19700
  }
18868
19701
  if (targetPlatform === "linux") {
18869
19702
  return [
18870
- join42(home, ".config", "Claude", "claude_desktop_config.json"),
18871
- join42(home, ".config", "claude", "claude_desktop_config.json")
19703
+ join43(home, ".config", "Claude", "claude_desktop_config.json"),
19704
+ join43(home, ".config", "claude", "claude_desktop_config.json")
18872
19705
  ];
18873
19706
  }
18874
19707
  if (targetPlatform === "win32" && process.env.APPDATA) {
18875
- return [join42(process.env.APPDATA, "Claude", "claude_desktop_config.json")];
19708
+ return [join43(process.env.APPDATA, "Claude", "claude_desktop_config.json")];
18876
19709
  }
18877
19710
  return [];
18878
19711
  }
@@ -18880,7 +19713,7 @@ function claudeManagedMcpConfigCandidates(targetPlatform) {
18880
19713
  if (targetPlatform === "darwin") return ["/Library/Application Support/ClaudeCode/managed-mcp.json"];
18881
19714
  if (targetPlatform === "linux") return ["/etc/claude-code/managed-mcp.json"];
18882
19715
  if (targetPlatform === "win32" && process.env.ProgramFiles) {
18883
- return [join42(process.env.ProgramFiles, "ClaudeCode", "managed-mcp.json")];
19716
+ return [join43(process.env.ProgramFiles, "ClaudeCode", "managed-mcp.json")];
18884
19717
  }
18885
19718
  return [];
18886
19719
  }
@@ -18888,8 +19721,8 @@ function discoveredProjectRoots(claudeState, currentDirectory, explicit = [], cu
18888
19721
  const roots = /* @__PURE__ */ new Set();
18889
19722
  const add = (value) => {
18890
19723
  if (typeof value !== "string" || !value.trim()) return;
18891
- const path = resolve5(value);
18892
- if (existsSync42(path)) roots.add(path);
19724
+ const path = resolve6(value);
19725
+ if (existsSync43(path)) roots.add(path);
18893
19726
  };
18894
19727
  add(currentDirectory);
18895
19728
  for (const path of explicit) add(path);
@@ -18900,17 +19733,17 @@ function discoveredProjectRoots(claudeState, currentDirectory, explicit = [], cu
18900
19733
  return [...roots];
18901
19734
  }
18902
19735
  function cursorWorkspaceStorageCandidates(home, targetPlatform) {
18903
- if (targetPlatform === "darwin") return [join42(home, "Library", "Application Support", "Cursor", "User", "workspaceStorage")];
18904
- if (targetPlatform === "linux") return [join42(home, ".config", "Cursor", "User", "workspaceStorage")];
19736
+ if (targetPlatform === "darwin") return [join43(home, "Library", "Application Support", "Cursor", "User", "workspaceStorage")];
19737
+ if (targetPlatform === "linux") return [join43(home, ".config", "Cursor", "User", "workspaceStorage")];
18905
19738
  if (targetPlatform === "win32" && process.env.APPDATA) {
18906
- return [join42(process.env.APPDATA, "Cursor", "User", "workspaceStorage")];
19739
+ return [join43(process.env.APPDATA, "Cursor", "User", "workspaceStorage")];
18907
19740
  }
18908
19741
  return [];
18909
19742
  }
18910
19743
  function cursorWorkspaceRoots(home, targetPlatform) {
18911
19744
  const roots = /* @__PURE__ */ new Set();
18912
19745
  for (const storage of cursorWorkspaceStorageCandidates(home, targetPlatform)) {
18913
- if (!existsSync42(storage)) continue;
19746
+ if (!existsSync43(storage)) continue;
18914
19747
  let entries = [];
18915
19748
  try {
18916
19749
  entries = readdirSync11(storage, { withFileTypes: true });
@@ -18919,12 +19752,12 @@ function cursorWorkspaceRoots(home, targetPlatform) {
18919
19752
  }
18920
19753
  for (const entry of entries) {
18921
19754
  if (!entry.isDirectory() || entry.isSymbolicLink?.()) continue;
18922
- const state = readJson(join42(storage, entry.name, "workspace.json"));
19755
+ const state = readJson(join43(storage, entry.name, "workspace.json"));
18923
19756
  const raw = state?.folder;
18924
19757
  if (typeof raw !== "string" || !raw.trim()) continue;
18925
19758
  try {
18926
19759
  const path = raw.startsWith("file:") ? fileURLToPath(raw) : raw;
18927
- if (existsSync42(path)) roots.add(resolve5(path));
19760
+ if (existsSync43(path)) roots.add(resolve6(path));
18928
19761
  } catch {
18929
19762
  }
18930
19763
  }
@@ -19000,10 +19833,10 @@ function hookArtifacts(harness, config) {
19000
19833
  harness,
19001
19834
  type: "hook",
19002
19835
  canonical_id: `${canonical(event)}:${commandHash.slice(0, 20)}`,
19003
- display_name: `${event} \xB7 ${basename3(String(entry.command).split(/\s+/)[0] || "hook")}`,
19836
+ display_name: `${event} \xB7 ${basename4(String(entry.command).split(/\s+/)[0] || "hook")}`,
19004
19837
  enabled: entry.enabled !== false,
19005
19838
  config_scope: "user",
19006
- package_name: basename3(String(entry.command).split(/\s+/)[0] || "") || void 0,
19839
+ package_name: basename4(String(entry.command).split(/\s+/)[0] || "") || void 0,
19007
19840
  config_hash: commandHash,
19008
19841
  metadata: { managed, events: [event] }
19009
19842
  });
@@ -19018,7 +19851,7 @@ function parseFrontmatter(content) {
19018
19851
  return { name: value("name"), version: value("version") };
19019
19852
  }
19020
19853
  function skillArtifacts(harness, root) {
19021
- if (!existsSync42(root)) return [];
19854
+ if (!existsSync43(root)) return [];
19022
19855
  const manifests = [];
19023
19856
  const visit = (dir) => {
19024
19857
  let entries;
@@ -19029,7 +19862,7 @@ function skillArtifacts(harness, root) {
19029
19862
  }
19030
19863
  for (const entry of entries) {
19031
19864
  if (entry.isSymbolicLink?.()) continue;
19032
- const path = join42(dir, entry.name);
19865
+ const path = join43(dir, entry.name);
19033
19866
  if (entry.isFile() && entry.name === "SKILL.md") manifests.push(path);
19034
19867
  else if (entry.isDirectory()) visit(path);
19035
19868
  }
@@ -19039,7 +19872,7 @@ function skillArtifacts(harness, root) {
19039
19872
  const content = readText(path);
19040
19873
  const frontmatter = parseFrontmatter(content);
19041
19874
  const rel = relative(root, path).replaceAll("\\", "/");
19042
- const name = frontmatter.name || basename3(join42(path, "..")) || "skill";
19875
+ const name = frontmatter.name || basename4(join43(path, "..")) || "skill";
19043
19876
  return {
19044
19877
  harness,
19045
19878
  type: "skill",
@@ -19054,7 +19887,7 @@ function skillArtifacts(harness, root) {
19054
19887
  });
19055
19888
  }
19056
19889
  function cursorExtensionArtifacts(root) {
19057
- if (!existsSync42(root)) return [];
19890
+ if (!existsSync43(root)) return [];
19058
19891
  let dirs = [];
19059
19892
  try {
19060
19893
  dirs = readdirSync11(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.isSymbolicLink());
@@ -19063,7 +19896,7 @@ function cursorExtensionArtifacts(root) {
19063
19896
  }
19064
19897
  const artifacts = [];
19065
19898
  for (const dir of dirs) {
19066
- const pkg = readJson(join42(root, dir.name, "package.json"));
19899
+ const pkg = readJson(join43(root, dir.name, "package.json"));
19067
19900
  if (!pkg) continue;
19068
19901
  const publisher = typeof pkg.publisher === "string" ? pkg.publisher : void 0;
19069
19902
  const name = typeof pkg.name === "string" ? pkg.name : dir.name;
@@ -19083,20 +19916,20 @@ function cursorExtensionArtifacts(root) {
19083
19916
  return artifacts;
19084
19917
  }
19085
19918
  function deploymentMode2(home) {
19086
- const raw = readText(join42(home, ".synkro", "config.env"));
19919
+ const raw = readText(join43(home, ".synkro", "config.env"));
19087
19920
  const value = (key) => raw.match(new RegExp(`^${key}=['"]?([^'"\\n]*)`, "m"))?.[1]?.toLowerCase();
19088
19921
  if (value("SYNKRO_GRADING_MODE") === "byok") return "byok";
19089
19922
  if (value("SYNKRO_STORAGE_MODE") === "cloud") return "cloud";
19090
19923
  return "local";
19091
19924
  }
19092
19925
  function telemetryHealth(home) {
19093
- const meta = readJson(join42(home, ".synkro", "telemetry-meta.json"));
19926
+ const meta = readJson(join43(home, ".synkro", "telemetry-meta.json"));
19094
19927
  const health = {};
19095
19928
  if (meta?.last_flush_ok_at && Number.isFinite(Date.parse(meta.last_flush_ok_at))) health.telemetry_last_flush_at = meta.last_flush_ok_at;
19096
19929
  if (meta?.last_flush_error) health.telemetry_last_error = "flush_failed";
19097
- const queue = join42(home, ".synkro", "telemetry-pending.jsonl");
19930
+ const queue = join43(home, ".synkro", "telemetry-pending.jsonl");
19098
19931
  try {
19099
- const size = statSync5(queue).size;
19932
+ const size = statSync6(queue).size;
19100
19933
  health.telemetry_backlog = size <= 5 * 1024 * 1024 ? readFileSync39(queue, "utf8").split("\n").filter(Boolean).length : Math.ceil(size / 1024);
19101
19934
  } catch {
19102
19935
  }
@@ -19138,7 +19971,7 @@ function harnessSnapshot(agent) {
19138
19971
  }
19139
19972
  const config = readJson(agent.settingsPath);
19140
19973
  const coverage = inspectCodexHooks(agent.settingsPath);
19141
- const toml = readText(join42(agent.configDir, "config.toml"));
19974
+ const toml = readText(join43(agent.configDir, "config.toml"));
19142
19975
  const permission = toml.match(/^\s*approval_policy\s*=\s*["']([^"']+)/m)?.[1];
19143
19976
  return {
19144
19977
  row: {
@@ -19155,11 +19988,11 @@ function harnessSnapshot(agent) {
19155
19988
  };
19156
19989
  }
19157
19990
  function collectOperationalInventory(options = {}) {
19158
- const home = options.homeDir ?? homedir44();
19991
+ const home = options.homeDir ?? homedir45();
19159
19992
  const detected = options.detectedAgents ?? detectAgents();
19160
19993
  const identity = getOperationalInstallationIdentity(options.identityPath);
19161
19994
  const targetPlatform = options.platformName ?? platform6();
19162
- const codexHome = options.homeDir ? join42(home, ".codex") : process.env.CODEX_HOME || join42(home, ".codex");
19995
+ const codexHome = options.homeDir ? join43(home, ".codex") : process.env.CODEX_HOME || join43(home, ".codex");
19163
19996
  const harnesses = [];
19164
19997
  const artifacts = [];
19165
19998
  for (const agent of detected) {
@@ -19167,7 +20000,7 @@ function collectOperationalInventory(options = {}) {
19167
20000
  harnesses.push(row2);
19168
20001
  artifacts.push(...hookArtifacts(row2.harness, config));
19169
20002
  }
19170
- const claudeJson = readJson(join42(home, ".claude.json"));
20003
+ const claudeJson = readJson(join43(home, ".claude.json"));
19171
20004
  artifacts.push(...mcpArtifactsFromJson("claude_code", claudeJson));
19172
20005
  if (claudeJson?.projects && typeof claudeJson.projects === "object") {
19173
20006
  for (const [projectPath, project] of Object.entries(claudeJson.projects)) {
@@ -19175,8 +20008,8 @@ function collectOperationalInventory(options = {}) {
19175
20008
  artifacts.push(...mcpArtifactsFromJson("claude_code", project, `local:${sha256(projectPath).slice(0, 16)}`));
19176
20009
  }
19177
20010
  }
19178
- artifacts.push(...mcpArtifactsFromJson("cursor", readJson(join42(home, ".cursor", "mcp.json"))));
19179
- artifacts.push(...codexMcpArtifacts(readText(join42(codexHome, "config.toml"))));
20011
+ artifacts.push(...mcpArtifactsFromJson("cursor", readJson(join43(home, ".cursor", "mcp.json"))));
20012
+ artifacts.push(...codexMcpArtifacts(readText(join43(codexHome, "config.toml"))));
19180
20013
  const projectRoots = discoveredProjectRoots(
19181
20014
  claudeJson,
19182
20015
  options.currentDirectory ?? process.cwd(),
@@ -19187,11 +20020,11 @@ function collectOperationalInventory(options = {}) {
19187
20020
  const scopeHash = sha256(projectRoot).slice(0, 16);
19188
20021
  artifacts.push(...mcpArtifactsFromJson(
19189
20022
  "claude_code",
19190
- readJson(join42(projectRoot, ".mcp.json")),
20023
+ readJson(join43(projectRoot, ".mcp.json")),
19191
20024
  `project:${scopeHash}`
19192
20025
  ));
19193
- const cursorProjectConfig = join42(projectRoot, ".cursor", "mcp.json");
19194
- if (resolve5(cursorProjectConfig) !== resolve5(join42(home, ".cursor", "mcp.json"))) {
20026
+ const cursorProjectConfig = join43(projectRoot, ".cursor", "mcp.json");
20027
+ if (resolve6(cursorProjectConfig) !== resolve6(join43(home, ".cursor", "mcp.json"))) {
19195
20028
  artifacts.push(...mcpArtifactsFromJson(
19196
20029
  "cursor",
19197
20030
  readJson(cursorProjectConfig),
@@ -19202,7 +20035,7 @@ function collectOperationalInventory(options = {}) {
19202
20035
  for (const managedPath of claudeManagedMcpConfigCandidates(targetPlatform)) {
19203
20036
  artifacts.push(...mcpArtifactsFromJson("claude_code", readJson(managedPath), "managed"));
19204
20037
  }
19205
- const desktopConfigPath = claudeDesktopConfigCandidates(home, targetPlatform).find((path) => existsSync42(path));
20038
+ const desktopConfigPath = claudeDesktopConfigCandidates(home, targetPlatform).find((path) => existsSync43(path));
19206
20039
  if (desktopConfigPath) {
19207
20040
  const desktopConfig = readJson(desktopConfigPath);
19208
20041
  harnesses.push({
@@ -19213,7 +20046,7 @@ function collectOperationalInventory(options = {}) {
19213
20046
  });
19214
20047
  artifacts.push(...mcpArtifactsFromJson("claude_desktop", desktopConfig));
19215
20048
  }
19216
- const claudeSettings = readJson(join42(home, ".claude", "settings.json"));
20049
+ const claudeSettings = readJson(join43(home, ".claude", "settings.json"));
19217
20050
  if (claudeSettings?.enabledPlugins && typeof claudeSettings.enabledPlugins === "object") {
19218
20051
  for (const [name, enabled] of Object.entries(claudeSettings.enabledPlugins)) {
19219
20052
  artifacts.push({
@@ -19227,10 +20060,10 @@ function collectOperationalInventory(options = {}) {
19227
20060
  });
19228
20061
  }
19229
20062
  }
19230
- artifacts.push(...skillArtifacts("claude_code", join42(home, ".claude", "skills")));
19231
- artifacts.push(...skillArtifacts("cursor", join42(home, ".cursor", "skills")));
19232
- artifacts.push(...skillArtifacts("codex", join42(codexHome, "skills")));
19233
- artifacts.push(...cursorExtensionArtifacts(join42(home, ".cursor", "extensions")));
20063
+ artifacts.push(...skillArtifacts("claude_code", join43(home, ".claude", "skills")));
20064
+ artifacts.push(...skillArtifacts("cursor", join43(home, ".cursor", "skills")));
20065
+ artifacts.push(...skillArtifacts("codex", join43(codexHome, "skills")));
20066
+ artifacts.push(...cursorExtensionArtifacts(join43(home, ".cursor", "extensions")));
19234
20067
  const uniqueArtifacts = /* @__PURE__ */ new Map();
19235
20068
  for (const artifact of artifacts) {
19236
20069
  const key = `${artifact.harness || "global"}:${artifact.type}:${artifact.canonical_id}`;
@@ -19283,18 +20116,18 @@ __export(sync_exports2, {
19283
20116
  syncOperationalInventoryDetached: () => syncOperationalInventoryDetached
19284
20117
  });
19285
20118
  import { createHash as createHash6, randomUUID as randomUUID6 } from "crypto";
19286
- import { spawn as spawn10 } from "child_process";
20119
+ import { spawn as spawn11 } from "child_process";
19287
20120
  import {
19288
- existsSync as existsSync43,
20121
+ existsSync as existsSync44,
19289
20122
  mkdirSync as mkdirSync25,
19290
20123
  readFileSync as readFileSync40,
19291
20124
  renameSync as renameSync10,
19292
- writeFileSync as writeFileSync30
20125
+ writeFileSync as writeFileSync31
19293
20126
  } from "fs";
19294
- import { homedir as homedir45 } from "os";
19295
- import { dirname as dirname13, join as join43 } from "path";
20127
+ import { homedir as homedir46 } from "os";
20128
+ import { dirname as dirname14, join as join44 } from "path";
19296
20129
  function syncStatePath() {
19297
- return process.env.SYNKRO_INVENTORY_SYNC_STATE_PATH || join43(homedir45(), ".synkro", "inventory-sync.json");
20130
+ return process.env.SYNKRO_INVENTORY_SYNC_STATE_PATH || join44(homedir46(), ".synkro", "inventory-sync.json");
19298
20131
  }
19299
20132
  function readState(path = syncStatePath()) {
19300
20133
  try {
@@ -19306,9 +20139,9 @@ function readState(path = syncStatePath()) {
19306
20139
  }
19307
20140
  function writeState(state, path = syncStatePath()) {
19308
20141
  try {
19309
- mkdirSync25(dirname13(path), { recursive: true, mode: 448 });
20142
+ mkdirSync25(dirname14(path), { recursive: true, mode: 448 });
19310
20143
  const temp = `${path}.${process.pid}.tmp`;
19311
- writeFileSync30(temp, JSON.stringify(state, null, 2) + "\n", { encoding: "utf8", mode: 384 });
20144
+ writeFileSync31(temp, JSON.stringify(state, null, 2) + "\n", { encoding: "utf8", mode: 384 });
19312
20145
  renameSync10(temp, path);
19313
20146
  } catch {
19314
20147
  }
@@ -19321,7 +20154,7 @@ function shouldSyncInventory(state, now = Date.now(), target) {
19321
20154
  return !Number.isFinite(lastAttempt) || lastAttempt <= 0 || now - lastAttempt >= FAILURE_RETRY_MS;
19322
20155
  }
19323
20156
  function readConfig() {
19324
- const path = join43(homedir45(), ".synkro", "config.env");
20157
+ const path = join44(homedir46(), ".synkro", "config.env");
19325
20158
  const out = {};
19326
20159
  try {
19327
20160
  for (const rawLine of readFileSync40(path, "utf8").split("\n")) {
@@ -19363,7 +20196,7 @@ function resolveInventoryGateway(raw) {
19363
20196
  }
19364
20197
  async function loadToken() {
19365
20198
  try {
19366
- const durable = readFileSync40(join43(homedir45(), ".synkro", ".mcp-jwt"), "utf8").trim();
20199
+ const durable = readFileSync40(join44(homedir46(), ".synkro", ".mcp-jwt"), "utf8").trim();
19367
20200
  if (durable) return durable;
19368
20201
  } catch {
19369
20202
  }
@@ -19481,8 +20314,8 @@ function syncOperationalInventoryDetached() {
19481
20314
  writeState({ ...state, last_attempt_at: (/* @__PURE__ */ new Date()).toISOString(), last_target: target }, path);
19482
20315
  try {
19483
20316
  const script = process.argv[1];
19484
- if (!script || !existsSync43(script)) return;
19485
- const child = spawn10(process.execPath, [script, "inventory-sync", "--detached"], {
20317
+ if (!script || !existsSync44(script)) return;
20318
+ const child = spawn11(process.execPath, [script, "inventory-sync", "--detached"], {
19486
20319
  detached: true,
19487
20320
  stdio: "ignore",
19488
20321
  env: { ...process.env, SYNKRO_INVENTORY_DETACHED: "1" }
@@ -19505,14 +20338,14 @@ var init_sync2 = __esm({
19505
20338
  });
19506
20339
 
19507
20340
  // cli/bootstrap.js
19508
- import { readFileSync as readFileSync41, existsSync as existsSync44 } from "fs";
19509
- import { resolve as resolve6 } from "path";
20341
+ import { readFileSync as readFileSync41, existsSync as existsSync45 } from "fs";
20342
+ import { resolve as resolve7 } from "path";
19510
20343
  process.title = "synkro";
19511
20344
  var envCandidates = [
19512
- resolve6(process.env.HOME ?? "", ".synkro", "config.env")
20345
+ resolve7(process.env.HOME ?? "", ".synkro", "config.env")
19513
20346
  ];
19514
20347
  for (const envPath of envCandidates) {
19515
- if (!existsSync44(envPath)) continue;
20348
+ if (!existsSync45(envPath)) continue;
19516
20349
  const envContent = readFileSync41(envPath, "utf-8");
19517
20350
  for (const line of envContent.split("\n")) {
19518
20351
  const trimmed = line.trim();
@@ -19530,7 +20363,7 @@ var subArgs = args.slice(1);
19530
20363
  var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
19531
20364
  var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "inventory-sync", "version", "--version", "-v", "help", "--help", "-h", ""]);
19532
20365
  function printVersion() {
19533
- console.log("1.10.6");
20366
+ console.log("1.10.8");
19534
20367
  }
19535
20368
  function printHelp2() {
19536
20369
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents