@synkro-sh/cli 1.7.18 → 1.7.24

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
@@ -269,6 +269,14 @@ function installCCHooks(settingsPath, config) {
269
269
  ],
270
270
  [SYNKRO_MARKER]: true
271
271
  });
272
+ settings.env = settings.env ?? {};
273
+ const existingBaseUrl = settings.env.ANTHROPIC_BASE_URL;
274
+ if (!existingBaseUrl || existingBaseUrl === USAGE_PROXY_URL) {
275
+ settings.env.ANTHROPIC_BASE_URL = USAGE_PROXY_URL;
276
+ } else {
277
+ console.warn(` \u26A0 ANTHROPIC_BASE_URL already set to ${existingBaseUrl} \u2014 leaving it; live usage capture is off for this agent.`);
278
+ }
279
+ if (Object.keys(settings.env).length === 0) delete settings.env;
272
280
  writeSettingsAtomic(settingsPath, settings);
273
281
  }
274
282
  function uninstallCCHooks(settingsPath) {
@@ -287,14 +295,19 @@ function uninstallCCHooks(settingsPath) {
287
295
  if (Object.keys(settings.hooks).length === 0) {
288
296
  delete settings.hooks;
289
297
  }
298
+ if (settings.env && settings.env.ANTHROPIC_BASE_URL === USAGE_PROXY_URL) {
299
+ delete settings.env.ANTHROPIC_BASE_URL;
300
+ if (Object.keys(settings.env).length === 0) delete settings.env;
301
+ }
290
302
  writeSettingsAtomic(settingsPath, settings);
291
303
  return true;
292
304
  }
293
- var SYNKRO_MARKER;
305
+ var SYNKRO_MARKER, USAGE_PROXY_URL;
294
306
  var init_ccHookConfig = __esm({
295
307
  "cli/installer/ccHookConfig.ts"() {
296
308
  "use strict";
297
309
  SYNKRO_MARKER = "__synkro_managed__";
310
+ USAGE_PROXY_URL = `http://127.0.0.1:${process.env.SYNKRO_HOST_USAGE_PROXY_PORT || "18927"}`;
298
311
  }
299
312
  });
300
313
 
@@ -10610,6 +10623,8 @@ async function dockerInstall(opts = {}) {
10610
10623
  "-p",
10611
10624
  `127.0.0.1:${HOST_CWE_PORT}:8930`,
10612
10625
  "-p",
10626
+ `127.0.0.1:${HOST_USAGE_PROXY_PORT}:8927`,
10627
+ "-p",
10613
10628
  `127.0.0.1:${HOST_PGLITE_PORT}:5433`,
10614
10629
  "-v",
10615
10630
  `${PGDATA_PATH}:/data/pgdata`,
@@ -10860,7 +10875,7 @@ function checkPgdata() {
10860
10875
  if (!hasPgControl) return { healthy: false, details: "pg_control/global directory missing" };
10861
10876
  return { healthy: true, details: `${entries.length} entries, WAL present, no stale PID` };
10862
10877
  }
10863
- var SYNKRO_DIR2, MCP_JWT_PATH, PGDATA_PATH, CLAUDE_HOST_STATE_DIR, CLAUDE_HOST_STATE_FILE, HOST_MCP_PORT, HOST_GRADER_PORT, HOST_CWE_PORT, HOST_PGLITE_PORT, CONTAINER_NAME, DEFAULT_IMAGE, DockerInstallError, BACKUP_DIR;
10878
+ var SYNKRO_DIR2, MCP_JWT_PATH, PGDATA_PATH, CLAUDE_HOST_STATE_DIR, CLAUDE_HOST_STATE_FILE, HOST_MCP_PORT, HOST_GRADER_PORT, HOST_CWE_PORT, HOST_USAGE_PROXY_PORT, HOST_PGLITE_PORT, CONTAINER_NAME, DEFAULT_IMAGE, DockerInstallError, BACKUP_DIR;
10864
10879
  var init_dockerInstall = __esm({
10865
10880
  "cli/local-cc/dockerInstall.ts"() {
10866
10881
  "use strict";
@@ -10874,6 +10889,7 @@ var init_dockerInstall = __esm({
10874
10889
  HOST_MCP_PORT = parseInt(process.env.SYNKRO_HOST_MCP_PORT || "18931", 10);
10875
10890
  HOST_GRADER_PORT = parseInt(process.env.SYNKRO_HOST_GRADER_PORT || "18929", 10);
10876
10891
  HOST_CWE_PORT = parseInt(process.env.SYNKRO_HOST_CWE_PORT || "18930", 10);
10892
+ HOST_USAGE_PROXY_PORT = parseInt(process.env.SYNKRO_HOST_USAGE_PROXY_PORT || "18927", 10);
10877
10893
  HOST_PGLITE_PORT = parseInt(process.env.SYNKRO_HOST_PGLITE_PORT || "15433", 10);
10878
10894
  CONTAINER_NAME = "synkro-server";
10879
10895
  DEFAULT_IMAGE = "ghcr.io/synkro-sh/synkro-server:latest";
@@ -10979,6 +10995,87 @@ var init_setupToken = __esm({
10979
10995
  }
10980
10996
  });
10981
10997
 
10998
+ // cli/local-cc/codexCloudSetup.ts
10999
+ var codexCloudSetup_exports = {};
11000
+ __export(codexCloudSetup_exports, {
11001
+ setupCodexCloud: () => setupCodexCloud
11002
+ });
11003
+ import { spawn as nodeSpawn2, spawnSync as spawnSync4 } from "child_process";
11004
+ import { readFileSync as readFileSync9, mkdirSync as mkdirSync9, rmSync, existsSync as existsSync10 } from "fs";
11005
+ import { homedir as homedir9 } from "os";
11006
+ import { join as join9 } from "path";
11007
+ function findCodexBinary() {
11008
+ if (process.env.SYNKRO_CODEX_BIN) return process.env.SYNKRO_CODEX_BIN;
11009
+ const r = spawnSync4("which", ["codex"], { encoding: "utf-8" });
11010
+ const p = (r.stdout || "").trim().split("\n")[0];
11011
+ return p || null;
11012
+ }
11013
+ function runCodexLogin(codexBin) {
11014
+ mkdirSync9(CODEX_CLOUD_HOME, { recursive: true, mode: 448 });
11015
+ return new Promise((resolve4, reject) => {
11016
+ const proc = nodeSpawn2(codexBin, ["login"], {
11017
+ stdio: "inherit",
11018
+ env: { ...process.env, CODEX_HOME: CODEX_CLOUD_HOME }
11019
+ });
11020
+ proc.on("error", (err) => reject(new Error(`failed to spawn codex login: ${err.message}`)));
11021
+ proc.on("close", (code) => code === 0 ? resolve4() : reject(new Error(`codex login exited with code ${code}`)));
11022
+ });
11023
+ }
11024
+ async function setupCodexCloud(gatewayUrl, bearerToken, onStatus) {
11025
+ const codexBin = findCodexBinary();
11026
+ if (!codexBin) {
11027
+ return { ok: false, error: "Codex CLI not found on PATH. Install Codex (https://developers.openai.com/codex), then re-run `synkro install`. (Set SYNKRO_CODEX_BIN to override the binary path.)" };
11028
+ }
11029
+ onStatus?.("Opening your browser to authorize your Codex subscription for Synkro Cloud\u2026");
11030
+ try {
11031
+ await runCodexLogin(codexBin);
11032
+ } catch (e) {
11033
+ return { ok: false, error: `Codex login failed: ${e.message}` };
11034
+ }
11035
+ const authPath = join9(CODEX_CLOUD_HOME, "auth.json");
11036
+ if (!existsSync10(authPath)) {
11037
+ return { ok: false, error: "codex login completed but no auth.json was written \u2014 did the browser approval finish?" };
11038
+ }
11039
+ let auth;
11040
+ try {
11041
+ auth = JSON.parse(readFileSync9(authPath, "utf-8"));
11042
+ } catch (e) {
11043
+ return { ok: false, error: `could not read codex auth.json: ${e.message}` };
11044
+ }
11045
+ if (!auth || typeof auth !== "object" || !auth.tokens?.refresh_token) {
11046
+ return { ok: false, error: "codex auth.json has no refresh token \u2014 cloud refresh would fail. Re-run login." };
11047
+ }
11048
+ onStatus?.("Connecting your Codex session to Synkro Cloud\u2026");
11049
+ let resp;
11050
+ try {
11051
+ resp = await fetch(`${gatewayUrl.replace(/\/+$/, "")}/codex/seed`, {
11052
+ method: "POST",
11053
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${bearerToken}` },
11054
+ body: JSON.stringify({ auth })
11055
+ });
11056
+ } catch (e) {
11057
+ return { ok: false, error: `failed to reach Synkro API: ${e.message}` };
11058
+ }
11059
+ if (!resp.ok) {
11060
+ const detail = await resp.text().catch(() => "");
11061
+ return { ok: false, error: `seed rejected (${resp.status}): ${detail.slice(0, 200)}` };
11062
+ }
11063
+ try {
11064
+ rmSync(CODEX_CLOUD_HOME, { recursive: true, force: true });
11065
+ } catch {
11066
+ }
11067
+ onStatus?.("\u2713 Codex subscription connected to Synkro Cloud.");
11068
+ return { ok: true };
11069
+ }
11070
+ var SYNKRO_DIR4, CODEX_CLOUD_HOME;
11071
+ var init_codexCloudSetup = __esm({
11072
+ "cli/local-cc/codexCloudSetup.ts"() {
11073
+ "use strict";
11074
+ SYNKRO_DIR4 = join9(homedir9(), ".synkro");
11075
+ CODEX_CLOUD_HOME = join9(SYNKRO_DIR4, "codex-cloud-session");
11076
+ }
11077
+ });
11078
+
10982
11079
  // cli/commands/setupGithub.ts
10983
11080
  var setupGithub_exports = {};
10984
11081
  __export(setupGithub_exports, {
@@ -10988,14 +11085,14 @@ __export(setupGithub_exports, {
10988
11085
  import { createInterface as createInterface2 } from "readline/promises";
10989
11086
  import { stdin as input, stdout as output } from "process";
10990
11087
  import { execSync as execSync5 } from "child_process";
10991
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
10992
- import { homedir as homedir9, platform as platform4 } from "os";
10993
- import { join as join9 } from "path";
11088
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
11089
+ import { homedir as homedir10, platform as platform4 } from "os";
11090
+ import { join as join10 } from "path";
10994
11091
  import { execFile as execFile2 } from "child_process";
10995
11092
  function readConfig() {
10996
- if (!existsSync10(CONFIG_PATH)) return {};
11093
+ if (!existsSync11(CONFIG_PATH)) return {};
10997
11094
  const out = {};
10998
- for (const line of readFileSync9(CONFIG_PATH, "utf-8").split("\n")) {
11095
+ for (const line of readFileSync10(CONFIG_PATH, "utf-8").split("\n")) {
10999
11096
  const t = line.trim();
11000
11097
  if (!t || t.startsWith("#")) continue;
11001
11098
  const eq = t.indexOf("=");
@@ -11295,15 +11392,15 @@ Will push secrets to ${selected.length} repo(s):`);
11295
11392
  console.log(`Secrets pushed: ${SECRET_NAMES.CLAUDE_OAUTH}, ${SECRET_NAMES.SYNKRO_API_KEY}`);
11296
11393
  console.log("Open a PR on any selected repo to trigger your first Synkro scan.");
11297
11394
  }
11298
- var SYNKRO_DIR4, CONFIG_PATH;
11395
+ var SYNKRO_DIR5, CONFIG_PATH;
11299
11396
  var init_setupGithub = __esm({
11300
11397
  "cli/commands/setupGithub.ts"() {
11301
11398
  "use strict";
11302
11399
  init_githubSetup();
11303
11400
  init_stub();
11304
11401
  init_setupToken();
11305
- SYNKRO_DIR4 = join9(homedir9(), ".synkro");
11306
- CONFIG_PATH = join9(SYNKRO_DIR4, "config.env");
11402
+ SYNKRO_DIR5 = join10(homedir10(), ".synkro");
11403
+ CONFIG_PATH = join10(SYNKRO_DIR5, "config.env");
11307
11404
  }
11308
11405
  });
11309
11406
 
@@ -11322,9 +11419,9 @@ __export(install_exports, {
11322
11419
  syncSkillFiles: () => syncSkillFiles,
11323
11420
  writeHookScripts: () => writeHookScripts
11324
11421
  });
11325
- import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, chmodSync as chmodSync2, readFileSync as readFileSync10, readdirSync as readdirSync3, unlinkSync as unlinkSync4, statSync } from "fs";
11326
- import { homedir as homedir10 } from "os";
11327
- import { join as join10 } from "path";
11422
+ import { existsSync as existsSync12, mkdirSync as mkdirSync10, writeFileSync as writeFileSync8, chmodSync as chmodSync2, readFileSync as readFileSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync4, statSync } from "fs";
11423
+ import { homedir as homedir11 } from "os";
11424
+ import { join as join11 } from "path";
11328
11425
  import { execSync as execSync6 } from "child_process";
11329
11426
  import { createInterface as createInterface3 } from "readline";
11330
11427
  import { createHash } from "crypto";
@@ -11332,7 +11429,7 @@ function resolvePersistedHookMode() {
11332
11429
  if (process.env.SYNKRO_HOOK_MODE === "full") return "full";
11333
11430
  if (process.env.SYNKRO_HOOK_MODE === "stub") return "stub";
11334
11431
  try {
11335
- const env = readFileSync10(CONFIG_PATH2, "utf-8");
11432
+ const env = readFileSync11(CONFIG_PATH2, "utf-8");
11336
11433
  if (/^SYNKRO_HOOK_MODE=['"]?full['"]?\s*$/m.test(env)) return "full";
11337
11434
  } catch {
11338
11435
  }
@@ -11468,37 +11565,51 @@ coding patterns and the dashboard shows full history. (Y/n) `
11468
11565
  rl.close();
11469
11566
  }
11470
11567
  }
11568
+ async function promptCodexCloudSetup() {
11569
+ if (!process.stdin.isTTY) return false;
11570
+ const rl = createInterface3({ input: process.stdin, output: process.stdout });
11571
+ return new Promise((resolve4) => {
11572
+ rl.question(
11573
+ "Connect a Codex (ChatGPT subscription) session for cloud grading? This opens a\nbrowser to authorize; your subscription is stored securely and refreshed for you. (y/N) ",
11574
+ (answer) => {
11575
+ rl.close();
11576
+ const a = answer.trim().toLowerCase();
11577
+ resolve4(a === "y" || a === "yes");
11578
+ }
11579
+ );
11580
+ });
11581
+ }
11471
11582
  function ensureSynkroDir() {
11472
- mkdirSync9(SYNKRO_DIR5, { recursive: true });
11473
- mkdirSync9(HOOKS_DIR, { recursive: true });
11474
- mkdirSync9(BIN_DIR, { recursive: true });
11475
- mkdirSync9(OFFSETS_DIR, { recursive: true });
11476
- mkdirSync9(join10(SYNKRO_DIR5, "sessions"), { recursive: true });
11583
+ mkdirSync10(SYNKRO_DIR6, { recursive: true });
11584
+ mkdirSync10(HOOKS_DIR, { recursive: true });
11585
+ mkdirSync10(BIN_DIR, { recursive: true });
11586
+ mkdirSync10(OFFSETS_DIR, { recursive: true });
11587
+ mkdirSync10(join11(SYNKRO_DIR6, "sessions"), { recursive: true });
11477
11588
  }
11478
11589
  function writeHookScripts(mode = "stub") {
11479
- const installExtractCorePath = join10(HOOKS_DIR, "installExtractCore.ts");
11480
- const bashScriptPath = join10(HOOKS_DIR, "cc-bash-judge.ts");
11481
- const bashFollowupScriptPath = join10(HOOKS_DIR, "cc-bash-followup.ts");
11482
- const editPrecheckScriptPath = join10(HOOKS_DIR, "cc-edit-precheck.ts");
11483
- const cwePrecheckScriptPath = join10(HOOKS_DIR, "cc-cwe-precheck.ts");
11484
- const cvePrecheckScriptPath = join10(HOOKS_DIR, "cc-cve-precheck.ts");
11485
- const planJudgeScriptPath = join10(HOOKS_DIR, "cc-plan-judge.ts");
11486
- const agentJudgeScriptPath = join10(HOOKS_DIR, "cc-agent-judge.ts");
11487
- const stopSummaryScriptPath = join10(HOOKS_DIR, "cc-stop-summary.ts");
11488
- const sessionStartScriptPath = join10(HOOKS_DIR, "cc-session-start.ts");
11489
- const transcriptSyncScriptPath = join10(HOOKS_DIR, "cc-transcript-sync.ts");
11490
- const userPromptSubmitScriptPath = join10(HOOKS_DIR, "cc-user-prompt-submit.ts");
11491
- const commonScriptPath = join10(HOOKS_DIR, "_synkro-common.ts");
11492
- const commonBashScriptPath = join10(HOOKS_DIR, "_synkro-common.sh");
11493
- const installScanScriptPath = join10(HOOKS_DIR, "cc-install-scan.ts");
11494
- const cursorBashJudgePath = join10(HOOKS_DIR, "cursor-bash-judge.ts");
11495
- const cursorEditCapturePath = join10(HOOKS_DIR, "cursor-edit-capture.ts");
11496
- const cursorAgentCapturePath = join10(HOOKS_DIR, "cursor-agent-capture.ts");
11497
- const mcpStdioProxyPath = join10(HOOKS_DIR, "mcp-stdio-proxy.ts");
11498
- const taskActivateIntentScriptPath = join10(HOOKS_DIR, "cc-task-activate-intent.ts");
11499
- const mcpGateScriptPath = join10(HOOKS_DIR, "cc-mcp-gate.ts");
11590
+ const installExtractCorePath = join11(HOOKS_DIR, "installExtractCore.ts");
11591
+ const bashScriptPath = join11(HOOKS_DIR, "cc-bash-judge.ts");
11592
+ const bashFollowupScriptPath = join11(HOOKS_DIR, "cc-bash-followup.ts");
11593
+ const editPrecheckScriptPath = join11(HOOKS_DIR, "cc-edit-precheck.ts");
11594
+ const cwePrecheckScriptPath = join11(HOOKS_DIR, "cc-cwe-precheck.ts");
11595
+ const cvePrecheckScriptPath = join11(HOOKS_DIR, "cc-cve-precheck.ts");
11596
+ const planJudgeScriptPath = join11(HOOKS_DIR, "cc-plan-judge.ts");
11597
+ const agentJudgeScriptPath = join11(HOOKS_DIR, "cc-agent-judge.ts");
11598
+ const stopSummaryScriptPath = join11(HOOKS_DIR, "cc-stop-summary.ts");
11599
+ const sessionStartScriptPath = join11(HOOKS_DIR, "cc-session-start.ts");
11600
+ const transcriptSyncScriptPath = join11(HOOKS_DIR, "cc-transcript-sync.ts");
11601
+ const userPromptSubmitScriptPath = join11(HOOKS_DIR, "cc-user-prompt-submit.ts");
11602
+ const commonScriptPath = join11(HOOKS_DIR, "_synkro-common.ts");
11603
+ const commonBashScriptPath = join11(HOOKS_DIR, "_synkro-common.sh");
11604
+ const installScanScriptPath = join11(HOOKS_DIR, "cc-install-scan.ts");
11605
+ const cursorBashJudgePath = join11(HOOKS_DIR, "cursor-bash-judge.ts");
11606
+ const cursorEditCapturePath = join11(HOOKS_DIR, "cursor-edit-capture.ts");
11607
+ const cursorAgentCapturePath = join11(HOOKS_DIR, "cursor-agent-capture.ts");
11608
+ const mcpStdioProxyPath = join11(HOOKS_DIR, "mcp-stdio-proxy.ts");
11609
+ const taskActivateIntentScriptPath = join11(HOOKS_DIR, "cc-task-activate-intent.ts");
11610
+ const mcpGateScriptPath = join11(HOOKS_DIR, "cc-mcp-gate.ts");
11500
11611
  if (mode === "stub") {
11501
- const stubCommonPath2 = join10(HOOKS_DIR, "_synkro-stub-common.ts");
11612
+ const stubCommonPath2 = join11(HOOKS_DIR, "_synkro-stub-common.ts");
11502
11613
  const stubFiles = [
11503
11614
  [stubCommonPath2, STUB_COMMON_TS],
11504
11615
  [bashScriptPath, STUB_BASH_JUDGE_TS],
@@ -11527,7 +11638,7 @@ function writeHookScripts(mode = "stub") {
11527
11638
  chmodSync2(mcpStdioProxyPath, 493);
11528
11639
  for (const stale of ["_synkro-common.ts", "_synkro-common.sh", "installExtractCore.ts"]) {
11529
11640
  try {
11530
- unlinkSync4(join10(HOOKS_DIR, stale));
11641
+ unlinkSync4(join11(HOOKS_DIR, stale));
11531
11642
  } catch {
11532
11643
  }
11533
11644
  }
@@ -11571,7 +11682,7 @@ function writeHookScripts(mode = "stub") {
11571
11682
  writeFileSync8(mcpStdioProxyPath, MCP_STDIO_PROXY_SRC, "utf-8");
11572
11683
  writeFileSync8(installExtractCorePath, "/**\n * Deterministic install-command extraction \u2014 no LLM, no network.\n * Shared by the API pkg-scan route and the hook scripts (copied to ~/.synkro/hooks/).\n */\nimport { parse as shellParse } from 'shell-quote';\n\nexport interface DeterministicPkgRequest {\n name: string;\n version: string;\n ecosystem: string;\n}\n\ninterface RawInstall {\n ecosystem: string;\n name: string;\n versionSpec: string | null;\n source: 'registry' | 'git' | 'local' | 'url' | 'unknown';\n}\n\nconst SEPARATOR_OPS = new Set(['&&', '||', ';', '|', '&', '\\n']);\n\n/** Split a shell command into command segments (each an argv string array). */\nexport function segmentCommand(command: string): string[][] {\n let tokens: unknown[];\n try {\n tokens = shellParse(command) as unknown[];\n } catch {\n return [command.split(/\\s+/).filter(Boolean)];\n }\n const segments: string[][] = [];\n let current: string[] = [];\n for (const tok of tokens) {\n if (typeof tok === 'string') {\n current.push(tok);\n continue;\n }\n if (tok && typeof tok === 'object') {\n const op = (tok as { op?: string }).op;\n const pattern = (tok as { pattern?: string }).pattern;\n if (op && SEPARATOR_OPS.has(op)) {\n if (current.length) segments.push(current);\n current = [];\n } else if (typeof pattern === 'string') {\n current.push(pattern);\n }\n }\n }\n if (current.length) segments.push(current);\n return segments;\n}\n\nconst PM_TABLE: Record<string, { subs: Set<string>; ecosystem: string }> = {\n npm: { subs: new Set(['install', 'i', 'add', 'ci']), ecosystem: 'npm' },\n pnpm: { subs: new Set(['add', 'install', 'i', 'dlx']), ecosystem: 'npm' },\n yarn: { subs: new Set(['add', 'install']), ecosystem: 'npm' },\n bun: { subs: new Set(['add', 'install', 'i']), ecosystem: 'npm' },\n pip: { subs: new Set(['install']), ecosystem: 'PyPI' },\n pip3: { subs: new Set(['install']), ecosystem: 'PyPI' },\n cargo: { subs: new Set(['add', 'install']), ecosystem: 'crates.io' },\n go: { subs: new Set(['get', 'install']), ecosystem: 'Go' },\n gem: { subs: new Set(['install']), ecosystem: 'RubyGems' },\n composer: { subs: new Set(['require']), ecosystem: 'Packagist' },\n};\nconst SYSTEM_PMS = new Set(['apt', 'apt-get', 'apk', 'brew', 'dnf', 'yum', 'pacman']);\nconst SYSTEM_SUBS = new Set(['install', 'add']);\n\nconst WRAPPERS = new Set(['sudo', 'doas', 'command', 'env', 'xargs', 'nice', 'time']);\nconst VALUE_FLAGS = new Set([\n '--filter', '-F', '-C', '--dir', '--prefix', '--registry', '--tag', '--features',\n '-v', '--version', '--index-url', '--extra-index-url', '--target', '-t',\n]);\n\nfunction basename(p: string): string {\n const i = p.lastIndexOf('/');\n return i >= 0 ? p.slice(i + 1) : p;\n}\n\nfunction stripPrefixes(argv: string[]): string[] {\n let i = 0;\n while (i < argv.length) {\n const t = argv[i];\n if (WRAPPERS.has(basename(t).toLowerCase())) { i++; continue; }\n if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(t)) { i++; continue; }\n break;\n }\n return argv.slice(i);\n}\n\nfunction looksLikePath(tok: string): boolean {\n return tok === '.' || tok === '..' || /^\\.{0,2}\\//.test(tok) || tok.startsWith('~/') || tok.startsWith('file:');\n}\n\n/** Shell redirect fragments (e.g. `2>&1` \u2192 argv `2`, `1`) \u2014 not package names. */\nfunction isRedirectFragment(tok: string): boolean {\n if (/^\\d+$/.test(tok)) return true;\n if (/^[<>]|[<>]$/.test(tok)) return true;\n if (tok === '&' || tok === '|') return true;\n if (/^\\d*[<>]/.test(tok)) return true;\n return false;\n}\n\nfunction parsePackageToken(tok: string, ecosystem: string): RawInstall | null {\n if (/^(https?:)?\\/\\//.test(tok) || tok.startsWith('git+') || tok.startsWith('git:')) {\n return { ecosystem, name: tok, versionSpec: null, source: tok.includes('git') ? 'git' : 'url' };\n }\n if (looksLikePath(tok)) {\n return { ecosystem, name: basename(tok.replace(/\\/+$/, '')) || tok, versionSpec: null, source: 'local' };\n }\n if (ecosystem === 'PyPI') {\n const noExtras = tok.replace(/\\[[^\\]]*\\]/g, '');\n const m = noExtras.match(/^([A-Za-z0-9_.-]+)\\s*([=~!<>].*)?$/);\n if (!m) return null;\n return { ecosystem, name: m[1], versionSpec: m[2] ? m[2].trim() : null, source: 'registry' };\n }\n if (ecosystem === 'Packagist') {\n // composer uses vendor/package:version-constraint (e.g. monolog/monolog:1.0.0).\n // Split on the first ':' after the vendor/ slash; never on the '/'.\n const ci = tok.indexOf(':');\n if (ci > 0) return { ecosystem, name: tok.slice(0, ci), versionSpec: tok.slice(ci + 1) || null, source: 'registry' };\n return { ecosystem, name: tok, versionSpec: null, source: 'registry' };\n }\n const at = tok.lastIndexOf('@');\n if (at > 0) {\n return { ecosystem, name: tok.slice(0, at), versionSpec: tok.slice(at + 1) || null, source: 'registry' };\n }\n return { ecosystem, name: tok, versionSpec: null, source: 'registry' };\n}\n\n/** Deterministic extraction for a single command segment. */\nexport function extractSegment(rawArgv: string[]): RawInstall[] {\n let argv = stripPrefixes(rawArgv);\n if (argv.length < 2) return [];\n let bin = basename(argv[0]).toLowerCase();\n\n if (bin === 'uv' && argv[1] === 'pip') { argv = argv.slice(1); bin = 'pip'; }\n if ((bin === 'python' || bin === 'python3') && argv.includes('-m')) {\n const mi = argv.indexOf('-m');\n if (argv[mi + 1] === 'pip') { argv = ['pip', ...argv.slice(mi + 2)]; bin = 'pip'; }\n }\n\n const isSystem = SYSTEM_PMS.has(bin);\n const entry = PM_TABLE[bin];\n if (!entry && !isSystem) return [];\n const ecosystem = entry ? entry.ecosystem : 'system';\n const subs = entry ? entry.subs : SYSTEM_SUBS;\n\n let subIdx = -1;\n for (let i = 1; i < argv.length; i++) {\n if (subs.has(argv[i].toLowerCase())) { subIdx = i; break; }\n }\n if (subIdx === -1) return [];\n\n const installs: RawInstall[] = [];\n // gem pins the version with a separate `-v`/`--version` flag rather than an\n // inline spec; capture it and apply to the package(s) in this segment.\n let flagVersion: string | null = null;\n for (let i = subIdx + 1; i < argv.length; i++) {\n const tok = argv[i];\n if (isRedirectFragment(tok)) break;\n if (tok.startsWith('-')) {\n const lower = tok.toLowerCase();\n if (ecosystem === 'RubyGems' && (lower === '-v' || lower === '--version')) {\n flagVersion = (argv[i + 1] || '').replace(/^[v=\\s]+/, '') || null;\n i++;\n continue;\n }\n if (VALUE_FLAGS.has(tok)) i++;\n continue;\n }\n const parsed = parsePackageToken(tok, ecosystem);\n if (parsed) installs.push(parsed);\n }\n if (flagVersion) {\n for (const ins of installs) {\n if (ins.source === 'registry' && !ins.versionSpec) ins.versionSpec = flagVersion;\n }\n }\n return installs;\n}\n\nconst ECO_TO_OSV: Record<string, string | null> = {\n npm: 'npm',\n pypi: 'PyPI', PyPI: 'PyPI',\n cargo: 'crates.io', 'crates.io': 'crates.io',\n go: 'Go', Go: 'Go',\n rubygems: 'RubyGems', RubyGems: 'RubyGems',\n packagist: 'Packagist', Packagist: 'Packagist',\n maven: 'Maven', Maven: 'Maven',\n nuget: 'NuGet', NuGet: 'NuGet',\n apt: null, brew: null, system: null, other: null,\n};\n\nfunction normalizeName(name: string, osvEco: string): string {\n const n = name.trim();\n if (osvEco === 'npm') return n.toLowerCase();\n if (osvEco === 'PyPI') return n.toLowerCase().replace(/[-_.]+/g, '-');\n return n;\n}\n\nfunction concretePin(spec: string | null): string | null {\n if (!spec) return null;\n const c = spec.trim().replace(/^[v=\\s]+/, '');\n if (c.toLowerCase() === 'latest' || c === '') return null;\n if (/[\\^~><|*\\sx]/i.test(c)) return null;\n return /^\\d[\\w.\\-+]*$/.test(c) ? c : null;\n}\n\nconst PKG_JSON_DEP_FIELDS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];\n\nfunction safeParseObject(text: string): Record<string, any> | null {\n try {\n const v = JSON.parse(text);\n return v && typeof v === 'object' && !Array.isArray(v) ? v : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Diff two package.json contents and return the registry packages that are\n * newly added or whose version spec changed in the new content. The caller\n * scans these against the vuln DB before letting the edit land \u2014 so a bare\n * `pnpm install` afterwards has nothing left to vet. Non-registry sources\n * (file:, link:, workspace:, git, http, relative paths) are skipped.\n */\nexport function extractPackageJsonDelta(oldText: string, newText: string): DeterministicPkgRequest[] {\n const newJson = safeParseObject(newText);\n if (!newJson) return [];\n const oldJson = safeParseObject(oldText) || {};\n\n const out = new Map<string, DeterministicPkgRequest>();\n for (const field of PKG_JSON_DEP_FIELDS) {\n const oldDeps = (oldJson[field] && typeof oldJson[field] === 'object') ? oldJson[field] : {};\n const newDeps = (newJson[field] && typeof newJson[field] === 'object') ? newJson[field] : {};\n for (const [rawName, version] of Object.entries(newDeps)) {\n if (typeof version !== 'string') continue;\n if (oldDeps[rawName] === version) continue;\n const spec = version.trim();\n if (\n spec.startsWith('file:') || spec.startsWith('link:') ||\n spec.startsWith('http') || spec.startsWith('git') ||\n spec.startsWith('workspace:') || spec.startsWith('catalog:') ||\n spec.startsWith('npm:') ||\n spec.startsWith('./') || spec.startsWith('../') ||\n spec === '' || spec === '*'\n ) continue;\n const name = rawName.toLowerCase();\n out.set(name, {\n name,\n version: concretePin(spec) ?? '*',\n ecosystem: 'npm',\n });\n }\n }\n return [...out.values()];\n}\n\n/**\n * Parse registry installs from a shell command without LLM/network.\n * Unpinned versions use '*' so OSV scans the full advisory history.\n */\nexport function extractDeterministicPkgRequests(command: string): DeterministicPkgRequest[] {\n const merged = new Map<string, DeterministicPkgRequest>();\n for (const r of segmentCommand(command).flatMap(extractSegment)) {\n if (r.source !== 'registry') continue;\n const osvEco = ECO_TO_OSV[r.ecosystem] ?? ECO_TO_OSV[r.ecosystem.toLowerCase()] ?? null;\n if (!osvEco) continue;\n const name = normalizeName(r.name, osvEco);\n if (!name) continue;\n const key = osvEco + '|' + name.toLowerCase();\n const version = concretePin(r.versionSpec) ?? '*';\n const prev = merged.get(key);\n if (!prev || (prev.version === '*' && version !== '*')) {\n merged.set(key, { name, version, ecosystem: osvEco });\n }\n }\n return [...merged.values()];\n}\n", "utf-8");
11573
11684
  writeFileSync8(taskActivateIntentScriptPath, STUB_TASK_ACTIVATE_INTENT_TS, "utf-8");
11574
- const stubCommonPath = join10(HOOKS_DIR, "_synkro-stub-common.ts");
11685
+ const stubCommonPath = join11(HOOKS_DIR, "_synkro-stub-common.ts");
11575
11686
  writeFileSync8(stubCommonPath, STUB_COMMON_TS, "utf-8");
11576
11687
  chmodSync2(stubCommonPath, 493);
11577
11688
  writeFileSync8(mcpGateScriptPath, STUB_MCP_GATE_TS, "utf-8");
@@ -11625,11 +11736,11 @@ function shellQuoteSingle(value) {
11625
11736
  }
11626
11737
  function resolveSynkroBundle() {
11627
11738
  const scriptPath = process.argv[1];
11628
- if (scriptPath && existsSync11(scriptPath)) return scriptPath;
11739
+ if (scriptPath && existsSync12(scriptPath)) return scriptPath;
11629
11740
  return null;
11630
11741
  }
11631
11742
  function writeConfigEnv(opts) {
11632
- const credsPath = join10(SYNKRO_DIR5, "credentials.json");
11743
+ const credsPath = join11(SYNKRO_DIR6, "credentials.json");
11633
11744
  const safeGateway = sanitizeConfigValue(opts.gatewayUrl);
11634
11745
  const safeUserId = sanitizeConfigValue(opts.userId);
11635
11746
  const safeOrgId = sanitizeConfigValue(opts.orgId);
@@ -11645,7 +11756,7 @@ function writeConfigEnv(opts) {
11645
11756
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
11646
11757
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
11647
11758
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
11648
- `SYNKRO_VERSION=${shellQuoteSingle("1.7.18")}`
11759
+ `SYNKRO_VERSION=${shellQuoteSingle("1.7.24")}`
11649
11760
  ];
11650
11761
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
11651
11762
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -11678,7 +11789,7 @@ async function getOrMintCloudToken(gatewayUrl) {
11678
11789
  assertGatewayAllowed(gatewayUrl);
11679
11790
  let stored = "";
11680
11791
  try {
11681
- stored = readFileSync10(CLOUD_JWT_PATH, "utf-8").trim();
11792
+ stored = readFileSync11(CLOUD_JWT_PATH, "utf-8").trim();
11682
11793
  } catch {
11683
11794
  }
11684
11795
  if (stored && !jwtExpired(stored)) return stored;
@@ -11737,7 +11848,7 @@ async function provisionCloudContainer(opts) {
11737
11848
  let cursorApiKey = "";
11738
11849
  if (cursorTotal > 0) {
11739
11850
  try {
11740
- cursorApiKey = readFileSync10(join10(SYNKRO_DIR5, "cursor-creds", "api-key"), "utf-8").trim();
11851
+ cursorApiKey = readFileSync11(join11(SYNKRO_DIR6, "cursor-creds", "api-key"), "utf-8").trim();
11741
11852
  } catch {
11742
11853
  }
11743
11854
  }
@@ -11894,8 +12005,8 @@ async function verifyCloudGrader(jwt2) {
11894
12005
  }
11895
12006
  function readPersistedDeployLocation() {
11896
12007
  try {
11897
- if (existsSync11(CONFIG_PATH2)) {
11898
- const m = readFileSync10(CONFIG_PATH2, "utf-8").match(/^SYNKRO_DEPLOY_LOCATION='([^']*)'/m);
12008
+ if (existsSync12(CONFIG_PATH2)) {
12009
+ const m = readFileSync11(CONFIG_PATH2, "utf-8").match(/^SYNKRO_DEPLOY_LOCATION='([^']*)'/m);
11899
12010
  if (m?.[1] === "cloud") return "cloud";
11900
12011
  }
11901
12012
  } catch {
@@ -11903,8 +12014,8 @@ function readPersistedDeployLocation() {
11903
12014
  return "local";
11904
12015
  }
11905
12016
  function updateConfigEnvLocation(location) {
11906
- if (!existsSync11(CONFIG_PATH2)) return;
11907
- let env = readFileSync10(CONFIG_PATH2, "utf-8");
12017
+ if (!existsSync12(CONFIG_PATH2)) return;
12018
+ let env = readFileSync11(CONFIG_PATH2, "utf-8");
11908
12019
  const set = (k, v) => {
11909
12020
  const re = new RegExp(`^${k}=.*$`, "m");
11910
12021
  const line = `${k}='${v}'`;
@@ -11919,7 +12030,7 @@ async function applyMcpConfig(opts) {
11919
12030
  if (!opts.hasClaudeCode && !opts.hasCursor) return;
11920
12031
  let mcpJwt = "";
11921
12032
  try {
11922
- mcpJwt = readFileSync10(join10(SYNKRO_DIR5, ".mcp-jwt"), "utf-8").trim();
12033
+ mcpJwt = readFileSync11(join11(SYNKRO_DIR6, ".mcp-jwt"), "utf-8").trim();
11923
12034
  } catch {
11924
12035
  }
11925
12036
  if (!mcpJwt) {
@@ -12037,8 +12148,8 @@ function resolveDeploymentMode() {
12037
12148
  const envOverride = process.env.SYNKRO_DEPLOYMENT_MODE?.toLowerCase();
12038
12149
  if (envOverride === "bare-host" || envOverride === "docker") return envOverride;
12039
12150
  try {
12040
- if (existsSync11(CONFIG_PATH2)) {
12041
- const m = readFileSync10(CONFIG_PATH2, "utf-8").match(/^SYNKRO_DEPLOYMENT_MODE='([^']*)'/m);
12151
+ if (existsSync12(CONFIG_PATH2)) {
12152
+ const m = readFileSync11(CONFIG_PATH2, "utf-8").match(/^SYNKRO_DEPLOYMENT_MODE='([^']*)'/m);
12042
12153
  const val = m?.[1]?.toLowerCase();
12043
12154
  if (val === "bare-host" || val === "docker") return val;
12044
12155
  }
@@ -12065,16 +12176,16 @@ function collectLocalMetadata(includeClaudeCode = true) {
12065
12176
  meta.cc_version = execSync6("claude --version", { encoding: "utf-8", timeout: 5e3 }).trim().split("\n")[0];
12066
12177
  } catch {
12067
12178
  }
12068
- const claudeDir = join10(homedir10(), ".claude");
12179
+ const claudeDir = join11(homedir11(), ".claude");
12069
12180
  try {
12070
- const settings = JSON.parse(readFileSync10(join10(claudeDir, "settings.json"), "utf-8"));
12181
+ const settings = JSON.parse(readFileSync11(join11(claudeDir, "settings.json"), "utf-8"));
12071
12182
  const plugins = Object.keys(settings.enabledPlugins ?? {}).filter((k) => settings.enabledPlugins[k]);
12072
12183
  if (plugins.length) meta.enabled_plugins = plugins;
12073
12184
  if (settings.permissions?.defaultMode) meta.permissions_mode = settings.permissions.defaultMode;
12074
12185
  } catch {
12075
12186
  }
12076
12187
  try {
12077
- const mcpCache = JSON.parse(readFileSync10(join10(claudeDir, "mcp-needs-auth-cache.json"), "utf-8"));
12188
+ const mcpCache = JSON.parse(readFileSync11(join11(claudeDir, "mcp-needs-auth-cache.json"), "utf-8"));
12078
12189
  const mcpNames = Object.keys(mcpCache);
12079
12190
  if (mcpNames.length) meta.mcp_servers = mcpNames;
12080
12191
  } catch {
@@ -12086,10 +12197,10 @@ function collectLocalMetadata(includeClaudeCode = true) {
12086
12197
  } catch {
12087
12198
  }
12088
12199
  try {
12089
- const sessionsDir = join10(claudeDir, "sessions");
12200
+ const sessionsDir = join11(claudeDir, "sessions");
12090
12201
  const files = readdirSync3(sessionsDir).filter((f) => f.endsWith(".json")).slice(-5);
12091
12202
  for (const f of files) {
12092
- const s = JSON.parse(readFileSync10(join10(sessionsDir, f), "utf-8"));
12203
+ const s = JSON.parse(readFileSync11(join11(sessionsDir, f), "utf-8"));
12093
12204
  if (s.version) {
12094
12205
  meta.cc_version = meta.cc_version || s.version;
12095
12206
  break;
@@ -12277,9 +12388,9 @@ async function installCommand(opts = {}) {
12277
12388
  const scripts = writeHookScripts(hookMode);
12278
12389
  console.log("Wrote hook scripts to ~/.synkro/hooks/\n");
12279
12390
  for (const mode of ["edit", "bash"]) {
12280
- const pidFile = join10(SYNKRO_DIR5, "daemon", mode, "daemon.pid");
12391
+ const pidFile = join11(SYNKRO_DIR6, "daemon", mode, "daemon.pid");
12281
12392
  try {
12282
- const pid = parseInt(readFileSync10(pidFile, "utf-8").trim(), 10);
12393
+ const pid = parseInt(readFileSync11(pidFile, "utf-8").trim(), 10);
12283
12394
  if (pid > 0) {
12284
12395
  process.kill(pid, "SIGTERM");
12285
12396
  console.log(`Stopped stale ${mode} grader daemon (pid ${pid})`);
@@ -12362,7 +12473,7 @@ async function installCommand(opts = {}) {
12362
12473
  if (mintResp.ok) {
12363
12474
  const minted = await mintResp.json();
12364
12475
  mcpJwt = minted.token;
12365
- writeFileSync8(join10(SYNKRO_DIR5, ".mcp-jwt"), mcpJwt + "\n", { mode: 384 });
12476
+ writeFileSync8(join11(SYNKRO_DIR6, ".mcp-jwt"), mcpJwt + "\n", { mode: 384 });
12366
12477
  } else {
12367
12478
  console.warn(" \u26A0 Could not mint MCP token \u2014 local server will reject requests until re-installed.");
12368
12479
  }
@@ -12390,7 +12501,7 @@ async function installCommand(opts = {}) {
12390
12501
  throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);
12391
12502
  }
12392
12503
  const minted = await mintResp.json();
12393
- writeFileSync8(join10(SYNKRO_DIR5, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
12504
+ writeFileSync8(join11(SYNKRO_DIR6, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
12394
12505
  const mcp = installMcpConfig({ gatewayUrl, bearerToken: minted.token });
12395
12506
  console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);
12396
12507
  console.log(` url: ${mcp.url}`);
@@ -12407,8 +12518,8 @@ async function installCommand(opts = {}) {
12407
12518
  if (hasCursor && !opts.noMcp) {
12408
12519
  try {
12409
12520
  if (useLocalMcp) {
12410
- const jwtPath = join10(SYNKRO_DIR5, ".mcp-jwt");
12411
- if (!existsSync11(jwtPath)) {
12521
+ const jwtPath = join11(SYNKRO_DIR6, ".mcp-jwt");
12522
+ if (!existsSync12(jwtPath)) {
12412
12523
  const mintResp = await fetch(`${gatewayUrl}/api/v1/cli/mcp-token`, {
12413
12524
  method: "POST",
12414
12525
  headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
@@ -12436,7 +12547,7 @@ async function installCommand(opts = {}) {
12436
12547
  throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);
12437
12548
  }
12438
12549
  const minted = await mintResp.json();
12439
- writeFileSync8(join10(SYNKRO_DIR5, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
12550
+ writeFileSync8(join11(SYNKRO_DIR6, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
12440
12551
  const mcp = installCursorMcpConfig({ gatewayUrl, bearerToken: minted.token });
12441
12552
  console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);
12442
12553
  console.log(` url: ${mcp.url}`);
@@ -12518,7 +12629,7 @@ async function installCommand(opts = {}) {
12518
12629
  const ready = await waitForContainerReady(6e4);
12519
12630
  if (ready) {
12520
12631
  console.log(" \u2713 container ready");
12521
- const mcpJwt = readFileSync10(join10(SYNKRO_DIR5, ".mcp-jwt"), "utf-8").trim();
12632
+ const mcpJwt = readFileSync11(join11(SYNKRO_DIR6, ".mcp-jwt"), "utf-8").trim();
12522
12633
  try {
12523
12634
  const ingestResp = await fetch(`http://127.0.0.1:${hostMcpPort}/api/ingest`, {
12524
12635
  method: "POST",
@@ -12553,6 +12664,11 @@ async function installCommand(opts = {}) {
12553
12664
  if (hasCursor) await promptCursorApiKey(opts);
12554
12665
  await provisionCloudContainer({ gatewayUrl, jwt: token, orgId, userId, email, hasClaudeCode, hasCursor });
12555
12666
  cloudGradeOk = await verifyCloudGrader(token);
12667
+ if (await promptCodexCloudSetup()) {
12668
+ const { setupCodexCloud: setupCodexCloud2 } = await Promise.resolve().then(() => (init_codexCloudSetup(), codexCloudSetup_exports));
12669
+ const res = await setupCodexCloud2(gatewayUrl, token, (m) => console.log(` ${m}`));
12670
+ if (!res.ok) console.warn(` \u26A0 Codex cloud setup skipped: ${res.error}`);
12671
+ }
12556
12672
  }
12557
12673
  if (transcriptConsent) {
12558
12674
  const repo = detectGitRepo2();
@@ -12561,7 +12677,7 @@ async function installCommand(opts = {}) {
12561
12677
  try {
12562
12678
  let mcpToken = "";
12563
12679
  try {
12564
- mcpToken = readFileSync10(join10(SYNKRO_DIR5, ".mcp-jwt"), "utf-8").trim();
12680
+ mcpToken = readFileSync11(join11(SYNKRO_DIR6, ".mcp-jwt"), "utf-8").trim();
12565
12681
  } catch {
12566
12682
  }
12567
12683
  if (mcpToken) {
@@ -12697,8 +12813,8 @@ function writeSynkroFileIfMissing(opts) {
12697
12813
  try {
12698
12814
  const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
12699
12815
  if (!root) return;
12700
- if (root === homedir10()) return;
12701
- const fp = join10(root, "synkro.toml");
12816
+ if (root === homedir11()) return;
12817
+ const fp = join11(root, "synkro.toml");
12702
12818
  let hasFile = false;
12703
12819
  try {
12704
12820
  hasFile = statSync(fp).isFile();
@@ -12743,9 +12859,9 @@ function readFullSynkroFile() {
12743
12859
  try {
12744
12860
  const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
12745
12861
  if (!root) return null;
12746
- const fp = join10(root, "synkro.toml");
12747
- if (!existsSync11(fp)) return null;
12748
- const parsed = parseSynkroToml2(readFileSync10(fp, "utf-8"));
12862
+ const fp = join11(root, "synkro.toml");
12863
+ if (!existsSync12(fp)) return null;
12864
+ const parsed = parseSynkroToml2(readFileSync11(fp, "utf-8"));
12749
12865
  const valid = ["claude-code", "cursor"];
12750
12866
  const harness = Array.isArray(parsed.harness) ? parsed.harness.filter((h) => valid.includes(h)) : ["claude-code", "cursor"];
12751
12867
  const resolved = resolveGraderPool(parsed);
@@ -12783,7 +12899,7 @@ function reconcileHarness() {
12783
12899
  console.log(`synkro.toml: harness=[${sf.harness.join(", ")}] pool=${poolLabel(sf.grader.pool)} mode=${sf.grader.mode}`);
12784
12900
  const scripts = writeHookScripts(resolvePersistedHookMode());
12785
12901
  console.log("Wrote hook scripts to ~/.synkro/hooks/");
12786
- const ccSettings = join10(homedir10(), ".claude", "settings.json");
12902
+ const ccSettings = join11(homedir11(), ".claude", "settings.json");
12787
12903
  if (wantCC) {
12788
12904
  installCCHooks(ccSettings, {
12789
12905
  bashJudgeScriptPath: scripts.bashScript,
@@ -12803,7 +12919,7 @@ function reconcileHarness() {
12803
12919
  });
12804
12920
  console.log(" \u2713 Claude Code hooks registered");
12805
12921
  try {
12806
- const mcpJwt = readFileSync10(join10(SYNKRO_DIR5, ".mcp-jwt"), "utf-8").trim();
12922
+ const mcpJwt = readFileSync11(join11(SYNKRO_DIR6, ".mcp-jwt"), "utf-8").trim();
12807
12923
  if (mcpJwt) {
12808
12924
  installMcpConfig({ gatewayUrl: "", bearerToken: mcpJwt, local: true });
12809
12925
  console.log(" \u2713 Claude Code MCP registered");
@@ -12815,7 +12931,7 @@ function reconcileHarness() {
12815
12931
  if (uninstallMcpConfig()) console.log(" \u2717 Claude Code MCP removed");
12816
12932
  if (uninstallClaudeDesktopMcpConfig()) console.log(" \u2717 Claude Desktop MCP removed");
12817
12933
  }
12818
- const cursorHooks = join10(homedir10(), ".cursor", "hooks.json");
12934
+ const cursorHooks = join11(homedir11(), ".cursor", "hooks.json");
12819
12935
  if (wantCursor) {
12820
12936
  installCursorHooks(cursorHooks, {
12821
12937
  bashJudgeScriptPath: scripts.cursorBashJudgeScript,
@@ -12897,7 +13013,7 @@ async function syncSkillFiles() {
12897
13013
  if (resolved.length === 0) return;
12898
13014
  const mcpPort = process.env.SYNKRO_MCP_PORT || "18931";
12899
13015
  const tasks = resolved.map((fp) => {
12900
- const content = readFileSync10(fp, "utf-8");
13016
+ const content = readFileSync11(fp, "utf-8");
12901
13017
  const source = `skill:${fp.split("/").pop()}`;
12902
13018
  if (!content.trim()) {
12903
13019
  console.log(` \u2298 skill ${source}: empty file, skipped`);
@@ -12915,15 +13031,15 @@ function discoverSkillFiles(repoRoot, excludeHashes) {
12915
13031
  const roots = [];
12916
13032
  const add = (p) => {
12917
13033
  try {
12918
- if (p && existsSync11(p) && statSync(p).isDirectory()) roots.push(p);
13034
+ if (p && existsSync12(p) && statSync(p).isDirectory()) roots.push(p);
12919
13035
  } catch {
12920
13036
  }
12921
13037
  };
12922
- add(join10(homedir10(), ".claude", "skills"));
12923
- add(join10(homedir10(), ".agents", "skills"));
13038
+ add(join11(homedir11(), ".claude", "skills"));
13039
+ add(join11(homedir11(), ".agents", "skills"));
12924
13040
  if (repoRoot) {
12925
- add(join10(repoRoot, ".claude", "skills"));
12926
- add(join10(repoRoot, ".agents", "skills"));
13041
+ add(join11(repoRoot, ".claude", "skills"));
13042
+ add(join11(repoRoot, ".agents", "skills"));
12927
13043
  }
12928
13044
  const out = [];
12929
13045
  const seen = /* @__PURE__ */ new Set();
@@ -12934,7 +13050,7 @@ function discoverSkillFiles(repoRoot, excludeHashes) {
12934
13050
  try {
12935
13051
  const st = statSync(file);
12936
13052
  if (!st.isFile() || st.size > 2e5) return;
12937
- content = readFileSync10(file, "utf-8");
13053
+ content = readFileSync11(file, "utf-8");
12938
13054
  } catch {
12939
13055
  return;
12940
13056
  }
@@ -12953,7 +13069,7 @@ function discoverSkillFiles(repoRoot, excludeHashes) {
12953
13069
  }
12954
13070
  for (const entry of entries) {
12955
13071
  if (entry.startsWith(".")) continue;
12956
- const full = join10(root, entry);
13072
+ const full = join11(root, entry);
12957
13073
  let st;
12958
13074
  try {
12959
13075
  st = statSync(full);
@@ -12961,8 +13077,8 @@ function discoverSkillFiles(repoRoot, excludeHashes) {
12961
13077
  continue;
12962
13078
  }
12963
13079
  if (st.isDirectory()) {
12964
- const skillMd = join10(full, "SKILL.md");
12965
- if (existsSync11(skillMd)) consider(skillMd, entry);
13080
+ const skillMd = join11(full, "SKILL.md");
13081
+ if (existsSync12(skillMd)) consider(skillMd, entry);
12966
13082
  } else if (/\.mdx?$/i.test(entry) && entry.toUpperCase() !== "README.MD") {
12967
13083
  consider(full, entry.replace(/\.mdx?$/i, ""));
12968
13084
  }
@@ -13000,7 +13116,7 @@ async function discoverAndIngestSkills() {
13000
13116
  if (sf?.skills?.length) {
13001
13117
  for (const fp of resolveSkillPaths(sf.skills, sf._repoRoot)) {
13002
13118
  try {
13003
- excludeHashes.add(createHash("sha256").update(readFileSync10(fp, "utf-8")).digest("hex"));
13119
+ excludeHashes.add(createHash("sha256").update(readFileSync11(fp, "utf-8")).digest("hex"));
13004
13120
  } catch {
13005
13121
  }
13006
13122
  }
@@ -13010,7 +13126,7 @@ async function discoverAndIngestSkills() {
13010
13126
  const setHash = discoverySetHash(found);
13011
13127
  let prev = "";
13012
13128
  try {
13013
- prev = readFileSync10(SKILLS_DISCOVERED_PATH, "utf-8").trim();
13129
+ prev = readFileSync11(SKILLS_DISCOVERED_PATH, "utf-8").trim();
13014
13130
  } catch {
13015
13131
  }
13016
13132
  if (prev === setHash) return;
@@ -13051,17 +13167,17 @@ function detectGitRepo2() {
13051
13167
  function getClaudeProjectsFolder() {
13052
13168
  const cwd = process.cwd();
13053
13169
  const sanitized = "-" + cwd.replace(/\//g, "-");
13054
- const projectsDir = join10(homedir10(), ".claude", "projects", sanitized);
13055
- return existsSync11(projectsDir) ? projectsDir : null;
13170
+ const projectsDir = join11(homedir11(), ".claude", "projects", sanitized);
13171
+ return existsSync12(projectsDir) ? projectsDir : null;
13056
13172
  }
13057
13173
  function extractSessionInsights(projectsDir) {
13058
13174
  const insights = [];
13059
13175
  const files = readdirSync3(projectsDir).filter((f) => f.endsWith(".jsonl"));
13060
13176
  for (const file of files) {
13061
13177
  const sessionId = file.replace(".jsonl", "");
13062
- const filePath = join10(projectsDir, file);
13178
+ const filePath = join11(projectsDir, file);
13063
13179
  try {
13064
- const content = readFileSync10(filePath, "utf-8");
13180
+ const content = readFileSync11(filePath, "utf-8");
13065
13181
  const lines = content.split("\n").filter(Boolean);
13066
13182
  for (let i = 0; i < lines.length; i++) {
13067
13183
  try {
@@ -13140,14 +13256,14 @@ function cursorProjectSlug(workspaceRoot) {
13140
13256
  return workspaceRoot.replace(/^[/]+/, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "");
13141
13257
  }
13142
13258
  function getCursorTranscriptsDir() {
13143
- const dir = join10(homedir10(), ".cursor", "projects", cursorProjectSlug(process.cwd()), "agent-transcripts");
13144
- return existsSync11(dir) ? dir : null;
13259
+ const dir = join11(homedir11(), ".cursor", "projects", cursorProjectSlug(process.cwd()), "agent-transcripts");
13260
+ return existsSync12(dir) ? dir : null;
13145
13261
  }
13146
13262
  function isSafeConvId(id) {
13147
13263
  return /^[A-Za-z0-9_-]+$/.test(id);
13148
13264
  }
13149
13265
  function parseCursorTranscriptFile(filePath) {
13150
- const content = readFileSync10(filePath, "utf-8");
13266
+ const content = readFileSync11(filePath, "utf-8");
13151
13267
  const lines = content.split("\n").filter(Boolean);
13152
13268
  const messages = [];
13153
13269
  for (let i = 0; i < lines.length; i++) {
@@ -13179,8 +13295,8 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
13179
13295
  for (let i = 0; i < convDirs.length; i++) {
13180
13296
  const convId = convDirs[i];
13181
13297
  if (!isSafeConvId(convId)) continue;
13182
- const filePath = join10(dir, convId, `${convId}.jsonl`);
13183
- if (!existsSync11(filePath)) continue;
13298
+ const filePath = join11(dir, convId, `${convId}.jsonl`);
13299
+ if (!existsSync12(filePath)) continue;
13184
13300
  try {
13185
13301
  const all = parseCursorTranscriptFile(filePath);
13186
13302
  const messages = all.length > 500 ? all.slice(-500) : all;
@@ -13202,8 +13318,8 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
13202
13318
  process.stdout.write(`\r Progress: ${i + 1}/${convDirs.length} sessions (${totalMessages} messages embedded) `);
13203
13319
  }
13204
13320
  try {
13205
- const lc = readFileSync10(filePath, "utf-8").split("\n").filter(Boolean).length;
13206
- writeFileSync8(join10(OFFSETS_DIR, convId), String(lc), "utf-8");
13321
+ const lc = readFileSync11(filePath, "utf-8").split("\n").filter(Boolean).length;
13322
+ writeFileSync8(join11(OFFSETS_DIR, convId), String(lc), "utf-8");
13207
13323
  } catch {
13208
13324
  }
13209
13325
  }
@@ -13211,7 +13327,7 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
13211
13327
  return { sessions: totalSessions, messages: totalMessages };
13212
13328
  }
13213
13329
  function parseTranscriptFile(filePath) {
13214
- const content = readFileSync10(filePath, "utf-8");
13330
+ const content = readFileSync11(filePath, "utf-8");
13215
13331
  const lines = content.split("\n").filter(Boolean);
13216
13332
  const messages = [];
13217
13333
  for (let i = 0; i < lines.length; i++) {
@@ -13259,7 +13375,7 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
13259
13375
  for (let i = 0; i < files.length; i++) {
13260
13376
  const file = files[i];
13261
13377
  const sessionId = file.replace(".jsonl", "");
13262
- const filePath = join10(projectsDir, file);
13378
+ const filePath = join11(projectsDir, file);
13263
13379
  try {
13264
13380
  const allMessages = parseTranscriptFile(filePath);
13265
13381
  const messages = allMessages.length > 500 ? allMessages.slice(-500) : allMessages;
@@ -13281,9 +13397,9 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
13281
13397
  process.stdout.write(`\r Progress: ${i + 1}/${files.length} sessions (${totalMessages} messages embedded) `);
13282
13398
  }
13283
13399
  try {
13284
- const content = readFileSync10(join10(projectsDir, file), "utf-8");
13400
+ const content = readFileSync11(join11(projectsDir, file), "utf-8");
13285
13401
  const lineCount = content.split("\n").filter(Boolean).length;
13286
- writeFileSync8(join10(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
13402
+ writeFileSync8(join11(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
13287
13403
  } catch {
13288
13404
  }
13289
13405
  }
@@ -13304,7 +13420,7 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
13304
13420
  const sessions = [];
13305
13421
  for (const file of batch) {
13306
13422
  const sessionId = file.replace(".jsonl", "");
13307
- const filePath = join10(projectsDir, file);
13423
+ const filePath = join11(projectsDir, file);
13308
13424
  try {
13309
13425
  const allMessages = parseTranscriptFile(filePath);
13310
13426
  const messages = allMessages.length > maxMessagesPerSession ? allMessages.slice(-maxMessagesPerSession) : allMessages;
@@ -13333,18 +13449,18 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
13333
13449
  }
13334
13450
  for (const file of batch) {
13335
13451
  const sessionId = file.replace(".jsonl", "");
13336
- const filePath = join10(projectsDir, file);
13452
+ const filePath = join11(projectsDir, file);
13337
13453
  try {
13338
- const content = readFileSync10(filePath, "utf-8");
13454
+ const content = readFileSync11(filePath, "utf-8");
13339
13455
  const lineCount = content.split("\n").filter(Boolean).length;
13340
- writeFileSync8(join10(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
13456
+ writeFileSync8(join11(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
13341
13457
  } catch {
13342
13458
  }
13343
13459
  }
13344
13460
  }
13345
13461
  return { sessions: totalSessions, messages: totalMessages };
13346
13462
  }
13347
- var SYNKRO_DIR5, HOOKS_DIR, BIN_DIR, CONFIG_PATH2, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, CLOUD_JWT_PATH, SKILLS_DISCOVERED_PATH;
13463
+ var SYNKRO_DIR6, HOOKS_DIR, BIN_DIR, CONFIG_PATH2, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, CLOUD_JWT_PATH, SKILLS_DISCOVERED_PATH;
13348
13464
  var init_install = __esm({
13349
13465
  "cli/commands/install.ts"() {
13350
13466
  "use strict";
@@ -13363,10 +13479,10 @@ var init_install = __esm({
13363
13479
  init_claudeDesktopTap();
13364
13480
  init_dockerInstall();
13365
13481
  init_setupToken();
13366
- SYNKRO_DIR5 = join10(homedir10(), ".synkro");
13367
- HOOKS_DIR = join10(SYNKRO_DIR5, "hooks");
13368
- BIN_DIR = join10(SYNKRO_DIR5, "bin");
13369
- CONFIG_PATH2 = join10(SYNKRO_DIR5, "config.env");
13482
+ SYNKRO_DIR6 = join11(homedir11(), ".synkro");
13483
+ HOOKS_DIR = join11(SYNKRO_DIR6, "hooks");
13484
+ BIN_DIR = join11(SYNKRO_DIR6, "bin");
13485
+ CONFIG_PATH2 = join11(SYNKRO_DIR6, "config.env");
13370
13486
  MCP_STDIO_PROXY_SRC = `#!/usr/bin/env bun
13371
13487
  import { readFileSync } from 'node:fs';
13372
13488
  import { homedir } from 'node:os';
@@ -13477,21 +13593,21 @@ rl.on('line', async (line) => {
13477
13593
  }
13478
13594
  });
13479
13595
  `;
13480
- OFFSETS_DIR = join10(SYNKRO_DIR5, ".transcript-offsets");
13481
- CLOUD_JWT_PATH = join10(SYNKRO_DIR5, ".cloud-jwt");
13482
- SKILLS_DISCOVERED_PATH = join10(SYNKRO_DIR5, ".skills-discovered");
13596
+ OFFSETS_DIR = join11(SYNKRO_DIR6, ".transcript-offsets");
13597
+ CLOUD_JWT_PATH = join11(SYNKRO_DIR6, ".cloud-jwt");
13598
+ SKILLS_DISCOVERED_PATH = join11(SYNKRO_DIR6, ".skills-discovered");
13483
13599
  }
13484
13600
  });
13485
13601
 
13486
13602
  // cli/local-cc/install.ts
13487
- import { existsSync as existsSync12, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9, readFileSync as readFileSync11, chmodSync as chmodSync3, copyFileSync as copyFileSync2, renameSync as renameSync4, unlinkSync as unlinkSync5, openSync, fsyncSync, closeSync } from "fs";
13488
- import { join as join11 } from "path";
13489
- import { homedir as homedir11 } from "os";
13490
- import { spawnSync as spawnSync5 } from "child_process";
13603
+ import { existsSync as existsSync13, mkdirSync as mkdirSync11, writeFileSync as writeFileSync9, readFileSync as readFileSync12, chmodSync as chmodSync3, copyFileSync as copyFileSync2, renameSync as renameSync4, unlinkSync as unlinkSync5, openSync, fsyncSync, closeSync } from "fs";
13604
+ import { join as join12 } from "path";
13605
+ import { homedir as homedir12 } from "os";
13606
+ import { spawnSync as spawnSync6 } from "child_process";
13491
13607
  function writePluginFiles() {
13492
13608
  for (const c of CHANNELS) {
13493
- mkdirSync10(c.sessionDir, { recursive: true });
13494
- mkdirSync10(c.pluginSettingsDir, { recursive: true });
13609
+ mkdirSync11(c.sessionDir, { recursive: true });
13610
+ mkdirSync11(c.pluginSettingsDir, { recursive: true });
13495
13611
  writeFileSync9(c.pluginPkgPath, PLUGIN_PACKAGE_JSON, "utf-8");
13496
13612
  writeFileSync9(
13497
13613
  c.pluginSettingsPath,
@@ -13514,7 +13630,7 @@ function writePluginFiles() {
13514
13630
  }
13515
13631
  function runBunInstall() {
13516
13632
  for (const c of CHANNELS) {
13517
- const r = spawnSync5("bun", ["install", "--silent"], {
13633
+ const r = spawnSync6("bun", ["install", "--silent"], {
13518
13634
  cwd: c.sessionDir,
13519
13635
  encoding: "utf-8",
13520
13636
  timeout: 12e4
@@ -13527,10 +13643,10 @@ function runBunInstall() {
13527
13643
  }
13528
13644
  }
13529
13645
  function safelyMutateClaudeJson(mutator) {
13530
- if (!existsSync12(CLAUDE_JSON_PATH)) {
13646
+ if (!existsSync13(CLAUDE_JSON_PATH)) {
13531
13647
  return;
13532
13648
  }
13533
- const originalText = readFileSync11(CLAUDE_JSON_PATH, "utf-8");
13649
+ const originalText = readFileSync12(CLAUDE_JSON_PATH, "utf-8");
13534
13650
  let parsed;
13535
13651
  try {
13536
13652
  parsed = JSON.parse(originalText);
@@ -13630,15 +13746,15 @@ function patchClaudeJson() {
13630
13746
  });
13631
13747
  }
13632
13748
  function installLocalCC() {
13633
- let bunCheck = spawnSync5("bun", ["--version"], { encoding: "utf-8" });
13749
+ let bunCheck = spawnSync6("bun", ["--version"], { encoding: "utf-8" });
13634
13750
  if (bunCheck.status !== 0) {
13635
13751
  if (process.platform === "darwin") {
13636
13752
  console.log(" Installing bun via brew...");
13637
- const brewR = spawnSync5("brew", ["install", "oven-sh/bun/bun"], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
13753
+ const brewR = spawnSync6("brew", ["install", "oven-sh/bun/bun"], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
13638
13754
  if (brewR.status !== 0) {
13639
13755
  throw new LocalCCInstallError("bun auto-install failed. Install manually: curl -fsSL https://bun.sh/install | bash");
13640
13756
  }
13641
- bunCheck = spawnSync5("bun", ["--version"], { encoding: "utf-8" });
13757
+ bunCheck = spawnSync6("bun", ["--version"], { encoding: "utf-8" });
13642
13758
  if (bunCheck.status !== 0) {
13643
13759
  throw new LocalCCInstallError("bun installed but not found on PATH. Restart your terminal and re-run install.");
13644
13760
  }
@@ -13672,42 +13788,42 @@ var CLAUDE_JSON_BACKUP_PATH, SESSION_DIR, PLUGIN_PATH, PLUGIN_PKG_PATH, PLUGIN_S
13672
13788
  var init_install2 = __esm({
13673
13789
  "cli/local-cc/install.ts"() {
13674
13790
  "use strict";
13675
- CLAUDE_JSON_BACKUP_PATH = join11(homedir11(), ".claude.json.synkro-bak");
13676
- SESSION_DIR = join11(homedir11(), ".synkro", "cc_sessions");
13677
- PLUGIN_PATH = join11(SESSION_DIR, "synkro-channel.ts");
13678
- PLUGIN_PKG_PATH = join11(SESSION_DIR, "package.json");
13679
- PLUGIN_SETTINGS_DIR = join11(SESSION_DIR, ".claude");
13680
- PLUGIN_SETTINGS_PATH = join11(PLUGIN_SETTINGS_DIR, "settings.json");
13681
- PROJECT_MCP_PATH = join11(SESSION_DIR, ".mcp.json");
13682
- CLAUDE_JSON_PATH = join11(homedir11(), ".claude.json");
13683
- RUN_SCRIPT_PATH = join11(SESSION_DIR, "run-claude.sh");
13791
+ CLAUDE_JSON_BACKUP_PATH = join12(homedir12(), ".claude.json.synkro-bak");
13792
+ SESSION_DIR = join12(homedir12(), ".synkro", "cc_sessions");
13793
+ PLUGIN_PATH = join12(SESSION_DIR, "synkro-channel.ts");
13794
+ PLUGIN_PKG_PATH = join12(SESSION_DIR, "package.json");
13795
+ PLUGIN_SETTINGS_DIR = join12(SESSION_DIR, ".claude");
13796
+ PLUGIN_SETTINGS_PATH = join12(PLUGIN_SETTINGS_DIR, "settings.json");
13797
+ PROJECT_MCP_PATH = join12(SESSION_DIR, ".mcp.json");
13798
+ CLAUDE_JSON_PATH = join12(homedir12(), ".claude.json");
13799
+ RUN_SCRIPT_PATH = join12(SESSION_DIR, "run-claude.sh");
13684
13800
  TMUX_SESSION_NAME = "synkro-local-cc";
13685
13801
  CHANNEL_1_PORT = 8941;
13686
- SESSION_DIR_2 = join11(homedir11(), ".synkro", "cc_sessions_2");
13687
- PLUGIN_PATH_2 = join11(SESSION_DIR_2, "synkro-channel.ts");
13688
- PLUGIN_PKG_PATH_2 = join11(SESSION_DIR_2, "package.json");
13689
- PLUGIN_SETTINGS_DIR_2 = join11(SESSION_DIR_2, ".claude");
13690
- PLUGIN_SETTINGS_PATH_2 = join11(PLUGIN_SETTINGS_DIR_2, "settings.json");
13691
- PROJECT_MCP_PATH_2 = join11(SESSION_DIR_2, ".mcp.json");
13692
- RUN_SCRIPT_PATH_2 = join11(SESSION_DIR_2, "run-claude.sh");
13802
+ SESSION_DIR_2 = join12(homedir12(), ".synkro", "cc_sessions_2");
13803
+ PLUGIN_PATH_2 = join12(SESSION_DIR_2, "synkro-channel.ts");
13804
+ PLUGIN_PKG_PATH_2 = join12(SESSION_DIR_2, "package.json");
13805
+ PLUGIN_SETTINGS_DIR_2 = join12(SESSION_DIR_2, ".claude");
13806
+ PLUGIN_SETTINGS_PATH_2 = join12(PLUGIN_SETTINGS_DIR_2, "settings.json");
13807
+ PROJECT_MCP_PATH_2 = join12(SESSION_DIR_2, ".mcp.json");
13808
+ RUN_SCRIPT_PATH_2 = join12(SESSION_DIR_2, "run-claude.sh");
13693
13809
  TMUX_SESSION_NAME_2 = "synkro-local-cc-2";
13694
13810
  CHANNEL_2_PORT = 8951;
13695
- SESSION_DIR_3 = join11(homedir11(), ".synkro", "cc_sessions_3");
13696
- PLUGIN_PATH_3 = join11(SESSION_DIR_3, "synkro-channel.ts");
13697
- PLUGIN_PKG_PATH_3 = join11(SESSION_DIR_3, "package.json");
13698
- PLUGIN_SETTINGS_DIR_3 = join11(SESSION_DIR_3, ".claude");
13699
- PLUGIN_SETTINGS_PATH_3 = join11(PLUGIN_SETTINGS_DIR_3, "settings.json");
13700
- PROJECT_MCP_PATH_3 = join11(SESSION_DIR_3, ".mcp.json");
13701
- RUN_SCRIPT_PATH_3 = join11(SESSION_DIR_3, "run-claude.sh");
13811
+ SESSION_DIR_3 = join12(homedir12(), ".synkro", "cc_sessions_3");
13812
+ PLUGIN_PATH_3 = join12(SESSION_DIR_3, "synkro-channel.ts");
13813
+ PLUGIN_PKG_PATH_3 = join12(SESSION_DIR_3, "package.json");
13814
+ PLUGIN_SETTINGS_DIR_3 = join12(SESSION_DIR_3, ".claude");
13815
+ PLUGIN_SETTINGS_PATH_3 = join12(PLUGIN_SETTINGS_DIR_3, "settings.json");
13816
+ PROJECT_MCP_PATH_3 = join12(SESSION_DIR_3, ".mcp.json");
13817
+ RUN_SCRIPT_PATH_3 = join12(SESSION_DIR_3, "run-claude.sh");
13702
13818
  TMUX_SESSION_NAME_3 = "synkro-local-cc-3";
13703
13819
  CHANNEL_3_PORT = 8942;
13704
- SESSION_DIR_4 = join11(homedir11(), ".synkro", "cc_sessions_4");
13705
- PLUGIN_PATH_4 = join11(SESSION_DIR_4, "synkro-channel.ts");
13706
- PLUGIN_PKG_PATH_4 = join11(SESSION_DIR_4, "package.json");
13707
- PLUGIN_SETTINGS_DIR_4 = join11(SESSION_DIR_4, ".claude");
13708
- PLUGIN_SETTINGS_PATH_4 = join11(PLUGIN_SETTINGS_DIR_4, "settings.json");
13709
- PROJECT_MCP_PATH_4 = join11(SESSION_DIR_4, ".mcp.json");
13710
- RUN_SCRIPT_PATH_4 = join11(SESSION_DIR_4, "run-claude.sh");
13820
+ SESSION_DIR_4 = join12(homedir12(), ".synkro", "cc_sessions_4");
13821
+ PLUGIN_PATH_4 = join12(SESSION_DIR_4, "synkro-channel.ts");
13822
+ PLUGIN_PKG_PATH_4 = join12(SESSION_DIR_4, "package.json");
13823
+ PLUGIN_SETTINGS_DIR_4 = join12(SESSION_DIR_4, ".claude");
13824
+ PLUGIN_SETTINGS_PATH_4 = join12(PLUGIN_SETTINGS_DIR_4, "settings.json");
13825
+ PROJECT_MCP_PATH_4 = join12(SESSION_DIR_4, ".mcp.json");
13826
+ RUN_SCRIPT_PATH_4 = join12(SESSION_DIR_4, "run-claude.sh");
13711
13827
  TMUX_SESSION_NAME_4 = "synkro-local-cc-4";
13712
13828
  CHANNEL_4_PORT = 8952;
13713
13829
  RUN_SCRIPT_SOURCE = `#!/usr/bin/env bash
@@ -13981,10 +14097,10 @@ var disconnect_exports = {};
13981
14097
  __export(disconnect_exports, {
13982
14098
  disconnectCommand: () => disconnectCommand
13983
14099
  });
13984
- import { existsSync as existsSync13, rmSync, readdirSync as readdirSync4 } from "fs";
13985
- import { homedir as homedir12 } from "os";
13986
- import { join as join12 } from "path";
13987
- import { spawnSync as spawnSync6 } from "child_process";
14100
+ import { existsSync as existsSync14, rmSync as rmSync2, readdirSync as readdirSync4 } from "fs";
14101
+ import { homedir as homedir13 } from "os";
14102
+ import { join as join13 } from "path";
14103
+ import { spawnSync as spawnSync7 } from "child_process";
13988
14104
  import { createInterface as createInterface4 } from "readline";
13989
14105
  async function tearDownLocalCC() {
13990
14106
  const docker = dockerStatus();
@@ -13998,7 +14114,7 @@ async function tearDownLocalCC() {
13998
14114
  console.log("\u2713 removed synkro-server container");
13999
14115
  try {
14000
14116
  const image = imageTag();
14001
- const r = spawnSync6("docker", ["rmi", "-f", image], { encoding: "utf-8", timeout: 3e4 });
14117
+ const r = spawnSync7("docker", ["rmi", "-f", image], { encoding: "utf-8", timeout: 3e4 });
14002
14118
  console.log(r.status === 0 ? `\u2713 removed Docker image ${image}` : "\xB7 no Docker image to remove");
14003
14119
  } catch {
14004
14120
  }
@@ -14061,34 +14177,34 @@ async function disconnectCommand(args2 = []) {
14061
14177
  const desktopMcpRemoved = uninstallClaudeDesktopMcpConfig();
14062
14178
  console.log(`${desktopMcpRemoved ? "\u2713" : "\xB7"} MCP guardrails (Claude Desktop): ${desktopMcpRemoved ? "removed from claude_desktop_config.json" : "no entry found"}`);
14063
14179
  }
14064
- if (existsSync13(SYNKRO_DIR6)) {
14180
+ if (existsSync14(SYNKRO_DIR7)) {
14065
14181
  if (purge) {
14066
- rmSync(SYNKRO_DIR6, { recursive: true, force: true });
14067
- console.log(`\u2713 wiped ${SYNKRO_DIR6} entirely \u2014 including all scan data and backups`);
14182
+ rmSync2(SYNKRO_DIR7, { recursive: true, force: true });
14183
+ console.log(`\u2713 wiped ${SYNKRO_DIR7} entirely \u2014 including all scan data and backups`);
14068
14184
  } else {
14069
- const keep = /* @__PURE__ */ new Set([join12(SYNKRO_DIR6, "pgdata"), join12(SYNKRO_DIR6, "pgdata-backups")]);
14185
+ const keep = /* @__PURE__ */ new Set([join13(SYNKRO_DIR7, "pgdata"), join13(SYNKRO_DIR7, "pgdata-backups")]);
14070
14186
  const preserved = [];
14071
- for (const entry of readdirSync4(SYNKRO_DIR6)) {
14072
- const full = join12(SYNKRO_DIR6, entry);
14187
+ for (const entry of readdirSync4(SYNKRO_DIR7)) {
14188
+ const full = join13(SYNKRO_DIR7, entry);
14073
14189
  if (keep.has(full)) {
14074
14190
  preserved.push(entry);
14075
14191
  continue;
14076
14192
  }
14077
- rmSync(full, { recursive: true, force: true });
14193
+ rmSync2(full, { recursive: true, force: true });
14078
14194
  }
14079
14195
  if (preserved.length > 0) {
14080
- console.log(`\u2713 removed Synkro config from ${SYNKRO_DIR6} (kept your scan data: ${preserved.join(", ")})`);
14196
+ console.log(`\u2713 removed Synkro config from ${SYNKRO_DIR7} (kept your scan data: ${preserved.join(", ")})`);
14081
14197
  console.log(" run `synkro uninstall --purge` to delete that too");
14082
14198
  } else {
14083
- console.log(`\u2713 removed ${SYNKRO_DIR6}`);
14199
+ console.log(`\u2713 removed ${SYNKRO_DIR7}`);
14084
14200
  }
14085
14201
  }
14086
14202
  } else {
14087
- console.log(`\xB7 ${SYNKRO_DIR6} already gone`);
14203
+ console.log(`\xB7 ${SYNKRO_DIR7} already gone`);
14088
14204
  }
14089
14205
  console.log(purge ? "\nSynkro fully removed \u2014 this machine is clean, as if it was never installed." : "\nSynkro uninstalled. Your scan data is preserved.");
14090
14206
  }
14091
- var SYNKRO_DIR6;
14207
+ var SYNKRO_DIR7;
14092
14208
  var init_disconnect = __esm({
14093
14209
  "cli/commands/disconnect.ts"() {
14094
14210
  "use strict";
@@ -14099,14 +14215,14 @@ var init_disconnect = __esm({
14099
14215
  init_install2();
14100
14216
  init_dockerInstall();
14101
14217
  init_macKeychain();
14102
- SYNKRO_DIR6 = join12(homedir12(), ".synkro");
14218
+ SYNKRO_DIR7 = join13(homedir13(), ".synkro");
14103
14219
  }
14104
14220
  });
14105
14221
 
14106
14222
  // cli/local-cc/turnLog.ts
14107
- import { appendFileSync, existsSync as existsSync14, mkdirSync as mkdirSync11, openSync as openSync2, readFileSync as readFileSync12, readSync, closeSync as closeSync2, statSync as statSync2, watchFile, unwatchFile } from "fs";
14108
- import { dirname as dirname6, join as join13 } from "path";
14109
- import { homedir as homedir13 } from "os";
14223
+ import { appendFileSync, existsSync as existsSync15, mkdirSync as mkdirSync12, openSync as openSync2, readFileSync as readFileSync13, readSync, closeSync as closeSync2, statSync as statSync2, watchFile, unwatchFile } from "fs";
14224
+ import { dirname as dirname6, join as join14 } from "path";
14225
+ import { homedir as homedir14 } from "os";
14110
14226
  function truncate(s, max = PREVIEW_MAX) {
14111
14227
  if (s.length <= max) return s;
14112
14228
  return s.slice(0, max) + "\u2026 [+" + (s.length - max) + " chars]";
@@ -14126,7 +14242,7 @@ function extractSeverity(result) {
14126
14242
  }
14127
14243
  function appendTurn(args2) {
14128
14244
  try {
14129
- mkdirSync11(dirname6(TURN_LOG_PATH), { recursive: true });
14245
+ mkdirSync12(dirname6(TURN_LOG_PATH), { recursive: true });
14130
14246
  const entry = {
14131
14247
  ts: new Date(args2.startedAt).toISOString(),
14132
14248
  role: args2.role,
@@ -14142,11 +14258,11 @@ function appendTurn(args2) {
14142
14258
  }
14143
14259
  }
14144
14260
  function readRecentTurns(n = 20) {
14145
- if (!existsSync14(TURN_LOG_PATH)) return [];
14261
+ if (!existsSync15(TURN_LOG_PATH)) return [];
14146
14262
  try {
14147
14263
  const size = statSync2(TURN_LOG_PATH).size;
14148
14264
  if (size === 0) return [];
14149
- const text = readFileSync12(TURN_LOG_PATH, "utf-8");
14265
+ const text = readFileSync13(TURN_LOG_PATH, "utf-8");
14150
14266
  const lines = text.split("\n").filter(Boolean);
14151
14267
  const lastN = lines.slice(-n).reverse();
14152
14268
  return lastN.map((line) => {
@@ -14162,8 +14278,8 @@ function readRecentTurns(n = 20) {
14162
14278
  }
14163
14279
  function followTurns(onEntry) {
14164
14280
  try {
14165
- mkdirSync11(dirname6(TURN_LOG_PATH), { recursive: true });
14166
- if (!existsSync14(TURN_LOG_PATH)) {
14281
+ mkdirSync12(dirname6(TURN_LOG_PATH), { recursive: true });
14282
+ if (!existsSync15(TURN_LOG_PATH)) {
14167
14283
  appendFileSync(TURN_LOG_PATH, "", "utf-8");
14168
14284
  }
14169
14285
  } catch {
@@ -14225,7 +14341,7 @@ var TURN_LOG_PATH, PREVIEW_MAX;
14225
14341
  var init_turnLog = __esm({
14226
14342
  "cli/local-cc/turnLog.ts"() {
14227
14343
  "use strict";
14228
- TURN_LOG_PATH = join13(homedir13(), ".synkro", "cc_sessions", "turns.log");
14344
+ TURN_LOG_PATH = join14(homedir14(), ".synkro", "cc_sessions", "turns.log");
14229
14345
  PREVIEW_MAX = 400;
14230
14346
  }
14231
14347
  });
@@ -14374,19 +14490,19 @@ var init_grade = __esm({
14374
14490
  });
14375
14491
 
14376
14492
  // cli/local-cc/pueue.ts
14377
- import { execFileSync as execFileSync3, spawnSync as spawnSync7, spawn as spawn2 } from "child_process";
14378
- import { homedir as homedir14 } from "os";
14379
- import { join as join14 } from "path";
14493
+ import { execFileSync as execFileSync3, spawnSync as spawnSync8, spawn as spawn2 } from "child_process";
14494
+ import { homedir as homedir15 } from "os";
14495
+ import { join as join15 } from "path";
14380
14496
  import { connect as connect2 } from "net";
14381
14497
  function pueueAvailable() {
14382
- const r = spawnSync7("pueue", ["--version"], { encoding: "utf-8" });
14498
+ const r = spawnSync8("pueue", ["--version"], { encoding: "utf-8" });
14383
14499
  if (r.status !== 0) {
14384
14500
  throw new PueueError("pueue CLI not found on PATH. Install pueue (https://github.com/Nukesor/pueue) and start `pueued`.");
14385
14501
  }
14386
14502
  }
14387
14503
  function statusJson() {
14388
14504
  pueueAvailable();
14389
- const r = spawnSync7("pueue", ["status", "--json"], { encoding: "utf-8" });
14505
+ const r = spawnSync8("pueue", ["status", "--json"], { encoding: "utf-8" });
14390
14506
  if (r.status !== 0) {
14391
14507
  throw new PueueError(`pueue status failed: ${r.stderr || r.stdout || "unknown error"} \u2014 is pueued running?`);
14392
14508
  }
@@ -14431,18 +14547,18 @@ function startTask(opts = {}) {
14431
14547
  let existing = findTask(ch);
14432
14548
  while (existing) {
14433
14549
  if (existing.status === "Running" || existing.status === "Queued") {
14434
- spawnSync7("tmux", ["kill-session", "-t", `=${ch.tmuxSession}`], { encoding: "utf-8" });
14435
- spawnSync7("pueue", ["kill", String(existing.id)], { encoding: "utf-8" });
14550
+ spawnSync8("tmux", ["kill-session", "-t", `=${ch.tmuxSession}`], { encoding: "utf-8" });
14551
+ spawnSync8("pueue", ["kill", String(existing.id)], { encoding: "utf-8" });
14436
14552
  for (let i = 0; i < 10; i++) {
14437
14553
  const check = findTask(ch);
14438
14554
  if (!check || check.id !== existing.id || check.status !== "Running" && check.status !== "Queued") break;
14439
- spawnSync7("sleep", ["0.5"], { encoding: "utf-8" });
14555
+ spawnSync8("sleep", ["0.5"], { encoding: "utf-8" });
14440
14556
  }
14441
14557
  }
14442
- spawnSync7("pueue", ["remove", String(existing.id)], { encoding: "utf-8" });
14558
+ spawnSync8("pueue", ["remove", String(existing.id)], { encoding: "utf-8" });
14443
14559
  existing = findTask(ch);
14444
14560
  }
14445
- const runScript = join14(cwd, "run-claude.sh");
14561
+ const runScript = join15(cwd, "run-claude.sh");
14446
14562
  const args2 = [
14447
14563
  "add",
14448
14564
  "--label",
@@ -14453,7 +14569,7 @@ function startTask(opts = {}) {
14453
14569
  "bash",
14454
14570
  runScript
14455
14571
  ];
14456
- const r = spawnSync7("pueue", args2, { encoding: "utf-8" });
14572
+ const r = spawnSync8("pueue", args2, { encoding: "utf-8" });
14457
14573
  if (r.status !== 0) {
14458
14574
  throw new PueueError(`pueue add failed: ${r.stderr || r.stdout}`);
14459
14575
  }
@@ -14464,25 +14580,25 @@ function startTask(opts = {}) {
14464
14580
  return created;
14465
14581
  }
14466
14582
  function stopTask(channel = CHANNEL_PRIMARY) {
14467
- spawnSync7("tmux", ["kill-session", "-t", `=${channel.tmuxSession}`], { encoding: "utf-8" });
14583
+ spawnSync8("tmux", ["kill-session", "-t", `=${channel.tmuxSession}`], { encoding: "utf-8" });
14468
14584
  let t = findTask(channel);
14469
14585
  while (t) {
14470
14586
  if (t.status === "Running" || t.status === "Queued") {
14471
- spawnSync7("pueue", ["kill", String(t.id)], { encoding: "utf-8" });
14587
+ spawnSync8("pueue", ["kill", String(t.id)], { encoding: "utf-8" });
14472
14588
  for (let i = 0; i < 10; i++) {
14473
14589
  const check = findTask(channel);
14474
14590
  if (!check || check.id !== t.id || check.status !== "Running" && check.status !== "Queued") break;
14475
- spawnSync7("sleep", ["0.5"], { encoding: "utf-8" });
14591
+ spawnSync8("sleep", ["0.5"], { encoding: "utf-8" });
14476
14592
  }
14477
14593
  }
14478
- spawnSync7("pueue", ["remove", String(t.id)], { encoding: "utf-8" });
14594
+ spawnSync8("pueue", ["remove", String(t.id)], { encoding: "utf-8" });
14479
14595
  t = findTask(channel);
14480
14596
  }
14481
14597
  }
14482
14598
  function tailLogs(lines = 80, channel = CHANNEL_PRIMARY) {
14483
14599
  const t = findTask(channel);
14484
14600
  if (!t) return `(no ${channel.taskLabel} task)`;
14485
- const r = spawnSync7("pueue", ["log", "--lines", String(lines), String(t.id)], { encoding: "utf-8" });
14601
+ const r = spawnSync8("pueue", ["log", "--lines", String(lines), String(t.id)], { encoding: "utf-8" });
14486
14602
  return r.stdout || r.stderr || "(no output)";
14487
14603
  }
14488
14604
  function ensureRunning(opts = {}) {
@@ -14507,8 +14623,8 @@ function probePort(host, port, timeoutMs = 500) {
14507
14623
  });
14508
14624
  }
14509
14625
  function tmuxDismissPrompts(tmuxSession = TMUX_SESSION) {
14510
- spawnSync7("tmux", ["send-keys", "-t", tmuxSession, "1"], { encoding: "utf-8" });
14511
- spawnSync7("tmux", ["send-keys", "-t", tmuxSession, "Enter"], { encoding: "utf-8" });
14626
+ spawnSync8("tmux", ["send-keys", "-t", tmuxSession, "1"], { encoding: "utf-8" });
14627
+ spawnSync8("tmux", ["send-keys", "-t", tmuxSession, "Enter"], { encoding: "utf-8" });
14512
14628
  }
14513
14629
  async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tmuxSession = TMUX_SESSION) {
14514
14630
  const deadline = Date.now() + timeoutMs;
@@ -14520,46 +14636,46 @@ async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tm
14520
14636
  return probePort(host, port);
14521
14637
  }
14522
14638
  function brewInstall(pkg) {
14523
- const brew = spawnSync7("brew", ["--version"], { encoding: "utf-8" });
14639
+ const brew = spawnSync8("brew", ["--version"], { encoding: "utf-8" });
14524
14640
  if (brew.status !== 0) return false;
14525
14641
  console.log(` Installing ${pkg} via brew...`);
14526
- const r = spawnSync7("brew", ["install", pkg], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
14642
+ const r = spawnSync8("brew", ["install", pkg], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
14527
14643
  return r.status === 0;
14528
14644
  }
14529
14645
  function assertPueueInstalled() {
14530
- let r = spawnSync7("pueue", ["--version"], { encoding: "utf-8" });
14646
+ let r = spawnSync8("pueue", ["--version"], { encoding: "utf-8" });
14531
14647
  if (r.status !== 0) {
14532
14648
  if (process.platform === "darwin" && brewInstall("pueue")) {
14533
- r = spawnSync7("pueue", ["--version"], { encoding: "utf-8" });
14649
+ r = spawnSync8("pueue", ["--version"], { encoding: "utf-8" });
14534
14650
  if (r.status !== 0) throw new PueueError("pueue install succeeded but binary not found on PATH.");
14535
14651
  } else {
14536
14652
  throw new PueueError("pueue not found. Install it: brew install pueue (macOS) or https://github.com/Nukesor/pueue");
14537
14653
  }
14538
14654
  }
14539
- const status = spawnSync7("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
14655
+ const status = spawnSync8("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
14540
14656
  if (status.status !== 0) {
14541
14657
  console.log(" Starting pueued daemon...");
14542
14658
  const child = spawn2("pueued", ["-d"], { stdio: "ignore", detached: true });
14543
14659
  child.unref();
14544
- spawnSync7("sleep", ["1"]);
14545
- const retry = spawnSync7("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
14660
+ spawnSync8("sleep", ["1"]);
14661
+ const retry = spawnSync8("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
14546
14662
  if (retry.status !== 0) {
14547
14663
  throw new PueueError("pueue daemon not reachable after starting pueued. Check `pueued` manually.");
14548
14664
  }
14549
14665
  }
14550
- spawnSync7("pueue", ["parallel", "2"], { encoding: "utf-8" });
14666
+ spawnSync8("pueue", ["parallel", "2"], { encoding: "utf-8" });
14551
14667
  }
14552
14668
  function assertClaudeInstalled() {
14553
- const r = spawnSync7("claude", ["--version"], { encoding: "utf-8" });
14669
+ const r = spawnSync8("claude", ["--version"], { encoding: "utf-8" });
14554
14670
  if (r.status !== 0) {
14555
14671
  throw new PueueError("claude CLI not found on PATH. Install Claude Code first: https://docs.claude.com/claude-code");
14556
14672
  }
14557
14673
  }
14558
14674
  function assertTmuxInstalled() {
14559
- let r = spawnSync7("tmux", ["-V"], { encoding: "utf-8" });
14675
+ let r = spawnSync8("tmux", ["-V"], { encoding: "utf-8" });
14560
14676
  if (r.status !== 0) {
14561
14677
  if (process.platform === "darwin" && brewInstall("tmux")) {
14562
- r = spawnSync7("tmux", ["-V"], { encoding: "utf-8" });
14678
+ r = spawnSync8("tmux", ["-V"], { encoding: "utf-8" });
14563
14679
  if (r.status !== 0) throw new PueueError("tmux install succeeded but binary not found on PATH.");
14564
14680
  } else {
14565
14681
  throw new PueueError("tmux not found. Install it: brew install tmux (macOS) or apt install tmux (Linux)");
@@ -14572,12 +14688,12 @@ var init_pueue = __esm({
14572
14688
  "use strict";
14573
14689
  TASK_LABEL = "synkro-local-cc";
14574
14690
  TMUX_SESSION = "synkro-local-cc";
14575
- SESSION_DIR2 = join14(homedir14(), ".synkro", "cc_sessions");
14691
+ SESSION_DIR2 = join15(homedir15(), ".synkro", "cc_sessions");
14576
14692
  TASK_LABEL_2 = "synkro-local-cc-2";
14577
14693
  TMUX_SESSION_2 = "synkro-local-cc-2";
14578
- SESSION_DIR_22 = join14(homedir14(), ".synkro", "cc_sessions_2");
14579
- SESSION_DIR_32 = join14(homedir14(), ".synkro", "cc_sessions_3");
14580
- SESSION_DIR_42 = join14(homedir14(), ".synkro", "cc_sessions_4");
14694
+ SESSION_DIR_22 = join15(homedir15(), ".synkro", "cc_sessions_2");
14695
+ SESSION_DIR_32 = join15(homedir15(), ".synkro", "cc_sessions_3");
14696
+ SESSION_DIR_42 = join15(homedir15(), ".synkro", "cc_sessions_4");
14581
14697
  PueueError = class extends Error {
14582
14698
  constructor(message, cause) {
14583
14699
  super(message);
@@ -14592,13 +14708,13 @@ var init_pueue = __esm({
14592
14708
  });
14593
14709
 
14594
14710
  // cli/local-cc/settings.ts
14595
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
14596
- import { homedir as homedir15 } from "os";
14597
- import { join as join15 } from "path";
14711
+ import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
14712
+ import { homedir as homedir16 } from "os";
14713
+ import { join as join16 } from "path";
14598
14714
  function isLocalCCEnabled() {
14599
- if (!existsSync15(CONFIG_PATH3)) return false;
14715
+ if (!existsSync16(CONFIG_PATH3)) return false;
14600
14716
  try {
14601
- const content = readFileSync13(CONFIG_PATH3, "utf-8");
14717
+ const content = readFileSync14(CONFIG_PATH3, "utf-8");
14602
14718
  const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);
14603
14719
  return match?.[1] === "yes";
14604
14720
  } catch {
@@ -14609,7 +14725,7 @@ var CONFIG_PATH3;
14609
14725
  var init_settings = __esm({
14610
14726
  "cli/local-cc/settings.ts"() {
14611
14727
  "use strict";
14612
- CONFIG_PATH3 = join15(homedir15(), ".synkro", "config.env");
14728
+ CONFIG_PATH3 = join16(homedir16(), ".synkro", "config.env");
14613
14729
  }
14614
14730
  });
14615
14731
 
@@ -14618,11 +14734,11 @@ var localCc_exports = {};
14618
14734
  __export(localCc_exports, {
14619
14735
  localCcCommand: () => localCcCommand
14620
14736
  });
14621
- import { spawnSync as spawnSync8 } from "child_process";
14622
- import { homedir as homedir16 } from "os";
14623
- import { join as join16 } from "path";
14737
+ import { spawnSync as spawnSync9 } from "child_process";
14738
+ import { homedir as homedir17 } from "os";
14739
+ import { join as join17 } from "path";
14624
14740
  import { readFileSync as fsReadFileSync, existsSync as fsExistsSync } from "fs";
14625
- import { existsSync as existsSync16, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
14741
+ import { existsSync as existsSync17, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "fs";
14626
14742
  function deploymentMode() {
14627
14743
  const env = (process.env.SYNKRO_DEPLOYMENT_MODE || "").toLowerCase();
14628
14744
  if (env === "docker") return "docker";
@@ -14728,15 +14844,15 @@ TROUBLESHOOTING
14728
14844
  `);
14729
14845
  }
14730
14846
  function readGatewayUrl() {
14731
- if (existsSync16(CONFIG_PATH4)) {
14732
- const m = readFileSync14(CONFIG_PATH4, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
14847
+ if (existsSync17(CONFIG_PATH4)) {
14848
+ const m = readFileSync15(CONFIG_PATH4, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
14733
14849
  if (m) return m[1];
14734
14850
  }
14735
14851
  return "https://api.synkro.sh";
14736
14852
  }
14737
14853
  function updateLocalInferenceFlag(enabled) {
14738
- if (!existsSync16(CONFIG_PATH4)) return;
14739
- let content = readFileSync14(CONFIG_PATH4, "utf-8");
14854
+ if (!existsSync17(CONFIG_PATH4)) return;
14855
+ let content = readFileSync15(CONFIG_PATH4, "utf-8");
14740
14856
  const flag = enabled ? "yes" : "no";
14741
14857
  if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
14742
14858
  content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);
@@ -14799,7 +14915,7 @@ async function cmdStatus() {
14799
14915
  }
14800
14916
  const ch1Up = await isChannelAvailable();
14801
14917
  console.log(`Channel 1 ${CHANNEL_HOST}:${CHANNEL_PORT}: ${ch1Up ? "reachable" : "unreachable"}`);
14802
- const tmux1 = spawnSync8("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
14918
+ const tmux1 = spawnSync9("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
14803
14919
  console.log(`tmux '${TMUX_SESSION_NAME}': ${tmux1.status === 0 ? "live" : "absent"}`);
14804
14920
  const t2 = findTask(CHANNEL_SECONDARY);
14805
14921
  if (!t2) {
@@ -14809,7 +14925,7 @@ async function cmdStatus() {
14809
14925
  }
14810
14926
  const ch2Up = await isChannelAvailable(CHANNEL_2_PORT);
14811
14927
  console.log(`Channel 2 ${CHANNEL_HOST}:${CHANNEL_2_PORT}: ${ch2Up ? "reachable" : "unreachable"}`);
14812
- const tmux2 = spawnSync8("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME_2}`], { encoding: "utf-8" });
14928
+ const tmux2 = spawnSync9("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME_2}`], { encoding: "utf-8" });
14813
14929
  console.log(`tmux '${TMUX_SESSION_NAME_2}': ${tmux2.status === 0 ? "live" : "absent"}`);
14814
14930
  }
14815
14931
  async function cmdEnable() {
@@ -15015,7 +15131,7 @@ function cmdLogs(rest) {
15015
15131
  }
15016
15132
  return "200";
15017
15133
  })();
15018
- spawnSync8("docker", ["logs", "--tail", tailArg, ...followFlag, "synkro-server"], { stdio: "inherit" });
15134
+ spawnSync9("docker", ["logs", "--tail", tailArg, ...followFlag, "synkro-server"], { stdio: "inherit" });
15019
15135
  return;
15020
15136
  }
15021
15137
  for (const arg of rest) {
@@ -15063,7 +15179,7 @@ function cmdLogs(rest) {
15063
15179
  function cmdAttach(rest) {
15064
15180
  assertTmuxInstalled();
15065
15181
  const readonly = rest.some((a) => a === "--readonly" || a === "-r");
15066
- const has = spawnSync8("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
15182
+ const has = spawnSync9("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
15067
15183
  if (has.status !== 0) {
15068
15184
  console.error(`No tmux session '${TMUX_SESSION_NAME}' running. Start it with: synkro local-cc start`);
15069
15185
  process.exit(1);
@@ -15076,7 +15192,7 @@ function cmdAttach(rest) {
15076
15192
  console.log("Detach with Ctrl-B then D. (Do not press Ctrl-C \u2014 that would interrupt claude.)");
15077
15193
  console.log();
15078
15194
  const args2 = readonly ? ["attach-session", "-r", "-t", TMUX_SESSION_NAME] : ["attach-session", "-t", TMUX_SESSION_NAME];
15079
- const r = spawnSync8("tmux", args2, { stdio: "inherit" });
15195
+ const r = spawnSync9("tmux", args2, { stdio: "inherit" });
15080
15196
  process.exit(r.status ?? 0);
15081
15197
  }
15082
15198
  async function cmdTest() {
@@ -15177,8 +15293,8 @@ var init_localCc = __esm({
15177
15293
  init_install();
15178
15294
  init_client();
15179
15295
  init_stub();
15180
- SYNKRO_CONFIG_PATH = join16(homedir16(), ".synkro", "config.env");
15181
- CONFIG_PATH4 = join16(homedir16(), ".synkro", "config.env");
15296
+ SYNKRO_CONFIG_PATH = join17(homedir17(), ".synkro", "config.env");
15297
+ CONFIG_PATH4 = join17(homedir17(), ".synkro", "config.env");
15182
15298
  }
15183
15299
  });
15184
15300
 
@@ -15187,15 +15303,15 @@ var import_exports = {};
15187
15303
  __export(import_exports, {
15188
15304
  importCommand: () => importCommand
15189
15305
  });
15190
- import { existsSync as existsSync17, readFileSync as readFileSync15, readdirSync as readdirSync5 } from "fs";
15191
- import { homedir as homedir17 } from "os";
15192
- import { join as join17 } from "path";
15306
+ import { existsSync as existsSync18, readFileSync as readFileSync16, readdirSync as readdirSync5 } from "fs";
15307
+ import { homedir as homedir18 } from "os";
15308
+ import { join as join18 } from "path";
15193
15309
  import { execSync as execSync7 } from "child_process";
15194
15310
  import { createInterface as createInterface5 } from "readline";
15195
15311
  function readConfigEnv() {
15196
15312
  const out = {};
15197
15313
  try {
15198
- for (const line of readFileSync15(CONFIG_PATH5, "utf-8").split("\n")) {
15314
+ for (const line of readFileSync16(CONFIG_PATH5, "utf-8").split("\n")) {
15199
15315
  const t = line.trim();
15200
15316
  if (!t || t.startsWith("#")) continue;
15201
15317
  const eq = t.indexOf("=");
@@ -15207,8 +15323,8 @@ function readConfigEnv() {
15207
15323
  }
15208
15324
  function projectsFolder() {
15209
15325
  const sanitized = process.cwd().replace(/\//g, "-");
15210
- const dir = join17(homedir17(), ".claude", "projects", sanitized);
15211
- return existsSync17(dir) ? dir : null;
15326
+ const dir = join18(homedir18(), ".claude", "projects", sanitized);
15327
+ return existsSync18(dir) ? dir : null;
15212
15328
  }
15213
15329
  function repoName() {
15214
15330
  try {
@@ -15231,7 +15347,7 @@ function extractText(content) {
15231
15347
  return "";
15232
15348
  }
15233
15349
  function parseSession(filePath, sessionId) {
15234
- const lines = readFileSync15(filePath, "utf-8").split("\n").filter(Boolean);
15350
+ const lines = readFileSync16(filePath, "utf-8").split("\n").filter(Boolean);
15235
15351
  const messages = [];
15236
15352
  const actions = [];
15237
15353
  let step = 0;
@@ -15304,7 +15420,7 @@ async function importCommand() {
15304
15420
  return;
15305
15421
  }
15306
15422
  }
15307
- const sessions = files.map((f) => parseSession(join17(dir, f), f.replace(".jsonl", ""))).filter((s) => s.messages.length > 0);
15423
+ const sessions = files.map((f) => parseSession(join18(dir, f), f.replace(".jsonl", ""))).filter((s) => s.messages.length > 0);
15308
15424
  const totalMsgs = sessions.reduce((n, s) => n + s.messages.length, 0);
15309
15425
  let ok = 0, fail = 0;
15310
15426
  if (isCloud) {
@@ -15364,7 +15480,7 @@ var init_import = __esm({
15364
15480
  "cli/commands/import.ts"() {
15365
15481
  "use strict";
15366
15482
  init_stub();
15367
- CONFIG_PATH5 = join17(homedir17(), ".synkro", "config.env");
15483
+ CONFIG_PATH5 = join18(homedir18(), ".synkro", "config.env");
15368
15484
  }
15369
15485
  });
15370
15486
 
@@ -15406,10 +15522,10 @@ var init_packVerify = __esm({
15406
15522
  });
15407
15523
 
15408
15524
  // cli/installer/lockfile.ts
15409
- import { existsSync as existsSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "fs";
15410
- import { join as join18 } from "path";
15525
+ import { existsSync as existsSync19, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "fs";
15526
+ import { join as join19 } from "path";
15411
15527
  function lockPath(repoRoot) {
15412
- return join18(repoRoot, LOCK_FILE);
15528
+ return join19(repoRoot, LOCK_FILE);
15413
15529
  }
15414
15530
  function writeLockfile(repoRoot, entries) {
15415
15531
  const sorted = [...entries].sort((a, b) => a.ref.localeCompare(b.ref));
@@ -15442,9 +15558,9 @@ var sync_exports = {};
15442
15558
  __export(sync_exports, {
15443
15559
  syncCommand: () => syncCommand
15444
15560
  });
15445
- import { existsSync as existsSync19, mkdirSync as mkdirSync12, readdirSync as readdirSync6, rmSync as rmSync2, writeFileSync as writeFileSync12 } from "fs";
15446
- import { homedir as homedir18 } from "os";
15447
- import { join as join19 } from "path";
15561
+ import { existsSync as existsSync20, mkdirSync as mkdirSync13, readdirSync as readdirSync6, rmSync as rmSync3, writeFileSync as writeFileSync12 } from "fs";
15562
+ import { homedir as homedir19 } from "os";
15563
+ import { join as join20 } from "path";
15448
15564
  function cacheKey(ref, version) {
15449
15565
  return ref.replace(/\//g, "__").replace(/[^\w.@-]/g, "_") + "@" + version + ".json";
15450
15566
  }
@@ -15475,8 +15591,8 @@ async function syncCommand(_args = []) {
15475
15591
  }
15476
15592
  const gateway = (process.env.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/\/$/, "");
15477
15593
  const cloud = process.env.SYNKRO_DEPLOY_LOCATION === "cloud";
15478
- const cacheDir = join19(homedir18(), ".synkro", "cache", "packs");
15479
- if (!cloud) mkdirSync12(cacheDir, { recursive: true });
15594
+ const cacheDir = join20(homedir19(), ".synkro", "cache", "packs");
15595
+ if (!cloud) mkdirSync13(cacheDir, { recursive: true });
15480
15596
  console.log(`Syncing ${refs.length} standard(s) from the registry\u2026`);
15481
15597
  const lock = [];
15482
15598
  const keptCacheFiles = /* @__PURE__ */ new Set();
@@ -15504,7 +15620,7 @@ async function syncCommand(_args = []) {
15504
15620
  if (!cloud) {
15505
15621
  const fname = cacheKey(ref, data.version);
15506
15622
  keptCacheFiles.add(fname);
15507
- writeFileSync12(join19(cacheDir, fname), JSON.stringify({
15623
+ writeFileSync12(join20(cacheDir, fname), JSON.stringify({
15508
15624
  ref,
15509
15625
  version: data.version,
15510
15626
  digest: data.digest,
@@ -15516,11 +15632,11 @@ async function syncCommand(_args = []) {
15516
15632
  const ruleCount = Array.isArray(pack.rules) ? pack.rules.length : 0;
15517
15633
  console.log(` \u2713 ${ref}:${data.version} \u2014 verified (${ruleCount} rule${ruleCount === 1 ? "" : "s"})`);
15518
15634
  }
15519
- if (!cloud && existsSync19(cacheDir)) {
15635
+ if (!cloud && existsSync20(cacheDir)) {
15520
15636
  for (const f of readdirSync6(cacheDir)) {
15521
15637
  if (f.endsWith(".json") && !keptCacheFiles.has(f)) {
15522
15638
  try {
15523
- rmSync2(join19(cacheDir, f));
15639
+ rmSync3(join20(cacheDir, f));
15524
15640
  } catch {
15525
15641
  }
15526
15642
  }
@@ -15656,13 +15772,13 @@ var config_exports = {};
15656
15772
  __export(config_exports, {
15657
15773
  configCommand: () => configCommand
15658
15774
  });
15659
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync13, existsSync as existsSync20 } from "fs";
15660
- import { join as join20 } from "path";
15661
- import { homedir as homedir19 } from "os";
15775
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync13, existsSync as existsSync21 } from "fs";
15776
+ import { join as join21 } from "path";
15777
+ import { homedir as homedir20 } from "os";
15662
15778
  function readConfigEnv2() {
15663
- if (!existsSync20(CONFIG_PATH6)) return {};
15779
+ if (!existsSync21(CONFIG_PATH6)) return {};
15664
15780
  const out = {};
15665
- for (const line of readFileSync17(CONFIG_PATH6, "utf-8").split("\n")) {
15781
+ for (const line of readFileSync18(CONFIG_PATH6, "utf-8").split("\n")) {
15666
15782
  const t = line.trim();
15667
15783
  if (!t || t.startsWith("#")) continue;
15668
15784
  const eq = t.indexOf("=");
@@ -15671,11 +15787,11 @@ function readConfigEnv2() {
15671
15787
  return out;
15672
15788
  }
15673
15789
  function updateConfigValue(key, value) {
15674
- if (!existsSync20(CONFIG_PATH6)) {
15790
+ if (!existsSync21(CONFIG_PATH6)) {
15675
15791
  console.error("No config found. Run `synkro install` first.");
15676
15792
  process.exit(1);
15677
15793
  }
15678
- const lines = readFileSync17(CONFIG_PATH6, "utf-8").split("\n");
15794
+ const lines = readFileSync18(CONFIG_PATH6, "utf-8").split("\n");
15679
15795
  const pattern = new RegExp(`^${key}=`);
15680
15796
  let found = false;
15681
15797
  const updated = lines.map((line) => {
@@ -15813,25 +15929,25 @@ To change:`);
15813
15929
  }
15814
15930
  if (inferenceValue !== "cloud") await reconcileContainer();
15815
15931
  }
15816
- var SYNKRO_DIR7, CONFIG_PATH6;
15932
+ var SYNKRO_DIR8, CONFIG_PATH6;
15817
15933
  var init_config = __esm({
15818
15934
  "cli/commands/config.ts"() {
15819
15935
  "use strict";
15820
15936
  init_stub();
15821
- SYNKRO_DIR7 = join20(homedir19(), ".synkro");
15822
- CONFIG_PATH6 = join20(SYNKRO_DIR7, "config.env");
15937
+ SYNKRO_DIR8 = join21(homedir20(), ".synkro");
15938
+ CONFIG_PATH6 = join21(SYNKRO_DIR8, "config.env");
15823
15939
  }
15824
15940
  });
15825
15941
 
15826
15942
  // cli/bootstrap.js
15827
- import { readFileSync as readFileSync18, existsSync as existsSync21 } from "fs";
15943
+ import { readFileSync as readFileSync19, existsSync as existsSync22 } from "fs";
15828
15944
  import { resolve as resolve3 } from "path";
15829
15945
  var envCandidates = [
15830
15946
  resolve3(process.env.HOME ?? "", ".synkro", "config.env")
15831
15947
  ];
15832
15948
  for (const envPath of envCandidates) {
15833
- if (!existsSync21(envPath)) continue;
15834
- const envContent = readFileSync18(envPath, "utf-8");
15949
+ if (!existsSync22(envPath)) continue;
15950
+ const envContent = readFileSync19(envPath, "utf-8");
15835
15951
  for (const line of envContent.split("\n")) {
15836
15952
  const trimmed = line.trim();
15837
15953
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -15846,7 +15962,7 @@ var args = process.argv.slice(2);
15846
15962
  var cmd = args[0] || "";
15847
15963
  var subArgs = args.slice(1);
15848
15964
  function printVersion() {
15849
- console.log("1.7.18");
15965
+ console.log("1.7.24");
15850
15966
  }
15851
15967
  function printHelp2() {
15852
15968
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents