@synkro-sh/cli 1.10.8 → 1.10.9

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
@@ -81,10 +81,10 @@ import { createHash } from "crypto";
81
81
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
82
82
  import { homedir as homedir2, hostname, platform } from "os";
83
83
  import { join as join2 } from "path";
84
- function readConfigValue(key) {
85
- if (process.env[key]) return String(process.env[key]).toLowerCase();
84
+ function readConfigValue(key2) {
85
+ if (process.env[key2]) return String(process.env[key2]).toLowerCase();
86
86
  try {
87
- const re = key === "SYNKRO_GRADING_MODE" ? /^SYNKRO_GRADING_MODE=['"]?([^'"\n]*)/m : /^SYNKRO_STORAGE_MODE=['"]?([^'"\n]*)/m;
87
+ const re = key2 === "SYNKRO_GRADING_MODE" ? /^SYNKRO_GRADING_MODE=['"]?([^'"\n]*)/m : /^SYNKRO_STORAGE_MODE=['"]?([^'"\n]*)/m;
88
88
  const m = readFileSync2(CONFIG_PATH, "utf-8").match(re);
89
89
  return m ? m[1].toLowerCase() : "";
90
90
  } catch {
@@ -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.8";
150
+ cliVersion2 = "1.10.9";
151
151
  } catch {
152
152
  }
153
153
  const creds = loadCredentialsIdentity();
@@ -789,9 +789,9 @@ function sanitize(raw, maxLen = 256) {
789
789
  function shellQuoteSingle(value) {
790
790
  return `'${value.replace(/'/g, "'\\''")}'`;
791
791
  }
792
- function writeConfigEnvFlag(key, value) {
792
+ function writeConfigEnvFlag(key2, value) {
793
793
  const safe = sanitize(value, 8);
794
- const line = `${key}=${shellQuoteSingle(safe)}`;
794
+ const line = `${key2}=${shellQuoteSingle(safe)}`;
795
795
  let content = "";
796
796
  if (existsSync5(CONFIG_PATH2)) {
797
797
  try {
@@ -800,7 +800,7 @@ function writeConfigEnvFlag(key, value) {
800
800
  content = "";
801
801
  }
802
802
  }
803
- const re = new RegExp(`^${key}=.*$`, "m");
803
+ const re = new RegExp(`^${key2}=.*$`, "m");
804
804
  if (re.test(content)) {
805
805
  content = content.replace(re, line);
806
806
  } else {
@@ -1231,15 +1231,15 @@ async function exportEvents(path) {
1231
1231
  writeFileSync4(path, "", "utf-8");
1232
1232
  return;
1233
1233
  }
1234
- const lines = [];
1234
+ const lines2 = [];
1235
1235
  try {
1236
1236
  const rows = await sql`SELECT * FROM telemetry_events ORDER BY occurred_at ASC`;
1237
1237
  for (const row2 of rows) {
1238
- lines.push(JSON.stringify(row2));
1238
+ lines2.push(JSON.stringify(row2));
1239
1239
  }
1240
1240
  } catch {
1241
1241
  }
1242
- writeFileSync4(path, lines.join("\n") + (lines.length > 0 ? "\n" : ""), "utf-8");
1242
+ writeFileSync4(path, lines2.join("\n") + (lines2.length > 0 ? "\n" : ""), "utf-8");
1243
1243
  }
1244
1244
  function pendingFileSize() {
1245
1245
  try {
@@ -1856,21 +1856,21 @@ function validateHooksPath(path) {
1856
1856
  return resolved;
1857
1857
  }
1858
1858
  function readHooksFile(rawPath) {
1859
- const safePath = validateHooksPath(rawPath);
1859
+ const safePath2 = validateHooksPath(rawPath);
1860
1860
  try {
1861
- const raw = readFileSync10(safePath, "utf-8");
1861
+ const raw = readFileSync10(safePath2, "utf-8");
1862
1862
  return JSON.parse(raw);
1863
1863
  } catch (err) {
1864
1864
  if (err?.code === "ENOENT") return { version: 1, hooks: {} };
1865
- throw new Error(`Failed to parse ${safePath}: ${err.message}`);
1865
+ throw new Error(`Failed to parse ${safePath2}: ${err.message}`);
1866
1866
  }
1867
1867
  }
1868
1868
  function writeHooksFileAtomic(rawPath, data) {
1869
- const safePath = validateHooksPath(rawPath);
1870
- mkdirSync5(dirname2(safePath), { recursive: true });
1871
- const tmpPath = `${safePath}.synkro.tmp`;
1869
+ const safePath2 = validateHooksPath(rawPath);
1870
+ mkdirSync5(dirname2(safePath2), { recursive: true });
1871
+ const tmpPath = `${safePath2}.synkro.tmp`;
1872
1872
  writeFileSync7(tmpPath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
1873
- renameSync4(tmpPath, safePath);
1873
+ renameSync4(tmpPath, safePath2);
1874
1874
  }
1875
1875
  function isSynkroEntry2(entry) {
1876
1876
  if (entry?.[SYNKRO_MARKER2]) return true;
@@ -2158,20 +2158,20 @@ function validateHooksPath2(path) {
2158
2158
  return resolved;
2159
2159
  }
2160
2160
  function readHooksFile2(rawPath) {
2161
- const safePath = validateHooksPath2(rawPath);
2162
- if (!existsSync12(safePath)) return { hooks: {} };
2161
+ const safePath2 = validateHooksPath2(rawPath);
2162
+ if (!existsSync12(safePath2)) return { hooks: {} };
2163
2163
  try {
2164
- return JSON.parse(readFileSync11(safePath, "utf-8"));
2164
+ return JSON.parse(readFileSync11(safePath2, "utf-8"));
2165
2165
  } catch (err) {
2166
- throw new Error(`Failed to parse ${safePath}: ${err.message}`);
2166
+ throw new Error(`Failed to parse ${safePath2}: ${err.message}`);
2167
2167
  }
2168
2168
  }
2169
2169
  function writeHooksFileAtomic2(rawPath, data) {
2170
- const safePath = validateHooksPath2(rawPath);
2171
- mkdirSync6(dirname3(safePath), { recursive: true });
2172
- const tmpPath = `${safePath}.synkro.tmp`;
2170
+ const safePath2 = validateHooksPath2(rawPath);
2171
+ mkdirSync6(dirname3(safePath2), { recursive: true });
2172
+ const tmpPath = `${safePath2}.synkro.tmp`;
2173
2173
  writeFileSync8(tmpPath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
2174
- renameSync5(tmpPath, safePath);
2174
+ renameSync5(tmpPath, safePath2);
2175
2175
  }
2176
2176
  function isSynkroEntry3(entry) {
2177
2177
  if (entry?.[SYNKRO_MARKER3]) return true;
@@ -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((resolve8) => {
2354
+ return new Promise((resolve9) => {
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
- resolve8(summary);
2372
+ resolve9(summary);
2373
2373
  };
2374
2374
  const timer = setTimeout(() => finish(null), 1e4);
2375
2375
  try {
@@ -2384,9 +2384,9 @@ function queryCodexHookTrust(codexBinary = "codex", cwd = process.cwd(), autoTru
2384
2384
  child.stdout.on("data", (chunk) => {
2385
2385
  stdout += chunk;
2386
2386
  pending += chunk;
2387
- const lines = pending.split("\n");
2388
- pending = lines.pop() || "";
2389
- for (const line of lines) {
2387
+ const lines2 = pending.split("\n");
2388
+ pending = lines2.pop() || "";
2389
+ for (const line of lines2) {
2390
2390
  let message;
2391
2391
  try {
2392
2392
  message = JSON.parse(line);
@@ -2620,11 +2620,11 @@ function writeCodexTomlAtomic(path, content) {
2620
2620
  renameSync6(tmpPath, path);
2621
2621
  }
2622
2622
  function removeCodexManagedBlock(content, path) {
2623
- const lines = content.split("\n");
2623
+ const lines2 = content.split("\n");
2624
2624
  const out = [];
2625
2625
  let removed = false;
2626
2626
  let inside = false;
2627
- for (const line of lines) {
2627
+ for (const line of lines2) {
2628
2628
  if (!inside && line.trim() === CODEX_MCP_BEGIN) {
2629
2629
  inside = true;
2630
2630
  removed = true;
@@ -2652,8 +2652,8 @@ function findCodexMcpSection(content) {
2652
2652
  body: content.slice(match.index, next?.index ?? content.length)
2653
2653
  };
2654
2654
  }
2655
- function readTomlString(block, key) {
2656
- const match = block.match(new RegExp(`^\\s*${key}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*")\\s*$`, "m"));
2655
+ function readTomlString(block, key2) {
2656
+ const match = block.match(new RegExp(`^\\s*${key2}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*")\\s*$`, "m"));
2657
2657
  if (!match) return null;
2658
2658
  try {
2659
2659
  return JSON.parse(match[1]);
@@ -5181,7 +5181,7 @@ function createCallbackServer() {
5181
5181
  "Access-Control-Allow-Headers": "Content-Type",
5182
5182
  "Vary": "Origin"
5183
5183
  };
5184
- return new Promise((resolve8, reject) => {
5184
+ return new Promise((resolve9, 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
- resolve8(authData);
5273
+ resolve9(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((resolve8) => rl.question(question, resolve8));
5609
+ return new Promise((resolve9) => rl.question(question, resolve9));
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((resolve8) => {
5848
+ await new Promise((resolve9) => {
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
- resolve8();
5864
+ resolve9();
5865
5865
  });
5866
5866
  });
5867
5867
  }
@@ -7034,8 +7034,8 @@ function cursorApiKeyConfigured() {
7034
7034
  return false;
7035
7035
  }
7036
7036
  }
7037
- function writeCursorApiKey(key) {
7038
- const trimmed = key.trim();
7037
+ function writeCursorApiKey(key2) {
7038
+ const trimmed = key2.trim();
7039
7039
  if (!trimmed) return;
7040
7040
  mkdirSync11(CURSOR_CREDS_DIR, { recursive: true });
7041
7041
  chmodSync2(CURSOR_CREDS_DIR, 448);
@@ -7043,15 +7043,15 @@ function writeCursorApiKey(key) {
7043
7043
  chmodSync2(CURSOR_API_KEY_FILE, 384);
7044
7044
  }
7045
7045
  async function validateCursorApiKey() {
7046
- let key;
7046
+ let key2;
7047
7047
  try {
7048
- key = readFileSync16(CURSOR_API_KEY_FILE, "utf-8").trim();
7048
+ key2 = readFileSync16(CURSOR_API_KEY_FILE, "utf-8").trim();
7049
7049
  } catch {
7050
7050
  return null;
7051
7051
  }
7052
- if (!key) return null;
7052
+ if (!key2) return null;
7053
7053
  try {
7054
- const auth = Buffer.from(`${key}:`).toString("base64");
7054
+ const auth = Buffer.from(`${key2}:`).toString("base64");
7055
7055
  const r = await fetch("https://api.cursor.com/v1/me", {
7056
7056
  headers: { Authorization: `Basic ${auth}` },
7057
7057
  signal: AbortSignal.timeout(8e3)
@@ -7391,15 +7391,15 @@ function parseSynkroToml(raw) {
7391
7391
  }
7392
7392
  const eq = line.indexOf("=");
7393
7393
  if (eq === -1) continue;
7394
- const key = line.slice(0, eq).trim().replace(/^["']|["']$/g, "");
7394
+ const key2 = line.slice(0, eq).trim().replace(/^["']|["']$/g, "");
7395
7395
  let valRaw = line.slice(eq + 1).trim();
7396
7396
  if (!valRaw.startsWith('"') && !valRaw.startsWith("'") && !valRaw.startsWith("[")) {
7397
7397
  const h = valRaw.indexOf("#");
7398
7398
  if (h !== -1) valRaw = valRaw.slice(0, h).trim();
7399
7399
  }
7400
7400
  const value = parseTomlValue(valRaw);
7401
- if (section) result[section][key] = value;
7402
- else result[key] = value;
7401
+ if (section) result[section][key2] = value;
7402
+ else result[key2] = value;
7403
7403
  }
7404
7404
  return result;
7405
7405
  }
@@ -7676,7 +7676,7 @@ async function dockerInstall(opts = {}) {
7676
7676
  "SYNKRO_CODEX_MODEL",
7677
7677
  "SYNKRO_CONDUCTOR_MODEL",
7678
7678
  "SYNKRO_ROUTE_MODEL"
7679
- ].flatMap((key) => process.env[key] ? ["-e", `${key}=${process.env[key]}`] : []),
7679
+ ].flatMap((key2) => process.env[key2] ? ["-e", `${key2}=${process.env[key2]}`] : []),
7680
7680
  // Fix-poll kill switch. Default ON in the image; a benchmark/headless run
7681
7681
  // (e.g. sec-code-bench) sets SYNKRO_FIX_POLL=0 so ask-mode violations skip the
7682
7682
  // interactive AskUserQuestion poll and fall through to generate-the-fix. Only
@@ -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.8";
7937
+ if (true) return "1.10.9";
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((resolve8, reject) => {
7969
+ return new Promise((resolve9, 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
- resolve8(token);
8036
+ resolve9(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((resolve8, reject) => {
8062
+ return new Promise((resolve9, 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 ? resolve8() : reject(new Error(`codex login exited with code ${code}`)));
8068
+ proc.on("close", (code) => code === 0 ? resolve9() : reject(new Error(`codex login exited with code ${code}`)));
8069
8069
  });
8070
8070
  }
8071
8071
  async function setupCodexCloud(gatewayUrl, bearerToken, onStatus) {
@@ -8477,8 +8477,8 @@ function addUsage(a, b) {
8477
8477
  }
8478
8478
  function parseCodexTranscriptUsage(transcript, options = {}) {
8479
8479
  if (!transcript) return null;
8480
- const lines = transcript.split("\n");
8481
- const hasCanonicalAssistant = lines.some((line) => {
8480
+ const lines2 = transcript.split("\n");
8481
+ const hasCanonicalAssistant = lines2.some((line) => {
8482
8482
  try {
8483
8483
  const entry = JSON.parse(line);
8484
8484
  return entry?.type === "event_msg" && entry?.payload?.type === "agent_message";
@@ -8492,8 +8492,8 @@ function parseCodexTranscriptUsage(transcript, options = {}) {
8492
8492
  let previous = { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 };
8493
8493
  let latest = null;
8494
8494
  let sawSnapshot = false;
8495
- for (let i = 0; i < lines.length; i++) {
8496
- const line = lines[i].trim();
8495
+ for (let i = 0; i < lines2.length; i++) {
8496
+ const line = lines2[i].trim();
8497
8497
  if (!line) continue;
8498
8498
  let entry;
8499
8499
  try {
@@ -8675,8 +8675,8 @@ function parseClaudeTranscriptUsage(transcript, fallbackDay = (/* @__PURE__ */ n
8675
8675
  const entryModel = typeof message.model === "string" && message.model ? message.model : "unknown";
8676
8676
  if (entryModel !== "<synthetic>") model = entryModel;
8677
8677
  const day = isoDay(entry.timestamp, fallbackDay);
8678
- const key = `${day}\0${entryModel}`;
8679
- const row2 = rollups.get(key) ?? {
8678
+ const key2 = `${day}\0${entryModel}`;
8679
+ const row2 = rollups.get(key2) ?? {
8680
8680
  day,
8681
8681
  model: entryModel,
8682
8682
  turns: 0,
@@ -8690,7 +8690,7 @@ function parseClaudeTranscriptUsage(transcript, fallbackDay = (/* @__PURE__ */ n
8690
8690
  row2.output_tokens += counts.output_tokens;
8691
8691
  row2.cache_creation_input_tokens += counts.cache_creation_input_tokens;
8692
8692
  row2.cache_read_input_tokens += counts.cache_read_input_tokens;
8693
- rollups.set(key, row2);
8693
+ rollups.set(key2, row2);
8694
8694
  turns += 1;
8695
8695
  usage2.input_tokens += counts.input_tokens;
8696
8696
  usage2.output_tokens += counts.output_tokens;
@@ -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((resolve8) => {
8770
+ const ask3 = () => new Promise((resolve9) => {
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 resolve8(detected);
8775
+ return resolve9(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 resolve8([detected[n - 1]]);
8780
+ return resolve9([detected[n - 1]]);
8781
8781
  }
8782
8782
  console.log("Invalid choice. Try again.");
8783
- resolve8(ask3());
8783
+ resolve9(ask3());
8784
8784
  });
8785
8785
  });
8786
8786
  return ask3();
@@ -8803,17 +8803,17 @@ 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((resolve8) => {
8806
+ const key2 = await new Promise((resolve9) => {
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
- resolve8(answer.trim());
8811
+ resolve9(answer.trim());
8812
8812
  }
8813
8813
  );
8814
8814
  });
8815
- if (key) {
8816
- writeCursorApiKey2(key);
8815
+ if (key2) {
8816
+ writeCursorApiKey2(key2);
8817
8817
  console.log(" \u2713 Cursor API key saved.");
8818
8818
  } else {
8819
8819
  console.log(" \u26A0 Skipped \u2014 Cursor workers will be idle. Re-run install or pass --cursor-api-key=\u2026 later.");
@@ -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((resolve8) => {
8826
+ return new Promise((resolve9) => {
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
- resolve8(a === "cloud" ? "cloud" : a === "local" ? "local" : current);
8835
+ resolve9(a === "cloud" ? "cloud" : a === "local" ? "local" : current);
8836
8836
  }
8837
8837
  );
8838
8838
  });
@@ -8986,7 +8986,7 @@ function writeConfigEnv(opts) {
8986
8986
  const safeTier = sanitizeConfigValue(opts.tier ?? "pro", 32);
8987
8987
  const safeInference = sanitizeConfigValue(opts.inference ?? "fast", 16);
8988
8988
  const safeSynkroBin = sanitizeConfigValue(opts.synkroBin ?? "", 1024);
8989
- const lines = [
8989
+ const lines2 = [
8990
8990
  "# Synkro CLI config (managed by synkro install)",
8991
8991
  "# JWT auth \u2014 the hook scripts read SYNKRO_CREDENTIALS_PATH at runtime",
8992
8992
  "# and send Authorization: Bearer <access_token> on every gateway call.",
@@ -8994,27 +8994,27 @@ 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.8")}`
8997
+ `SYNKRO_VERSION=${shellQuoteSingle2("1.10.9")}`
8998
8998
  ];
8999
- if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
9000
- if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
9001
- if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle2(safeOrgId)}`);
9002
- if (safeEmail) lines.push(`SYNKRO_EMAIL=${shellQuoteSingle2(safeEmail)}`);
8999
+ if (safeSynkroBin) lines2.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
9000
+ if (safeUserId) lines2.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
9001
+ if (safeOrgId) lines2.push(`SYNKRO_ORG_ID=${shellQuoteSingle2(safeOrgId)}`);
9002
+ if (safeEmail) lines2.push(`SYNKRO_EMAIL=${shellQuoteSingle2(safeEmail)}`);
9003
9003
  if (opts.transcriptConsent !== void 0) {
9004
- lines.push(`SYNKRO_TRANSCRIPT_CONSENT=${shellQuoteSingle2(opts.transcriptConsent ? "yes" : "no")}`);
9004
+ lines2.push(`SYNKRO_TRANSCRIPT_CONSENT=${shellQuoteSingle2(opts.transcriptConsent ? "yes" : "no")}`);
9005
9005
  }
9006
- if (opts.transcriptConsentCC !== void 0) lines.push(`SYNKRO_TRANSCRIPT_CONSENT_CC=${shellQuoteSingle2(opts.transcriptConsentCC ? "yes" : "no")}`);
9007
- if (opts.transcriptConsentCursor !== void 0) lines.push(`SYNKRO_TRANSCRIPT_CONSENT_CURSOR=${shellQuoteSingle2(opts.transcriptConsentCursor ? "yes" : "no")}`);
9008
- if (opts.transcriptConsentCodex !== void 0) lines.push(`SYNKRO_TRANSCRIPT_CONSENT_CODEX=${shellQuoteSingle2(opts.transcriptConsentCodex ? "yes" : "no")}`);
9009
- lines.push(`SYNKRO_LOCAL_INFERENCE=${shellQuoteSingle2(opts.localInference ? "yes" : "no")}`);
9006
+ if (opts.transcriptConsentCC !== void 0) lines2.push(`SYNKRO_TRANSCRIPT_CONSENT_CC=${shellQuoteSingle2(opts.transcriptConsentCC ? "yes" : "no")}`);
9007
+ if (opts.transcriptConsentCursor !== void 0) lines2.push(`SYNKRO_TRANSCRIPT_CONSENT_CURSOR=${shellQuoteSingle2(opts.transcriptConsentCursor ? "yes" : "no")}`);
9008
+ if (opts.transcriptConsentCodex !== void 0) lines2.push(`SYNKRO_TRANSCRIPT_CONSENT_CODEX=${shellQuoteSingle2(opts.transcriptConsentCodex ? "yes" : "no")}`);
9009
+ lines2.push(`SYNKRO_LOCAL_INFERENCE=${shellQuoteSingle2(opts.localInference ? "yes" : "no")}`);
9010
9010
  const safeMode = sanitizeConfigValue(opts.deploymentMode ?? "docker", 16);
9011
- lines.push(`SYNKRO_DEPLOYMENT_MODE=${shellQuoteSingle2(safeMode)}`);
9012
- lines.push(`SYNKRO_GRADING_MODE=${shellQuoteSingle2(sanitizeConfigValue(opts.gradingMode ?? "local", 16))}`);
9013
- lines.push(`SYNKRO_STORAGE_MODE=${shellQuoteSingle2(sanitizeConfigValue(opts.storageMode ?? "local", 16))}`);
9014
- lines.push(`SYNKRO_DEPLOY_LOCATION=${shellQuoteSingle2(sanitizeConfigValue(opts.deployLocation ?? "local", 16))}`);
9015
- lines.push(`SYNKRO_HOOK_MODE=${shellQuoteSingle2(sanitizeConfigValue(opts.hookMode ?? "stub", 8))}`);
9016
- lines.push("");
9017
- writeFileSync17(CONFIG_PATH4, lines.join("\n"), "utf-8");
9011
+ lines2.push(`SYNKRO_DEPLOYMENT_MODE=${shellQuoteSingle2(safeMode)}`);
9012
+ lines2.push(`SYNKRO_GRADING_MODE=${shellQuoteSingle2(sanitizeConfigValue(opts.gradingMode ?? "local", 16))}`);
9013
+ lines2.push(`SYNKRO_STORAGE_MODE=${shellQuoteSingle2(sanitizeConfigValue(opts.storageMode ?? "local", 16))}`);
9014
+ lines2.push(`SYNKRO_DEPLOY_LOCATION=${shellQuoteSingle2(sanitizeConfigValue(opts.deployLocation ?? "local", 16))}`);
9015
+ lines2.push(`SYNKRO_HOOK_MODE=${shellQuoteSingle2(sanitizeConfigValue(opts.hookMode ?? "stub", 8))}`);
9016
+ lines2.push("");
9017
+ writeFileSync17(CONFIG_PATH4, lines2.join("\n"), "utf-8");
9018
9018
  chmodSync5(CONFIG_PATH4, 384);
9019
9019
  }
9020
9020
  function persistedTranscriptConsent(source) {
@@ -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.8",
9744
+ cli_version_to: "1.10.9",
9745
9745
  agents_detected: agents.map((a) => a.kind),
9746
9746
  with_github: false,
9747
9747
  with_local_cc: false,
@@ -10303,15 +10303,15 @@ function parseSynkroToml2(raw) {
10303
10303
  }
10304
10304
  const eq = line.indexOf("=");
10305
10305
  if (eq === -1) continue;
10306
- const key = line.slice(0, eq).trim().replace(/^["']|["']$/g, "");
10306
+ const key2 = line.slice(0, eq).trim().replace(/^["']|["']$/g, "");
10307
10307
  let valRaw = line.slice(eq + 1).trim();
10308
10308
  if (!valRaw.startsWith('"') && !valRaw.startsWith("'") && !valRaw.startsWith("[")) {
10309
10309
  const h = valRaw.indexOf("#");
10310
10310
  if (h !== -1) valRaw = valRaw.slice(0, h).trim();
10311
10311
  }
10312
10312
  const value = parseTomlValue2(valRaw);
10313
- if (section) result[section][key] = value;
10314
- else result[key] = value;
10313
+ if (section) result[section][key2] = value;
10314
+ else result[key2] = value;
10315
10315
  }
10316
10316
  return result;
10317
10317
  }
@@ -10592,8 +10592,8 @@ async function syncSkillFiles() {
10592
10592
  console.log(` \u2298 skill ${source}: empty file, skipped`);
10593
10593
  return null;
10594
10594
  }
10595
- const lines = content.split("\n").length;
10596
- console.log(` \u2192 read ${source} (${lines} lines, ${(content.length / 1024).toFixed(1)} KB)`);
10595
+ const lines2 = content.split("\n").length;
10596
+ console.log(` \u2192 read ${source} (${lines2} lines, ${(content.length / 1024).toFixed(1)} KB)`);
10597
10597
  return { source, content };
10598
10598
  }).filter(Boolean);
10599
10599
  if (tasks.length === 0) return;
@@ -10872,10 +10872,10 @@ function extractSessionInsights(projectsDir) {
10872
10872
  const filePath = join19(projectsDir, file);
10873
10873
  try {
10874
10874
  const content = readFileSync21(filePath, "utf-8");
10875
- const lines = content.split("\n").filter(Boolean);
10876
- for (let i = 0; i < lines.length; i++) {
10875
+ const lines2 = content.split("\n").filter(Boolean);
10876
+ for (let i = 0; i < lines2.length; i++) {
10877
10877
  try {
10878
- const entry = JSON.parse(lines[i]);
10878
+ const entry = JSON.parse(lines2[i]);
10879
10879
  if (entry.type === "user" && typeof entry.message?.content === "string" && entry.message.content.startsWith("This session is being continued")) {
10880
10880
  insights.push({
10881
10881
  session_id: sessionId,
@@ -10888,9 +10888,9 @@ function extractSessionInsights(projectsDir) {
10888
10888
  }
10889
10889
  }
10890
10890
  const userMessages = [];
10891
- for (let i = lines.length - 1; i >= 0 && userMessages.length < 20; i--) {
10891
+ for (let i = lines2.length - 1; i >= 0 && userMessages.length < 20; i--) {
10892
10892
  try {
10893
- const entry = JSON.parse(lines[i]);
10893
+ const entry = JSON.parse(lines2[i]);
10894
10894
  if (entry.type === "user") {
10895
10895
  const text = typeof entry.message?.content === "string" ? entry.message.content : Array.isArray(entry.message?.content) ? entry.message.content.map((b) => b.text ?? b).filter((t) => typeof t === "string").join(" ") : null;
10896
10896
  if (text && text.length > 10 && text.length < 2e3 && !text.startsWith("This session is being continued")) {
@@ -10949,13 +10949,13 @@ function extractTextContent(content) {
10949
10949
  function getCodexTranscriptFiles(repo) {
10950
10950
  const sessionsDir = join19(process.env.CODEX_HOME || join19(homedir21(), ".codex"), "sessions");
10951
10951
  if (!existsSync22(sessionsDir)) return [];
10952
- let relative2 = [];
10952
+ let relative3 = [];
10953
10953
  try {
10954
- relative2 = readdirSync4(sessionsDir, { recursive: true, encoding: "utf-8" });
10954
+ relative3 = readdirSync4(sessionsDir, { recursive: true, encoding: "utf-8" });
10955
10955
  } catch {
10956
10956
  return [];
10957
10957
  }
10958
- return relative2.filter((p) => p.endsWith(".jsonl")).map((p) => join19(sessionsDir, p)).filter((filePath) => {
10958
+ return relative3.filter((p) => p.endsWith(".jsonl")).map((p) => join19(sessionsDir, p)).filter((filePath) => {
10959
10959
  try {
10960
10960
  const first = readFileSync21(filePath, "utf-8").split("\n", 1)[0];
10961
10961
  const meta = JSON.parse(first);
@@ -10972,11 +10972,11 @@ function isJsonSyntaxError(error) {
10972
10972
  }
10973
10973
  function parseCodexTranscriptFile(filePath) {
10974
10974
  const transcript = readFileSync21(filePath, "utf-8");
10975
- const lines = transcript.split("\n");
10975
+ const lines2 = transcript.split("\n");
10976
10976
  const transcriptUsage = parseCodexTranscriptUsage(transcript);
10977
10977
  let sessionId = "";
10978
10978
  let model = "";
10979
- for (const line of lines) {
10979
+ for (const line of lines2) {
10980
10980
  try {
10981
10981
  const entry = JSON.parse(line);
10982
10982
  if (entry.type === "session_meta") {
@@ -11060,11 +11060,11 @@ function isSafeConvId(id) {
11060
11060
  }
11061
11061
  function parseCursorTranscriptFile(filePath) {
11062
11062
  const content = readFileSync21(filePath, "utf-8");
11063
- const lines = content.split("\n").filter(Boolean);
11063
+ const lines2 = content.split("\n").filter(Boolean);
11064
11064
  const messages = [];
11065
- for (let i = 0; i < lines.length; i++) {
11065
+ for (let i = 0; i < lines2.length; i++) {
11066
11066
  try {
11067
- const entry = JSON.parse(lines[i]);
11067
+ const entry = JSON.parse(lines2[i]);
11068
11068
  const role = entry.role || entry.message?.role;
11069
11069
  if (role !== "user" && role !== "assistant") continue;
11070
11070
  const text = extractTextContent(entry.message?.content ?? entry.content);
@@ -11124,11 +11124,11 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
11124
11124
  }
11125
11125
  function parseTranscriptFile(filePath) {
11126
11126
  const content = readFileSync21(filePath, "utf-8");
11127
- const lines = content.split("\n").filter(Boolean);
11127
+ const lines2 = content.split("\n").filter(Boolean);
11128
11128
  const messages = [];
11129
- for (let i = 0; i < lines.length; i++) {
11129
+ for (let i = 0; i < lines2.length; i++) {
11130
11130
  try {
11131
- const entry = JSON.parse(lines[i]);
11131
+ const entry = JSON.parse(lines2[i]);
11132
11132
  if (entry.type !== "user" && entry.type !== "assistant") continue;
11133
11133
  const msg = {
11134
11134
  message_index: i,
@@ -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((resolve8) => {
12033
+ return new Promise((resolve9) => {
12034
12034
  rl.question(" Type 'yes' to wipe everything (anything else cancels): ", (answer) => {
12035
12035
  rl.close();
12036
- resolve8(answer.trim().toLowerCase() === "yes");
12036
+ resolve9(answer.trim().toLowerCase() === "yes");
12037
12037
  });
12038
12038
  });
12039
12039
  }
@@ -12194,8 +12194,8 @@ function readRecentTurns(n = 20) {
12194
12194
  const size = statSync3(TURN_LOG_PATH).size;
12195
12195
  if (size === 0) return [];
12196
12196
  const text = readFileSync23(TURN_LOG_PATH, "utf-8");
12197
- const lines = text.split("\n").filter(Boolean);
12198
- const lastN = lines.slice(-n).reverse();
12197
+ const lines2 = text.split("\n").filter(Boolean);
12198
+ const lastN = lines2.slice(-n).reverse();
12199
12199
  return lastN.map((line) => {
12200
12200
  try {
12201
12201
  return JSON.parse(line);
@@ -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((resolve8, reject) => {
12289
+ const result = await new Promise((resolve9, 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
- resolve8(String(parsed.result ?? ""));
12315
+ resolve9(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((resolve8) => {
12341
+ return new Promise((resolve9) => {
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
- resolve8(ok);
12348
+ resolve9(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((resolve8, reject) => {
12380
+ return new Promise((resolve9, reject) => {
12381
12381
  const chunks = [];
12382
12382
  process.stdin.on("data", (c) => chunks.push(c));
12383
- process.stdin.on("end", () => resolve8(Buffer.concat(chunks).toString("utf-8")));
12383
+ process.stdin.on("end", () => resolve9(Buffer.concat(chunks).toString("utf-8")));
12384
12384
  process.stdin.on("error", reject);
12385
12385
  });
12386
12386
  }
@@ -12467,9 +12467,9 @@ async function fetchOrgRules(gatewayUrl, apiKey) {
12467
12467
  function applyLiteralMatchNegative(rules, file) {
12468
12468
  if (!file.patch) return [];
12469
12469
  const findings = [];
12470
- const lines = file.patch.split("\n");
12470
+ const lines2 = file.patch.split("\n");
12471
12471
  let currentNewLine = 0;
12472
- for (const line of lines) {
12472
+ for (const line of lines2) {
12473
12473
  if (line.startsWith("@@")) {
12474
12474
  const m = line.match(/\+(\d+)(?:,\d+)?/);
12475
12475
  if (m) currentNewLine = parseInt(m[1], 10);
@@ -12597,12 +12597,12 @@ async function fetchScanContext(gatewayUrl, apiKey, repo, prNumber, sha) {
12597
12597
  }
12598
12598
  function getFileDiffWithLines(file) {
12599
12599
  if (!file.patch) return { hunks: "", newFileLineMap: /* @__PURE__ */ new Map() };
12600
- const lines = file.patch.split("\n");
12600
+ const lines2 = file.patch.split("\n");
12601
12601
  const annotated = [];
12602
12602
  const lineMap = /* @__PURE__ */ new Map();
12603
12603
  let currentNewLine = 0;
12604
12604
  let patchIndex = 0;
12605
- for (const line of lines) {
12605
+ for (const line of lines2) {
12606
12606
  patchIndex++;
12607
12607
  if (line.startsWith("@@")) {
12608
12608
  const match = line.match(/\+(\d+)(?:,\d+)?/);
@@ -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((resolve8) => {
12637
+ return new Promise((resolve9) => {
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
- resolve8({ findings: [], latencyMs });
12665
+ resolve9({ 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
- resolve8({ findings, latencyMs });
12684
+ resolve9({ findings, latencyMs });
12685
12685
  } catch (parseErr) {
12686
12686
  console.warn(` failed to parse claude response: ${stdout.slice(0, 300)}`);
12687
- resolve8({ findings: [], latencyMs });
12687
+ resolve9({ 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((resolve8) => {
12736
+ return new Promise((resolve9) => {
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
- resolve8(fallbackReview(findings));
12763
+ resolve9(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
- resolve8({ summary: review.summary || "", comments, severity: maxSeverity });
12784
+ resolve9({ summary: review.summary || "", comments, severity: maxSeverity });
12785
12785
  } catch {
12786
12786
  console.warn(` failed to parse opus response, using fallback`);
12787
- resolve8(fallbackReview(findings));
12787
+ resolve9(fallbackReview(findings));
12788
12788
  }
12789
12789
  });
12790
12790
  });
@@ -12792,15 +12792,15 @@ function spawnOpusConsolidator(findings, claudeToken) {
12792
12792
  function fallbackReview(findings) {
12793
12793
  const grouped = /* @__PURE__ */ new Map();
12794
12794
  for (const f of findings) {
12795
- const key = `${f.file}::${f.category}`;
12796
- if (!grouped.has(key)) grouped.set(key, []);
12797
- grouped.get(key).push(f);
12795
+ const key2 = `${f.file}::${f.category}`;
12796
+ if (!grouped.has(key2)) grouped.set(key2, []);
12797
+ grouped.get(key2).push(f);
12798
12798
  }
12799
12799
  const comments = [];
12800
12800
  for (const [, group] of grouped) {
12801
12801
  const first = group[0];
12802
- const lines = group.map((f) => f.line);
12803
- const linesStr = lines.length > 1 ? `Lines ${lines.join(", ")}` : `Line ${lines[0]}`;
12802
+ const lines2 = group.map((f) => f.line);
12803
+ const linesStr = lines2.length > 1 ? `Lines ${lines2.join(", ")}` : `Line ${lines2[0]}`;
12804
12804
  const severityEmoji = first.severity === "critical" ? "\u{1F534}" : first.severity === "high" ? "\u{1F7E0}" : first.severity === "medium" ? "\u{1F7E1}" : "\u{1F535}";
12805
12805
  comments.push({
12806
12806
  path: first.file,
@@ -13254,10 +13254,10 @@ function stopTask(channel = CHANNEL_PRIMARY) {
13254
13254
  t = findTask(channel);
13255
13255
  }
13256
13256
  }
13257
- function tailLogs(lines = 80, channel = CHANNEL_PRIMARY) {
13257
+ function tailLogs(lines2 = 80, channel = CHANNEL_PRIMARY) {
13258
13258
  const t = findTask(channel);
13259
13259
  if (!t) return `(no ${channel.taskLabel} task)`;
13260
- const r = spawnSync9("pueue", ["log", "--lines", String(lines), String(t.id)], { encoding: "utf-8" });
13260
+ const r = spawnSync9("pueue", ["log", "--lines", String(lines2), String(t.id)], { encoding: "utf-8" });
13261
13261
  return r.stdout || r.stderr || "(no output)";
13262
13262
  }
13263
13263
  function ensureRunning(opts = {}) {
@@ -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((resolve8) => {
13270
+ return new Promise((resolve9) => {
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
- resolve8(ok);
13277
+ resolve9(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((resolve8) => {
13827
+ return new Promise((resolve9) => {
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
- resolve8();
13835
+ resolve9();
13836
13836
  };
13837
13837
  process.on("SIGINT", onSigint);
13838
13838
  });
@@ -14050,7 +14050,7 @@ function extractToolResultText(content, e) {
14050
14050
  function parseSession(file, seenStableIds) {
14051
14051
  const { filePath, sessionId, parentSessionId } = file;
14052
14052
  const transcript = readFileSync27(filePath, "utf-8");
14053
- const lines = transcript.split("\n").filter(Boolean);
14053
+ const lines2 = transcript.split("\n").filter(Boolean);
14054
14054
  const transcriptUsage = parseClaudeTranscriptUsage(
14055
14055
  transcript,
14056
14056
  statSync4(filePath).mtime.toISOString().slice(0, 10),
@@ -14062,10 +14062,10 @@ function parseSession(file, seenStableIds) {
14062
14062
  const messages = [];
14063
14063
  const actions = [];
14064
14064
  let step = 0;
14065
- for (let i = 0; i < lines.length; i++) {
14065
+ for (let i = 0; i < lines2.length; i++) {
14066
14066
  let e;
14067
14067
  try {
14068
- e = JSON.parse(lines[i]);
14068
+ e = JSON.parse(lines2[i]);
14069
14069
  } catch {
14070
14070
  continue;
14071
14071
  }
@@ -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((resolve8) => rl.question(q, (a) => {
14124
+ return new Promise((resolve9) => rl.question(q, (a) => {
14125
14125
  rl.close();
14126
- resolve8(/^y(es)?$/i.test(a.trim()));
14126
+ resolve9(/^y(es)?$/i.test(a.trim()));
14127
14127
  }));
14128
14128
  }
14129
14129
  async function importCommand() {
@@ -14261,8 +14261,8 @@ function computeDigest(canonical2) {
14261
14261
  }
14262
14262
  function verifySignature(digest, signatureB64, publicKeyPem) {
14263
14263
  try {
14264
- const key = crypto.createPublicKey(publicKeyPem);
14265
- return crypto.verify(null, Buffer.from(digest, "utf8"), key, Buffer.from(signatureB64, "base64"));
14264
+ const key2 = crypto.createPublicKey(publicKeyPem);
14265
+ return crypto.verify(null, Buffer.from(digest, "utf8"), key2, Buffer.from(signatureB64, "base64"));
14266
14266
  } catch {
14267
14267
  return false;
14268
14268
  }
@@ -14654,6 +14654,7 @@ function buildSpawnAgent(opts) {
14654
14654
  ["tmux", "set-option", "-t", session, "-q", "@synkro_harness", opts.harness],
14655
14655
  ["tmux", "set-option", "-t", session, "-q", "@synkro_space", opts.space],
14656
14656
  ["tmux", "set-option", "-t", session, "-q", "@synkro_backend", opts.backend],
14657
+ ["tmux", "set-option", "-t", session, "-q", "@synkro_mode", opts.mode || "native"],
14657
14658
  // Keep the pane visible after exit so the sidebar can render 'done'
14658
14659
  // instead of the agent silently vanishing.
14659
14660
  ["tmux", "set-option", "-t", session, "remain-on-exit", "on"]
@@ -14675,7 +14676,7 @@ function buildAgentSnapshot() {
14675
14676
  return ["sh", "-c", script];
14676
14677
  }
14677
14678
  function parseAgentSnapshot(output) {
14678
- const lines = String(output || "").split("\n");
14679
+ const lines2 = String(output || "").split("\n");
14679
14680
  const captures = /* @__PURE__ */ new Map();
14680
14681
  const listLines = [];
14681
14682
  let current = null;
@@ -14683,7 +14684,7 @@ function parseAgentSnapshot(output) {
14683
14684
  const flush2 = () => {
14684
14685
  if (current) captures.set(current, chunk.join("\n"));
14685
14686
  };
14686
- for (const line of lines) {
14687
+ for (const line of lines2) {
14687
14688
  if (line.startsWith("===")) {
14688
14689
  flush2();
14689
14690
  current = line.slice(3).trim();
@@ -14962,12 +14963,12 @@ async function discoverAgents(runner, backend, memory) {
14962
14963
  }
14963
14964
  function offlineAgents(live, records) {
14964
14965
  const alive = new Set(live.map((agent) => agent.session));
14965
- return records.filter((record) => !alive.has(record.session)).map((record) => ({
14966
- name: record.name,
14967
- session: record.session,
14968
- harness: record.harness,
14969
- space: record.space,
14970
- backend: record.backend,
14966
+ return records.filter((record2) => !alive.has(record2.session)).map((record2) => ({
14967
+ name: record2.name,
14968
+ session: record2.session,
14969
+ harness: record2.harness,
14970
+ space: record2.space,
14971
+ backend: record2.backend,
14971
14972
  status: "offline"
14972
14973
  }));
14973
14974
  }
@@ -15004,8 +15005,8 @@ function saveRecords(records) {
15004
15005
  } catch {
15005
15006
  }
15006
15007
  }
15007
- function recordSession(record) {
15008
- saveRecords([...loadRecords().filter((row2) => row2.session !== record.session), record]);
15008
+ function recordSession(record2) {
15009
+ saveRecords([...loadRecords().filter((row2) => row2.session !== record2.session), record2]);
15009
15010
  }
15010
15011
  function forgetSession(session) {
15011
15012
  saveRecords(loadRecords().filter((row2) => row2.session !== session));
@@ -15040,11 +15041,11 @@ function lastAgentFor(space) {
15040
15041
  return loadLastAgents()[String(space || "").replace(/\/+$/, "")] || "";
15041
15042
  }
15042
15043
  function rememberLastAgent(space, session) {
15043
- const key = String(space || "").replace(/\/+$/, "");
15044
- if (!key || !session) return;
15044
+ const key2 = String(space || "").replace(/\/+$/, "");
15045
+ if (!key2 || !session) return;
15045
15046
  try {
15046
15047
  mkdirSync21(dirname10(LAST_AGENT_FILE), { recursive: true });
15047
- writeFileSync24(LAST_AGENT_FILE, JSON.stringify({ ...loadLastAgents(), [key]: session }, null, 2));
15048
+ writeFileSync24(LAST_AGENT_FILE, JSON.stringify({ ...loadLastAgents(), [key2]: session }, null, 2));
15048
15049
  } catch {
15049
15050
  }
15050
15051
  }
@@ -15083,9 +15084,17 @@ var init_manifest = __esm({
15083
15084
  });
15084
15085
 
15085
15086
  // cli/ui/launch.ts
15086
- import { mkdirSync as mkdirSync22, writeFileSync as writeFileSync25 } from "fs";
15087
+ import { mkdirSync as mkdirSync22, statSync as statSync5, writeFileSync as writeFileSync25 } from "fs";
15087
15088
  import { homedir as homedir34 } from "os";
15088
15089
  import { join as join34 } from "path";
15090
+ function buildStamp(bootPath) {
15091
+ try {
15092
+ const stat = statSync5(bootPath);
15093
+ return String(stat.size) + ":" + String(Math.floor(stat.mtimeMs));
15094
+ } catch {
15095
+ return bootPath;
15096
+ }
15097
+ }
15089
15098
  function sidebarColumns(totalColumns) {
15090
15099
  return String(Math.max(20, Math.min(34, Math.round(totalColumns * 0.24))));
15091
15100
  }
@@ -15157,6 +15166,7 @@ async function styleOuterSession(bootPath, repoCwd, sidebarWidth) {
15157
15166
  const tabPopup = "display-popup -E -w 60 -h 18 -S fg=colour111 " + shellQuote3(tabLauncher);
15158
15167
  const dispatch = 'if-shell -F "#{==:#{mouse_status_range},tabmenu}" "' + tabPopup + '" "select-window -t="';
15159
15168
  const style = [
15169
+ ["set-option", "-t", UI_SESSION, "-q", "@synkro_build", buildStamp(bootPath)],
15160
15170
  // Mouse: drag the pane border to resize the sidebar, click a pane to
15161
15171
  // focus it, click rows/chips. Without this the fixed split reads as
15162
15172
  // "blocked in".
@@ -15190,6 +15200,38 @@ async function styleOuterSession(bootPath, repoCwd, sidebarWidth) {
15190
15200
  for (const argv of style) await run(HOST, ["tmux", ...argv]);
15191
15201
  for (const argv of buildClipboardBindings(UI_SESSION)) await run(HOST, argv);
15192
15202
  }
15203
+ function parseUiPanes(output) {
15204
+ return String(output || "").split("\n").map((line) => line.split("|")).filter((cols) => cols.length >= 4).map(([window, pane, width, ...command]) => ({
15205
+ window,
15206
+ pane,
15207
+ width: Number(width) || 0,
15208
+ command: command.join("|")
15209
+ })).filter((row2) => Boolean(row2.window && row2.pane));
15210
+ }
15211
+ async function refreshUiShell(bootPath, repoCwd) {
15212
+ const expected = buildStamp(bootPath);
15213
+ const current = await run(HOST, ["tmux", "show-options", "-t", UI_SESSION, "-v", "@synkro_build"]);
15214
+ if (current.ok && current.stdout.trim() === expected) return;
15215
+ const listed = await run(HOST, [
15216
+ "tmux",
15217
+ "list-panes",
15218
+ "-s",
15219
+ "-t",
15220
+ UI_SESSION,
15221
+ "-F",
15222
+ "#{window_id}|#{pane_id}|#{pane_width}|#{pane_start_command}"
15223
+ ]);
15224
+ if (!listed.ok) return;
15225
+ const panes = parseUiPanes(listed.stdout);
15226
+ const sidebars = panes.filter((row2) => row2.command.includes("--sidebar"));
15227
+ if (sidebars.length === 0) return;
15228
+ await styleOuterSession(bootPath, repoCwd, sidebars[0].width || 30);
15229
+ for (const sidebar of sidebars) {
15230
+ const center = panes.find((row2) => row2.window === sidebar.window && row2.pane !== sidebar.pane);
15231
+ if (!center) continue;
15232
+ await run(HOST, ["tmux", "respawn-pane", "-k", "-t", sidebar.pane, sidebarCommand(bootPath, center.pane, repoCwd)]);
15233
+ }
15234
+ }
15193
15235
  async function buildTab(bootPath, repoCwd, spec) {
15194
15236
  await run(HOST, ["tmux", "set-option", "-g", "history-limit", "50000"]);
15195
15237
  let windowTarget;
@@ -15282,6 +15324,8 @@ async function launchUi(bootPath, repoCwd) {
15282
15324
  rememberSpace(repoCwd);
15283
15325
  if (!await uiSessionExists()) {
15284
15326
  await buildTab(bootPath, repoCwd, { cwd: repoCwd, center: makeTerminalCommand(bootPath), focus: "sidebar" });
15327
+ } else {
15328
+ await refreshUiShell(bootPath, repoCwd);
15285
15329
  }
15286
15330
  await pruneCollapsedClients();
15287
15331
  return runInherit(process.env.TMUX ? ["tmux", "switch-client", "-t", UI_SESSION] : ["tmux", "attach-session", "-t", UI_SESSION]);
@@ -15338,10 +15382,10 @@ function row(selected, width, content) {
15338
15382
  return STYLE.select + body.split(STYLE.reset).join(STYLE.reset + STYLE.select) + STYLE.reset;
15339
15383
  }
15340
15384
  function stripForPad(text, width) {
15341
- let visible = 0;
15385
+ let visible2 = 0;
15342
15386
  let out = "";
15343
15387
  let index = 0;
15344
- while (index < text.length && visible < width) {
15388
+ while (index < text.length && visible2 < width) {
15345
15389
  if (text.startsWith(ESC, index)) {
15346
15390
  const end = text.indexOf("m", index);
15347
15391
  if (end === -1) break;
@@ -15350,13 +15394,13 @@ function stripForPad(text, width) {
15350
15394
  } else {
15351
15395
  out += text[index];
15352
15396
  index += 1;
15353
- visible += 1;
15397
+ visible2 += 1;
15354
15398
  }
15355
15399
  }
15356
- return out + " ".repeat(Math.max(0, width - visible));
15400
+ return out + " ".repeat(Math.max(0, width - visible2));
15357
15401
  }
15358
15402
  function visibleLength(text) {
15359
- let visible = 0;
15403
+ let visible2 = 0;
15360
15404
  let index = 0;
15361
15405
  while (index < text.length) {
15362
15406
  if (text.startsWith(ESC, index)) {
@@ -15364,11 +15408,11 @@ function visibleLength(text) {
15364
15408
  if (end === -1) break;
15365
15409
  index = end + 1;
15366
15410
  } else {
15367
- visible += 1;
15411
+ visible2 += 1;
15368
15412
  index += 1;
15369
15413
  }
15370
15414
  }
15371
- return visible;
15415
+ return visible2;
15372
15416
  }
15373
15417
  function splitRow(width, left, right) {
15374
15418
  const gap = Math.max(1, width - 2 - visibleLength(left) - visibleLength(right));
@@ -15380,10 +15424,10 @@ function windowAround(count, selected, capacity) {
15380
15424
  return { start, end: start + capacity };
15381
15425
  }
15382
15426
  function renderCollapsed(state, width, height) {
15383
- const lines = [];
15427
+ const lines2 = [];
15384
15428
  const targets = [];
15385
15429
  const push2 = (text, target = null) => {
15386
- lines.push(stripForPad(" " + text, width));
15430
+ lines2.push(stripForPad(" " + text, width));
15387
15431
  targets.push(target);
15388
15432
  };
15389
15433
  push2("");
@@ -15400,16 +15444,16 @@ function renderCollapsed(state, width, height) {
15400
15444
  });
15401
15445
  push2("");
15402
15446
  push2(STYLE.dim + "+" + STYLE.reset, { kind: "new" });
15403
- while (lines.length < height - 1) push2("");
15447
+ while (lines2.length < height - 1) push2("");
15404
15448
  push2(STYLE.dim + "\u203A\u203A" + STYLE.reset, { kind: "collapse" });
15405
- return { lines: lines.slice(0, height), targets: targets.slice(0, height) };
15449
+ return { lines: lines2.slice(0, height), targets: targets.slice(0, height) };
15406
15450
  }
15407
15451
  function renderLayout(state, width = 30, height = 40) {
15408
15452
  if (state.collapsed) return renderCollapsed(state, width, height);
15409
- const lines = [];
15453
+ const lines2 = [];
15410
15454
  const targets = [];
15411
15455
  const push2 = (line, target = null) => {
15412
- lines.push(line);
15456
+ lines2.push(line);
15413
15457
  targets.push(target);
15414
15458
  };
15415
15459
  const chrome = 8;
@@ -15431,7 +15475,7 @@ function renderLayout(state, width = 30, height = 40) {
15431
15475
  push2(row(selected, width, " " + STYLE.branch + clip(space.branch, width - 6 - (space.track ? space.track.length + 1 : 0)) + STYLE.reset + drift), target);
15432
15476
  });
15433
15477
  if (state.spaces.length === 0) push2(row(false, width, STYLE.dim + "no spaces found" + STYLE.reset));
15434
- while (lines.length < 3 + spacesCapacity * 2) push2(pad("", width));
15478
+ while (lines2.length < 3 + spacesCapacity * 2) push2(pad("", width));
15435
15479
  push2("");
15436
15480
  push2(splitRow(width, "new", "menu"), { kind: "new" });
15437
15481
  push2(STYLE.dim + "\u2500".repeat(Math.max(0, width)) + STYLE.reset);
@@ -15476,13 +15520,13 @@ function renderLayout(state, width = 30, height = 40) {
15476
15520
  push2("");
15477
15521
  push2(row(false, width, STYLE.blocked + "\u26D4 needs consent" + STYLE.reset));
15478
15522
  for (const action of actionsForAsk(selectedAgent.ask)) {
15479
- const key = action === "track" ? "g" : action === "skip" ? "s" : "y";
15480
- push2(row(false, width, STYLE.dim + " " + key + " \u2014 " + action + STYLE.reset));
15523
+ const key2 = action === "track" ? "g" : action === "skip" ? "s" : "y";
15524
+ push2(row(false, width, STYLE.dim + " " + key2 + " \u2014 " + action + STYLE.reset));
15481
15525
  }
15482
15526
  }
15483
- while (lines.length < height - 1) push2(pad("", width));
15527
+ while (lines2.length < height - 1) push2(pad("", width));
15484
15528
  push2(stripForPad(pad("", width - 3) + STYLE.dim + "\u2039\u2039 " + STYLE.reset, width), { kind: "collapse" });
15485
- return { lines: lines.slice(0, height), targets: targets.slice(0, height) };
15529
+ return { lines: lines2.slice(0, height), targets: targets.slice(0, height) };
15486
15530
  }
15487
15531
  function nextSelection(state, spaces, agents, delta) {
15488
15532
  const flat = state.section === "spaces" ? state.spaceIndex : spaces + state.agentIndex;
@@ -15631,8 +15675,8 @@ async function spawnAgent(info, request) {
15631
15675
  if (request.backend === "container") {
15632
15676
  cwd = request.cwd.startsWith(CONTAINER_WORK) ? request.cwd : await provisionContainerWorkspace(runner, slug);
15633
15677
  }
15634
- const command = harnessCommand(request);
15635
- for (const argv of buildSpawnAgent({ name: slug, cwd, command, harness: request.harness, space: cwd, backend: request.backend })) {
15678
+ const command = request.command || harnessCommand(request);
15679
+ for (const argv of buildSpawnAgent({ name: slug, cwd, command, harness: request.harness, space: cwd, backend: request.backend, mode: request.mode })) {
15636
15680
  const result = await run(runner, argv);
15637
15681
  if (!result.ok && argv[1] === "new-session") {
15638
15682
  return { ok: false, session, error: result.stderr.trim() || "tmux new-session failed" };
@@ -15646,7 +15690,8 @@ async function spawnAgent(info, request) {
15646
15690
  harness: request.harness,
15647
15691
  space: cwd,
15648
15692
  spaceName: request.spaceName,
15649
- backend: request.backend
15693
+ backend: request.backend,
15694
+ mode: request.mode || "native"
15650
15695
  });
15651
15696
  return { ok: true, session };
15652
15697
  }
@@ -16046,45 +16091,45 @@ async function runSidebar() {
16046
16091
  }
16047
16092
  await activate(target);
16048
16093
  }
16049
- async function handleKey(key) {
16050
- if (key === " ") state.section = state.section === "spaces" ? "agents" : "spaces";
16051
- else if (key === "j" || key === CSI + "B") moveSelection(1);
16052
- else if (key === "k" || key === CSI + "A") moveSelection(-1);
16053
- else if (key === "\r") {
16094
+ async function handleKey(key2) {
16095
+ if (key2 === " ") state.section = state.section === "spaces" ? "agents" : "spaces";
16096
+ else if (key2 === "j" || key2 === CSI + "B") moveSelection(1);
16097
+ else if (key2 === "k" || key2 === CSI + "A") moveSelection(-1);
16098
+ else if (key2 === "\r") {
16054
16099
  if (state.section === "agents") await attachSelected();
16055
16100
  else await openDialog("new-tab");
16056
- } else if (key === "n" || key === "T") {
16101
+ } else if (key2 === "n" || key2 === "T") {
16057
16102
  worldChanged = true;
16058
16103
  await openDialog("new-tab");
16059
- } else if (key === "m") await mainMenu();
16060
- else if (key === "O") void openDialog("new-workspace");
16061
- else if (key === "C") {
16104
+ } else if (key2 === "m") await mainMenu();
16105
+ else if (key2 === "O") void openDialog("new-workspace");
16106
+ else if (key2 === "C") {
16062
16107
  worldChanged = true;
16063
16108
  closeSelectedWorkspace();
16064
- } else if (key === "r") {
16109
+ } else if (key2 === "r") {
16065
16110
  worldChanged = true;
16066
16111
  await restoreSelected();
16067
- } else if (key === "d") {
16112
+ } else if (key2 === "d") {
16068
16113
  const client = await attachedClient();
16069
16114
  await run(host, client ? ["tmux", "detach-client", "-t", client] : ["tmux", "detach-client"]);
16070
- } else if (key === "K") await keybindsMenu();
16071
- else if (key === "G") {
16115
+ } else if (key2 === "K") await keybindsMenu();
16116
+ else if (key2 === "G") {
16072
16117
  state.grouped = !state.grouped;
16073
16118
  applyFilter();
16074
- } else if (key === "a") {
16119
+ } else if (key2 === "a") {
16075
16120
  state.filter = state.filter === "space" ? "all" : "space";
16076
16121
  applyFilter();
16077
- } else if (key === "<" || key === ">" || key === "," || key === ".") await toggleCollapsed();
16078
- else if (key === "x") {
16122
+ } else if (key2 === "<" || key2 === ">" || key2 === "," || key2 === ".") await toggleCollapsed();
16123
+ else if (key2 === "x") {
16079
16124
  worldChanged = true;
16080
16125
  await confirmKillAgent();
16081
- } else if (key === "i") {
16126
+ } else if (key2 === "i") {
16082
16127
  const agent = state.agents[state.agentIndex];
16083
16128
  if (agent) await run(runnerFor(agent.backend), buildInterrupt(agent.session));
16084
- } else if (key === "g") await consent("track");
16085
- else if (key === "s") await consent("skip");
16086
- else if (key === "y") await consent("stay");
16087
- else if (key === "q" || key === KEY_CTRL_C) {
16129
+ } else if (key2 === "g") await consent("track");
16130
+ else if (key2 === "s") await consent("skip");
16131
+ else if (key2 === "y") await consent("stay");
16132
+ else if (key2 === "q" || key2 === KEY_CTRL_C) {
16088
16133
  await snapshotTabs();
16089
16134
  releaseAwake();
16090
16135
  await run(host, ["tmux", "kill-session", "-t", outerSession]);
@@ -16206,8 +16251,8 @@ var init_repos = __esm({
16206
16251
 
16207
16252
  // cli/ui/tabs.ts
16208
16253
  import { execSync as execSync7 } from "child_process";
16209
- function isLegacyCodexRenderer(command) {
16210
- return /(?:^|\s)ui\s+--run\s+codex(?:\s|$)/.test(String(command || "").trim());
16254
+ function embeddedSessionCommand(bootPath, harness, cwd) {
16255
+ return ["node", bootPath, "ui", "--run", harness, cwd].map(shellQuote3).join(" ");
16211
16256
  }
16212
16257
  function repoRoot() {
16213
16258
  try {
@@ -16229,6 +16274,29 @@ async function createTab(bootPath, kind, spacePath) {
16229
16274
  });
16230
16275
  return;
16231
16276
  }
16277
+ if (kind === "cursor-synkro" || kind === "codex-synkro") {
16278
+ const harness2 = kind === "codex-synkro" ? "codex" : "cursor";
16279
+ const command = embeddedSessionCommand(bootPath, harness2, spacePath);
16280
+ const spawned2 = await spawnAgent({ runner: HOST2, backend: "host", note: "runtime: host" }, {
16281
+ name: space + "-" + harness2 + "-synkro-" + String(process.pid % 1e4),
16282
+ harness: harness2,
16283
+ spaceName: space,
16284
+ cwd: spacePath,
16285
+ backend: "host",
16286
+ command,
16287
+ mode: "embedded"
16288
+ });
16289
+ if (!spawned2.ok) return;
16290
+ await buildTab(bootPath, repo, {
16291
+ cwd: spacePath,
16292
+ center: buildCenterAttachCommand(HOST2, spawned2.session),
16293
+ title: tabTitle(harness2, space),
16294
+ kind,
16295
+ agentSession: spawned2.session,
16296
+ focus: "center"
16297
+ });
16298
+ return;
16299
+ }
16232
16300
  if (kind === "settings") {
16233
16301
  await buildTab(bootPath, repo, {
16234
16302
  cwd: spacePath,
@@ -16272,43 +16340,26 @@ async function openAgentTab(bootPath, session) {
16272
16340
  "-p",
16273
16341
  "-t",
16274
16342
  session,
16275
- ["#{@synkro_harness}", "#{@synkro_space}", "#{@synkro_backend}"].join("|")
16343
+ ["#{@synkro_harness}", "#{@synkro_space}", "#{@synkro_backend}", "#{@synkro_mode}"].join("|")
16276
16344
  ]);
16277
- const [harness, space, backend] = (meta.stdout.trim() || "||").split("|");
16345
+ const [harness, space, backend, mode] = (meta.stdout.trim() || "|||").split("|");
16278
16346
  const containerHosted = backend === "container";
16279
- let runner = containerHosted ? info.runner : HOST2;
16280
- let activeSession = session;
16347
+ const runner = mode === "embedded" ? HOST2 : containerHosted ? info.runner : HOST2;
16281
16348
  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;
16349
+ const alive = (await run(runner, ["tmux", "has-session", "-t", session])).ok;
16300
16350
  if (!alive) return;
16301
16351
  await buildTab(bootPath, repoRoot(), {
16302
16352
  cwd: space || repoRoot(),
16303
- center: buildCenterAttachCommand(runner, activeSession),
16353
+ center: buildCenterAttachCommand(runner, session),
16304
16354
  title: tabTitle(harness || "claude", spaceName),
16305
- kind: harness || "claude",
16306
- agentSession: activeSession,
16355
+ kind: mode === "embedded" ? (harness || "cursor") + "-synkro" : harness || "claude",
16356
+ agentSession: session,
16307
16357
  focus: "center"
16308
16358
  });
16309
16359
  }
16310
16360
  async function restoreTabs(bootPath, repoCwd) {
16311
16361
  const tabs = loadTabs();
16362
+ const records = loadRecords();
16312
16363
  if (tabs.length === 0) return false;
16313
16364
  const info = await detectContainerBackend();
16314
16365
  const hostSessions = (await run(HOST2, ["tmux", "list-sessions", "-F", "#{session_name}"])).stdout;
@@ -16318,16 +16369,11 @@ async function restoreTabs(bootPath, repoCwd) {
16318
16369
  for (const tab of tabs) {
16319
16370
  const space = tab.cwd.split("/").filter(Boolean).pop() || "space";
16320
16371
  const containerHosted = info.backend === "container" && (await run(info.runner, ["test", "-d", tab.cwd])).ok;
16321
- const harness = ["claude", "codex", "cursor"].includes(tab.kind) ? tab.kind : "";
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
- }
16372
+ const embedded = tab.kind === "cursor-synkro" || tab.kind === "codex-synkro";
16373
+ const harness = embedded ? tab.kind.replace("-synkro", "") : ["claude", "codex", "cursor"].includes(tab.kind) ? tab.kind : "";
16374
+ const sessionRunner = embedded || harness === "codex" ? HOST2 : containerHosted ? info.runner : HOST2;
16375
+ const sessionAlive = Boolean(tab.agentSession && alive.has(tab.agentSession));
16376
+ const record2 = records.find((row2) => row2.session === tab.agentSession);
16331
16377
  if (tab.agentSession && sessionAlive) {
16332
16378
  await run(sessionRunner, buildEnableMouse(tab.agentSession));
16333
16379
  for (const argv of buildClipboardBindings(tab.agentSession)) {
@@ -16342,20 +16388,23 @@ async function restoreTabs(bootPath, repoCwd) {
16342
16388
  focus: "center"
16343
16389
  });
16344
16390
  } else if (harness) {
16391
+ const command = embedded ? embeddedSessionCommand(bootPath, harness, tab.cwd) : void 0;
16345
16392
  const spawned = await spawnAgent(info, {
16346
- name: space + "-" + harness + "-" + String(process.pid % 1e4),
16393
+ name: record2?.name || space + "-" + harness + "-" + String(process.pid % 1e4),
16347
16394
  harness,
16348
16395
  spaceName: space,
16349
16396
  cwd: tab.cwd,
16350
- backend: harness === "codex" ? "host" : containerHosted ? "container" : "host",
16351
- resume: true
16397
+ backend: embedded || harness === "codex" ? "host" : containerHosted ? "container" : "host",
16398
+ resume: !embedded,
16399
+ command,
16400
+ mode: embedded ? "embedded" : "native"
16352
16401
  });
16353
- const restoredInContainer = harness !== "codex" && containerHosted;
16402
+ const restoredInContainer = !embedded && harness !== "codex" && containerHosted;
16354
16403
  await buildTab(bootPath, repoCwd, spawned.ok ? {
16355
16404
  cwd: tab.cwd,
16356
16405
  center: buildCenterAttachCommand(restoredInContainer ? info.runner : HOST2, spawned.session),
16357
16406
  title: tabTitle(harness, space),
16358
- kind: harness,
16407
+ kind: embedded ? tab.kind : harness,
16359
16408
  agentSession: spawned.session,
16360
16409
  focus: "center"
16361
16410
  } : { cwd: tab.cwd, center: makeTerminalCommand(bootPath), title: tabTitle("terminal", space), kind: "terminal", focus: "center" });
@@ -16387,11 +16436,20 @@ var init_tabs = __esm({
16387
16436
  // cli/ui/dialog.ts
16388
16437
  import { existsSync as existsSync37 } from "fs";
16389
16438
  import { homedir as homedir37 } from "os";
16439
+ function providerChoices(harnesses) {
16440
+ return [
16441
+ { value: "terminal", label: TAB_GLYPHS.terminal + " Terminal" },
16442
+ ...harnesses.map((harness) => ({
16443
+ value: harness === "cursor" || harness === "codex" ? harness + "-synkro" : harness,
16444
+ label: (TAB_GLYPHS[harness] || "") + " " + (HARNESS_LABELS[harness] || harness)
16445
+ }))
16446
+ ];
16447
+ }
16390
16448
  function write(text) {
16391
16449
  process.stdout.write(text);
16392
16450
  }
16393
16451
  function visibleLength2(text) {
16394
- let visible = 0;
16452
+ let visible2 = 0;
16395
16453
  let index = 0;
16396
16454
  while (index < text.length) {
16397
16455
  if (text.startsWith(CSI2, index)) {
@@ -16399,11 +16457,11 @@ function visibleLength2(text) {
16399
16457
  if (end === -1) break;
16400
16458
  index = end + 1;
16401
16459
  } else {
16402
- visible += 1;
16460
+ visible2 += 1;
16403
16461
  index += 1;
16404
16462
  }
16405
16463
  }
16406
- return visible;
16464
+ return visible2;
16407
16465
  }
16408
16466
  function clipPath(text, max) {
16409
16467
  const value = String(text || "");
@@ -16446,34 +16504,34 @@ async function pick(opts) {
16446
16504
  const perItem = opts.choices.some((choice) => choice.detail) ? 2 : 1;
16447
16505
  const visibleItems = Math.max(1, Math.floor(Math.max(2, height - 5) / perItem));
16448
16506
  const firstRow = 4;
16449
- return new Promise((resolve8) => {
16507
+ return new Promise((resolve9) => {
16450
16508
  const view = () => opts.choices.filter((choice) => matches(choice, filter));
16451
16509
  const draw = () => {
16452
16510
  const shown = view();
16453
16511
  if (selected >= shown.length) selected = Math.max(0, shown.length - 1);
16454
16512
  if (selected < top) top = selected;
16455
16513
  if (selected >= top + visibleItems) top = selected - visibleItems + 1;
16456
- const lines = [];
16457
- lines.push(" " + S.title + opts.title + S.reset);
16514
+ const lines2 = [];
16515
+ lines2.push(" " + S.title + opts.title + S.reset);
16458
16516
  const left = filter && !opts.menu ? " " + S.accent + "/ " + S.reset + filter + "\u258C" : " " + S.dim + opts.hint + S.reset;
16459
16517
  const right = opts.menu ? "" : S.dim + String(shown.length) + (shown.length === 1 ? " match" : " matches") + S.reset;
16460
- lines.push(padRow(left, Math.max(0, width - visibleLength2(right) - 1)) + right);
16461
- lines.push("");
16462
- if (shown.length === 0) lines.push(" " + S.dim + (opts.emptyNote || "nothing matches") + S.reset);
16518
+ lines2.push(padRow(left, Math.max(0, width - visibleLength2(right) - 1)) + right);
16519
+ lines2.push("");
16520
+ if (shown.length === 0) lines2.push(" " + S.dim + (opts.emptyNote || "nothing matches") + S.reset);
16463
16521
  shown.slice(top, top + visibleItems).forEach((choice, offset) => {
16464
16522
  const index = top + offset;
16465
16523
  const isSelected = index === selected;
16466
16524
  const marker = isSelected ? S.accent + "\u203A" + S.reset + " " : " ";
16467
16525
  const note = choice.note ? S.dim + choice.note + S.reset : "";
16468
16526
  const head = " " + marker + S.bold + clip2(choice.label, width - 6 - visibleLength2(note)) + S.reset;
16469
- lines.push(selectable(padRow(head, Math.max(0, width - visibleLength2(note) - 1)) + note, width, isSelected));
16527
+ lines2.push(selectable(padRow(head, Math.max(0, width - visibleLength2(note) - 1)) + note, width, isSelected));
16470
16528
  if (perItem === 2) {
16471
- lines.push(selectable(" " + S.muted + clipPath(choice.detail || "", width - 6) + S.reset, width, isSelected));
16529
+ lines2.push(selectable(" " + S.muted + clipPath(choice.detail || "", width - 6) + S.reset, width, isSelected));
16472
16530
  }
16473
16531
  });
16474
- while (lines.length < height - 1) lines.push("");
16475
- lines.push(" " + S.dim + opts.footer + S.reset);
16476
- write(CSI2 + "H" + lines.slice(0, height).map((line) => padRow(line, width)).join("\n"));
16532
+ while (lines2.length < height - 1) lines2.push("");
16533
+ lines2.push(" " + S.dim + opts.footer + S.reset);
16534
+ write(CSI2 + "H" + lines2.slice(0, height).map((line) => padRow(line, width)).join("\n"));
16477
16535
  };
16478
16536
  const rowOfItem = (index) => firstRow + (index - top) * perItem;
16479
16537
  const MOUSE = /\[<(\d+);(\d+);(\d+)([Mm])/g;
@@ -16494,7 +16552,7 @@ async function pick(opts) {
16494
16552
  const hit = shown.findIndex((_, index) => y >= rowOfItem(index) && y < rowOfItem(index) + perItem);
16495
16553
  if (hit >= 0) {
16496
16554
  process.stdin.off("data", onData);
16497
- resolve8(shown[hit].value);
16555
+ resolve9(shown[hit].value);
16498
16556
  return;
16499
16557
  }
16500
16558
  }
@@ -16502,13 +16560,13 @@ async function pick(opts) {
16502
16560
  if (!sawMouse) {
16503
16561
  if (input === KEY_ESC && !input.includes("[<") || input === KEY_CTRL_C2) {
16504
16562
  process.stdin.off("data", onData);
16505
- resolve8(null);
16563
+ resolve9(null);
16506
16564
  return;
16507
16565
  }
16508
16566
  if (input === "\r") {
16509
16567
  if (shown.length === 0) return;
16510
16568
  process.stdin.off("data", onData);
16511
- resolve8(shown[selected].value);
16569
+ resolve9(shown[selected].value);
16512
16570
  return;
16513
16571
  }
16514
16572
  const down = input === CSI2 + "B" || opts.menu && (input === "j" || input === CSI2 + "C");
@@ -16532,9 +16590,9 @@ async function readLine(opts) {
16532
16590
  let value = "";
16533
16591
  let error = "";
16534
16592
  const width = Math.max(20, Number(process.stdout.columns || 80));
16535
- return new Promise((resolve8) => {
16593
+ return new Promise((resolve9) => {
16536
16594
  const draw = () => {
16537
- const lines = [
16595
+ const lines2 = [
16538
16596
  " " + S.title + opts.title + S.reset,
16539
16597
  "",
16540
16598
  " " + S.dim + opts.label + S.reset,
@@ -16545,13 +16603,13 @@ async function readLine(opts) {
16545
16603
  "",
16546
16604
  " " + S.dim + opts.footer + S.reset
16547
16605
  ];
16548
- write(CSI2 + "2J" + CSI2 + "H" + lines.map((line) => padRow(line, width)).join("\n"));
16606
+ write(CSI2 + "2J" + CSI2 + "H" + lines2.map((line) => padRow(line, width)).join("\n"));
16549
16607
  };
16550
16608
  const onData = (chunk) => {
16551
16609
  const input = chunk.toString("utf8");
16552
16610
  if (input === KEY_ESC || input === KEY_CTRL_C2) {
16553
16611
  process.stdin.off("data", onData);
16554
- resolve8(null);
16612
+ resolve9(null);
16555
16613
  return;
16556
16614
  }
16557
16615
  if (input === "\r") {
@@ -16563,7 +16621,7 @@ async function readLine(opts) {
16563
16621
  return;
16564
16622
  }
16565
16623
  process.stdin.off("data", onData);
16566
- resolve8(value.trim());
16624
+ resolve9(value.trim());
16567
16625
  })();
16568
16626
  return;
16569
16627
  }
@@ -16823,13 +16881,7 @@ async function runDialog(kind, repoCwd, argA = "", argB = "") {
16823
16881
  process.exit(0);
16824
16882
  }
16825
16883
  const harnesses = await detectHarnesses();
16826
- const sessions = [
16827
- { value: "terminal", label: TAB_GLYPHS.terminal + " Terminal" },
16828
- ...harnesses.map((harness) => ({
16829
- value: harness,
16830
- label: (TAB_GLYPHS[harness] || "") + " " + (HARNESS_LABELS[harness] || harness)
16831
- }))
16832
- ];
16884
+ const sessions = providerChoices(harnesses);
16833
16885
  for (; ; ) {
16834
16886
  const session = await pick({
16835
16887
  menu: true,
@@ -16896,15 +16948,27 @@ function terminalText(text) {
16896
16948
  return String(text).replace(/[\u0000-\u001F\u007F-\u009F]/g, "");
16897
16949
  }
16898
16950
  function clip3(text, max) {
16899
- const value = String(text || "").replace(/\s+/g, " ").trim();
16951
+ const value = cleanOutput(text).replace(/\s+/g, " ").trim();
16900
16952
  if (max <= 1) return value;
16901
16953
  return value.length <= max ? value : value.slice(0, max - 1) + "\u2026";
16902
16954
  }
16955
+ function cleanOutput(text) {
16956
+ return String(text || "").replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, "").replace(/\x1b\[[0-?]*[ -\/]*[@-~]/g, "").replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "");
16957
+ }
16903
16958
  function seconds(ms) {
16904
16959
  const total = Math.max(0, Math.round(ms / 1e3));
16905
16960
  if (total < 60) return total + "s";
16906
16961
  return Math.floor(total / 60) + "m" + String(total % 60).padStart(2, "0") + "s";
16907
16962
  }
16963
+ function blockNextStep(reason) {
16964
+ if (/tracking (?:decision|is required)|task tracking|skip tracking|\bconductor\b/i.test(reason)) {
16965
+ return "Next: reply \u201Ctrack it\u201D to create a task, or \u201Cskip tracking\u201D to continue untracked.";
16966
+ }
16967
+ if (/\bCWE-\d+\b|\bCVE-\d{4}-\d+\b/i.test(reason)) {
16968
+ return "Next: revise the flagged code, then retry. Do not bypass the rule.";
16969
+ }
16970
+ return "Next: resolve the rule above, then retry.";
16971
+ }
16908
16972
  function statusLine(opts) {
16909
16973
  const hint = opts.hint ? " \xB7 " + opts.hint : "";
16910
16974
  const body = opts.text + " (" + seconds(opts.elapsedMs) + hint + ")";
@@ -16914,86 +16978,103 @@ function statusLine(opts) {
16914
16978
  function renderEvent(event, width = 100) {
16915
16979
  const body = Math.max(30, width - 4);
16916
16980
  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
16981
  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
- ];
16982
+ return [];
16926
16983
  case "user-message":
16927
- return ["", S2.user + " \u276F " + S2.reset + S2.bold + clip3(event.text, body) + S2.reset, ""];
16984
+ return ["", S2.user + " \u276F " + S2.reset + S2.bold + clip3(event.text, body) + S2.reset];
16928
16985
  // Live state, not transcript. The runner promotes these to the status line.
16929
16986
  case "thinking":
16930
16987
  return [];
16931
16988
  case "assistant-message": {
16932
- const lines = wrap(event.text, body - 2);
16989
+ const lines2 = wrap(event.text, body - 2);
16933
16990
  return [
16934
16991
  "",
16935
- ...lines.map((line, index) => index === 0 ? S2.agent + " " + BULLET + " " + line + S2.reset : S2.agent + " " + line + S2.reset),
16936
- ""
16992
+ ...lines2.map((line, index) => index === 0 ? S2.agent + " " + BULLET + " " + line + S2.reset : S2.agent + " " + line + S2.reset)
16937
16993
  ];
16938
16994
  }
16939
16995
  case "tool-start": {
16940
16996
  const label = TOOL_LABEL[event.kind] || TOOL_LABEL.other;
16941
16997
  return [
16942
- S2.tool + " " + BULLET + " " + label + S2.reset + S2.dim + "(" + clip3(event.target, body - label.length - 8) + ")" + S2.reset
16998
+ "",
16999
+ S2.panel + S2.tool + " " + BULLET + " " + label + S2.reset + S2.panel + S2.secondary + "(" + clip3(event.target, body - label.length - 8) + ")" + S2.reset
16943
17000
  ];
16944
17001
  }
16945
17002
  case "tool-end": {
16946
17003
  if (event.blocked) {
16947
17004
  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)
17005
+ S2.panel + S2.blocked + " \u29C9 Blocked" + S2.reset + S2.panel + S2.secondary + " " + clip3(terminalText(event.target), body - 21) + S2.reset,
17006
+ ...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),
17007
+ ...wrap(blockNextStep(event.reason), body - 6).map((line) => S2.secondary + " " + line + S2.reset)
16950
17008
  ];
16951
17009
  }
17010
+ const timing = event.durationMs == null ? "" : " \xB7 " + seconds(event.durationMs);
16952
17011
  const detail = event.ok ? event.output.trim() ? clip3(terminalText(event.output), body - 10) : "done" : "failed" + (event.exitCode === null ? "" : " (exit " + event.exitCode + ")");
16953
17012
  const tint = event.ok ? S2.ok : S2.blocked;
16954
- return [tint + " " + ELBOW + " " + S2.reset + S2.dim + detail + S2.reset];
17013
+ const changes = (event.fileChanges || []).flatMap((change) => wrap(change.path, body - 20).map((line, index) => S2.panel + S2.secondary + (index === 0 ? " " : " ") + line + (index === 0 ? " " + S2.ok + "+" + change.additions + S2.secondary + " " + S2.blocked + "-" + change.deletions : "") + S2.reset));
17014
+ return [
17015
+ S2.panel + tint + " " + ELBOW + " " + S2.reset + S2.panel + S2.secondary + detail + timing + S2.reset,
17016
+ ...changes
17017
+ ];
16955
17018
  }
16956
17019
  // A clean finish needs no announcement: the prompt returning IS the signal.
16957
17020
  case "turn-end":
16958
17021
  return event.ok ? [] : ["", S2.blocked + " " + BULLET + " turn ended with an error" + S2.reset, ""];
16959
17022
  case "notice":
16960
- return [S2.dim + " " + clip3(event.text, body) + S2.reset];
17023
+ return [S2.secondary + " " + clip3(event.text, body) + S2.reset];
17024
+ case "plan":
17025
+ return [
17026
+ S2.panel + S2.tool + " Plan" + S2.reset,
17027
+ ...event.steps.map((row2) => S2.panel + (row2.status === "completed" ? S2.ok + " \u2713 " : S2.secondary + " \xB7 ") + clip3(row2.step, body - 4) + S2.reset)
17028
+ ];
17029
+ case "assistant-delta":
17030
+ case "usage":
17031
+ return [];
16961
17032
  default:
16962
17033
  return [];
16963
17034
  }
16964
17035
  }
16965
17036
  function wrap(text, width) {
16966
- const words = String(text || "").replace(/\s+/g, " ").trim().split(" ").filter(Boolean);
17037
+ const words = cleanOutput(text).replace(/\s+/g, " ").trim().split(" ").filter(Boolean);
16967
17038
  if (words.length === 0) return [];
16968
- const lines = [];
17039
+ const lines2 = [];
16969
17040
  let line = "";
16970
17041
  for (const word of words) {
16971
17042
  if (!line) line = word;
16972
17043
  else if ((line + " " + word).length <= width) line += " " + word;
16973
17044
  else {
16974
- lines.push(line);
17045
+ lines2.push(line);
16975
17046
  line = word;
16976
17047
  }
16977
17048
  }
16978
- if (line) lines.push(line);
16979
- return lines;
17049
+ if (line) lines2.push(line);
17050
+ return lines2;
16980
17051
  }
16981
- var ESC2, S2, CLEAR_LINE, BULLET, ELBOW, SPINNER, TOOL_LABEL;
17052
+ var ESC2, color, S2, CLEAR_LINE, BULLET, ELBOW, SPINNER, TOOL_LABEL;
16982
17053
  var init_render2 = __esm({
16983
17054
  "cli/harness/render.ts"() {
16984
17055
  "use strict";
16985
17056
  ESC2 = "\x1B[";
17057
+ color = (hex, plane = 38) => {
17058
+ const [r, g, b] = hex.match(/../g).map((part) => Number.parseInt(part, 16));
17059
+ return ESC2 + plane + ";2;" + r + ";" + g + ";" + b + "m";
17060
+ };
16986
17061
  S2 = {
16987
17062
  reset: ESC2 + "0m",
16988
17063
  dim: ESC2 + "2m",
16989
17064
  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"
17065
+ canvas: color("101416", 48),
17066
+ panel: color("171D1F", 48),
17067
+ composer: color("13191B", 48),
17068
+ text: color("C4C8C4"),
17069
+ secondary: color("7F8A86"),
17070
+ border: color("2A3334"),
17071
+ user: color("87A68E"),
17072
+ agent: color("C4C8C4"),
17073
+ think: color("B29A6A"),
17074
+ tool: color("7F9CA5"),
17075
+ ok: color("789B82"),
17076
+ blocked: color("B87474"),
17077
+ rule: color("B87474")
16997
17078
  };
16998
17079
  CLEAR_LINE = "\r" + ESC2 + "2K";
16999
17080
  BULLET = "\u23FA";
@@ -17015,262 +17096,14 @@ var init_render2 = __esm({
17015
17096
 
17016
17097
  // cli/harness/composer.ts
17017
17098
  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";
17099
+ import { existsSync as existsSync38, mkdtempSync as mkdtempSync2, rmSync as rmSync7, statSync as statSync6, writeFileSync as writeFileSync27 } from "fs";
17019
17100
  import { homedir as homedir38, tmpdir } from "os";
17020
17101
  import { basename as basename3, dirname as dirname12, extname, isAbsolute as isAbsolute2, join as join37, resolve as resolve5, sep as sep3 } from "path";
17021
17102
  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
17103
  var init_composer = __esm({
17268
17104
  "cli/harness/composer.ts"() {
17269
17105
  "use strict";
17270
17106
  init_render2();
17271
- PASTE_START = "\x1B[200~";
17272
- PASTE_END = "\x1B[201~";
17273
- IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp"]);
17274
17107
  }
17275
17108
  });
17276
17109
 
@@ -17303,6 +17136,193 @@ var init_events = __esm({
17303
17136
  }
17304
17137
  });
17305
17138
 
17139
+ // cli/harness/changes.ts
17140
+ import { lstatSync as lstatSync2, readFileSync as readFileSync33, readlinkSync } from "fs";
17141
+ import { resolve as resolve6, relative } from "path";
17142
+ function lines(text) {
17143
+ if (!text) return [];
17144
+ const rows = text.replace(/\r\n/g, "\n").split("\n");
17145
+ if (rows[rows.length - 1] === "") rows.pop();
17146
+ return rows;
17147
+ }
17148
+ function lineChangeCounts(before, after) {
17149
+ const a = lines(before);
17150
+ const b = lines(after);
17151
+ if (a.length === 0) return { additions: b.length, deletions: 0 };
17152
+ if (b.length === 0) return { additions: 0, deletions: a.length };
17153
+ const max = a.length + b.length;
17154
+ let frontier = /* @__PURE__ */ new Map([[1, 0]]);
17155
+ for (let distance = 0; distance <= Math.min(max, MAX_DIFF_DISTANCE); distance++) {
17156
+ const next = /* @__PURE__ */ new Map();
17157
+ for (let diagonal = -distance; diagonal <= distance; diagonal += 2) {
17158
+ const down = frontier.get(diagonal + 1) ?? -1;
17159
+ const right = (frontier.get(diagonal - 1) ?? -1) + 1;
17160
+ let x = diagonal === -distance || diagonal !== distance && right < down ? down : right;
17161
+ if (x < 0) x = 0;
17162
+ let y = x - diagonal;
17163
+ while (x < a.length && y < b.length && a[x] === b[y]) {
17164
+ x++;
17165
+ y++;
17166
+ }
17167
+ next.set(diagonal, x);
17168
+ if (x >= a.length && y >= b.length) {
17169
+ return {
17170
+ additions: (distance + b.length - a.length) / 2,
17171
+ deletions: (distance + a.length - b.length) / 2
17172
+ };
17173
+ }
17174
+ }
17175
+ frontier = next;
17176
+ }
17177
+ let start = 0;
17178
+ while (start < a.length && start < b.length && a[start] === b[start]) start++;
17179
+ let aEnd = a.length;
17180
+ let bEnd = b.length;
17181
+ while (aEnd > start && bEnd > start && a[aEnd - 1] === b[bEnd - 1]) {
17182
+ aEnd--;
17183
+ bEnd--;
17184
+ }
17185
+ return { additions: bEnd - start, deletions: aEnd - start };
17186
+ }
17187
+ function unifiedDiffCounts(diff) {
17188
+ let additions = 0;
17189
+ let deletions = 0;
17190
+ for (const line of String(diff || "").split("\n")) {
17191
+ if (line.startsWith("+++") || line.startsWith("---")) continue;
17192
+ if (line.startsWith("+")) additions++;
17193
+ else if (line.startsWith("-")) deletions++;
17194
+ }
17195
+ return { additions, deletions };
17196
+ }
17197
+ function safePath(cwd, file) {
17198
+ const absolute = resolve6(cwd, file);
17199
+ const rel = relative(cwd, absolute);
17200
+ return rel && rel !== ".." && !rel.startsWith("../") ? rel : null;
17201
+ }
17202
+ function worktreeContent(cwd, file) {
17203
+ const rel = safePath(cwd, file);
17204
+ if (!rel) return null;
17205
+ try {
17206
+ const absolute = resolve6(cwd, rel);
17207
+ const stat = lstatSync2(absolute);
17208
+ if (stat.isSymbolicLink()) return "symlink:" + readlinkSync(absolute);
17209
+ if (!stat.isFile() || stat.size > MAX_TEXT_BYTES) return null;
17210
+ const content = readFileSync33(absolute);
17211
+ return content.includes(0) ? null : content.toString("utf8");
17212
+ } catch {
17213
+ return null;
17214
+ }
17215
+ }
17216
+ function fileOperation(event) {
17217
+ return (event.type === "tool-start" || event.type === "tool-end") && /^(edit|write|delete)$/.test(event.kind);
17218
+ }
17219
+ function eventPaths(cwd, event) {
17220
+ const provided = event.type === "tool-end" ? (event.fileChanges || []).map((change) => change.path) : [];
17221
+ const targets = String(event.target || "").split(/,\s*/);
17222
+ return [...new Set([...provided, ...targets].map((file) => safePath(cwd, file)).filter((file) => Boolean(file)))];
17223
+ }
17224
+ function snapshotOperation(cwd, event) {
17225
+ return new Map(eventPaths(cwd, event).map((file) => [file, worktreeContent(cwd, file)]));
17226
+ }
17227
+ var MAX_TEXT_BYTES, MAX_DIFF_DISTANCE, OperationChangeTracker;
17228
+ var init_changes = __esm({
17229
+ "cli/harness/changes.ts"() {
17230
+ "use strict";
17231
+ MAX_TEXT_BYTES = 2 * 1024 * 1024;
17232
+ MAX_DIFF_DISTANCE = 4e3;
17233
+ OperationChangeTracker = class {
17234
+ constructor(cwd, capture) {
17235
+ this.cwd = cwd;
17236
+ this.capture = capture;
17237
+ }
17238
+ cwd;
17239
+ capture;
17240
+ starts = /* @__PURE__ */ new Map();
17241
+ snapshot(event) {
17242
+ return this.capture ? this.capture() : snapshotOperation(this.cwd, event);
17243
+ }
17244
+ observe(event) {
17245
+ if (event.type === "tool-start") {
17246
+ if (fileOperation(event)) this.starts.set(event.id, this.snapshot(event));
17247
+ return event;
17248
+ }
17249
+ if (event.type !== "tool-end") return event;
17250
+ const before = this.starts.get(event.id);
17251
+ this.starts.delete(event.id);
17252
+ if (!before) return event;
17253
+ const after = this.snapshot(event);
17254
+ const files = [.../* @__PURE__ */ new Set([...before.keys(), ...after.keys()])].sort();
17255
+ const fileChanges2 = [];
17256
+ for (const file of files) {
17257
+ const oldText = before.get(file);
17258
+ const newText = after.has(file) ? after.get(file) : worktreeContent(this.cwd, file);
17259
+ if (oldText === newText || oldText === null && newText === null) continue;
17260
+ const counts = lineChangeCounts(oldText || "", newText || "");
17261
+ fileChanges2.push({ path: file, ...counts });
17262
+ }
17263
+ const provided = event.fileChanges || [];
17264
+ const measured = new Map(fileChanges2.map((change) => [change.path, change]));
17265
+ const merged = provided.map((change) => {
17266
+ const actual = measured.get(change.path) || measured.get(safePath(this.cwd, change.path) || "");
17267
+ if (actual && change.additions === 0 && change.deletions === 0) return actual;
17268
+ return change;
17269
+ });
17270
+ const providedPaths = new Set(provided.flatMap((change) => [change.path, safePath(this.cwd, change.path) || ""]));
17271
+ for (const change of fileChanges2) if (!providedPaths.has(change.path)) merged.push(change);
17272
+ return { ...event, fileChanges: merged.length ? merged : fileChanges2 };
17273
+ }
17274
+ };
17275
+ }
17276
+ });
17277
+
17278
+ // cli/codexUsage.ts
17279
+ function record(value) {
17280
+ return value != null && typeof value === "object" && !Array.isArray(value) ? value : null;
17281
+ }
17282
+ function finiteNumber(value) {
17283
+ const n = Number(value);
17284
+ return Number.isFinite(n) ? n : null;
17285
+ }
17286
+ function rateWindow(value) {
17287
+ const row2 = record(value);
17288
+ if (!row2) return null;
17289
+ const usedPercent = finiteNumber(row2.usedPercent);
17290
+ if (usedPercent == null) return null;
17291
+ return {
17292
+ durationMins: finiteNumber(row2.windowDurationMins),
17293
+ usedPercent,
17294
+ resetsAt: finiteNumber(row2.resetsAt)
17295
+ };
17296
+ }
17297
+ function codexUsageFromRateLimitsResponse(value) {
17298
+ const response = record(value);
17299
+ if (!response) return null;
17300
+ const byLimitId = record(response.rateLimitsByLimitId);
17301
+ const snapshot = record(byLimitId?.codex) || record(response.rateLimits);
17302
+ if (!snapshot) return null;
17303
+ const primary = rateWindow(snapshot.primary);
17304
+ const secondary = rateWindow(snapshot.secondary);
17305
+ const windows = [primary, secondary].filter((w) => w != null);
17306
+ if (windows.length === 0) return null;
17307
+ const fiveHour = windows.find((w) => w.durationMins === 300) || windows.find((w) => w.durationMins != null && w.durationMins <= 360) || (primary?.durationMins == null ? primary : null);
17308
+ const sevenDay = windows.find((w) => w.durationMins === 10080) || windows.find((w) => w.durationMins != null && w.durationMins >= 1440) || (secondary !== fiveHour && secondary?.durationMins == null ? secondary : null);
17309
+ const status = typeof snapshot.rateLimitReachedType === "string" ? snapshot.rateLimitReachedType : snapshot.spendControlReached === true ? "spend_control_reached" : null;
17310
+ return {
17311
+ util5h: fiveHour ? fiveHour.usedPercent / 100 : null,
17312
+ util7d: sevenDay ? sevenDay.usedPercent / 100 : null,
17313
+ reset5h: fiveHour?.resetsAt ?? null,
17314
+ reset7d: sevenDay?.resetsAt ?? null,
17315
+ status,
17316
+ planType: typeof snapshot.planType === "string" ? snapshot.planType : null,
17317
+ limitId: typeof snapshot.limitId === "string" ? snapshot.limitId : null
17318
+ };
17319
+ }
17320
+ var init_codexUsage = __esm({
17321
+ "cli/codexUsage.ts"() {
17322
+ "use strict";
17323
+ }
17324
+ });
17325
+
17306
17326
  // cli/harness/cursor.ts
17307
17327
  function unwrapReplay(text) {
17308
17328
  return text.replace(/<\/?user_query>/gi, "").trim();
@@ -17315,10 +17335,10 @@ function textOf(message) {
17315
17335
  }
17316
17336
  function toolPayload(toolCall) {
17317
17337
  if (!toolCall) return { kind: "other", body: {} };
17318
- for (const [key, kind] of Object.entries(TOOL_KINDS)) {
17319
- if (toolCall[key]) return { kind, body: toolCall[key] };
17338
+ for (const [key2, kind] of Object.entries(TOOL_KINDS)) {
17339
+ if (toolCall[key2]) return { kind, body: toolCall[key2] };
17320
17340
  }
17321
- const fallback = Object.keys(toolCall).find((key) => key.endsWith("ToolCall"));
17341
+ const fallback = Object.keys(toolCall).find((key2) => key2.endsWith("ToolCall"));
17322
17342
  return fallback ? { kind: "other", body: toolCall[fallback] } : { kind: "other", body: {} };
17323
17343
  }
17324
17344
  function targetOf(kind, body) {
@@ -17327,7 +17347,25 @@ function targetOf(kind, body) {
17327
17347
  const text = typeof candidate === "string" ? candidate : JSON.stringify(candidate ?? "");
17328
17348
  return text || body?.description || kind;
17329
17349
  }
17330
- function parseCursorLine(line) {
17350
+ function completedFileChanges(body) {
17351
+ const success = body?.result?.success || {};
17352
+ const rows = Array.isArray(success.changes) ? success.changes : Array.isArray(success.files) ? success.files : [];
17353
+ return rows.flatMap((change) => {
17354
+ const path = String(change?.path || change?.filePath || change?.file_path || "");
17355
+ if (!path) return [];
17356
+ const diff = String(change?.diff || change?.patch || "");
17357
+ const hasAdditions = change?.additions != null || change?.linesAdded != null;
17358
+ const hasDeletions = change?.deletions != null || change?.linesRemoved != null;
17359
+ if (!diff && !hasAdditions && !hasDeletions) return [];
17360
+ const counted = unifiedDiffCounts(diff);
17361
+ return [{
17362
+ path,
17363
+ additions: hasAdditions ? Number(change?.additions ?? change?.linesAdded) : counted.additions,
17364
+ deletions: hasDeletions ? Number(change?.deletions ?? change?.linesRemoved) : counted.deletions
17365
+ }];
17366
+ });
17367
+ }
17368
+ function parseCursorLine(line, streamPartial = false) {
17331
17369
  const trimmed = String(line || "").trim();
17332
17370
  if (!trimmed) return [];
17333
17371
  let frame;
@@ -17353,7 +17391,7 @@ function parseCursorLine(line) {
17353
17391
  }
17354
17392
  if (type === "assistant") {
17355
17393
  const text = textOf(frame.message);
17356
- return text ? [{ type: "assistant-message", text }] : [];
17394
+ return text ? [streamPartial ? { type: "assistant-delta", id: String(frame.model_call_id || ""), text } : { type: "assistant-message", text }] : [];
17357
17395
  }
17358
17396
  if (type === "thinking" && subtype === "delta" && frame.text) {
17359
17397
  return [{ type: "thinking", text: String(frame.text) }];
@@ -17382,7 +17420,8 @@ function parseCursorLine(line) {
17382
17420
  reason: rejected ? blocked ? blockReason(rawReason) : rawReason || "rejected" : "",
17383
17421
  pollId: blocked ? fixPollId(rawReason) : "",
17384
17422
  exitCode: success ? Number(success.exitCode ?? 0) : null,
17385
- output: String(success?.stdout || success?.stderr || "")
17423
+ output: String(success?.stdout || success?.stderr || ""),
17424
+ fileChanges: completedFileChanges(body)
17386
17425
  }];
17387
17426
  }
17388
17427
  return [];
@@ -17397,11 +17436,15 @@ function parseCursorLine(line) {
17397
17436
  }];
17398
17437
  }
17399
17438
  if (type === "result") {
17400
- return [{
17401
- type: "turn-end",
17402
- ok: !frame.is_error,
17403
- text: String(frame.result || "")
17404
- }];
17439
+ const result = String(frame.result || "");
17440
+ return [
17441
+ ...streamPartial && result ? [{ type: "assistant-message", text: result }] : [],
17442
+ {
17443
+ type: "turn-end",
17444
+ ok: !frame.is_error,
17445
+ text: result
17446
+ }
17447
+ ];
17405
17448
  }
17406
17449
  return [];
17407
17450
  }
@@ -17420,12 +17463,14 @@ function feed(buffer, chunk) {
17420
17463
  const rest = parts.pop() ?? "";
17421
17464
  return { lines: parts, rest };
17422
17465
  }
17423
- function cursorArgs(prompt) {
17466
+ function cursorArgs(prompt, resumeId = "") {
17424
17467
  return [
17425
17468
  "-p",
17426
17469
  prompt,
17427
17470
  "--output-format",
17428
17471
  "stream-json",
17472
+ "--stream-partial-output",
17473
+ ...resumeId ? ["--resume", resumeId] : [],
17429
17474
  // --force auto-runs tools but does NOT bypass hooks (verified live), so
17430
17475
  // Synkro's guards still gate every call; --trust loads workspace hooks.
17431
17476
  "--force",
@@ -17437,6 +17482,7 @@ var init_cursor = __esm({
17437
17482
  "cli/harness/cursor.ts"() {
17438
17483
  "use strict";
17439
17484
  init_events();
17485
+ init_changes();
17440
17486
  TOOL_KINDS = {
17441
17487
  shellToolCall: "shell",
17442
17488
  readToolCall: "read",
@@ -17473,6 +17519,7 @@ function createTurnSink(opts) {
17473
17519
  const now = opts.now || (() => Date.now());
17474
17520
  const showPrompt = opts.showPrompt !== false;
17475
17521
  let blocked = 0;
17522
+ let sessionId = "";
17476
17523
  let replaying = false;
17477
17524
  const seen = /* @__PURE__ */ new Set();
17478
17525
  let sawTurnEnd = false;
@@ -17553,6 +17600,7 @@ function createTurnSink(opts) {
17553
17600
  };
17554
17601
  const emit2 = (event) => {
17555
17602
  events.push(event);
17603
+ if (event.type === "session-start" && event.sessionId) sessionId = event.sessionId;
17556
17604
  if (event.type === "tool-end" && event.blocked) blocked += 1;
17557
17605
  opts.onEvent?.(event);
17558
17606
  if (event.type === "notice" && event.kind === "retry") {
@@ -17603,25 +17651,25 @@ function createTurnSink(opts) {
17603
17651
  }
17604
17652
  replaying = true;
17605
17653
  }
17606
- const key = replayKey(event);
17607
- if (key) {
17608
- if (replaying && seen.has(key)) {
17654
+ const key2 = replayKey(event);
17655
+ if (key2) {
17656
+ if (replaying && seen.has(key2)) {
17609
17657
  lastWasAnswer = event.type === "assistant-message";
17610
17658
  settle();
17611
17659
  return;
17612
17660
  }
17613
- seen.add(key);
17661
+ seen.add(key2);
17614
17662
  }
17615
- if (key || event.type === "thinking") lastWasAnswer = event.type === "assistant-message";
17663
+ if (key2 || event.type === "thinking") lastWasAnswer = event.type === "assistant-message";
17616
17664
  if (event.type === "turn-end") sawTurnEnd = true;
17617
17665
  if (event.type === "assistant-message") sawAnswer = true;
17618
17666
  emit2(event);
17619
17667
  };
17620
17668
  return {
17621
17669
  chunk(text) {
17622
- const { lines, rest } = feed(buffer, text);
17670
+ const { lines: lines2, rest } = feed(buffer, text);
17623
17671
  buffer = rest;
17624
- for (const line of lines) for (const event of parseCursorLine(line)) take(event);
17672
+ for (const line of lines2) for (const event of parseCursorLine(line, opts.streamPartial)) take(event);
17625
17673
  },
17626
17674
  notice(text) {
17627
17675
  emit2({ type: "notice", text });
@@ -17644,7 +17692,7 @@ function createTurnSink(opts) {
17644
17692
  }
17645
17693
  }
17646
17694
  stopStatus();
17647
- return { events, blocked, exitCode: interrupted ? 130 : sawAnswer ? 0 : exit };
17695
+ return { events, blocked, exitCode: interrupted ? 130 : sawAnswer ? 0 : exit, sessionId };
17648
17696
  }
17649
17697
  };
17650
17698
  }
@@ -17660,10 +17708,11 @@ async function runCursorTurn(opts) {
17660
17708
  animate: Boolean(process.stdout.isTTY),
17661
17709
  showPrompt: opts.showPrompt,
17662
17710
  showHeader: opts.showHeader,
17663
- onGiveUp: () => stopHarness()
17711
+ onGiveUp: () => stopHarness(),
17712
+ streamPartial: true
17664
17713
  });
17665
- return new Promise((resolve8) => {
17666
- const child = spawn9("cursor-agent", cursorArgs(opts.prompt), {
17714
+ return new Promise((resolve9) => {
17715
+ const child = spawn9("cursor-agent", cursorArgs(opts.prompt, opts.resumeId), {
17667
17716
  cwd: opts.cwd,
17668
17717
  stdio: ["ignore", "pipe", "pipe"]
17669
17718
  });
@@ -17691,11 +17740,11 @@ async function runCursorTurn(opts) {
17691
17740
  });
17692
17741
  child.on("close", (code) => {
17693
17742
  opts.signal?.removeEventListener("abort", onAbort);
17694
- resolve8(sink.finish(code ?? 0, interrupted));
17743
+ resolve9(sink.finish(code ?? 0, interrupted));
17695
17744
  });
17696
17745
  child.on("error", (error) => {
17697
17746
  sink.notice("failed to start cursor-agent: " + String(error));
17698
- resolve8(sink.finish(1, interrupted));
17747
+ resolve9(sink.finish(1, interrupted));
17699
17748
  });
17700
17749
  });
17701
17750
  }
@@ -17711,360 +17760,553 @@ var init_run = __esm({
17711
17760
 
17712
17761
  // cli/harness/codex.ts
17713
17762
  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: "" };
17763
+ import { createInterface as createInterface6 } from "readline";
17764
+ function stringify(value) {
17765
+ if (typeof value === "string") return value;
17766
+ try {
17767
+ return JSON.stringify(value);
17768
+ } catch {
17769
+ return String(value ?? "");
17770
+ }
17771
+ }
17772
+ function toolKind(item) {
17773
+ if (item.type === "commandExecution") return "shell";
17774
+ if (item.type === "fileChange") return "edit";
17775
+ if (item.type === "webSearch") return "search";
17776
+ return "other";
17777
+ }
17778
+ function toolTarget(item) {
17779
+ if (item.type === "commandExecution") return item.command || "command";
17780
+ if (item.type === "fileChange") return (item.changes || []).map((change) => change.path || change.filePath || "").filter(Boolean).join(", ") || "workspace files";
17781
+ if (item.type === "mcpToolCall") return [item.server, item.tool].filter(Boolean).join(".") || "MCP tool";
17782
+ if (item.type === "dynamicToolCall") return [item.namespace, item.tool].filter(Boolean).join(".") || "tool";
17783
+ return item.query || item.type || "tool";
17784
+ }
17785
+ function fileChanges(item) {
17786
+ if (item.type !== "fileChange" || !Array.isArray(item.changes)) return [];
17787
+ return item.changes.flatMap((change) => {
17788
+ const path = String(change.path || change.filePath || "");
17789
+ if (!path) return [];
17790
+ const diff = String(change.diff || change.patch || "");
17791
+ const hasAdditions = change.additions != null || change.linesAdded != null;
17792
+ const hasDeletions = change.deletions != null || change.linesRemoved != null;
17793
+ if (!diff && !hasAdditions && !hasDeletions) return [];
17794
+ const counted = unifiedDiffCounts(diff);
17795
+ const additions = hasAdditions ? Number(change.additions ?? change.linesAdded) : counted.additions;
17796
+ const deletions = hasDeletions ? Number(change.deletions ?? change.linesRemoved) : counted.deletions;
17797
+ return [{ path, additions, deletions }];
17798
+ });
17729
17799
  }
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 "";
17800
+ function codexAccountUsageEvent(value) {
17801
+ const usage2 = codexUsageFromRateLimitsResponse(value);
17802
+ if (usage2?.util7d == null) return null;
17803
+ return {
17804
+ type: "account-usage",
17805
+ period: "weekly",
17806
+ remainingPercent: Math.max(0, Math.min(100, Math.round((1 - usage2.util7d) * 100))),
17807
+ resetsAt: usage2.reset7d
17808
+ };
17735
17809
  }
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");
17810
+ function codexMessageTurnId(message) {
17811
+ return String(message.params?.turnId || message.params?.turn?.id || "");
17812
+ }
17813
+ function codexMessageIsTurnScoped(message) {
17814
+ const method = String(message.method || "");
17815
+ return method.startsWith("item/") || method === "hook/completed" || method === "turn/plan/updated";
17816
+ }
17817
+ function codexEvents(message) {
17818
+ const method = String(message.method || "");
17819
+ const params = message.params || {};
17820
+ if (method === "account/rateLimits/updated") {
17821
+ const event = codexAccountUsageEvent(params);
17822
+ return event ? [event] : [];
17823
+ }
17824
+ const item = params.item || {};
17825
+ if (method === "item/agentMessage/delta") return [{ type: "assistant-delta", id: params.itemId || "", text: params.delta || "" }];
17826
+ if (method === "item/reasoning/summaryTextDelta") return [{ type: "thinking", text: params.delta || "Thinking" }];
17827
+ const tools = ["commandExecution", "fileChange", "mcpToolCall", "dynamicToolCall", "webSearch"];
17828
+ if (method === "item/started" && tools.includes(item.type)) {
17829
+ return [{ type: "tool-start", id: item.id || "", kind: toolKind(item), target: toolTarget(item), description: "" }];
17830
+ }
17831
+ if (method === "item/completed" && item.type === "agentMessage") return [{ type: "assistant-message", text: item.text || "" }];
17832
+ if (method === "item/completed" && tools.includes(item.type)) {
17833
+ const raw = stringify(item.aggregatedOutput || item.error?.message || item.result?.content || item.contentItems || "");
17797
17834
  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__");
17835
+ const blocked = BLOCK_MARKER.test(raw) || /declined|blocked|denied/.test(status);
17836
+ const ok = !blocked && !/failed|error/.test(status) && (item.exitCode === null || item.exitCode === void 0 || item.exitCode === 0);
17811
17837
  return [{
17812
17838
  type: "tool-end",
17813
- id: String(item.id),
17814
- ...itemTarget(item),
17815
- ok: status === "completed" && Number(item.exitCode ?? 0) === 0,
17839
+ id: item.id || "",
17840
+ kind: toolKind(item),
17841
+ target: toolTarget(item),
17842
+ ok,
17816
17843
  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
17844
+ reason: blocked ? blockReason(raw || status) : "",
17845
+ pollId: blocked ? fixPollId(raw) : "",
17846
+ exitCode: typeof item.exitCode === "number" ? item.exitCode : null,
17847
+ output: raw,
17848
+ durationMs: typeof item.durationMs === "number" ? item.durationMs : null,
17849
+ fileChanges: fileChanges(item)
17822
17850
  }];
17823
17851
  }
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;
17852
+ if (method === "hook/completed") {
17853
+ const run2 = params.run || {};
17854
+ if (run2.status !== "blocked") return [];
17855
+ const reason = (run2.entries || []).map((entry) => entry.text || "").filter(Boolean).join("\n") || run2.statusMessage || "blocked by policy";
17856
+ return [{
17857
+ type: "tool-end",
17858
+ id: "hook:" + (run2.id || ""),
17859
+ kind: "other",
17860
+ target: run2.eventName || "Guard",
17861
+ ok: false,
17862
+ blocked: true,
17863
+ reason: blockReason(reason),
17864
+ exitCode: null,
17865
+ output: reason,
17866
+ pollId: fixPollId(reason),
17867
+ durationMs: typeof run2.durationMs === "number" ? run2.durationMs : null
17868
+ }];
17850
17869
  }
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();
17870
+ if (method === "turn/plan/updated") {
17871
+ return [{ type: "plan", steps: (params.plan || []).map((row2) => ({ step: row2.step || "", status: row2.status || "pending" })) }];
17882
17872
  }
17873
+ if (method === "thread/tokenUsage/updated") {
17874
+ const usage2 = params.tokenUsage || {};
17875
+ const total = usage2.total || usage2.last || {};
17876
+ const used = Number(total.totalTokens ?? total.total_tokens ?? usage2.totalTokens ?? 0);
17877
+ const context = Number(usage2.modelContextWindow ?? usage2.model_context_window ?? 0);
17878
+ return [{ type: "usage", used, contextWindow: context || null }];
17879
+ }
17880
+ if (method === "warning" || method === "error") return [{ type: "notice", text: params.message || params.error?.message || "Codex transport warning" }];
17881
+ return [];
17883
17882
  }
17884
- var CodexAppSession;
17883
+ var CodexSession;
17885
17884
  var init_codex = __esm({
17886
17885
  "cli/harness/codex.ts"() {
17887
17886
  "use strict";
17888
17887
  init_composer();
17889
17888
  init_events();
17889
+ init_changes();
17890
+ init_codexUsage();
17890
17891
  init_run();
17891
17892
  init_render2();
17892
- CodexAppSession = class {
17893
+ CodexSession = class {
17894
+ constructor(cwd, resumeId = "") {
17895
+ this.cwd = cwd;
17896
+ this.resumeId = resumeId;
17897
+ }
17898
+ cwd;
17899
+ resumeId;
17893
17900
  child = null;
17894
- buffer = "";
17895
17901
  nextId = 1;
17896
17902
  pending = /* @__PURE__ */ new Map();
17897
- initialized = false;
17903
+ onEvent = null;
17904
+ activeTurn = "";
17905
+ turnDone = null;
17906
+ completedTurns = /* @__PURE__ */ new Map();
17907
+ accountUsage = null;
17898
17908
  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) {
17909
+ model = "";
17910
+ request(method, params, timeoutMs = 0) {
17911
+ if (!this.child) return Promise.reject(new Error("Codex app-server is not running"));
17906
17912
  const id = this.nextId++;
17907
- return new Promise((resolve8, reject) => {
17908
- this.pending.set(id, { resolve: resolve8, reject });
17909
- this.send({ id, method, params });
17913
+ this.child.stdin.write(JSON.stringify({ method, id, params }) + "\n");
17914
+ return new Promise((resolve9, reject) => {
17915
+ const timer = timeoutMs > 0 ? setTimeout(() => {
17916
+ this.pending.delete(id);
17917
+ reject(new Error(method + " timed out"));
17918
+ }, timeoutMs) : null;
17919
+ this.pending.set(id, {
17920
+ resolve: (value) => {
17921
+ if (timer) clearTimeout(timer);
17922
+ resolve9(value);
17923
+ },
17924
+ reject: (error) => {
17925
+ if (timer) clearTimeout(timer);
17926
+ reject(error);
17927
+ }
17928
+ });
17910
17929
  });
17911
17930
  }
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, {});
17931
+ notify(method, params = {}) {
17932
+ this.child?.stdin.write(JSON.stringify({ method, params }) + "\n");
17936
17933
  }
17937
- handleLine(line) {
17938
- let message;
17939
- try {
17940
- message = JSON.parse(line);
17941
- } catch {
17934
+ receive(message) {
17935
+ if (message.id !== void 0 && !message.method) {
17936
+ const pending = this.pending.get(message.id);
17937
+ if (!pending) return;
17938
+ this.pending.delete(message.id);
17939
+ if (message.error) pending.reject(new Error(message.error.message || stringify(message.error)));
17940
+ else pending.resolve(message.result);
17942
17941
  return;
17943
17942
  }
17944
- if (message.method && message.id !== void 0) {
17945
- void this.handleServerRequest(message);
17943
+ if (message.id !== void 0 && message.method) {
17944
+ this.child?.stdin.write(JSON.stringify({ id: message.id, result: { decision: "decline" } }) + "\n");
17945
+ this.onEvent?.({ type: "notice", text: "Codex requested an approval the embedded policy did not permit." });
17946
17946
  return;
17947
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;
17948
+ const messageTurnId = codexMessageTurnId(message);
17949
+ const staleTurnEvent = codexMessageIsTurnScoped(message) && (!this.activeTurn || messageTurnId && messageTurnId !== this.activeTurn);
17950
+ for (const event of codexEvents(message)) {
17951
+ if (event.type === "account-usage") this.accountUsage = event;
17952
+ if (!staleTurnEvent) this.onEvent?.(event);
17960
17953
  }
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);
17954
+ if (message.method === "turn/completed" && message.params?.turn?.id === this.activeTurn) {
17955
+ const status = String(message.params.turn.status || "");
17956
+ this.completedTurns.set(message.params.turn.id, status);
17957
+ this.turnDone?.(status);
17958
+ } else if (message.method === "turn/completed" && message.params?.turn?.id) {
17959
+ this.completedTurns.set(message.params.turn.id, String(message.params.turn.status || ""));
17967
17960
  }
17968
17961
  }
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");
17962
+ async start() {
17963
+ this.child = spawn10("codex", ["app-server", "--stdio"], { cwd: this.cwd, stdio: ["pipe", "pipe", "pipe"] });
17964
+ this.child.on("error", (cause) => {
17965
+ const error = cause instanceof Error ? cause : new Error(String(cause));
17966
+ for (const pending of this.pending.values()) pending.reject(error);
17967
+ this.pending.clear();
17968
+ this.turnDone?.("failed");
17969
+ });
17970
+ createInterface6({ input: this.child.stdout }).on("line", (line) => {
17971
+ try {
17972
+ this.receive(JSON.parse(line));
17973
+ } catch {
17981
17974
  }
17982
17975
  });
17983
17976
  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;
17977
+ const message = String(chunk || "").trim();
17978
+ if (message) this.onEvent?.({ type: "notice", text: message });
17996
17979
  });
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);
17980
+ this.child.once("exit", (code) => {
17981
+ const error = new Error("Codex app-server exited" + (code === null ? "" : " (" + code + ")"));
17982
+ for (const pending of this.pending.values()) pending.reject(error);
18006
17983
  this.pending.clear();
17984
+ this.turnDone?.("failed");
18007
17985
  });
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");
17986
+ await this.request("initialize", { clientInfo: { name: "synkro-terminal", title: "Synkro", version: "1" }, capabilities: { experimentalApi: true } });
17987
+ this.notify("initialized");
17988
+ try {
17989
+ const event = codexAccountUsageEvent(await this.request("account/rateLimits/read", null, 4e3));
17990
+ if (event) this.accountUsage = event;
17991
+ } catch {
17992
+ }
17993
+ let result;
17994
+ try {
17995
+ result = this.resumeId ? await this.request("thread/resume", { threadId: this.resumeId, cwd: this.cwd, approvalPolicy: "never", sandbox: "workspace-write" }) : await this.request("thread/start", { cwd: this.cwd, approvalPolicy: "never", sandbox: "workspace-write", serviceName: "Synkro" });
17996
+ } catch (error) {
17997
+ if (!this.resumeId) throw error;
17998
+ result = await this.request("thread/start", { cwd: this.cwd, approvalPolicy: "never", sandbox: "workspace-write", serviceName: "Synkro" });
17999
+ }
18000
+ this.threadId = result.thread?.id || "";
18001
+ this.model = result.model || "";
18021
18002
  }
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
- });
18003
+ async runTurn(prompt, onEvent, signal) {
18004
+ let blocked = 0;
18005
+ let turnId = "";
18006
+ let interrupt = null;
18007
+ this.onEvent = (event) => {
18008
+ if (event.type === "tool-end" && event.blocked) blocked++;
18009
+ onEvent(event);
18046
18010
  };
18047
- if (opts.signal?.aborted) onAbort();
18048
- else opts.signal?.addEventListener("abort", onAbort, { once: true });
18049
18011
  try {
18050
- await this.request("turn/start", {
18051
- threadId: this.threadId,
18052
- cwd: opts.cwd,
18053
- input: codexUserInput({ text: opts.prompt, images: opts.images || [] })
18012
+ onEvent({ type: "session-start", sessionId: this.threadId, model: this.model || "Codex", cwd: this.cwd, authSource: "login" });
18013
+ if (this.accountUsage) onEvent(this.accountUsage);
18014
+ onEvent({ type: "user-message", text: prompt });
18015
+ const response = await this.request("turn/start", { threadId: this.threadId, input: [{ type: "text", text: prompt }] });
18016
+ turnId = response.turn?.id || "";
18017
+ this.activeTurn = turnId;
18018
+ let aborted = false;
18019
+ let interruptSent = false;
18020
+ interrupt = () => {
18021
+ aborted = true;
18022
+ if (interruptSent || !this.activeTurn) return;
18023
+ interruptSent = true;
18024
+ void this.request("turn/interrupt", { threadId: this.threadId, turnId: this.activeTurn }).catch(() => {
18025
+ });
18026
+ };
18027
+ if (signal?.aborted) interrupt();
18028
+ else signal?.addEventListener("abort", interrupt, { once: true });
18029
+ const alreadyDone = this.completedTurns.get(turnId);
18030
+ const status = alreadyDone ?? await new Promise((resolve9) => {
18031
+ this.turnDone = resolve9;
18054
18032
  });
18055
- return await result;
18033
+ this.completedTurns.delete(turnId);
18034
+ signal?.removeEventListener("abort", interrupt);
18035
+ this.activeTurn = "";
18036
+ this.turnDone = null;
18037
+ this.onEvent = null;
18038
+ onEvent({ type: "turn-end", ok: aborted || /completed/.test(status), text: aborted ? "interrupted" : status });
18039
+ return { exitCode: aborted ? 130 : /completed/.test(status) ? 0 : 1, blocked, sessionId: this.threadId };
18056
18040
  } finally {
18057
- opts.signal?.removeEventListener("abort", onAbort);
18041
+ if (interrupt) signal?.removeEventListener("abort", interrupt);
18042
+ if (turnId) this.completedTurns.delete(turnId);
18043
+ this.activeTurn = "";
18044
+ this.turnDone = null;
18045
+ this.onEvent = null;
18058
18046
  }
18059
18047
  }
18060
18048
  close() {
18061
- this.child?.kill();
18049
+ this.child?.kill("SIGTERM");
18062
18050
  this.child = null;
18063
18051
  }
18064
18052
  };
18065
18053
  }
18066
18054
  });
18067
18055
 
18056
+ // cli/harness/screen.ts
18057
+ var ANSI, REVERSE, STREAM_DRAW_MS, STATUS_TICK_MS, resetDateFormat, visible, pathLabel, SynkroScreen;
18058
+ var init_screen = __esm({
18059
+ "cli/harness/screen.ts"() {
18060
+ "use strict";
18061
+ init_render2();
18062
+ init_changes();
18063
+ ANSI = /\x1b\[[0-?]*[ -\/]*[@-~]/g;
18064
+ REVERSE = "\x1B[7m";
18065
+ STREAM_DRAW_MS = 80;
18066
+ STATUS_TICK_MS = 240;
18067
+ resetDateFormat = new Intl.DateTimeFormat("en-US", { month: "short", day: "numeric" });
18068
+ visible = (value) => value.replace(ANSI, "").length;
18069
+ pathLabel = (cwd) => {
18070
+ const parts = cwd.split("/").filter(Boolean);
18071
+ return parts.length > 2 ? "\u2026/" + parts.slice(-2).join("/") : cwd;
18072
+ };
18073
+ SynkroScreen = class {
18074
+ constructor(harness, cwd, write2 = (text) => process.stdout.write(text)) {
18075
+ this.harness = harness;
18076
+ this.cwd = cwd;
18077
+ this.write = write2;
18078
+ this.changes = new OperationChangeTracker(cwd);
18079
+ }
18080
+ harness;
18081
+ cwd;
18082
+ write;
18083
+ transcript = [];
18084
+ draft = "";
18085
+ input = "";
18086
+ selectionAnchor = null;
18087
+ selectionFocus = null;
18088
+ status = "Ready";
18089
+ statusSince = Date.now();
18090
+ tick = 0;
18091
+ used = 0;
18092
+ context = null;
18093
+ weeklyRemaining = null;
18094
+ weeklyResetsAt = null;
18095
+ model = "";
18096
+ timer = null;
18097
+ drawTimer = null;
18098
+ lastDrawAt = 0;
18099
+ scrollOffset = 0;
18100
+ changes;
18101
+ resize = () => this.draw();
18102
+ enter() {
18103
+ this.write("\x1B[?1049h\x1B[?25l\x1B[?1000h\x1B[?1002h\x1B[?1006h");
18104
+ process.stdout.on("resize", this.resize);
18105
+ this.timer = setInterval(() => {
18106
+ if (this.status === "Ready") return;
18107
+ this.tick++;
18108
+ this.queueDraw();
18109
+ }, STATUS_TICK_MS);
18110
+ this.timer.unref();
18111
+ this.draw();
18112
+ }
18113
+ leave() {
18114
+ if (this.timer) clearInterval(this.timer);
18115
+ if (this.drawTimer) clearTimeout(this.drawTimer);
18116
+ process.stdout.removeListener("resize", this.resize);
18117
+ this.write(S2.reset + "\x1B[?1006l\x1B[?1002l\x1B[?1000l\x1B[?25h\x1B[?1049l");
18118
+ }
18119
+ setInput(value) {
18120
+ this.input = cleanOutput(value);
18121
+ this.selectionAnchor = null;
18122
+ this.selectionFocus = null;
18123
+ this.draw();
18124
+ }
18125
+ selectionRange() {
18126
+ if (this.selectionAnchor === null || this.selectionFocus === null || this.selectionAnchor === this.selectionFocus) return null;
18127
+ return [Math.min(this.selectionAnchor, this.selectionFocus), Math.max(this.selectionAnchor, this.selectionFocus)];
18128
+ }
18129
+ clearSelection() {
18130
+ this.selectionAnchor = null;
18131
+ this.selectionFocus = null;
18132
+ this.draw();
18133
+ }
18134
+ scrollBy(lines2) {
18135
+ this.scrollOffset = Math.max(0, this.scrollOffset + lines2);
18136
+ this.draw();
18137
+ }
18138
+ scrollToBottom() {
18139
+ this.scrollOffset = 0;
18140
+ this.draw();
18141
+ }
18142
+ /** Select text using one-based SGR mouse coordinates from the terminal. */
18143
+ selectInputAt(x, y, extend, width = this.width(), height = this.height()) {
18144
+ const rows = this.inputRows(width);
18145
+ const composerLength = rows.length + 2;
18146
+ const viewport = Math.max(3, height - composerLength - 2);
18147
+ const lineIndex = y - (viewport + 2);
18148
+ const row2 = rows[lineIndex];
18149
+ if (!row2) return;
18150
+ const index = Math.min(row2.end, row2.start + Math.max(0, x - 5));
18151
+ if (!extend || this.selectionAnchor === null) this.selectionAnchor = index;
18152
+ this.selectionFocus = index;
18153
+ this.draw();
18154
+ }
18155
+ updateStatus(value) {
18156
+ if (this.status === value) return false;
18157
+ this.status = value;
18158
+ this.statusSince = Date.now();
18159
+ return true;
18160
+ }
18161
+ setStatus(value) {
18162
+ if (this.updateStatus(value)) this.draw();
18163
+ }
18164
+ add(event) {
18165
+ event = this.changes.observe(event);
18166
+ if (event.type === "session-start") {
18167
+ this.model = event.model;
18168
+ this.draw();
18169
+ return;
18170
+ }
18171
+ if (event.type === "assistant-delta") {
18172
+ this.draft += event.text;
18173
+ this.updateStatus("Writing\u2026");
18174
+ this.queueDraw();
18175
+ return;
18176
+ }
18177
+ if (event.type === "assistant-message") this.draft = "";
18178
+ if (event.type === "usage") {
18179
+ this.used = event.used;
18180
+ this.context = event.contextWindow;
18181
+ if (event.model) this.model = event.model;
18182
+ this.draw();
18183
+ return;
18184
+ }
18185
+ if (event.type === "account-usage") {
18186
+ this.weeklyRemaining = event.remainingPercent;
18187
+ this.weeklyResetsAt = event.resetsAt;
18188
+ this.draw();
18189
+ return;
18190
+ }
18191
+ if (event.type === "thinking") {
18192
+ if (this.updateStatus(event.text || "Thinking\u2026")) this.draw();
18193
+ return;
18194
+ }
18195
+ if (event.type === "tool-start") this.updateStatus("Working\u2026");
18196
+ if (event.type === "tool-end") this.updateStatus(event.blocked ? "Guard enforced" : "Working\u2026");
18197
+ if (event.type === "turn-end") this.updateStatus(event.ok ? "Ready" : "Turn ended");
18198
+ const rendered = renderEvent(event, this.width());
18199
+ if (this.scrollOffset > 0) this.scrollOffset += rendered.length;
18200
+ this.transcript.push(...rendered);
18201
+ this.draw();
18202
+ }
18203
+ width() {
18204
+ return Math.max(50, Number(process.stdout.columns || 100));
18205
+ }
18206
+ height() {
18207
+ return Math.max(16, Number(process.stdout.rows || 30));
18208
+ }
18209
+ inputRows(width) {
18210
+ const lineWidth = Math.max(1, Math.max(30, width - 4) - 4);
18211
+ if (!this.input) return [{ text: "", start: 0, end: 0 }];
18212
+ const rows = [];
18213
+ for (let start = 0; start < this.input.length; start += lineWidth) {
18214
+ const text = this.input.slice(start, start + lineWidth);
18215
+ rows.push({ text, start, end: start + text.length });
18216
+ }
18217
+ return rows.slice(-3);
18218
+ }
18219
+ selectedText(row2) {
18220
+ const range = this.selectionRange();
18221
+ if (!range || range[1] <= row2.start || range[0] >= row2.end) return S2.text + row2.text;
18222
+ const from = Math.max(range[0], row2.start) - row2.start;
18223
+ const to = Math.min(range[1], row2.end) - row2.start;
18224
+ return S2.text + row2.text.slice(0, from) + REVERSE + row2.text.slice(from, to) + S2.reset + S2.composer + S2.text + row2.text.slice(to);
18225
+ }
18226
+ frame(width = this.width(), height = this.height()) {
18227
+ const inner = Math.max(30, width - 4);
18228
+ const provider = this.harness === "codex" ? "Codex" : "Cursor";
18229
+ const draft = this.draft ? ["", ...wrap(this.draft, inner - 2).map((line, i) => S2.agent + " " + (i ? " " : "\u23FA ") + line + S2.reset)] : [];
18230
+ const status = this.status === "Ready" ? S2.secondary + " \xB7 Ready" + S2.reset : statusLine({ tick: this.tick, text: this.status, elapsedMs: Date.now() - this.statusSince, width });
18231
+ const rule = S2.border + " " + "\u2500".repeat(inner) + S2.reset;
18232
+ const inputRows = this.inputRows(width);
18233
+ const composer = [rule, ...inputRows.map((row2, i) => S2.composer + (i ? " " : S2.user + " \u276F ") + this.selectedText(row2) + (i === inputRows.length - 1 ? S2.user + " \u258C" : "") + S2.reset), rule];
18234
+ const usedPercent = this.context ? Math.min(100, Math.round(this.used / this.context * 100)) : null;
18235
+ const contextUsage = usedPercent === null ? "\u2014 context" : usedPercent + "% context";
18236
+ const resetDate = this.weeklyResetsAt == null ? "" : " \xB7 " + resetDateFormat.format(new Date(this.weeklyResetsAt * 1e3));
18237
+ const accountUsage = this.weeklyRemaining === null ? "Weekly \u2014" : "Weekly " + this.weeklyRemaining + "% left" + resetDate;
18238
+ const usage2 = contextUsage + " \xB7 " + accountUsage;
18239
+ const left = S2.secondary + " " + pathLabel(this.cwd) + S2.reset + S2.canvas;
18240
+ const badge = S2.panel + S2.secondary + " " + usage2 + " " + S2.reset + S2.canvas;
18241
+ const right = badge + S2.secondary + " \xB7 " + provider + (this.model ? " " + cleanOutput(this.model) : "") + " \xB7 \u29C9 powered by synkro " + S2.reset + S2.canvas;
18242
+ const footer = left + " ".repeat(Math.max(1, width - visible(left) - visible(right))) + right;
18243
+ const viewport = Math.max(3, height - composer.length - 2);
18244
+ const fullContent = [...this.transcript, ...draft, status];
18245
+ const maxScroll = Math.max(0, fullContent.length - viewport);
18246
+ const effectiveOffset = Math.min(this.scrollOffset, maxScroll);
18247
+ const end = fullContent.length - effectiveOffset;
18248
+ const content = fullContent.slice(Math.max(0, end - viewport), end);
18249
+ while (content.length < viewport) content.unshift("");
18250
+ const rows = [...content, ...composer, footer].slice(0, height);
18251
+ while (rows.length < height) rows.push("");
18252
+ return rows.map((line) => {
18253
+ const fill = line.startsWith(S2.panel) ? S2.panel : line.includes(S2.composer) ? S2.composer : S2.canvas;
18254
+ return S2.canvas + line + fill + " ".repeat(Math.max(0, width - visible(line))) + S2.reset;
18255
+ }).join("\n");
18256
+ }
18257
+ queueDraw() {
18258
+ if (this.drawTimer) return;
18259
+ const delay = Math.max(0, STREAM_DRAW_MS - (Date.now() - this.lastDrawAt));
18260
+ this.drawTimer = setTimeout(() => {
18261
+ this.drawTimer = null;
18262
+ this.draw();
18263
+ }, delay);
18264
+ this.drawTimer.unref?.();
18265
+ }
18266
+ draw() {
18267
+ if (this.drawTimer) {
18268
+ clearTimeout(this.drawTimer);
18269
+ this.drawTimer = null;
18270
+ }
18271
+ this.lastDrawAt = Date.now();
18272
+ this.write("\x1B[H" + this.frame());
18273
+ }
18274
+ };
18275
+ }
18276
+ });
18277
+
18278
+ // cli/harness/state.ts
18279
+ import { mkdirSync as mkdirSync24, readFileSync as readFileSync34, writeFileSync as writeFileSync28 } from "fs";
18280
+ import { homedir as homedir39 } from "os";
18281
+ import { dirname as dirname13, join as join38 } from "path";
18282
+ function load(file = STATE_FILE) {
18283
+ try {
18284
+ const value = JSON.parse(readFileSync34(file, "utf8"));
18285
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
18286
+ } catch {
18287
+ return {};
18288
+ }
18289
+ }
18290
+ function loadSessionId(harness, cwd, file = STATE_FILE) {
18291
+ return load(file)[key(harness, cwd)] || "";
18292
+ }
18293
+ function saveSessionId(harness, cwd, sessionId, file = STATE_FILE) {
18294
+ if (!sessionId) return;
18295
+ try {
18296
+ mkdirSync24(dirname13(file), { recursive: true });
18297
+ writeFileSync28(file, JSON.stringify({ ...load(file), [key(harness, cwd)]: sessionId }, null, 2));
18298
+ } catch {
18299
+ }
18300
+ }
18301
+ var STATE_FILE, key;
18302
+ var init_state = __esm({
18303
+ "cli/harness/state.ts"() {
18304
+ "use strict";
18305
+ STATE_FILE = join38(homedir39(), ".synkro", "terminal-sessions.json");
18306
+ key = (harness, cwd) => harness + ":" + cwd.replace(/\/+$/, "");
18307
+ }
18308
+ });
18309
+
18068
18310
  // cli/harness/fixPoll.ts
18069
18311
  function pollBaseUrl() {
18070
18312
  const port = String(process.env.SYNKRO_MCP_PORT || "18931");
@@ -18098,7 +18340,7 @@ function fixPollLines(poll, selected, width = 100) {
18098
18340
  const max = Math.max(32, width - 10);
18099
18341
  const choices = [...poll.candidates, "None of the above"];
18100
18342
  const scope = [poll.ruleId, poll.filePath].filter(Boolean).join(" \xB7 ");
18101
- const lines = [
18343
+ const lines2 = [
18102
18344
  "",
18103
18345
  S2.bold + S2.rule + " Synkro needs your decision" + S2.reset,
18104
18346
  ...scope ? [S2.dim + " " + neutralizeTerminalControls(scope) + S2.reset] : [],
@@ -18107,11 +18349,11 @@ function fixPollLines(poll, selected, width = 100) {
18107
18349
  choices.forEach((choice, index) => {
18108
18350
  const prefix = index === selected ? S2.user + " \u276F " : S2.dim + " ";
18109
18351
  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);
18352
+ lines2.push(prefix + (text[0] || "") + S2.reset);
18353
+ for (const continuation of text.slice(1)) lines2.push(" " + continuation);
18112
18354
  });
18113
- lines.push("", S2.dim + " \u2191/\u2193 select \xB7 Enter confirm \xB7 1-" + choices.length + " choose" + S2.reset);
18114
- return lines;
18355
+ lines2.push("", S2.dim + " \u2191/\u2193 select \xB7 Enter confirm \xB7 1-" + choices.length + " choose" + S2.reset);
18356
+ return lines2;
18115
18357
  }
18116
18358
  async function pickFixPoll(poll, io = {}) {
18117
18359
  const input = io.input || process.stdin;
@@ -18121,27 +18363,27 @@ async function pickFixPoll(poll, io = {}) {
18121
18363
  let rendered = 0;
18122
18364
  const draw = () => {
18123
18365
  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;
18366
+ const lines2 = fixPollLines(poll, selected, Number(output.columns || 100));
18367
+ output.write(lines2.join("\n") + "\n");
18368
+ rendered = lines2.length;
18127
18369
  };
18128
- const choice = await new Promise((resolve8) => {
18370
+ const choice = await new Promise((resolve9) => {
18129
18371
  const choices = poll.candidates.length + 1;
18130
18372
  const wasRaw = Boolean(input.isRaw);
18131
18373
  const done = (index) => {
18132
18374
  input.off("data", onData);
18133
18375
  if (input.setRawMode) input.setRawMode(wasRaw);
18134
- resolve8(index === poll.candidates.length ? -1 : index);
18376
+ resolve9(index === poll.candidates.length ? -1 : index);
18135
18377
  };
18136
18378
  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;
18379
+ const key2 = chunk.toString("utf8");
18380
+ if (key2 === "") return done(poll.candidates.length);
18381
+ if (key2 === "\x1B" || key2.toLowerCase() === "n") return done(poll.candidates.length);
18382
+ if (key2 === "\r" || key2 === "\n") return done(selected);
18383
+ if (key2 === "\x1B[A" || key2 === "k") selected = (selected - 1 + choices) % choices;
18384
+ else if (key2 === "\x1B[B" || key2 === "j") selected = (selected + 1) % choices;
18385
+ else if (/^[1-9]$/.test(key2)) {
18386
+ const index = Number(key2) - 1;
18145
18387
  if (index < choices) return done(index);
18146
18388
  } else return;
18147
18389
  draw();
@@ -18173,134 +18415,227 @@ var init_fixPoll = __esm({
18173
18415
  }
18174
18416
  });
18175
18417
 
18176
- // cli/harness/identity.ts
18177
- import { execFileSync as execFileSync6 } from "child_process";
18178
- import { homedir as homedir39 } from "os";
18179
- function shortenPath(path, home = homedir39()) {
18180
- const value = String(path || "");
18181
- if (home && value === home) return "~";
18182
- if (home && value.startsWith(home + "/")) return "~" + value.slice(home.length);
18183
- return value;
18184
- }
18185
- function parseCursorAccount(output) {
18186
- const match = String(output || "").match(/logged in as\s+(\S+)/i);
18187
- return match ? match[1].trim() : "";
18418
+ // cli/harness/session.ts
18419
+ function deleteSelectedText(value, range) {
18420
+ if (!range) return value.slice(0, -1);
18421
+ return value.slice(0, range[0]) + value.slice(range[1]);
18188
18422
  }
18189
- function cursorAccount() {
18190
- try {
18191
- const out = execFileSync6("cursor-agent", ["status"], {
18192
- encoding: "utf8",
18193
- timeout: 5e3,
18194
- stdio: ["ignore", "pipe", "pipe"]
18195
- });
18196
- return parseCursorAccount(out);
18197
- } catch {
18198
- return "";
18423
+ function handleScrollInput(screen, data) {
18424
+ let handled = false;
18425
+ const mousePattern = /\x1b\[<(\d+);\d+;\d+[Mm]/g;
18426
+ let mouse;
18427
+ while ((mouse = mousePattern.exec(data)) !== null) {
18428
+ const button = Number(mouse[1]);
18429
+ if (button === 64 || button === 65) {
18430
+ screen.scrollBy(button === 64 ? 3 : -3);
18431
+ handled = true;
18432
+ }
18199
18433
  }
18200
- }
18201
- function synkroAccount() {
18202
- let email = "";
18203
- try {
18204
- email = String(getUserInfo().email || "");
18205
- } catch {
18206
- return { email: "", needsLogin: false };
18434
+ if (data.includes("\x1B[5~")) {
18435
+ screen.scrollBy(10);
18436
+ handled = true;
18207
18437
  }
18208
- let needsLogin = false;
18209
- try {
18210
- needsLogin = isTokenExpired() && !loadCredentials()?.refresh_token;
18211
- } catch {
18438
+ if (data.includes("\x1B[6~")) {
18439
+ screen.scrollBy(-10);
18440
+ handled = true;
18212
18441
  }
18213
- return { email, needsLogin };
18214
- }
18215
- function readIdentity(harness) {
18216
- const synkro = synkroAccount();
18217
- return {
18218
- harness: harness === "cursor" ? cursorAccount() : "",
18219
- synkro: synkro.email,
18220
- needsLogin: synkro.needsLogin
18221
- };
18442
+ return handled;
18222
18443
  }
18223
- function identityHeader(opts) {
18224
- const lines = ["", S2.bold + " " + shortenPath(opts.cwd, opts.home) + S2.reset];
18225
- const label = (name, value, note = "") => S2.dim + " " + name.padEnd(7) + S2.reset + S2.think + value + S2.reset + note;
18226
- if (opts.identity.harness) lines.push(label(opts.harness, opts.identity.harness));
18227
- if (opts.identity.synkro) {
18228
- lines.push(label("synkro", opts.identity.synkro, opts.identity.needsLogin ? S2.blocked + " \xB7 session ended, run synkro login" + S2.reset : ""));
18229
- }
18230
- lines.push("");
18231
- return lines;
18444
+ function printEvent(event) {
18445
+ const lines2 = renderEvent(event, Number(process.stdout.columns || 100));
18446
+ if (lines2.length) process.stdout.write(lines2.join("\n") + "\n");
18232
18447
  }
18233
- var init_identity2 = __esm({
18234
- "cli/harness/identity.ts"() {
18235
- "use strict";
18236
- init_auth();
18237
- init_render2();
18238
- }
18239
- });
18240
-
18241
- // cli/harness/session.ts
18242
18448
  async function runOnce(harness, cwd, prompt, echoPrompt = true, showHeader = true, signal) {
18243
- if (harness !== "cursor" && harness !== "codex") {
18449
+ if (!supported(harness)) {
18244
18450
  process.stdout.write(S2.dim + " " + harness + " sessions are not embedded yet\n" + S2.reset);
18245
18451
  return 1;
18246
18452
  }
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);
18249
- if (result.blocked > 0) {
18250
- process.stdout.write(
18251
- S2.blocked + " " + result.blocked + " action" + (result.blocked === 1 ? "" : "s") + " blocked by policy" + S2.reset + "\n"
18252
- );
18453
+ const resumeId = loadSessionId(harness, cwd);
18454
+ if (harness === "cursor") {
18455
+ const result = await runCursorTurn({ prompt, cwd, showPrompt: echoPrompt, showHeader, signal, resumeId });
18456
+ saveSessionId(harness, cwd, result.sessionId || "");
18457
+ await resolveFixPolls(result.events);
18458
+ return result.exitCode;
18459
+ }
18460
+ const session = new CodexSession(cwd, resumeId);
18461
+ const events = [];
18462
+ try {
18463
+ await session.start();
18464
+ const result = await session.runTurn(prompt, (event) => {
18465
+ events.push(event);
18466
+ printEvent(event);
18467
+ }, signal);
18468
+ saveSessionId(harness, cwd, result.sessionId);
18469
+ await resolveFixPolls(events);
18470
+ return result.exitCode;
18471
+ } finally {
18472
+ session.close();
18253
18473
  }
18254
- return result.exitCode;
18255
18474
  }
18256
- async function runGovernedSession(harness, cwd, prompt) {
18257
- if (prompt) return runOnce(harness, cwd, prompt);
18258
- process.stdout.write(identityHeader({ cwd, harness, identity: readIdentity(harness) }).join("\n") + "\n");
18259
- const codex = harness === "codex" ? new CodexAppSession() : null;
18260
- let turn = null;
18261
- const interruptTurn = () => {
18262
- turn?.abort();
18475
+ function readPrompt(screen) {
18476
+ return new Promise((resolve9) => {
18477
+ let value = "";
18478
+ const finish = (answer) => {
18479
+ process.stdin.removeListener("data", onData);
18480
+ screen.setInput("");
18481
+ resolve9(answer);
18482
+ };
18483
+ const onData = (chunk) => {
18484
+ const data = chunk.toString("utf8");
18485
+ const scrolled = handleScrollInput(screen, data);
18486
+ const mousePattern = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
18487
+ let mouse;
18488
+ let sawMouse = false;
18489
+ while ((mouse = mousePattern.exec(data)) !== null) {
18490
+ sawMouse = true;
18491
+ const button = Number(mouse[1]);
18492
+ if (button === 0 || button === 32) {
18493
+ screen.selectInputAt(Number(mouse[2]), Number(mouse[3]), button === 32 || mouse[4] === "m");
18494
+ }
18495
+ }
18496
+ if (sawMouse || scrolled) return;
18497
+ const selection = screen.selectionRange();
18498
+ if (data === "\x1B[3~") {
18499
+ value = deleteSelectedText(value, selection);
18500
+ screen.setInput(value);
18501
+ return;
18502
+ }
18503
+ if (data === "\x1B") {
18504
+ if (selection) {
18505
+ value = deleteSelectedText(value, selection);
18506
+ screen.setInput(value);
18507
+ } else finish(null);
18508
+ return;
18509
+ }
18510
+ if (data.startsWith("\x1B")) return;
18511
+ for (const char of data) {
18512
+ if (char === "" || char === "") {
18513
+ finish(null);
18514
+ return;
18515
+ }
18516
+ if (char === "\r" || char === "\n") {
18517
+ finish(value);
18518
+ return;
18519
+ }
18520
+ if (char === "\x7F" || char === "\b") value = deleteSelectedText(value, screen.selectionRange());
18521
+ else if (char === "") value = "";
18522
+ else if (char === "") value = value.replace(/\s*\S+\s*$/, "");
18523
+ else if (char >= " ") {
18524
+ const range = screen.selectionRange();
18525
+ value = range ? value.slice(0, range[0]) + char + value.slice(range[1]) : value + char;
18526
+ }
18527
+ screen.setInput(value);
18528
+ }
18529
+ };
18530
+ process.stdin.on("data", onData);
18531
+ screen.setInput(value);
18532
+ });
18533
+ }
18534
+ async function cancellable(screen, run2) {
18535
+ const controller = new AbortController();
18536
+ const onData = (chunk) => {
18537
+ const text = chunk.toString("utf8");
18538
+ if (handleScrollInput(screen, text)) return;
18539
+ if (text.includes("") || text === "\x1B") {
18540
+ screen.setStatus("Interrupting\u2026");
18541
+ controller.abort();
18542
+ }
18263
18543
  };
18264
- process.on("SIGINT", interruptTurn);
18265
- let first = true;
18544
+ process.stdin.on("data", onData);
18266
18545
  try {
18546
+ return await run2(controller.signal);
18547
+ } finally {
18548
+ process.stdin.removeListener("data", onData);
18549
+ }
18550
+ }
18551
+ async function resolveScreenFixPolls(screen, events) {
18552
+ if (!events.some((event) => event.type === "tool-end" && Boolean(event.pollId))) return;
18553
+ screen.leave();
18554
+ try {
18555
+ await resolveFixPolls(events);
18556
+ } finally {
18557
+ screen.enter();
18558
+ }
18559
+ }
18560
+ async function runSynkroSession(harness, cwd, prompt) {
18561
+ if (prompt) return runOnce(harness, cwd, prompt);
18562
+ if (!supported(harness)) return runOnce(harness, cwd, "");
18563
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
18564
+ process.stderr.write("synkro terminal requires a TTY\n");
18565
+ return 1;
18566
+ }
18567
+ const screen = new SynkroScreen(harness, cwd);
18568
+ const resumeId = loadSessionId(harness, cwd);
18569
+ let codex = null;
18570
+ process.stdin.setRawMode(true);
18571
+ process.stdin.resume();
18572
+ screen.enter();
18573
+ try {
18574
+ if (harness === "codex") {
18575
+ screen.setStatus("Connecting to Codex\u2026");
18576
+ codex = new CodexSession(cwd, resumeId);
18577
+ await codex.start();
18578
+ saveSessionId(harness, cwd, codex.threadId);
18579
+ screen.add({ type: "session-start", sessionId: codex.threadId, model: codex.model || "Codex", cwd, authSource: "login" });
18580
+ screen.setStatus("Ready");
18581
+ }
18267
18582
  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;
18583
+ const answer = await readPrompt(screen);
18584
+ if (answer === null) break;
18585
+ const line = answer.trim();
18586
+ if (!line) continue;
18587
+ if (line === "/exit" || line === "/quit" || line === "exit" || line === "quit") break;
18588
+ screen.setStatus("Working\u2026");
18589
+ const events = [];
18273
18590
  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
- );
18591
+ if (harness === "cursor") {
18592
+ const result = await cancellable(screen, (signal) => runCursorTurn({
18593
+ prompt: line,
18594
+ cwd,
18595
+ signal,
18596
+ resumeId: loadSessionId(harness, cwd),
18597
+ write: () => {
18598
+ },
18599
+ onEvent: (event) => {
18600
+ events.push(event);
18601
+ screen.add(event);
18602
+ },
18603
+ showHeader: false
18604
+ }));
18605
+ saveSessionId(harness, cwd, result.sessionId || "");
18606
+ } else {
18607
+ const result = await cancellable(screen, (signal) => codex.runTurn(line, (event) => {
18608
+ events.push(event);
18609
+ screen.add(event);
18610
+ }, signal));
18611
+ saveSessionId(harness, cwd, result.sessionId);
18282
18612
  }
18283
- first = false;
18284
- } finally {
18285
- cleanupPromptDraft(draft);
18286
- turn = null;
18613
+ await resolveScreenFixPolls(screen, events);
18614
+ } catch (error) {
18615
+ screen.add({ type: "notice", text: error instanceof Error ? error.message : String(error) });
18616
+ screen.add({ type: "turn-end", ok: false, text: "" });
18287
18617
  }
18618
+ screen.setStatus("Ready");
18288
18619
  }
18620
+ return 0;
18289
18621
  } finally {
18290
- process.off("SIGINT", interruptTurn);
18291
18622
  codex?.close();
18623
+ screen.leave();
18624
+ process.stdin.setRawMode(false);
18625
+ process.stdin.pause();
18292
18626
  }
18293
- return 0;
18294
18627
  }
18628
+ var supported;
18295
18629
  var init_session = __esm({
18296
18630
  "cli/harness/session.ts"() {
18297
18631
  "use strict";
18298
- init_composer();
18299
- init_run();
18300
18632
  init_codex();
18301
- init_fixPoll();
18302
- init_identity2();
18633
+ init_run();
18303
18634
  init_render2();
18635
+ init_screen();
18636
+ init_state();
18637
+ init_fixPoll();
18638
+ supported = (harness) => harness === "cursor" || harness === "codex";
18304
18639
  }
18305
18640
  });
18306
18641
 
@@ -18341,17 +18676,19 @@ async function takeover(kind, cwd) {
18341
18676
  const runner = backend === "container" ? info.runner : HOST3;
18342
18677
  return runInherit(["env", "TMUX=", ...runnerInteractiveArgs(runner, ["tmux", "attach-session", "-t", spawned.session])]);
18343
18678
  }
18344
- async function restoreSession(session) {
18345
- const record = loadRecords().find((row2) => row2.session === session);
18346
- if (!record) return;
18679
+ async function restoreSession(session, bootPath) {
18680
+ const record2 = loadRecords().find((row2) => row2.session === session);
18681
+ if (!record2) return;
18347
18682
  const info = await detectContainerBackend();
18348
18683
  await spawnAgent(info, {
18349
- name: record.name,
18350
- harness: record.harness,
18351
- spaceName: record.spaceName,
18352
- cwd: record.space,
18353
- backend: record.harness === "codex" ? "host" : record.backend,
18354
- resume: true
18684
+ name: record2.name,
18685
+ harness: record2.harness,
18686
+ spaceName: record2.spaceName,
18687
+ cwd: record2.space,
18688
+ backend: record2.mode === "embedded" ? "host" : record2.backend,
18689
+ resume: record2.mode !== "embedded",
18690
+ mode: record2.mode,
18691
+ command: record2.mode === "embedded" ? embeddedSessionCommand(bootPath, record2.harness, record2.space) : void 0
18355
18692
  });
18356
18693
  }
18357
18694
  async function printStatus() {
@@ -18404,7 +18741,7 @@ async function uiCommand(args2) {
18404
18741
  const harness = args2[runAt + 1] || "cursor";
18405
18742
  const cwd = args2[runAt + 2] || repoRoot();
18406
18743
  const prompt = args2.slice(runAt + 3).join(" ").trim();
18407
- process.exitCode = await runGovernedSession(harness, cwd, prompt);
18744
+ process.exitCode = await runSynkroSession(harness, cwd, prompt);
18408
18745
  return;
18409
18746
  }
18410
18747
  const takeoverAt = args2.indexOf("--takeover");
@@ -18414,7 +18751,7 @@ async function uiCommand(args2) {
18414
18751
  }
18415
18752
  const restoreAt = args2.indexOf("--restore");
18416
18753
  if (restoreAt !== -1) {
18417
- await restoreSession(args2[restoreAt + 1] || "");
18754
+ await restoreSession(args2[restoreAt + 1] || "", bootPath);
18418
18755
  return;
18419
18756
  }
18420
18757
  if (args2.includes("--status")) {
@@ -18492,12 +18829,12 @@ __export(linear_exports, {
18492
18829
  formatLinks: () => formatLinks,
18493
18830
  linearCommand: () => linearCommand
18494
18831
  });
18495
- import { readFileSync as readFileSync33 } from "fs";
18832
+ import { readFileSync as readFileSync35 } from "fs";
18496
18833
  import { homedir as homedir40 } from "os";
18497
- import { join as join38 } from "path";
18834
+ import { join as join39 } from "path";
18498
18835
  function mcpJwt() {
18499
18836
  try {
18500
- return readFileSync33(join38(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
18837
+ return readFileSync35(join39(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
18501
18838
  } catch {
18502
18839
  return "";
18503
18840
  }
@@ -18536,7 +18873,7 @@ var SYNKRO_DIR14, PORT2, BASE;
18536
18873
  var init_linear = __esm({
18537
18874
  "cli/commands/linear.ts"() {
18538
18875
  "use strict";
18539
- SYNKRO_DIR14 = join38(homedir40(), ".synkro");
18876
+ SYNKRO_DIR14 = join39(homedir40(), ".synkro");
18540
18877
  PORT2 = process.env.SYNKRO_MCP_PORT || "18931";
18541
18878
  BASE = `http://127.0.0.1:${PORT2}`;
18542
18879
  }
@@ -18544,13 +18881,13 @@ var init_linear = __esm({
18544
18881
 
18545
18882
  // cli/scanning/cveReachability.ts
18546
18883
  import { parse } from "@babel/parser";
18547
- import { readFileSync as readFileSync34 } from "fs";
18884
+ import { readFileSync as readFileSync36 } from "fs";
18548
18885
  function walk(node, visit) {
18549
18886
  if (!node || typeof node.type !== "string") return;
18550
18887
  visit(node);
18551
- for (const key of Object.keys(node)) {
18552
- if (key === "loc" || key === "start" || key === "end" || key === "range" || key === "leadingComments" || key === "trailingComments") continue;
18553
- const child = node[key];
18888
+ for (const key2 of Object.keys(node)) {
18889
+ if (key2 === "loc" || key2 === "start" || key2 === "end" || key2 === "range" || key2 === "leadingComments" || key2 === "trailingComments") continue;
18890
+ const child = node[key2];
18554
18891
  if (Array.isArray(child)) {
18555
18892
  for (const c of child) if (c && typeof c.type === "string") walk(c, visit);
18556
18893
  } else if (child && typeof child.type === "string") walk(child, visit);
@@ -18685,9 +19022,9 @@ var init_cveReachability = __esm({
18685
19022
  });
18686
19023
 
18687
19024
  // cli/reachability/reachabilityScan.ts
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";
19025
+ import { spawnSync as spawnSync13, execFileSync as execFileSync6 } from "child_process";
19026
+ import { readFileSync as readFileSync37, writeFileSync as writeFileSync29, existsSync as existsSync39, readdirSync as readdirSync10 } from "fs";
19027
+ import { join as join40 } from "path";
18691
19028
  import { homedir as homedir41 } from "os";
18692
19029
  import { createRequire } from "module";
18693
19030
  function walkSourceFiles(repoRoot3, maxFiles = 4e3, maxBytes = 5e5) {
@@ -18705,7 +19042,7 @@ function walkSourceFiles(repoRoot3, maxFiles = 4e3, maxBytes = 5e5) {
18705
19042
  }
18706
19043
  for (const e of ents) {
18707
19044
  if (files.length >= maxFiles) break;
18708
- const full = join39(dir, e.name);
19045
+ const full = join40(dir, e.name);
18709
19046
  if (e.isDirectory()) {
18710
19047
  if (!SKIP2.has(e.name) && !e.name.startsWith(".")) stack.push(full);
18711
19048
  continue;
@@ -18713,7 +19050,7 @@ function walkSourceFiles(repoRoot3, maxFiles = 4e3, maxBytes = 5e5) {
18713
19050
  if (!EXT.test(e.name) || e.name.endsWith(".d.ts")) continue;
18714
19051
  const rel = full.startsWith(repoRoot3 + "/") ? full.slice(repoRoot3.length + 1) : full;
18715
19052
  try {
18716
- const content = readFileSync35(full, "utf8");
19053
+ const content = readFileSync37(full, "utf8");
18717
19054
  if (content.length <= maxBytes) files.push({ path: rel, content });
18718
19055
  } catch {
18719
19056
  }
@@ -18732,12 +19069,12 @@ function cleanVersion(spec) {
18732
19069
  function gatherManifestVersions(repoRoot3) {
18733
19070
  const out = {};
18734
19071
  const dirs = [repoRoot3];
18735
- const pkgsDir = join39(repoRoot3, "packages");
19072
+ const pkgsDir = join40(repoRoot3, "packages");
18736
19073
  if (existsSync39(pkgsDir)) {
18737
19074
  try {
18738
19075
  for (const d of readdirSync10(pkgsDir)) {
18739
- const pd = join39(pkgsDir, d);
18740
- if (existsSync39(join39(pd, "package.json"))) dirs.push(pd);
19076
+ const pd = join40(pkgsDir, d);
19077
+ if (existsSync39(join40(pd, "package.json"))) dirs.push(pd);
18741
19078
  }
18742
19079
  } catch {
18743
19080
  }
@@ -18746,7 +19083,7 @@ function gatherManifestVersions(repoRoot3) {
18746
19083
  for (const dir of dirs) {
18747
19084
  let pkg;
18748
19085
  try {
18749
- pkg = JSON.parse(readFileSync35(join39(dir, "package.json"), "utf8"));
19086
+ pkg = JSON.parse(readFileSync37(join40(dir, "package.json"), "utf8"));
18750
19087
  } catch {
18751
19088
  continue;
18752
19089
  }
@@ -18766,28 +19103,28 @@ function findJelly(repoRoot3) {
18766
19103
  try {
18767
19104
  const pkgJson = require2.resolve("@cs-au-dk/jelly/package.json");
18768
19105
  const dir = pkgJson.slice(0, pkgJson.length - "package.json".length);
18769
- const pkg = JSON.parse(readFileSync35(pkgJson, "utf8"));
19106
+ const pkg = JSON.parse(readFileSync37(pkgJson, "utf8"));
18770
19107
  const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin && (pkg.bin.jelly || pkg.bin[Object.keys(pkg.bin)[0]]);
18771
19108
  if (bin) {
18772
- const p = join39(dir, bin);
19109
+ const p = join40(dir, bin);
18773
19110
  if (existsSync39(p)) return p;
18774
19111
  }
18775
19112
  } catch {
18776
19113
  }
18777
19114
  for (const base of [repoRoot3, process.cwd()]) {
18778
- const b = join39(base, "node_modules", ".bin", "jelly");
19115
+ const b = join40(base, "node_modules", ".bin", "jelly");
18779
19116
  if (existsSync39(b)) return b;
18780
19117
  }
18781
19118
  return null;
18782
19119
  }
18783
19120
  function findEntries(repoRoot3) {
18784
19121
  const dirs = [repoRoot3];
18785
- const pkgsDir = join39(repoRoot3, "packages");
19122
+ const pkgsDir = join40(repoRoot3, "packages");
18786
19123
  if (existsSync39(pkgsDir)) {
18787
19124
  try {
18788
19125
  for (const d of readdirSync10(pkgsDir)) {
18789
- const pd = join39(pkgsDir, d);
18790
- if (existsSync39(join39(pd, "package.json"))) dirs.push(pd);
19126
+ const pd = join40(pkgsDir, d);
19127
+ if (existsSync39(join40(pd, "package.json"))) dirs.push(pd);
18791
19128
  }
18792
19129
  } catch {
18793
19130
  }
@@ -18795,11 +19132,11 @@ function findEntries(repoRoot3) {
18795
19132
  const entries = [];
18796
19133
  for (const dir of dirs) {
18797
19134
  try {
18798
- const pkg = JSON.parse(readFileSync35(join39(dir, "package.json"), "utf8"));
19135
+ const pkg = JSON.parse(readFileSync37(join40(dir, "package.json"), "utf8"));
18799
19136
  const cands = [pkg.source, pkg.module, pkg.main, "src/index.ts", "src/index.js", "src/main.ts", "src/server.ts", "index.ts", "index.js"];
18800
19137
  for (const c of cands) {
18801
19138
  if (typeof c !== "string") continue;
18802
- const f = join39(dir, c);
19139
+ const f = join40(dir, c);
18803
19140
  if (existsSync39(f)) {
18804
19141
  entries.push(f);
18805
19142
  break;
@@ -18812,7 +19149,7 @@ function findEntries(repoRoot3) {
18812
19149
  }
18813
19150
  function currentCommit(repoRoot3) {
18814
19151
  try {
18815
- return execFileSync7("git", ["rev-parse", "HEAD"], { cwd: repoRoot3, encoding: "utf8" }).trim();
19152
+ return execFileSync6("git", ["rev-parse", "HEAD"], { cwd: repoRoot3, encoding: "utf8" }).trim();
18816
19153
  } catch {
18817
19154
  return "";
18818
19155
  }
@@ -18835,7 +19172,7 @@ function runReachabilityScan(repoRoot3, opts = {}) {
18835
19172
  const commit = currentCommit(repoRoot3);
18836
19173
  if (!opts.force && commit && existsSync39(REACHABILITY_PATH)) {
18837
19174
  try {
18838
- const prev = JSON.parse(readFileSync35(REACHABILITY_PATH, "utf8"));
19175
+ const prev = JSON.parse(readFileSync37(REACHABILITY_PATH, "utf8"));
18839
19176
  if (prev.commit === commit) return { ok: true, cached: true, packages: Object.keys(prev.packages || {}).length };
18840
19177
  } catch {
18841
19178
  }
@@ -18924,7 +19261,7 @@ function runReachabilityScan(repoRoot3, opts = {}) {
18924
19261
  if (Object.keys(packages).length === 0) return { ok: false, reason: "no package usage found (no jelly output, no AST imports)" };
18925
19262
  const file = { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), commit, tool, packages, versions: gatherManifestVersions(repoRoot3) };
18926
19263
  try {
18927
- writeFileSync28(REACHABILITY_PATH, JSON.stringify(file, null, 2));
19264
+ writeFileSync29(REACHABILITY_PATH, JSON.stringify(file, null, 2));
18928
19265
  } catch (e) {
18929
19266
  return { ok: false, reason: "write failed: " + String(e.message || e) };
18930
19267
  }
@@ -18936,7 +19273,7 @@ var init_reachabilityScan = __esm({
18936
19273
  "use strict";
18937
19274
  init_cveReachability();
18938
19275
  require2 = createRequire(import.meta.url);
18939
- REACHABILITY_PATH = join39(homedir41(), ".synkro", "reachability.json");
19276
+ REACHABILITY_PATH = join40(homedir41(), ".synkro", "reachability.json");
18940
19277
  }
18941
19278
  });
18942
19279
 
@@ -18945,15 +19282,15 @@ var reachabilityScan_exports = {};
18945
19282
  __export(reachabilityScan_exports, {
18946
19283
  reachabilityScanCommand: () => reachabilityScanCommand
18947
19284
  });
18948
- import { readFileSync as readFileSync36, existsSync as existsSync40 } from "fs";
18949
- import { join as join40 } from "path";
19285
+ import { readFileSync as readFileSync38, existsSync as existsSync40 } from "fs";
19286
+ import { join as join41 } from "path";
18950
19287
  import { homedir as homedir42 } from "os";
18951
- import { execFileSync as execFileSync8 } from "child_process";
19288
+ import { execFileSync as execFileSync7 } from "child_process";
18952
19289
  function readConfigEnv4() {
18953
- const p = join40(SYNKRO_DIR15, "config.env");
19290
+ const p = join41(SYNKRO_DIR15, "config.env");
18954
19291
  if (!existsSync40(p)) return {};
18955
19292
  const out = {};
18956
- for (const line of readFileSync36(p, "utf-8").split("\n")) {
19293
+ for (const line of readFileSync38(p, "utf-8").split("\n")) {
18957
19294
  const t = line.trim();
18958
19295
  if (!t || t.startsWith("#")) continue;
18959
19296
  const eq = t.indexOf("=");
@@ -18963,7 +19300,7 @@ function readConfigEnv4() {
18963
19300
  }
18964
19301
  function repoRoot2() {
18965
19302
  try {
18966
- return execFileSync8("git", ["rev-parse", "--show-toplevel"], { encoding: "utf-8" }).trim();
19303
+ return execFileSync7("git", ["rev-parse", "--show-toplevel"], { encoding: "utf-8" }).trim();
18967
19304
  } catch {
18968
19305
  return process.cwd();
18969
19306
  }
@@ -18971,7 +19308,7 @@ function repoRoot2() {
18971
19308
  function repoSlug(root) {
18972
19309
  const run2 = (a) => {
18973
19310
  try {
18974
- return execFileSync8("git", a, { encoding: "utf-8" }).trim();
19311
+ return execFileSync7("git", a, { encoding: "utf-8" }).trim();
18975
19312
  } catch {
18976
19313
  return "";
18977
19314
  }
@@ -18985,11 +19322,11 @@ async function pushToCloud(cfg, repo) {
18985
19322
  while (gwBase.endsWith("/")) gwBase = gwBase.slice(0, -1);
18986
19323
  let jwt2 = "";
18987
19324
  try {
18988
- jwt2 = readFileSync36(join40(SYNKRO_DIR15, ".mcp-jwt"), "utf-8").trim();
19325
+ jwt2 = readFileSync38(join41(SYNKRO_DIR15, ".mcp-jwt"), "utf-8").trim();
18989
19326
  } catch {
18990
19327
  }
18991
19328
  if (!jwt2 || !existsSync40(REACHABILITY_PATH)) return;
18992
- const body = readFileSync36(REACHABILITY_PATH, "utf-8");
19329
+ const body = readFileSync38(REACHABILITY_PATH, "utf-8");
18993
19330
  try {
18994
19331
  const resp = await fetch(gwBase + "/api/v1/reachability?repo=" + encodeURIComponent(repo), {
18995
19332
  method: "POST",
@@ -19021,7 +19358,7 @@ var init_reachabilityScan2 = __esm({
19021
19358
  "cli/commands/reachabilityScan.ts"() {
19022
19359
  "use strict";
19023
19360
  init_reachabilityScan();
19024
- SYNKRO_DIR15 = join40(homedir42(), ".synkro");
19361
+ SYNKRO_DIR15 = join41(homedir42(), ".synkro");
19025
19362
  }
19026
19363
  });
19027
19364
 
@@ -19151,13 +19488,13 @@ var config_exports = {};
19151
19488
  __export(config_exports, {
19152
19489
  configCommand: () => configCommand
19153
19490
  });
19154
- import { readFileSync as readFileSync37, writeFileSync as writeFileSync29, existsSync as existsSync41 } from "fs";
19155
- import { join as join41 } from "path";
19491
+ import { readFileSync as readFileSync39, writeFileSync as writeFileSync30, existsSync as existsSync41 } from "fs";
19492
+ import { join as join42 } from "path";
19156
19493
  import { homedir as homedir43 } from "os";
19157
19494
  function readConfigEnv5() {
19158
19495
  if (!existsSync41(CONFIG_PATH9)) return {};
19159
19496
  const out = {};
19160
- for (const line of readFileSync37(CONFIG_PATH9, "utf-8").split("\n")) {
19497
+ for (const line of readFileSync39(CONFIG_PATH9, "utf-8").split("\n")) {
19161
19498
  const t = line.trim();
19162
19499
  if (!t || t.startsWith("#")) continue;
19163
19500
  const eq = t.indexOf("=");
@@ -19165,23 +19502,23 @@ function readConfigEnv5() {
19165
19502
  }
19166
19503
  return out;
19167
19504
  }
19168
- function updateConfigValue(key, value) {
19505
+ function updateConfigValue(key2, value) {
19169
19506
  if (!existsSync41(CONFIG_PATH9)) {
19170
19507
  console.error("No config found. Run `synkro install` first.");
19171
19508
  process.exit(1);
19172
19509
  }
19173
- const lines = readFileSync37(CONFIG_PATH9, "utf-8").split("\n");
19174
- const pattern = new RegExp(`^${key}=`);
19510
+ const lines2 = readFileSync39(CONFIG_PATH9, "utf-8").split("\n");
19511
+ const pattern = new RegExp(`^${key2}=`);
19175
19512
  let found = false;
19176
- const updated = lines.map((line) => {
19513
+ const updated = lines2.map((line) => {
19177
19514
  if (pattern.test(line.trim())) {
19178
19515
  found = true;
19179
- return `${key}='${value}'`;
19516
+ return `${key2}='${value}'`;
19180
19517
  }
19181
19518
  return line;
19182
19519
  });
19183
- if (!found) updated.splice(updated.length - 1, 0, `${key}='${value}'`);
19184
- writeFileSync29(CONFIG_PATH9, updated.join("\n"), "utf-8");
19520
+ if (!found) updated.splice(updated.length - 1, 0, `${key2}='${value}'`);
19521
+ writeFileSync30(CONFIG_PATH9, updated.join("\n"), "utf-8");
19185
19522
  }
19186
19523
  function resolveInferenceMode(cfg) {
19187
19524
  if ((cfg.SYNKRO_GRADING_MODE || "local") === "byok") return "byok";
@@ -19339,8 +19676,8 @@ var init_config = __esm({
19339
19676
  "use strict";
19340
19677
  init_stub();
19341
19678
  init_optout();
19342
- SYNKRO_DIR16 = join41(homedir43(), ".synkro");
19343
- CONFIG_PATH9 = join41(SYNKRO_DIR16, "config.env");
19679
+ SYNKRO_DIR16 = join42(homedir43(), ".synkro");
19680
+ CONFIG_PATH9 = join42(SYNKRO_DIR16, "config.env");
19344
19681
  }
19345
19682
  });
19346
19683
 
@@ -19349,7 +19686,7 @@ var telemetry_exports2 = {};
19349
19686
  __export(telemetry_exports2, {
19350
19687
  telemetryCommand: () => telemetryCommand
19351
19688
  });
19352
- import { createInterface as createInterface6 } from "readline";
19689
+ import { createInterface as createInterface7 } from "readline";
19353
19690
  function parseFlag(args2, name) {
19354
19691
  const prefix = `--${name}=`;
19355
19692
  for (const a of args2) if (a.startsWith(prefix)) return a.slice(prefix.length);
@@ -19430,12 +19767,12 @@ async function runExport(args2) {
19430
19767
  }
19431
19768
  function confirmYesNo(question) {
19432
19769
  if (!process.stdin.isTTY) return Promise.resolve(false);
19433
- return new Promise((resolve8) => {
19434
- const rl = createInterface6({ input: process.stdin, output: process.stdout });
19770
+ return new Promise((resolve9) => {
19771
+ const rl = createInterface7({ input: process.stdin, output: process.stdout });
19435
19772
  rl.question(`${question} (y/N): `, (answer) => {
19436
19773
  rl.close();
19437
19774
  const t = answer.trim().toLowerCase();
19438
- resolve8(t === "y" || t === "yes");
19775
+ resolve9(t === "y" || t === "yes");
19439
19776
  });
19440
19777
  });
19441
19778
  }
@@ -19530,11 +19867,11 @@ Usage:
19530
19867
 
19531
19868
  // cli/inventory/identity.ts
19532
19869
  import { randomUUID as randomUUID5 } from "crypto";
19533
- import { existsSync as existsSync42, mkdirSync as mkdirSync24, readFileSync as readFileSync38, renameSync as renameSync9, writeFileSync as writeFileSync30 } from "fs";
19870
+ import { existsSync as existsSync42, mkdirSync as mkdirSync25, readFileSync as readFileSync40, renameSync as renameSync9, writeFileSync as writeFileSync31 } from "fs";
19534
19871
  import { homedir as homedir44 } from "os";
19535
- import { dirname as dirname13, join as join42 } from "path";
19872
+ import { dirname as dirname14, join as join43 } from "path";
19536
19873
  function operationalIdentityPath() {
19537
- return process.env.SYNKRO_OPERATIONAL_IDENTITY_PATH || join42(homedir44(), ".synkro", "installation.json");
19874
+ return process.env.SYNKRO_OPERATIONAL_IDENTITY_PATH || join43(homedir44(), ".synkro", "installation.json");
19538
19875
  }
19539
19876
  function validIdentity(value) {
19540
19877
  if (!value || typeof value !== "object") return false;
@@ -19542,9 +19879,9 @@ function validIdentity(value) {
19542
19879
  return typeof row2.installation_id === "string" && UUID_RE.test(row2.installation_id) && typeof row2.created_at === "string" && Number.isFinite(Date.parse(row2.created_at));
19543
19880
  }
19544
19881
  function writeIdentity(path, identity) {
19545
- mkdirSync24(dirname13(path), { recursive: true, mode: 448 });
19882
+ mkdirSync25(dirname14(path), { recursive: true, mode: 448 });
19546
19883
  const temp = `${path}.${process.pid}.${randomUUID5()}.tmp`;
19547
- writeFileSync30(temp, JSON.stringify(identity, null, 2) + "\n", { encoding: "utf8", mode: 384 });
19884
+ writeFileSync31(temp, JSON.stringify(identity, null, 2) + "\n", { encoding: "utf8", mode: 384 });
19548
19885
  renameSync9(temp, path);
19549
19886
  }
19550
19887
  function getOperationalInstallationIdentity(path = operationalIdentityPath()) {
@@ -19552,7 +19889,7 @@ function getOperationalInstallationIdentity(path = operationalIdentityPath()) {
19552
19889
  if (prior) return prior;
19553
19890
  if (existsSync42(path)) {
19554
19891
  try {
19555
- const parsed = JSON.parse(readFileSync38(path, "utf8"));
19892
+ const parsed = JSON.parse(readFileSync40(path, "utf8"));
19556
19893
  if (validIdentity(parsed)) {
19557
19894
  cached4.set(path, parsed);
19558
19895
  return parsed;
@@ -19566,7 +19903,7 @@ function getOperationalInstallationIdentity(path = operationalIdentityPath()) {
19566
19903
  return identity;
19567
19904
  }
19568
19905
  var UUID_RE, cached4;
19569
- var init_identity3 = __esm({
19906
+ var init_identity2 = __esm({
19570
19907
  "cli/inventory/identity.ts"() {
19571
19908
  "use strict";
19572
19909
  UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
@@ -19578,12 +19915,12 @@ var init_identity3 = __esm({
19578
19915
  import { createHash as createHash5 } from "crypto";
19579
19916
  import {
19580
19917
  existsSync as existsSync43,
19581
- readFileSync as readFileSync39,
19918
+ readFileSync as readFileSync41,
19582
19919
  readdirSync as readdirSync11,
19583
- statSync as statSync6
19920
+ statSync as statSync7
19584
19921
  } from "fs";
19585
19922
  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";
19923
+ import { basename as basename4, join as join44, relative as relative2, resolve as resolve7 } from "path";
19587
19924
  import { fileURLToPath } from "url";
19588
19925
  function sha256(value) {
19589
19926
  return createHash5("sha256").update(value).digest("hex");
@@ -19593,7 +19930,7 @@ function pseudonymousHostnameHash(installationId, host) {
19593
19930
  }
19594
19931
  function cliVersion() {
19595
19932
  try {
19596
- return "1.10.8";
19933
+ return "1.10.9";
19597
19934
  } catch {
19598
19935
  return "0.0.0";
19599
19936
  }
@@ -19601,7 +19938,7 @@ function cliVersion() {
19601
19938
  function readJson(path) {
19602
19939
  try {
19603
19940
  if (!existsSync43(path)) return null;
19604
- const parsed = JSON.parse(readFileSync39(path, "utf8"));
19941
+ const parsed = JSON.parse(readFileSync41(path, "utf8"));
19605
19942
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
19606
19943
  } catch {
19607
19944
  return null;
@@ -19610,7 +19947,7 @@ function readJson(path) {
19610
19947
  function readText(path) {
19611
19948
  try {
19612
19949
  if (!existsSync43(path)) return "";
19613
- return readFileSync39(path, "utf8");
19950
+ return readFileSync41(path, "utf8");
19614
19951
  } catch {
19615
19952
  return "";
19616
19953
  }
@@ -19696,16 +20033,16 @@ function mcpArtifactsFromJson(harness, config, configScope = "user") {
19696
20033
  }
19697
20034
  function claudeDesktopConfigCandidates(home, targetPlatform) {
19698
20035
  if (targetPlatform === "darwin") {
19699
- return [join43(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")];
20036
+ return [join44(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")];
19700
20037
  }
19701
20038
  if (targetPlatform === "linux") {
19702
20039
  return [
19703
- join43(home, ".config", "Claude", "claude_desktop_config.json"),
19704
- join43(home, ".config", "claude", "claude_desktop_config.json")
20040
+ join44(home, ".config", "Claude", "claude_desktop_config.json"),
20041
+ join44(home, ".config", "claude", "claude_desktop_config.json")
19705
20042
  ];
19706
20043
  }
19707
20044
  if (targetPlatform === "win32" && process.env.APPDATA) {
19708
- return [join43(process.env.APPDATA, "Claude", "claude_desktop_config.json")];
20045
+ return [join44(process.env.APPDATA, "Claude", "claude_desktop_config.json")];
19709
20046
  }
19710
20047
  return [];
19711
20048
  }
@@ -19713,7 +20050,7 @@ function claudeManagedMcpConfigCandidates(targetPlatform) {
19713
20050
  if (targetPlatform === "darwin") return ["/Library/Application Support/ClaudeCode/managed-mcp.json"];
19714
20051
  if (targetPlatform === "linux") return ["/etc/claude-code/managed-mcp.json"];
19715
20052
  if (targetPlatform === "win32" && process.env.ProgramFiles) {
19716
- return [join43(process.env.ProgramFiles, "ClaudeCode", "managed-mcp.json")];
20053
+ return [join44(process.env.ProgramFiles, "ClaudeCode", "managed-mcp.json")];
19717
20054
  }
19718
20055
  return [];
19719
20056
  }
@@ -19721,7 +20058,7 @@ function discoveredProjectRoots(claudeState, currentDirectory, explicit = [], cu
19721
20058
  const roots = /* @__PURE__ */ new Set();
19722
20059
  const add = (value) => {
19723
20060
  if (typeof value !== "string" || !value.trim()) return;
19724
- const path = resolve6(value);
20061
+ const path = resolve7(value);
19725
20062
  if (existsSync43(path)) roots.add(path);
19726
20063
  };
19727
20064
  add(currentDirectory);
@@ -19733,10 +20070,10 @@ function discoveredProjectRoots(claudeState, currentDirectory, explicit = [], cu
19733
20070
  return [...roots];
19734
20071
  }
19735
20072
  function cursorWorkspaceStorageCandidates(home, targetPlatform) {
19736
- if (targetPlatform === "darwin") return [join43(home, "Library", "Application Support", "Cursor", "User", "workspaceStorage")];
19737
- if (targetPlatform === "linux") return [join43(home, ".config", "Cursor", "User", "workspaceStorage")];
20073
+ if (targetPlatform === "darwin") return [join44(home, "Library", "Application Support", "Cursor", "User", "workspaceStorage")];
20074
+ if (targetPlatform === "linux") return [join44(home, ".config", "Cursor", "User", "workspaceStorage")];
19738
20075
  if (targetPlatform === "win32" && process.env.APPDATA) {
19739
- return [join43(process.env.APPDATA, "Cursor", "User", "workspaceStorage")];
20076
+ return [join44(process.env.APPDATA, "Cursor", "User", "workspaceStorage")];
19740
20077
  }
19741
20078
  return [];
19742
20079
  }
@@ -19752,12 +20089,12 @@ function cursorWorkspaceRoots(home, targetPlatform) {
19752
20089
  }
19753
20090
  for (const entry of entries) {
19754
20091
  if (!entry.isDirectory() || entry.isSymbolicLink?.()) continue;
19755
- const state = readJson(join43(storage, entry.name, "workspace.json"));
20092
+ const state = readJson(join44(storage, entry.name, "workspace.json"));
19756
20093
  const raw = state?.folder;
19757
20094
  if (typeof raw !== "string" || !raw.trim()) continue;
19758
20095
  try {
19759
20096
  const path = raw.startsWith("file:") ? fileURLToPath(raw) : raw;
19760
- if (existsSync43(path)) roots.add(resolve6(path));
20097
+ if (existsSync43(path)) roots.add(resolve7(path));
19761
20098
  } catch {
19762
20099
  }
19763
20100
  }
@@ -19769,8 +20106,8 @@ function codexMcpArtifacts(content) {
19769
20106
  const sections = [...content.matchAll(/^\s*\[\s*([^\]]+)\s*\]\s*$/gm)];
19770
20107
  for (let index = 0; index < sections.length && artifacts.length < 1e3; index++) {
19771
20108
  const section = sections[index];
19772
- const key = section[1].trim();
19773
- const root = key.match(/^mcp_servers\s*\.\s*(?:"((?:[^"\\]|\\.)+)"|'([^']+)'|([A-Za-z0-9_-]+))$/);
20109
+ const key2 = section[1].trim();
20110
+ const root = key2.match(/^mcp_servers\s*\.\s*(?:"((?:[^"\\]|\\.)+)"|'([^']+)'|([A-Za-z0-9_-]+))$/);
19774
20111
  if (!root) continue;
19775
20112
  let name = root[1] || root[2] || root[3];
19776
20113
  if (root[1]) {
@@ -19783,8 +20120,8 @@ function codexMcpArtifacts(content) {
19783
20120
  const start = (section.index ?? 0) + section[0].length;
19784
20121
  const end = sections[index + 1]?.index ?? content.length;
19785
20122
  const block = content.slice(start, end);
19786
- const stringValue = (key2) => {
19787
- const found = block.match(new RegExp(`^\\s*${key2}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*")`, "m"));
20123
+ const stringValue = (key3) => {
20124
+ const found = block.match(new RegExp(`^\\s*${key3}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*")`, "m"));
19788
20125
  if (!found) return void 0;
19789
20126
  try {
19790
20127
  return JSON.parse(found[1]);
@@ -19816,9 +20153,9 @@ function flattenHookEntries(value) {
19816
20153
  const out = [];
19817
20154
  for (const entry of value) {
19818
20155
  if (!entry || typeof entry !== "object") continue;
19819
- const record = entry;
19820
- if (typeof record.command === "string") out.push(record);
19821
- if (Array.isArray(record.hooks)) out.push(...flattenHookEntries(record.hooks));
20156
+ const record2 = entry;
20157
+ if (typeof record2.command === "string") out.push(record2);
20158
+ if (Array.isArray(record2.hooks)) out.push(...flattenHookEntries(record2.hooks));
19822
20159
  }
19823
20160
  return out;
19824
20161
  }
@@ -19847,7 +20184,7 @@ function hookArtifacts(harness, config) {
19847
20184
  function parseFrontmatter(content) {
19848
20185
  const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
19849
20186
  if (!match) return {};
19850
- const value = (key) => match[1].match(new RegExp(`^${key}:\\s*["']?([^"'\\n]+)`, "m"))?.[1]?.trim();
20187
+ const value = (key2) => match[1].match(new RegExp(`^${key2}:\\s*["']?([^"'\\n]+)`, "m"))?.[1]?.trim();
19851
20188
  return { name: value("name"), version: value("version") };
19852
20189
  }
19853
20190
  function skillArtifacts(harness, root) {
@@ -19862,7 +20199,7 @@ function skillArtifacts(harness, root) {
19862
20199
  }
19863
20200
  for (const entry of entries) {
19864
20201
  if (entry.isSymbolicLink?.()) continue;
19865
- const path = join43(dir, entry.name);
20202
+ const path = join44(dir, entry.name);
19866
20203
  if (entry.isFile() && entry.name === "SKILL.md") manifests.push(path);
19867
20204
  else if (entry.isDirectory()) visit(path);
19868
20205
  }
@@ -19871,8 +20208,8 @@ function skillArtifacts(harness, root) {
19871
20208
  return manifests.map((path) => {
19872
20209
  const content = readText(path);
19873
20210
  const frontmatter = parseFrontmatter(content);
19874
- const rel = relative(root, path).replaceAll("\\", "/");
19875
- const name = frontmatter.name || basename4(join43(path, "..")) || "skill";
20211
+ const rel = relative2(root, path).replaceAll("\\", "/");
20212
+ const name = frontmatter.name || basename4(join44(path, "..")) || "skill";
19876
20213
  return {
19877
20214
  harness,
19878
20215
  type: "skill",
@@ -19896,7 +20233,7 @@ function cursorExtensionArtifacts(root) {
19896
20233
  }
19897
20234
  const artifacts = [];
19898
20235
  for (const dir of dirs) {
19899
- const pkg = readJson(join43(root, dir.name, "package.json"));
20236
+ const pkg = readJson(join44(root, dir.name, "package.json"));
19900
20237
  if (!pkg) continue;
19901
20238
  const publisher = typeof pkg.publisher === "string" ? pkg.publisher : void 0;
19902
20239
  const name = typeof pkg.name === "string" ? pkg.name : dir.name;
@@ -19916,21 +20253,21 @@ function cursorExtensionArtifacts(root) {
19916
20253
  return artifacts;
19917
20254
  }
19918
20255
  function deploymentMode2(home) {
19919
- const raw = readText(join43(home, ".synkro", "config.env"));
19920
- const value = (key) => raw.match(new RegExp(`^${key}=['"]?([^'"\\n]*)`, "m"))?.[1]?.toLowerCase();
20256
+ const raw = readText(join44(home, ".synkro", "config.env"));
20257
+ const value = (key2) => raw.match(new RegExp(`^${key2}=['"]?([^'"\\n]*)`, "m"))?.[1]?.toLowerCase();
19921
20258
  if (value("SYNKRO_GRADING_MODE") === "byok") return "byok";
19922
20259
  if (value("SYNKRO_STORAGE_MODE") === "cloud") return "cloud";
19923
20260
  return "local";
19924
20261
  }
19925
20262
  function telemetryHealth(home) {
19926
- const meta = readJson(join43(home, ".synkro", "telemetry-meta.json"));
20263
+ const meta = readJson(join44(home, ".synkro", "telemetry-meta.json"));
19927
20264
  const health = {};
19928
20265
  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;
19929
20266
  if (meta?.last_flush_error) health.telemetry_last_error = "flush_failed";
19930
- const queue = join43(home, ".synkro", "telemetry-pending.jsonl");
20267
+ const queue = join44(home, ".synkro", "telemetry-pending.jsonl");
19931
20268
  try {
19932
- const size = statSync6(queue).size;
19933
- health.telemetry_backlog = size <= 5 * 1024 * 1024 ? readFileSync39(queue, "utf8").split("\n").filter(Boolean).length : Math.ceil(size / 1024);
20269
+ const size = statSync7(queue).size;
20270
+ health.telemetry_backlog = size <= 5 * 1024 * 1024 ? readFileSync41(queue, "utf8").split("\n").filter(Boolean).length : Math.ceil(size / 1024);
19934
20271
  } catch {
19935
20272
  }
19936
20273
  return health;
@@ -19971,7 +20308,7 @@ function harnessSnapshot(agent) {
19971
20308
  }
19972
20309
  const config = readJson(agent.settingsPath);
19973
20310
  const coverage = inspectCodexHooks(agent.settingsPath);
19974
- const toml = readText(join43(agent.configDir, "config.toml"));
20311
+ const toml = readText(join44(agent.configDir, "config.toml"));
19975
20312
  const permission = toml.match(/^\s*approval_policy\s*=\s*["']([^"']+)/m)?.[1];
19976
20313
  return {
19977
20314
  row: {
@@ -19992,7 +20329,7 @@ function collectOperationalInventory(options = {}) {
19992
20329
  const detected = options.detectedAgents ?? detectAgents();
19993
20330
  const identity = getOperationalInstallationIdentity(options.identityPath);
19994
20331
  const targetPlatform = options.platformName ?? platform6();
19995
- const codexHome = options.homeDir ? join43(home, ".codex") : process.env.CODEX_HOME || join43(home, ".codex");
20332
+ const codexHome = options.homeDir ? join44(home, ".codex") : process.env.CODEX_HOME || join44(home, ".codex");
19996
20333
  const harnesses = [];
19997
20334
  const artifacts = [];
19998
20335
  for (const agent of detected) {
@@ -20000,7 +20337,7 @@ function collectOperationalInventory(options = {}) {
20000
20337
  harnesses.push(row2);
20001
20338
  artifacts.push(...hookArtifacts(row2.harness, config));
20002
20339
  }
20003
- const claudeJson = readJson(join43(home, ".claude.json"));
20340
+ const claudeJson = readJson(join44(home, ".claude.json"));
20004
20341
  artifacts.push(...mcpArtifactsFromJson("claude_code", claudeJson));
20005
20342
  if (claudeJson?.projects && typeof claudeJson.projects === "object") {
20006
20343
  for (const [projectPath, project] of Object.entries(claudeJson.projects)) {
@@ -20008,8 +20345,8 @@ function collectOperationalInventory(options = {}) {
20008
20345
  artifacts.push(...mcpArtifactsFromJson("claude_code", project, `local:${sha256(projectPath).slice(0, 16)}`));
20009
20346
  }
20010
20347
  }
20011
- artifacts.push(...mcpArtifactsFromJson("cursor", readJson(join43(home, ".cursor", "mcp.json"))));
20012
- artifacts.push(...codexMcpArtifacts(readText(join43(codexHome, "config.toml"))));
20348
+ artifacts.push(...mcpArtifactsFromJson("cursor", readJson(join44(home, ".cursor", "mcp.json"))));
20349
+ artifacts.push(...codexMcpArtifacts(readText(join44(codexHome, "config.toml"))));
20013
20350
  const projectRoots = discoveredProjectRoots(
20014
20351
  claudeJson,
20015
20352
  options.currentDirectory ?? process.cwd(),
@@ -20020,11 +20357,11 @@ function collectOperationalInventory(options = {}) {
20020
20357
  const scopeHash = sha256(projectRoot).slice(0, 16);
20021
20358
  artifacts.push(...mcpArtifactsFromJson(
20022
20359
  "claude_code",
20023
- readJson(join43(projectRoot, ".mcp.json")),
20360
+ readJson(join44(projectRoot, ".mcp.json")),
20024
20361
  `project:${scopeHash}`
20025
20362
  ));
20026
- const cursorProjectConfig = join43(projectRoot, ".cursor", "mcp.json");
20027
- if (resolve6(cursorProjectConfig) !== resolve6(join43(home, ".cursor", "mcp.json"))) {
20363
+ const cursorProjectConfig = join44(projectRoot, ".cursor", "mcp.json");
20364
+ if (resolve7(cursorProjectConfig) !== resolve7(join44(home, ".cursor", "mcp.json"))) {
20028
20365
  artifacts.push(...mcpArtifactsFromJson(
20029
20366
  "cursor",
20030
20367
  readJson(cursorProjectConfig),
@@ -20046,7 +20383,7 @@ function collectOperationalInventory(options = {}) {
20046
20383
  });
20047
20384
  artifacts.push(...mcpArtifactsFromJson("claude_desktop", desktopConfig));
20048
20385
  }
20049
- const claudeSettings = readJson(join43(home, ".claude", "settings.json"));
20386
+ const claudeSettings = readJson(join44(home, ".claude", "settings.json"));
20050
20387
  if (claudeSettings?.enabledPlugins && typeof claudeSettings.enabledPlugins === "object") {
20051
20388
  for (const [name, enabled] of Object.entries(claudeSettings.enabledPlugins)) {
20052
20389
  artifacts.push({
@@ -20060,14 +20397,14 @@ function collectOperationalInventory(options = {}) {
20060
20397
  });
20061
20398
  }
20062
20399
  }
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")));
20400
+ artifacts.push(...skillArtifacts("claude_code", join44(home, ".claude", "skills")));
20401
+ artifacts.push(...skillArtifacts("cursor", join44(home, ".cursor", "skills")));
20402
+ artifacts.push(...skillArtifacts("codex", join44(codexHome, "skills")));
20403
+ artifacts.push(...cursorExtensionArtifacts(join44(home, ".cursor", "extensions")));
20067
20404
  const uniqueArtifacts = /* @__PURE__ */ new Map();
20068
20405
  for (const artifact of artifacts) {
20069
- const key = `${artifact.harness || "global"}:${artifact.type}:${artifact.canonical_id}`;
20070
- uniqueArtifacts.set(key, artifact);
20406
+ const key2 = `${artifact.harness || "global"}:${artifact.type}:${artifact.canonical_id}`;
20407
+ uniqueArtifacts.set(key2, artifact);
20071
20408
  }
20072
20409
  const codingHarnesses = harnesses.filter((row2) => row2.harness === "claude_code" || row2.harness === "cursor" || row2.harness === "codex");
20073
20410
  const health = telemetryHealth(home) ?? {};
@@ -20100,7 +20437,7 @@ var init_collector = __esm({
20100
20437
  init_ccHookConfig();
20101
20438
  init_cursorHookConfig();
20102
20439
  init_codexHookConfig();
20103
- init_identity3();
20440
+ init_identity2();
20104
20441
  }
20105
20442
  });
20106
20443
 
@@ -20119,19 +20456,19 @@ import { createHash as createHash6, randomUUID as randomUUID6 } from "crypto";
20119
20456
  import { spawn as spawn11 } from "child_process";
20120
20457
  import {
20121
20458
  existsSync as existsSync44,
20122
- mkdirSync as mkdirSync25,
20123
- readFileSync as readFileSync40,
20459
+ mkdirSync as mkdirSync26,
20460
+ readFileSync as readFileSync42,
20124
20461
  renameSync as renameSync10,
20125
- writeFileSync as writeFileSync31
20462
+ writeFileSync as writeFileSync32
20126
20463
  } from "fs";
20127
20464
  import { homedir as homedir46 } from "os";
20128
- import { dirname as dirname14, join as join44 } from "path";
20465
+ import { dirname as dirname15, join as join45 } from "path";
20129
20466
  function syncStatePath() {
20130
- return process.env.SYNKRO_INVENTORY_SYNC_STATE_PATH || join44(homedir46(), ".synkro", "inventory-sync.json");
20467
+ return process.env.SYNKRO_INVENTORY_SYNC_STATE_PATH || join45(homedir46(), ".synkro", "inventory-sync.json");
20131
20468
  }
20132
20469
  function readState(path = syncStatePath()) {
20133
20470
  try {
20134
- const parsed = JSON.parse(readFileSync40(path, "utf8"));
20471
+ const parsed = JSON.parse(readFileSync42(path, "utf8"));
20135
20472
  return parsed && typeof parsed === "object" ? parsed : {};
20136
20473
  } catch {
20137
20474
  return {};
@@ -20139,9 +20476,9 @@ function readState(path = syncStatePath()) {
20139
20476
  }
20140
20477
  function writeState(state, path = syncStatePath()) {
20141
20478
  try {
20142
- mkdirSync25(dirname14(path), { recursive: true, mode: 448 });
20479
+ mkdirSync26(dirname15(path), { recursive: true, mode: 448 });
20143
20480
  const temp = `${path}.${process.pid}.tmp`;
20144
- writeFileSync31(temp, JSON.stringify(state, null, 2) + "\n", { encoding: "utf8", mode: 384 });
20481
+ writeFileSync32(temp, JSON.stringify(state, null, 2) + "\n", { encoding: "utf8", mode: 384 });
20145
20482
  renameSync10(temp, path);
20146
20483
  } catch {
20147
20484
  }
@@ -20154,18 +20491,18 @@ function shouldSyncInventory(state, now = Date.now(), target) {
20154
20491
  return !Number.isFinite(lastAttempt) || lastAttempt <= 0 || now - lastAttempt >= FAILURE_RETRY_MS;
20155
20492
  }
20156
20493
  function readConfig() {
20157
- const path = join44(homedir46(), ".synkro", "config.env");
20494
+ const path = join45(homedir46(), ".synkro", "config.env");
20158
20495
  const out = {};
20159
20496
  try {
20160
- for (const rawLine of readFileSync40(path, "utf8").split("\n")) {
20497
+ for (const rawLine of readFileSync42(path, "utf8").split("\n")) {
20161
20498
  const line = rawLine.trim();
20162
20499
  if (!line || line.startsWith("#")) continue;
20163
20500
  const index = line.indexOf("=");
20164
20501
  if (index <= 0) continue;
20165
- const key = line.slice(0, index).trim();
20502
+ const key2 = line.slice(0, index).trim();
20166
20503
  let value = line.slice(index + 1).trim();
20167
20504
  if (value.startsWith("'") && value.endsWith("'") || value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1);
20168
- out[key] = value;
20505
+ out[key2] = value;
20169
20506
  }
20170
20507
  } catch {
20171
20508
  }
@@ -20196,7 +20533,7 @@ function resolveInventoryGateway(raw) {
20196
20533
  }
20197
20534
  async function loadToken() {
20198
20535
  try {
20199
- const durable = readFileSync40(join44(homedir46(), ".synkro", ".mcp-jwt"), "utf8").trim();
20536
+ const durable = readFileSync42(join45(homedir46(), ".synkro", ".mcp-jwt"), "utf8").trim();
20200
20537
  if (durable) return durable;
20201
20538
  } catch {
20202
20539
  }
@@ -20212,7 +20549,7 @@ async function loadToken() {
20212
20549
  function stable(value) {
20213
20550
  if (Array.isArray(value)) return value.map(stable);
20214
20551
  if (!value || typeof value !== "object") return value;
20215
- return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, child]) => [key, stable(child)]));
20552
+ return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key2, child]) => [key2, stable(child)]));
20216
20553
  }
20217
20554
  function inventorySnapshotChunks(snapshot, maxBytes = INVENTORY_CHUNK_BYTES) {
20218
20555
  const { collected_at: _heartbeat, ...material } = snapshot;
@@ -20338,23 +20675,23 @@ var init_sync2 = __esm({
20338
20675
  });
20339
20676
 
20340
20677
  // cli/bootstrap.js
20341
- import { readFileSync as readFileSync41, existsSync as existsSync45 } from "fs";
20342
- import { resolve as resolve7 } from "path";
20678
+ import { readFileSync as readFileSync43, existsSync as existsSync45 } from "fs";
20679
+ import { resolve as resolve8 } from "path";
20343
20680
  process.title = "synkro";
20344
20681
  var envCandidates = [
20345
- resolve7(process.env.HOME ?? "", ".synkro", "config.env")
20682
+ resolve8(process.env.HOME ?? "", ".synkro", "config.env")
20346
20683
  ];
20347
20684
  for (const envPath of envCandidates) {
20348
20685
  if (!existsSync45(envPath)) continue;
20349
- const envContent = readFileSync41(envPath, "utf-8");
20686
+ const envContent = readFileSync43(envPath, "utf-8");
20350
20687
  for (const line of envContent.split("\n")) {
20351
20688
  const trimmed = line.trim();
20352
20689
  if (!trimmed || trimmed.startsWith("#")) continue;
20353
20690
  const eqIndex = trimmed.indexOf("=");
20354
20691
  if (eqIndex <= 0) continue;
20355
- const key = trimmed.slice(0, eqIndex).trim();
20692
+ const key2 = trimmed.slice(0, eqIndex).trim();
20356
20693
  const value = trimmed.slice(eqIndex + 1).trim().replace(/^['"]|['"]$/g, "");
20357
- if (!process.env[key] && !value.startsWith("op://")) process.env[key] = value;
20694
+ if (!process.env[key2] && !value.startsWith("op://")) process.env[key2] = value;
20358
20695
  }
20359
20696
  }
20360
20697
  var args = process.argv.slice(2);
@@ -20363,7 +20700,7 @@ var subArgs = args.slice(1);
20363
20700
  var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
20364
20701
  var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "inventory-sync", "version", "--version", "-v", "help", "--help", "-h", ""]);
20365
20702
  function printVersion() {
20366
- console.log("1.10.8");
20703
+ console.log("1.10.9");
20367
20704
  }
20368
20705
  function printHelp2() {
20369
20706
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents