@synkro-sh/cli 1.7.63 → 1.7.65

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
@@ -132,7 +132,7 @@ function getIdentity() {
132
132
  if (cached2) return cached2;
133
133
  let cliVersion = "0.0.0";
134
134
  try {
135
- cliVersion = "1.7.63";
135
+ cliVersion = "1.7.65";
136
136
  } catch {
137
137
  }
138
138
  const creds = loadCredentialsIdentity();
@@ -2252,7 +2252,13 @@ function loadMcpJwt(): string {
2252
2252
 
2253
2253
  let _cfgCache: Record<string, string> | null = null;
2254
2254
  function cfgVal(key: string): string {
2255
- if (process.env[key]) return process.env[key] as string;
2255
+ // ~/.synkro/config.env (written by "synkro install") is AUTHORITATIVE for the keys it
2256
+ // defines. This hook runs under bun, which AUTO-LOADS a .env/.env.local from the repo
2257
+ // cwd into process.env — so a repo's own dev vars (e.g. SYNKRO_GATEWAY_URL=localhost for a
2258
+ // local "wrangler dev") would otherwise hijack the installed config and silently redirect
2259
+ // cloud grades to a dead gateway (fail-open, "nothing fires"). So read config.env FIRST and
2260
+ // only fall back to process.env for keys config.env doesn't set (e.g. SYNKRO_HOOK_FORMAT,
2261
+ // which is passed on the cursor hook command, never written to config.env).
2256
2262
  if (!_cfgCache) {
2257
2263
  _cfgCache = {};
2258
2264
  try {
@@ -2263,7 +2269,8 @@ function cfgVal(key: string): string {
2263
2269
  }
2264
2270
  } catch {}
2265
2271
  }
2266
- return _cfgCache[key] || '';
2272
+ if (_cfgCache[key]) return _cfgCache[key];
2273
+ return process.env[key] || '';
2267
2274
  }
2268
2275
  function deployIsCloud(): boolean { return cfgVal('SYNKRO_DEPLOY_LOCATION') === 'cloud'; }
2269
2276
 
@@ -5863,87 +5870,6 @@ var init_setupToken = __esm({
5863
5870
  }
5864
5871
  });
5865
5872
 
5866
- // cli/local-cc/codexCloudSetup.ts
5867
- var codexCloudSetup_exports = {};
5868
- __export(codexCloudSetup_exports, {
5869
- setupCodexCloud: () => setupCodexCloud
5870
- });
5871
- import { spawn as nodeSpawn2, spawnSync as spawnSync4 } from "child_process";
5872
- import { readFileSync as readFileSync16, mkdirSync as mkdirSync12, rmSync, existsSync as existsSync19 } from "fs";
5873
- import { homedir as homedir16 } from "os";
5874
- import { join as join16 } from "path";
5875
- function findCodexBinary() {
5876
- if (process.env.SYNKRO_CODEX_BIN) return process.env.SYNKRO_CODEX_BIN;
5877
- const r = spawnSync4("which", ["codex"], { encoding: "utf-8" });
5878
- const p = (r.stdout || "").trim().split("\n")[0];
5879
- return p || null;
5880
- }
5881
- function runCodexLogin(codexBin) {
5882
- mkdirSync12(CODEX_CLOUD_HOME, { recursive: true, mode: 448 });
5883
- return new Promise((resolve4, reject) => {
5884
- const proc = nodeSpawn2(codexBin, ["login"], {
5885
- stdio: "inherit",
5886
- env: { ...process.env, CODEX_HOME: CODEX_CLOUD_HOME }
5887
- });
5888
- proc.on("error", (err) => reject(new Error(`failed to spawn codex login: ${err.message}`)));
5889
- proc.on("close", (code) => code === 0 ? resolve4() : reject(new Error(`codex login exited with code ${code}`)));
5890
- });
5891
- }
5892
- async function setupCodexCloud(gatewayUrl, bearerToken, onStatus) {
5893
- const codexBin = findCodexBinary();
5894
- if (!codexBin) {
5895
- 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.)" };
5896
- }
5897
- onStatus?.("Opening your browser to authorize your Codex subscription for Synkro Cloud\u2026");
5898
- try {
5899
- await runCodexLogin(codexBin);
5900
- } catch (e) {
5901
- return { ok: false, error: `Codex login failed: ${e.message}` };
5902
- }
5903
- const authPath = join16(CODEX_CLOUD_HOME, "auth.json");
5904
- if (!existsSync19(authPath)) {
5905
- return { ok: false, error: "codex login completed but no auth.json was written \u2014 did the browser approval finish?" };
5906
- }
5907
- let auth;
5908
- try {
5909
- auth = JSON.parse(readFileSync16(authPath, "utf-8"));
5910
- } catch (e) {
5911
- return { ok: false, error: `could not read codex auth.json: ${e.message}` };
5912
- }
5913
- if (!auth || typeof auth !== "object" || !auth.tokens?.refresh_token) {
5914
- return { ok: false, error: "codex auth.json has no refresh token \u2014 cloud refresh would fail. Re-run login." };
5915
- }
5916
- onStatus?.("Connecting your Codex session to Synkro Cloud\u2026");
5917
- let resp;
5918
- try {
5919
- resp = await fetch(`${gatewayUrl.replace(/\/+$/, "")}/codex/seed`, {
5920
- method: "POST",
5921
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${bearerToken}` },
5922
- body: JSON.stringify({ auth })
5923
- });
5924
- } catch (e) {
5925
- return { ok: false, error: `failed to reach Synkro API: ${e.message}` };
5926
- }
5927
- if (!resp.ok) {
5928
- const detail = await resp.text().catch(() => "");
5929
- return { ok: false, error: `seed rejected (${resp.status}): ${detail.slice(0, 200)}` };
5930
- }
5931
- try {
5932
- rmSync(CODEX_CLOUD_HOME, { recursive: true, force: true });
5933
- } catch {
5934
- }
5935
- onStatus?.("\u2713 Codex subscription connected to Synkro Cloud.");
5936
- return { ok: true };
5937
- }
5938
- var SYNKRO_DIR9, CODEX_CLOUD_HOME;
5939
- var init_codexCloudSetup = __esm({
5940
- "cli/local-cc/codexCloudSetup.ts"() {
5941
- "use strict";
5942
- SYNKRO_DIR9 = join16(homedir16(), ".synkro");
5943
- CODEX_CLOUD_HOME = join16(SYNKRO_DIR9, "codex-cloud-session");
5944
- }
5945
- });
5946
-
5947
5873
  // cli/commands/setupGithub.ts
5948
5874
  var setupGithub_exports = {};
5949
5875
  __export(setupGithub_exports, {
@@ -5953,14 +5879,14 @@ __export(setupGithub_exports, {
5953
5879
  import { createInterface as createInterface2 } from "readline/promises";
5954
5880
  import { stdin as input, stdout as output } from "process";
5955
5881
  import { execSync as execSync5 } from "child_process";
5956
- import { existsSync as existsSync20, readFileSync as readFileSync17 } from "fs";
5957
- import { homedir as homedir17, platform as platform5 } from "os";
5958
- import { join as join17 } from "path";
5882
+ import { existsSync as existsSync19, readFileSync as readFileSync16 } from "fs";
5883
+ import { homedir as homedir16, platform as platform5 } from "os";
5884
+ import { join as join16 } from "path";
5959
5885
  import { execFile as execFile2 } from "child_process";
5960
5886
  function readConfig() {
5961
- if (!existsSync20(CONFIG_PATH3)) return {};
5887
+ if (!existsSync19(CONFIG_PATH3)) return {};
5962
5888
  const out = {};
5963
- for (const line of readFileSync17(CONFIG_PATH3, "utf-8").split("\n")) {
5889
+ for (const line of readFileSync16(CONFIG_PATH3, "utf-8").split("\n")) {
5964
5890
  const t = line.trim();
5965
5891
  if (!t || t.startsWith("#")) continue;
5966
5892
  const eq = t.indexOf("=");
@@ -6260,15 +6186,15 @@ Will push secrets to ${selected.length} repo(s):`);
6260
6186
  console.log(`Secrets pushed: ${SECRET_NAMES.CLAUDE_OAUTH}, ${SECRET_NAMES.SYNKRO_API_KEY}`);
6261
6187
  console.log("Open a PR on any selected repo to trigger your first Synkro scan.");
6262
6188
  }
6263
- var SYNKRO_DIR10, CONFIG_PATH3;
6189
+ var SYNKRO_DIR9, CONFIG_PATH3;
6264
6190
  var init_setupGithub = __esm({
6265
6191
  "cli/commands/setupGithub.ts"() {
6266
6192
  "use strict";
6267
6193
  init_githubSetup();
6268
6194
  init_stub();
6269
6195
  init_setupToken();
6270
- SYNKRO_DIR10 = join17(homedir17(), ".synkro");
6271
- CONFIG_PATH3 = join17(SYNKRO_DIR10, "config.env");
6196
+ SYNKRO_DIR9 = join16(homedir16(), ".synkro");
6197
+ CONFIG_PATH3 = join16(SYNKRO_DIR9, "config.env");
6272
6198
  }
6273
6199
  });
6274
6200
 
@@ -6287,9 +6213,9 @@ __export(install_exports, {
6287
6213
  syncSkillFiles: () => syncSkillFiles,
6288
6214
  writeHookScripts: () => writeHookScripts
6289
6215
  });
6290
- import { existsSync as existsSync21, mkdirSync as mkdirSync13, writeFileSync as writeFileSync13, chmodSync as chmodSync3, readFileSync as readFileSync18, readdirSync as readdirSync3, unlinkSync as unlinkSync6, statSync as statSync2 } from "fs";
6291
- import { homedir as homedir18 } from "os";
6292
- import { join as join18 } from "path";
6216
+ import { existsSync as existsSync20, mkdirSync as mkdirSync12, writeFileSync as writeFileSync13, chmodSync as chmodSync3, readFileSync as readFileSync17, readdirSync as readdirSync3, unlinkSync as unlinkSync6, statSync as statSync2 } from "fs";
6217
+ import { homedir as homedir17 } from "os";
6218
+ import { join as join17 } from "path";
6293
6219
  import { execSync as execSync6 } from "child_process";
6294
6220
  import { createInterface as createInterface3 } from "readline";
6295
6221
  import { createHash as createHash2 } from "crypto";
@@ -6376,15 +6302,20 @@ async function promptCursorApiKey(opts) {
6376
6302
  console.log(" \u26A0 Skipped \u2014 Cursor workers will be idle. Re-run install or pass --cursor-api-key=\u2026 later.");
6377
6303
  }
6378
6304
  }
6379
- async function promptDeployLocation() {
6380
- if (!process.stdin.isTTY) return "local";
6305
+ async function promptDeployLocation(current = "local") {
6306
+ if (!process.stdin.isTTY) return current;
6307
+ const other = current === "cloud" ? "local" : "cloud";
6381
6308
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
6382
6309
  return new Promise((resolve4) => {
6383
6310
  rl.question(
6384
- "Where should Synkro run?\n local \u2014 a grading container on this machine (Docker) (default)\n cloud \u2014 a private container hosted by Synkro (Cloudflare)\nBoth use your own Claude key. Choose [local] / cloud: ",
6311
+ `Where should Synkro run?
6312
+ local \u2014 a grading container on this machine (Docker)
6313
+ cloud \u2014 a private container hosted by Synkro (Cloudflare)
6314
+ Both use your own Claude key. Choose [${current}] / ${other}: `,
6385
6315
  (answer) => {
6386
6316
  rl.close();
6387
- resolve4(answer.trim().toLowerCase() === "cloud" ? "cloud" : "local");
6317
+ const a = answer.trim().toLowerCase();
6318
+ resolve4(a === "cloud" ? "cloud" : a === "local" ? "local" : current);
6388
6319
  }
6389
6320
  );
6390
6321
  });
@@ -6419,52 +6350,38 @@ coding patterns and the dashboard shows full history. (Y/n) `
6419
6350
  rl.close();
6420
6351
  }
6421
6352
  }
6422
- async function promptCodexCloudSetup() {
6423
- if (!process.stdin.isTTY) return false;
6424
- const rl = createInterface3({ input: process.stdin, output: process.stdout });
6425
- return new Promise((resolve4) => {
6426
- rl.question(
6427
- "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) ",
6428
- (answer) => {
6429
- rl.close();
6430
- const a = answer.trim().toLowerCase();
6431
- resolve4(a === "y" || a === "yes");
6432
- }
6433
- );
6434
- });
6435
- }
6436
6353
  function ensureSynkroDir() {
6437
- mkdirSync13(SYNKRO_DIR11, { recursive: true });
6438
- mkdirSync13(HOOKS_DIR, { recursive: true });
6439
- mkdirSync13(BIN_DIR, { recursive: true });
6440
- mkdirSync13(OFFSETS_DIR, { recursive: true });
6441
- mkdirSync13(join18(SYNKRO_DIR11, "sessions"), { recursive: true });
6354
+ mkdirSync12(SYNKRO_DIR10, { recursive: true });
6355
+ mkdirSync12(HOOKS_DIR, { recursive: true });
6356
+ mkdirSync12(BIN_DIR, { recursive: true });
6357
+ mkdirSync12(OFFSETS_DIR, { recursive: true });
6358
+ mkdirSync12(join17(SYNKRO_DIR10, "sessions"), { recursive: true });
6442
6359
  }
6443
6360
  function writeHookScripts() {
6444
- const installExtractCorePath = join18(HOOKS_DIR, "installExtractCore.ts");
6445
- const bashScriptPath = join18(HOOKS_DIR, "cc-bash-judge.ts");
6446
- const skillJudgeScriptPath = join18(HOOKS_DIR, "cc-skill-judge.ts");
6447
- const cursorSkillJudgePath = join18(HOOKS_DIR, "cursor-skill-judge.ts");
6448
- const bashFollowupScriptPath = join18(HOOKS_DIR, "cc-bash-followup.ts");
6449
- const editPrecheckScriptPath = join18(HOOKS_DIR, "cc-edit-precheck.ts");
6450
- const cwePrecheckScriptPath = join18(HOOKS_DIR, "cc-cwe-precheck.ts");
6451
- const cvePrecheckScriptPath = join18(HOOKS_DIR, "cc-cve-precheck.ts");
6452
- const planJudgeScriptPath = join18(HOOKS_DIR, "cc-plan-judge.ts");
6453
- const agentJudgeScriptPath = join18(HOOKS_DIR, "cc-agent-judge.ts");
6454
- const stopSummaryScriptPath = join18(HOOKS_DIR, "cc-stop-summary.ts");
6455
- const sessionStartScriptPath = join18(HOOKS_DIR, "cc-session-start.ts");
6456
- const transcriptSyncScriptPath = join18(HOOKS_DIR, "cc-transcript-sync.ts");
6457
- const userPromptSubmitScriptPath = join18(HOOKS_DIR, "cc-user-prompt-submit.ts");
6458
- const commonScriptPath = join18(HOOKS_DIR, "_synkro-common.ts");
6459
- const commonBashScriptPath = join18(HOOKS_DIR, "_synkro-common.sh");
6460
- const installScanScriptPath = join18(HOOKS_DIR, "cc-install-scan.ts");
6461
- const cursorBashJudgePath = join18(HOOKS_DIR, "cursor-bash-judge.ts");
6462
- const cursorEditCapturePath = join18(HOOKS_DIR, "cursor-edit-capture.ts");
6463
- const cursorAgentCapturePath = join18(HOOKS_DIR, "cursor-agent-capture.ts");
6464
- const mcpStdioProxyPath = join18(HOOKS_DIR, "mcp-stdio-proxy.ts");
6465
- const taskActivateIntentScriptPath = join18(HOOKS_DIR, "cc-task-activate-intent.ts");
6466
- const mcpGateScriptPath = join18(HOOKS_DIR, "cc-mcp-gate.ts");
6467
- const stubCommonPath = join18(HOOKS_DIR, "_synkro-stub-common.ts");
6361
+ const installExtractCorePath = join17(HOOKS_DIR, "installExtractCore.ts");
6362
+ const bashScriptPath = join17(HOOKS_DIR, "cc-bash-judge.ts");
6363
+ const skillJudgeScriptPath = join17(HOOKS_DIR, "cc-skill-judge.ts");
6364
+ const cursorSkillJudgePath = join17(HOOKS_DIR, "cursor-skill-judge.ts");
6365
+ const bashFollowupScriptPath = join17(HOOKS_DIR, "cc-bash-followup.ts");
6366
+ const editPrecheckScriptPath = join17(HOOKS_DIR, "cc-edit-precheck.ts");
6367
+ const cwePrecheckScriptPath = join17(HOOKS_DIR, "cc-cwe-precheck.ts");
6368
+ const cvePrecheckScriptPath = join17(HOOKS_DIR, "cc-cve-precheck.ts");
6369
+ const planJudgeScriptPath = join17(HOOKS_DIR, "cc-plan-judge.ts");
6370
+ const agentJudgeScriptPath = join17(HOOKS_DIR, "cc-agent-judge.ts");
6371
+ const stopSummaryScriptPath = join17(HOOKS_DIR, "cc-stop-summary.ts");
6372
+ const sessionStartScriptPath = join17(HOOKS_DIR, "cc-session-start.ts");
6373
+ const transcriptSyncScriptPath = join17(HOOKS_DIR, "cc-transcript-sync.ts");
6374
+ const userPromptSubmitScriptPath = join17(HOOKS_DIR, "cc-user-prompt-submit.ts");
6375
+ const commonScriptPath = join17(HOOKS_DIR, "_synkro-common.ts");
6376
+ const commonBashScriptPath = join17(HOOKS_DIR, "_synkro-common.sh");
6377
+ const installScanScriptPath = join17(HOOKS_DIR, "cc-install-scan.ts");
6378
+ const cursorBashJudgePath = join17(HOOKS_DIR, "cursor-bash-judge.ts");
6379
+ const cursorEditCapturePath = join17(HOOKS_DIR, "cursor-edit-capture.ts");
6380
+ const cursorAgentCapturePath = join17(HOOKS_DIR, "cursor-agent-capture.ts");
6381
+ const mcpStdioProxyPath = join17(HOOKS_DIR, "mcp-stdio-proxy.ts");
6382
+ const taskActivateIntentScriptPath = join17(HOOKS_DIR, "cc-task-activate-intent.ts");
6383
+ const mcpGateScriptPath = join17(HOOKS_DIR, "cc-mcp-gate.ts");
6384
+ const stubCommonPath = join17(HOOKS_DIR, "_synkro-stub-common.ts");
6468
6385
  const stubFiles = [
6469
6386
  [stubCommonPath, STUB_COMMON_TS],
6470
6387
  [bashScriptPath, STUB_BASH_JUDGE_TS],
@@ -6495,7 +6412,7 @@ function writeHookScripts() {
6495
6412
  chmodSync3(mcpStdioProxyPath, 493);
6496
6413
  for (const stale of ["_synkro-common.ts", "_synkro-common.sh", "installExtractCore.ts"]) {
6497
6414
  try {
6498
- unlinkSync6(join18(HOOKS_DIR, stale));
6415
+ unlinkSync6(join17(HOOKS_DIR, stale));
6499
6416
  } catch {
6500
6417
  }
6501
6418
  }
@@ -6530,11 +6447,11 @@ function shellQuoteSingle2(value) {
6530
6447
  }
6531
6448
  function resolveSynkroBundle() {
6532
6449
  const scriptPath = process.argv[1];
6533
- if (scriptPath && existsSync21(scriptPath)) return scriptPath;
6450
+ if (scriptPath && existsSync20(scriptPath)) return scriptPath;
6534
6451
  return null;
6535
6452
  }
6536
6453
  function writeConfigEnv(opts) {
6537
- const credsPath = join18(SYNKRO_DIR11, "credentials.json");
6454
+ const credsPath = join17(SYNKRO_DIR10, "credentials.json");
6538
6455
  const safeGateway = sanitizeConfigValue(opts.gatewayUrl);
6539
6456
  const safeUserId = sanitizeConfigValue(opts.userId);
6540
6457
  const safeOrgId = sanitizeConfigValue(opts.orgId);
@@ -6550,7 +6467,7 @@ function writeConfigEnv(opts) {
6550
6467
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
6551
6468
  `SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
6552
6469
  `SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
6553
- `SYNKRO_VERSION=${shellQuoteSingle2("1.7.63")}`
6470
+ `SYNKRO_VERSION=${shellQuoteSingle2("1.7.65")}`
6554
6471
  ];
6555
6472
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
6556
6473
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
@@ -6583,7 +6500,7 @@ async function getOrMintCloudToken(gatewayUrl) {
6583
6500
  assertGatewayAllowed(gatewayUrl);
6584
6501
  let stored = "";
6585
6502
  try {
6586
- stored = readFileSync18(CLOUD_JWT_PATH, "utf-8").trim();
6503
+ stored = readFileSync17(CLOUD_JWT_PATH, "utf-8").trim();
6587
6504
  } catch {
6588
6505
  }
6589
6506
  if (stored && !jwtExpired(stored)) return stored;
@@ -6642,7 +6559,7 @@ async function provisionCloudContainer(opts) {
6642
6559
  let cursorApiKey = "";
6643
6560
  if (cursorTotal > 0) {
6644
6561
  try {
6645
- cursorApiKey = readFileSync18(join18(SYNKRO_DIR11, "cursor-creds", "api-key"), "utf-8").trim();
6562
+ cursorApiKey = readFileSync17(join17(SYNKRO_DIR10, "cursor-creds", "api-key"), "utf-8").trim();
6646
6563
  } catch {
6647
6564
  }
6648
6565
  }
@@ -6799,8 +6716,8 @@ async function verifyCloudGrader(jwt2) {
6799
6716
  }
6800
6717
  function readPersistedDeployLocation() {
6801
6718
  try {
6802
- if (existsSync21(CONFIG_PATH4)) {
6803
- const m = readFileSync18(CONFIG_PATH4, "utf-8").match(/^SYNKRO_DEPLOY_LOCATION='([^']*)'/m);
6719
+ if (existsSync20(CONFIG_PATH4)) {
6720
+ const m = readFileSync17(CONFIG_PATH4, "utf-8").match(/^SYNKRO_DEPLOY_LOCATION='([^']*)'/m);
6804
6721
  if (m?.[1] === "cloud") return "cloud";
6805
6722
  }
6806
6723
  } catch {
@@ -6808,8 +6725,8 @@ function readPersistedDeployLocation() {
6808
6725
  return "local";
6809
6726
  }
6810
6727
  function updateConfigEnvLocation(location) {
6811
- if (!existsSync21(CONFIG_PATH4)) return;
6812
- let env = readFileSync18(CONFIG_PATH4, "utf-8");
6728
+ if (!existsSync20(CONFIG_PATH4)) return;
6729
+ let env = readFileSync17(CONFIG_PATH4, "utf-8");
6813
6730
  const set = (k, v) => {
6814
6731
  const re = new RegExp(`^${k}=.*$`, "m");
6815
6732
  const line = `${k}='${v}'`;
@@ -6824,7 +6741,7 @@ async function applyMcpConfig(opts) {
6824
6741
  if (!opts.hasClaudeCode && !opts.hasCursor) return;
6825
6742
  let mcpJwt = "";
6826
6743
  try {
6827
- mcpJwt = readFileSync18(join18(SYNKRO_DIR11, ".mcp-jwt"), "utf-8").trim();
6744
+ mcpJwt = readFileSync17(join17(SYNKRO_DIR10, ".mcp-jwt"), "utf-8").trim();
6828
6745
  } catch {
6829
6746
  }
6830
6747
  if (!mcpJwt) {
@@ -6859,13 +6776,15 @@ async function applyMcpConfig(opts) {
6859
6776
  }
6860
6777
  async function reconcileDeployLocation() {
6861
6778
  const sf = readFullSynkroFile();
6862
- const desired = sf?.grader.location === "cloud" ? "cloud" : "local";
6779
+ const tomlLoc = sf?.grader.location === "cloud" ? "cloud" : "local";
6863
6780
  const current = readPersistedDeployLocation();
6781
+ const desired = await promptDeployLocation(current || tomlLoc);
6782
+ if (desired !== tomlLoc) updateSynkroTomlLocation(desired);
6864
6783
  if (desired === current) return { location: desired, changed: false };
6865
6784
  const hasClaudeCode = sf ? sf.harness.includes("claude-code") : true;
6866
6785
  const hasCursor = sf ? sf.harness.includes("cursor") : false;
6867
6786
  console.log(`
6868
- synkro.toml: deploy location changed ${current} \u2192 ${desired}
6787
+ Deploy location: ${current || "(none)"} \u2192 ${desired}
6869
6788
  `);
6870
6789
  if (!isAuthenticated()) {
6871
6790
  console.log(" Opening browser for Synkro auth...");
@@ -6942,8 +6861,8 @@ function resolveDeploymentMode() {
6942
6861
  const envOverride = process.env.SYNKRO_DEPLOYMENT_MODE?.toLowerCase();
6943
6862
  if (envOverride === "bare-host" || envOverride === "docker") return envOverride;
6944
6863
  try {
6945
- if (existsSync21(CONFIG_PATH4)) {
6946
- const m = readFileSync18(CONFIG_PATH4, "utf-8").match(/^SYNKRO_DEPLOYMENT_MODE='([^']*)'/m);
6864
+ if (existsSync20(CONFIG_PATH4)) {
6865
+ const m = readFileSync17(CONFIG_PATH4, "utf-8").match(/^SYNKRO_DEPLOYMENT_MODE='([^']*)'/m);
6947
6866
  const val = m?.[1]?.toLowerCase();
6948
6867
  if (val === "bare-host" || val === "docker") return val;
6949
6868
  }
@@ -6970,16 +6889,16 @@ function collectLocalMetadata(includeClaudeCode = true) {
6970
6889
  meta.cc_version = execSync6("claude --version", { encoding: "utf-8", timeout: 5e3 }).trim().split("\n")[0];
6971
6890
  } catch {
6972
6891
  }
6973
- const claudeDir = join18(homedir18(), ".claude");
6892
+ const claudeDir = join17(homedir17(), ".claude");
6974
6893
  try {
6975
- const settings = JSON.parse(readFileSync18(join18(claudeDir, "settings.json"), "utf-8"));
6894
+ const settings = JSON.parse(readFileSync17(join17(claudeDir, "settings.json"), "utf-8"));
6976
6895
  const plugins = Object.keys(settings.enabledPlugins ?? {}).filter((k) => settings.enabledPlugins[k]);
6977
6896
  if (plugins.length) meta.enabled_plugins = plugins;
6978
6897
  if (settings.permissions?.defaultMode) meta.permissions_mode = settings.permissions.defaultMode;
6979
6898
  } catch {
6980
6899
  }
6981
6900
  try {
6982
- const mcpCache = JSON.parse(readFileSync18(join18(claudeDir, "mcp-needs-auth-cache.json"), "utf-8"));
6901
+ const mcpCache = JSON.parse(readFileSync17(join17(claudeDir, "mcp-needs-auth-cache.json"), "utf-8"));
6983
6902
  const mcpNames = Object.keys(mcpCache);
6984
6903
  if (mcpNames.length) meta.mcp_servers = mcpNames;
6985
6904
  } catch {
@@ -6991,10 +6910,10 @@ function collectLocalMetadata(includeClaudeCode = true) {
6991
6910
  } catch {
6992
6911
  }
6993
6912
  try {
6994
- const sessionsDir = join18(claudeDir, "sessions");
6913
+ const sessionsDir = join17(claudeDir, "sessions");
6995
6914
  const files = readdirSync3(sessionsDir).filter((f) => f.endsWith(".json")).slice(-5);
6996
6915
  for (const f of files) {
6997
- const s = JSON.parse(readFileSync18(join18(sessionsDir, f), "utf-8"));
6916
+ const s = JSON.parse(readFileSync17(join17(sessionsDir, f), "utf-8"));
6998
6917
  if (s.version) {
6999
6918
  meta.cc_version = meta.cc_version || s.version;
7000
6919
  break;
@@ -7127,9 +7046,11 @@ async function installCommand(opts = {}) {
7127
7046
  (a) => a.kind === "claude_code" && wantCC || a.kind === "cursor" && wantCursor
7128
7047
  );
7129
7048
  if (agents.length === 0 && detected.length > 0) agents = detected;
7130
- deployLocation = existingSynkro.grader.location === "cloud" ? "cloud" : "local";
7131
7049
  gradingMode = existingSynkro.grader.mode === "byok" ? "byok" : "local";
7050
+ const tomlLocation = existingSynkro.grader.location === "cloud" ? "cloud" : "local";
7051
+ deployLocation = await promptDeployLocation(tomlLocation);
7132
7052
  storageMode = deployLocation === "cloud" ? "cloud" : "local";
7053
+ if (deployLocation !== tomlLocation) updateSynkroTomlLocation(deployLocation);
7133
7054
  console.log(`Using .synkro config:`);
7134
7055
  for (const w of existingSynkro.warnings) console.warn(` \u26A0 ${w}`);
7135
7056
  console.log(` harness: ${existingSynkro.harness.join(", ")}`);
@@ -7190,7 +7111,7 @@ async function installCommand(opts = {}) {
7190
7111
  await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
7191
7112
  emit("install", {
7192
7113
  phase: "started",
7193
- cli_version_to: "1.7.63",
7114
+ cli_version_to: "1.7.65",
7194
7115
  agents_detected: agents.map((a) => a.kind),
7195
7116
  with_github: false,
7196
7117
  with_local_cc: false,
@@ -7202,9 +7123,9 @@ async function installCommand(opts = {}) {
7202
7123
  const scripts = writeHookScripts();
7203
7124
  console.log("Wrote hook scripts to ~/.synkro/hooks/\n");
7204
7125
  for (const mode of ["edit", "bash"]) {
7205
- const pidFile = join18(SYNKRO_DIR11, "daemon", mode, "daemon.pid");
7126
+ const pidFile = join17(SYNKRO_DIR10, "daemon", mode, "daemon.pid");
7206
7127
  try {
7207
- const pid = parseInt(readFileSync18(pidFile, "utf-8").trim(), 10);
7128
+ const pid = parseInt(readFileSync17(pidFile, "utf-8").trim(), 10);
7208
7129
  if (pid > 0) {
7209
7130
  process.kill(pid, "SIGTERM");
7210
7131
  console.log(`Stopped stale ${mode} grader daemon (pid ${pid})`);
@@ -7289,7 +7210,7 @@ async function installCommand(opts = {}) {
7289
7210
  if (mintResp.ok) {
7290
7211
  const minted = await mintResp.json();
7291
7212
  mcpJwt = minted.token;
7292
- writeFileSync13(join18(SYNKRO_DIR11, ".mcp-jwt"), mcpJwt + "\n", { mode: 384 });
7213
+ writeFileSync13(join17(SYNKRO_DIR10, ".mcp-jwt"), mcpJwt + "\n", { mode: 384 });
7293
7214
  } else {
7294
7215
  console.warn(" \u26A0 Could not mint MCP token \u2014 local server will reject requests until re-installed.");
7295
7216
  }
@@ -7317,7 +7238,7 @@ async function installCommand(opts = {}) {
7317
7238
  throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);
7318
7239
  }
7319
7240
  const minted = await mintResp.json();
7320
- writeFileSync13(join18(SYNKRO_DIR11, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
7241
+ writeFileSync13(join17(SYNKRO_DIR10, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
7321
7242
  const mcp = installMcpConfig({ gatewayUrl, bearerToken: minted.token });
7322
7243
  console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);
7323
7244
  console.log(` url: ${mcp.url}`);
@@ -7334,8 +7255,8 @@ async function installCommand(opts = {}) {
7334
7255
  if (hasCursor && !opts.noMcp) {
7335
7256
  try {
7336
7257
  if (useLocalMcp) {
7337
- const jwtPath = join18(SYNKRO_DIR11, ".mcp-jwt");
7338
- if (!existsSync21(jwtPath)) {
7258
+ const jwtPath = join17(SYNKRO_DIR10, ".mcp-jwt");
7259
+ if (!existsSync20(jwtPath)) {
7339
7260
  const mintResp = await fetch(`${gatewayUrl}/api/v1/cli/mcp-token`, {
7340
7261
  method: "POST",
7341
7262
  headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
@@ -7363,7 +7284,7 @@ async function installCommand(opts = {}) {
7363
7284
  throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);
7364
7285
  }
7365
7286
  const minted = await mintResp.json();
7366
- writeFileSync13(join18(SYNKRO_DIR11, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
7287
+ writeFileSync13(join17(SYNKRO_DIR10, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
7367
7288
  const mcp = installCursorMcpConfig({ gatewayUrl, bearerToken: minted.token });
7368
7289
  console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);
7369
7290
  console.log(` url: ${mcp.url}`);
@@ -7456,7 +7377,7 @@ async function installCommand(opts = {}) {
7456
7377
  const ready = await waitForContainerReady(6e4);
7457
7378
  if (ready) {
7458
7379
  console.log(" \u2713 container ready");
7459
- const mcpJwt = readFileSync18(join18(SYNKRO_DIR11, ".mcp-jwt"), "utf-8").trim();
7380
+ const mcpJwt = readFileSync17(join17(SYNKRO_DIR10, ".mcp-jwt"), "utf-8").trim();
7460
7381
  try {
7461
7382
  const ingestResp = await fetch(`http://127.0.0.1:${hostMcpPort}/api/ingest`, {
7462
7383
  method: "POST",
@@ -7491,11 +7412,6 @@ async function installCommand(opts = {}) {
7491
7412
  if (graderUsesCursor) await promptCursorApiKey(opts);
7492
7413
  await provisionCloudContainer({ gatewayUrl, jwt: token, orgId, userId, email, hasClaudeCode, hasCursor });
7493
7414
  cloudGradeOk = await verifyCloudGrader(token);
7494
- if (await promptCodexCloudSetup()) {
7495
- const { setupCodexCloud: setupCodexCloud2 } = await Promise.resolve().then(() => (init_codexCloudSetup(), codexCloudSetup_exports));
7496
- const res = await setupCodexCloud2(gatewayUrl, token, (m) => console.log(` ${m}`));
7497
- if (!res.ok) console.warn(` \u26A0 Codex cloud setup skipped: ${res.error}`);
7498
- }
7499
7415
  }
7500
7416
  if (transcriptConsent) {
7501
7417
  const repo = detectGitRepo2();
@@ -7504,7 +7420,7 @@ async function installCommand(opts = {}) {
7504
7420
  try {
7505
7421
  let mcpToken = "";
7506
7422
  try {
7507
- mcpToken = readFileSync18(join18(SYNKRO_DIR11, ".mcp-jwt"), "utf-8").trim();
7423
+ mcpToken = readFileSync17(join17(SYNKRO_DIR10, ".mcp-jwt"), "utf-8").trim();
7508
7424
  } catch {
7509
7425
  }
7510
7426
  if (mcpToken) {
@@ -7640,8 +7556,8 @@ function writeSynkroFileIfMissing(opts) {
7640
7556
  try {
7641
7557
  const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
7642
7558
  if (!root) return;
7643
- if (root === homedir18()) return;
7644
- const fp = join18(root, "synkro.toml");
7559
+ if (root === homedir17()) return;
7560
+ const fp = join17(root, "synkro.toml");
7645
7561
  let hasFile = false;
7646
7562
  try {
7647
7563
  hasFile = statSync2(fp).isFile();
@@ -7682,13 +7598,35 @@ function writeSynkroFileIfMissing(opts) {
7682
7598
  } catch {
7683
7599
  }
7684
7600
  }
7601
+ function updateSynkroTomlLocation(location) {
7602
+ try {
7603
+ const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
7604
+ if (!root || root === homedir17()) return;
7605
+ const fp = join17(root, "synkro.toml");
7606
+ let txt = "";
7607
+ try {
7608
+ if (!statSync2(fp).isFile()) return;
7609
+ txt = readFileSync17(fp, "utf-8");
7610
+ } catch {
7611
+ return;
7612
+ }
7613
+ const re = /^(\s*location\s*=\s*).*$/m;
7614
+ if (!re.test(txt)) return;
7615
+ const next = txt.replace(re, `$1"${location}"`);
7616
+ if (next !== txt) {
7617
+ writeFileSync13(fp, next, "utf-8");
7618
+ console.log(` synkro.toml: [grader] location = "${location}"`);
7619
+ }
7620
+ } catch {
7621
+ }
7622
+ }
7685
7623
  function readFullSynkroFile() {
7686
7624
  try {
7687
7625
  const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
7688
7626
  if (!root) return null;
7689
- const fp = join18(root, "synkro.toml");
7690
- if (!existsSync21(fp)) return null;
7691
- const parsed = parseSynkroToml2(readFileSync18(fp, "utf-8"));
7627
+ const fp = join17(root, "synkro.toml");
7628
+ if (!existsSync20(fp)) return null;
7629
+ const parsed = parseSynkroToml2(readFileSync17(fp, "utf-8"));
7692
7630
  const valid = ["claude-code", "cursor"];
7693
7631
  const harness = Array.isArray(parsed.harness) ? parsed.harness.filter((h) => valid.includes(h)) : ["claude-code", "cursor"];
7694
7632
  const resolved = resolveGraderPool(parsed);
@@ -7726,7 +7664,7 @@ function reconcileHarness() {
7726
7664
  console.log(`synkro.toml: harness=[${sf.harness.join(", ")}] pool=${poolLabel(sf.grader.pool)} mode=${sf.grader.mode}`);
7727
7665
  const scripts = writeHookScripts();
7728
7666
  console.log("Wrote hook scripts to ~/.synkro/hooks/");
7729
- const ccSettings = join18(homedir18(), ".claude", "settings.json");
7667
+ const ccSettings = join17(homedir17(), ".claude", "settings.json");
7730
7668
  if (wantCC) {
7731
7669
  installCCHooks(ccSettings, {
7732
7670
  bashJudgeScriptPath: scripts.bashScript,
@@ -7747,7 +7685,7 @@ function reconcileHarness() {
7747
7685
  });
7748
7686
  console.log(" \u2713 Claude Code hooks registered");
7749
7687
  try {
7750
- const mcpJwt = readFileSync18(join18(SYNKRO_DIR11, ".mcp-jwt"), "utf-8").trim();
7688
+ const mcpJwt = readFileSync17(join17(SYNKRO_DIR10, ".mcp-jwt"), "utf-8").trim();
7751
7689
  if (mcpJwt) {
7752
7690
  installMcpConfig({ gatewayUrl: "", bearerToken: mcpJwt, local: true });
7753
7691
  console.log(" \u2713 Claude Code MCP registered");
@@ -7759,7 +7697,7 @@ function reconcileHarness() {
7759
7697
  if (uninstallMcpConfig()) console.log(" \u2717 Claude Code MCP removed");
7760
7698
  if (uninstallClaudeDesktopMcpConfig()) console.log(" \u2717 Claude Desktop MCP removed");
7761
7699
  }
7762
- const cursorHooks = join18(homedir18(), ".cursor", "hooks.json");
7700
+ const cursorHooks = join17(homedir17(), ".cursor", "hooks.json");
7763
7701
  if (wantCursor) {
7764
7702
  installCursorHooks(cursorHooks, {
7765
7703
  bashJudgeScriptPath: scripts.cursorBashJudgeScript,
@@ -7842,7 +7780,7 @@ async function syncSkillFiles() {
7842
7780
  if (resolved.length === 0) return;
7843
7781
  const mcpPort = process.env.SYNKRO_MCP_PORT || "18931";
7844
7782
  const tasks = resolved.map((fp) => {
7845
- const content = readFileSync18(fp, "utf-8");
7783
+ const content = readFileSync17(fp, "utf-8");
7846
7784
  const source = `skill:${fp.split("/").pop()}`;
7847
7785
  if (!content.trim()) {
7848
7786
  console.log(` \u2298 skill ${source}: empty file, skipped`);
@@ -7863,15 +7801,15 @@ function discoverSkillFiles(repoRoot, excludeHashes, ingestedHashes, ingestedNam
7863
7801
  const roots = [];
7864
7802
  const add = (p) => {
7865
7803
  try {
7866
- if (p && existsSync21(p) && statSync2(p).isDirectory()) roots.push(p);
7804
+ if (p && existsSync20(p) && statSync2(p).isDirectory()) roots.push(p);
7867
7805
  } catch {
7868
7806
  }
7869
7807
  };
7870
- add(join18(homedir18(), ".claude", "skills"));
7871
- add(join18(homedir18(), ".agents", "skills"));
7808
+ add(join17(homedir17(), ".claude", "skills"));
7809
+ add(join17(homedir17(), ".agents", "skills"));
7872
7810
  if (repoRoot) {
7873
- add(join18(repoRoot, ".claude", "skills"));
7874
- add(join18(repoRoot, ".agents", "skills"));
7811
+ add(join17(repoRoot, ".claude", "skills"));
7812
+ add(join17(repoRoot, ".agents", "skills"));
7875
7813
  }
7876
7814
  const out = [];
7877
7815
  const seen = /* @__PURE__ */ new Set();
@@ -7882,7 +7820,7 @@ function discoverSkillFiles(repoRoot, excludeHashes, ingestedHashes, ingestedNam
7882
7820
  try {
7883
7821
  const st = statSync2(file);
7884
7822
  if (!st.isFile() || st.size > 2e5) return;
7885
- content = readFileSync18(file, "utf-8");
7823
+ content = readFileSync17(file, "utf-8");
7886
7824
  } catch {
7887
7825
  return;
7888
7826
  }
@@ -7902,7 +7840,7 @@ function discoverSkillFiles(repoRoot, excludeHashes, ingestedHashes, ingestedNam
7902
7840
  }
7903
7841
  for (const entry of entries) {
7904
7842
  if (entry.startsWith(".")) continue;
7905
- const full = join18(root, entry);
7843
+ const full = join17(root, entry);
7906
7844
  let st;
7907
7845
  try {
7908
7846
  st = statSync2(full);
@@ -7910,8 +7848,8 @@ function discoverSkillFiles(repoRoot, excludeHashes, ingestedHashes, ingestedNam
7910
7848
  continue;
7911
7849
  }
7912
7850
  if (st.isDirectory()) {
7913
- const skillMd = join18(full, "SKILL.md");
7914
- if (existsSync21(skillMd)) consider(skillMd, entry);
7851
+ const skillMd = join17(full, "SKILL.md");
7852
+ if (existsSync20(skillMd)) consider(skillMd, entry);
7915
7853
  } else if (/\.mdx?$/i.test(entry) && entry.toUpperCase() !== "README.MD") {
7916
7854
  consider(full, entry.replace(/\.mdx?$/i, ""));
7917
7855
  }
@@ -7960,7 +7898,7 @@ async function discoverAndIngestSkills() {
7960
7898
  if (sf?.skills?.length) {
7961
7899
  for (const fp of resolveSkillPaths(sf.skills, sf._repoRoot)) {
7962
7900
  try {
7963
- excludeHashes.add(createHash2("sha256").update(readFileSync18(fp, "utf-8")).digest("hex"));
7901
+ excludeHashes.add(createHash2("sha256").update(readFileSync17(fp, "utf-8")).digest("hex"));
7964
7902
  } catch {
7965
7903
  }
7966
7904
  }
@@ -7991,7 +7929,7 @@ async function discoverAndIngestSkills() {
7991
7929
  const setHash = discoverySetHash(selectable);
7992
7930
  let prev = "";
7993
7931
  try {
7994
- prev = readFileSync18(SKILLS_DISCOVERED_PATH, "utf-8").trim();
7932
+ prev = readFileSync17(SKILLS_DISCOVERED_PATH, "utf-8").trim();
7995
7933
  } catch {
7996
7934
  }
7997
7935
  if (prev === setHash) return;
@@ -8031,17 +7969,17 @@ function detectGitRepo2() {
8031
7969
  function getClaudeProjectsFolder() {
8032
7970
  const cwd = process.cwd();
8033
7971
  const sanitized = "-" + cwd.replace(/\//g, "-");
8034
- const projectsDir = join18(homedir18(), ".claude", "projects", sanitized);
8035
- return existsSync21(projectsDir) ? projectsDir : null;
7972
+ const projectsDir = join17(homedir17(), ".claude", "projects", sanitized);
7973
+ return existsSync20(projectsDir) ? projectsDir : null;
8036
7974
  }
8037
7975
  function extractSessionInsights(projectsDir) {
8038
7976
  const insights = [];
8039
7977
  const files = readdirSync3(projectsDir).filter((f) => f.endsWith(".jsonl"));
8040
7978
  for (const file of files) {
8041
7979
  const sessionId = file.replace(".jsonl", "");
8042
- const filePath = join18(projectsDir, file);
7980
+ const filePath = join17(projectsDir, file);
8043
7981
  try {
8044
- const content = readFileSync18(filePath, "utf-8");
7982
+ const content = readFileSync17(filePath, "utf-8");
8045
7983
  const lines = content.split("\n").filter(Boolean);
8046
7984
  for (let i = 0; i < lines.length; i++) {
8047
7985
  try {
@@ -8120,14 +8058,14 @@ function cursorProjectSlug(workspaceRoot) {
8120
8058
  return workspaceRoot.replace(/^[/]+/, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "");
8121
8059
  }
8122
8060
  function getCursorTranscriptsDir() {
8123
- const dir = join18(homedir18(), ".cursor", "projects", cursorProjectSlug(process.cwd()), "agent-transcripts");
8124
- return existsSync21(dir) ? dir : null;
8061
+ const dir = join17(homedir17(), ".cursor", "projects", cursorProjectSlug(process.cwd()), "agent-transcripts");
8062
+ return existsSync20(dir) ? dir : null;
8125
8063
  }
8126
8064
  function isSafeConvId(id) {
8127
8065
  return /^[A-Za-z0-9_-]+$/.test(id);
8128
8066
  }
8129
8067
  function parseCursorTranscriptFile(filePath) {
8130
- const content = readFileSync18(filePath, "utf-8");
8068
+ const content = readFileSync17(filePath, "utf-8");
8131
8069
  const lines = content.split("\n").filter(Boolean);
8132
8070
  const messages = [];
8133
8071
  for (let i = 0; i < lines.length; i++) {
@@ -8159,8 +8097,8 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
8159
8097
  for (let i = 0; i < convDirs.length; i++) {
8160
8098
  const convId = convDirs[i];
8161
8099
  if (!isSafeConvId(convId)) continue;
8162
- const filePath = join18(dir, convId, `${convId}.jsonl`);
8163
- if (!existsSync21(filePath)) continue;
8100
+ const filePath = join17(dir, convId, `${convId}.jsonl`);
8101
+ if (!existsSync20(filePath)) continue;
8164
8102
  try {
8165
8103
  const all = parseCursorTranscriptFile(filePath);
8166
8104
  const messages = all.length > 500 ? all.slice(-500) : all;
@@ -8182,8 +8120,8 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
8182
8120
  process.stdout.write(`\r Progress: ${i + 1}/${convDirs.length} sessions (${totalMessages} messages embedded) `);
8183
8121
  }
8184
8122
  try {
8185
- const lc = readFileSync18(filePath, "utf-8").split("\n").filter(Boolean).length;
8186
- writeFileSync13(join18(OFFSETS_DIR, convId), String(lc), "utf-8");
8123
+ const lc = readFileSync17(filePath, "utf-8").split("\n").filter(Boolean).length;
8124
+ writeFileSync13(join17(OFFSETS_DIR, convId), String(lc), "utf-8");
8187
8125
  } catch {
8188
8126
  }
8189
8127
  }
@@ -8191,7 +8129,7 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
8191
8129
  return { sessions: totalSessions, messages: totalMessages };
8192
8130
  }
8193
8131
  function parseTranscriptFile(filePath) {
8194
- const content = readFileSync18(filePath, "utf-8");
8132
+ const content = readFileSync17(filePath, "utf-8");
8195
8133
  const lines = content.split("\n").filter(Boolean);
8196
8134
  const messages = [];
8197
8135
  for (let i = 0; i < lines.length; i++) {
@@ -8239,7 +8177,7 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
8239
8177
  for (let i = 0; i < files.length; i++) {
8240
8178
  const file = files[i];
8241
8179
  const sessionId = file.replace(".jsonl", "");
8242
- const filePath = join18(projectsDir, file);
8180
+ const filePath = join17(projectsDir, file);
8243
8181
  try {
8244
8182
  const allMessages = parseTranscriptFile(filePath);
8245
8183
  const messages = allMessages.length > 500 ? allMessages.slice(-500) : allMessages;
@@ -8261,9 +8199,9 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
8261
8199
  process.stdout.write(`\r Progress: ${i + 1}/${files.length} sessions (${totalMessages} messages embedded) `);
8262
8200
  }
8263
8201
  try {
8264
- const content = readFileSync18(join18(projectsDir, file), "utf-8");
8202
+ const content = readFileSync17(join17(projectsDir, file), "utf-8");
8265
8203
  const lineCount = content.split("\n").filter(Boolean).length;
8266
- writeFileSync13(join18(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
8204
+ writeFileSync13(join17(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
8267
8205
  } catch {
8268
8206
  }
8269
8207
  }
@@ -8284,7 +8222,7 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
8284
8222
  const sessions = [];
8285
8223
  for (const file of batch) {
8286
8224
  const sessionId = file.replace(".jsonl", "");
8287
- const filePath = join18(projectsDir, file);
8225
+ const filePath = join17(projectsDir, file);
8288
8226
  try {
8289
8227
  const allMessages = parseTranscriptFile(filePath);
8290
8228
  const messages = allMessages.length > maxMessagesPerSession ? allMessages.slice(-maxMessagesPerSession) : allMessages;
@@ -8313,18 +8251,18 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
8313
8251
  }
8314
8252
  for (const file of batch) {
8315
8253
  const sessionId = file.replace(".jsonl", "");
8316
- const filePath = join18(projectsDir, file);
8254
+ const filePath = join17(projectsDir, file);
8317
8255
  try {
8318
- const content = readFileSync18(filePath, "utf-8");
8256
+ const content = readFileSync17(filePath, "utf-8");
8319
8257
  const lineCount = content.split("\n").filter(Boolean).length;
8320
- writeFileSync13(join18(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
8258
+ writeFileSync13(join17(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
8321
8259
  } catch {
8322
8260
  }
8323
8261
  }
8324
8262
  }
8325
8263
  return { sessions: totalSessions, messages: totalMessages };
8326
8264
  }
8327
- var SYNKRO_DIR11, HOOKS_DIR, BIN_DIR, CONFIG_PATH4, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, CLOUD_JWT_PATH, SKILLS_DISCOVERED_PATH;
8265
+ var SYNKRO_DIR10, HOOKS_DIR, BIN_DIR, CONFIG_PATH4, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, CLOUD_JWT_PATH, SKILLS_DISCOVERED_PATH;
8328
8266
  var init_install = __esm({
8329
8267
  "cli/commands/install.ts"() {
8330
8268
  "use strict";
@@ -8342,10 +8280,10 @@ var init_install = __esm({
8342
8280
  init_claudeDesktopTap();
8343
8281
  init_dockerInstall();
8344
8282
  init_setupToken();
8345
- SYNKRO_DIR11 = join18(homedir18(), ".synkro");
8346
- HOOKS_DIR = join18(SYNKRO_DIR11, "hooks");
8347
- BIN_DIR = join18(SYNKRO_DIR11, "bin");
8348
- CONFIG_PATH4 = join18(SYNKRO_DIR11, "config.env");
8283
+ SYNKRO_DIR10 = join17(homedir17(), ".synkro");
8284
+ HOOKS_DIR = join17(SYNKRO_DIR10, "hooks");
8285
+ BIN_DIR = join17(SYNKRO_DIR10, "bin");
8286
+ CONFIG_PATH4 = join17(SYNKRO_DIR10, "config.env");
8349
8287
  MCP_STDIO_PROXY_SRC = `#!/usr/bin/env bun
8350
8288
  import { readFileSync } from 'node:fs';
8351
8289
  import { homedir } from 'node:os';
@@ -8456,21 +8394,21 @@ rl.on('line', async (line) => {
8456
8394
  }
8457
8395
  });
8458
8396
  `;
8459
- OFFSETS_DIR = join18(SYNKRO_DIR11, ".transcript-offsets");
8460
- CLOUD_JWT_PATH = join18(SYNKRO_DIR11, ".cloud-jwt");
8461
- SKILLS_DISCOVERED_PATH = join18(SYNKRO_DIR11, ".skills-discovered");
8397
+ OFFSETS_DIR = join17(SYNKRO_DIR10, ".transcript-offsets");
8398
+ CLOUD_JWT_PATH = join17(SYNKRO_DIR10, ".cloud-jwt");
8399
+ SKILLS_DISCOVERED_PATH = join17(SYNKRO_DIR10, ".skills-discovered");
8462
8400
  }
8463
8401
  });
8464
8402
 
8465
8403
  // cli/local-cc/install.ts
8466
- import { existsSync as existsSync22, mkdirSync as mkdirSync14, writeFileSync as writeFileSync14, readFileSync as readFileSync19, chmodSync as chmodSync4, copyFileSync as copyFileSync2, renameSync as renameSync6, unlinkSync as unlinkSync7, openSync as openSync2, fsyncSync, closeSync as closeSync2 } from "fs";
8467
- import { join as join19 } from "path";
8468
- import { homedir as homedir19 } from "os";
8469
- import { spawnSync as spawnSync6 } from "child_process";
8404
+ import { existsSync as existsSync21, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14, readFileSync as readFileSync18, chmodSync as chmodSync4, copyFileSync as copyFileSync2, renameSync as renameSync6, unlinkSync as unlinkSync7, openSync as openSync2, fsyncSync, closeSync as closeSync2 } from "fs";
8405
+ import { join as join18 } from "path";
8406
+ import { homedir as homedir18 } from "os";
8407
+ import { spawnSync as spawnSync5 } from "child_process";
8470
8408
  function writePluginFiles() {
8471
8409
  for (const c of CHANNELS) {
8472
- mkdirSync14(c.sessionDir, { recursive: true });
8473
- mkdirSync14(c.pluginSettingsDir, { recursive: true });
8410
+ mkdirSync13(c.sessionDir, { recursive: true });
8411
+ mkdirSync13(c.pluginSettingsDir, { recursive: true });
8474
8412
  writeFileSync14(c.pluginPkgPath, PLUGIN_PACKAGE_JSON, "utf-8");
8475
8413
  writeFileSync14(
8476
8414
  c.pluginSettingsPath,
@@ -8493,7 +8431,7 @@ function writePluginFiles() {
8493
8431
  }
8494
8432
  function runBunInstall() {
8495
8433
  for (const c of CHANNELS) {
8496
- const r = spawnSync6("bun", ["install", "--silent"], {
8434
+ const r = spawnSync5("bun", ["install", "--silent"], {
8497
8435
  cwd: c.sessionDir,
8498
8436
  encoding: "utf-8",
8499
8437
  timeout: 12e4
@@ -8506,10 +8444,10 @@ function runBunInstall() {
8506
8444
  }
8507
8445
  }
8508
8446
  function safelyMutateClaudeJson(mutator) {
8509
- if (!existsSync22(CLAUDE_JSON_PATH)) {
8447
+ if (!existsSync21(CLAUDE_JSON_PATH)) {
8510
8448
  return;
8511
8449
  }
8512
- const originalText = readFileSync19(CLAUDE_JSON_PATH, "utf-8");
8450
+ const originalText = readFileSync18(CLAUDE_JSON_PATH, "utf-8");
8513
8451
  let parsed;
8514
8452
  try {
8515
8453
  parsed = JSON.parse(originalText);
@@ -8609,15 +8547,15 @@ function patchClaudeJson() {
8609
8547
  });
8610
8548
  }
8611
8549
  function installLocalCC() {
8612
- let bunCheck = spawnSync6("bun", ["--version"], { encoding: "utf-8" });
8550
+ let bunCheck = spawnSync5("bun", ["--version"], { encoding: "utf-8" });
8613
8551
  if (bunCheck.status !== 0) {
8614
8552
  if (process.platform === "darwin") {
8615
8553
  console.log(" Installing bun via brew...");
8616
- const brewR = spawnSync6("brew", ["install", "oven-sh/bun/bun"], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
8554
+ const brewR = spawnSync5("brew", ["install", "oven-sh/bun/bun"], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
8617
8555
  if (brewR.status !== 0) {
8618
8556
  throw new LocalCCInstallError("bun auto-install failed. Install manually: curl -fsSL https://bun.sh/install | bash");
8619
8557
  }
8620
- bunCheck = spawnSync6("bun", ["--version"], { encoding: "utf-8" });
8558
+ bunCheck = spawnSync5("bun", ["--version"], { encoding: "utf-8" });
8621
8559
  if (bunCheck.status !== 0) {
8622
8560
  throw new LocalCCInstallError("bun installed but not found on PATH. Restart your terminal and re-run install.");
8623
8561
  }
@@ -8651,42 +8589,42 @@ var CLAUDE_JSON_BACKUP_PATH, SESSION_DIR, PLUGIN_PATH, PLUGIN_PKG_PATH, PLUGIN_S
8651
8589
  var init_install2 = __esm({
8652
8590
  "cli/local-cc/install.ts"() {
8653
8591
  "use strict";
8654
- CLAUDE_JSON_BACKUP_PATH = join19(homedir19(), ".claude.json.synkro-bak");
8655
- SESSION_DIR = join19(homedir19(), ".synkro", "cc_sessions");
8656
- PLUGIN_PATH = join19(SESSION_DIR, "synkro-channel.ts");
8657
- PLUGIN_PKG_PATH = join19(SESSION_DIR, "package.json");
8658
- PLUGIN_SETTINGS_DIR = join19(SESSION_DIR, ".claude");
8659
- PLUGIN_SETTINGS_PATH = join19(PLUGIN_SETTINGS_DIR, "settings.json");
8660
- PROJECT_MCP_PATH = join19(SESSION_DIR, ".mcp.json");
8661
- CLAUDE_JSON_PATH = join19(homedir19(), ".claude.json");
8662
- RUN_SCRIPT_PATH = join19(SESSION_DIR, "run-claude.sh");
8592
+ CLAUDE_JSON_BACKUP_PATH = join18(homedir18(), ".claude.json.synkro-bak");
8593
+ SESSION_DIR = join18(homedir18(), ".synkro", "cc_sessions");
8594
+ PLUGIN_PATH = join18(SESSION_DIR, "synkro-channel.ts");
8595
+ PLUGIN_PKG_PATH = join18(SESSION_DIR, "package.json");
8596
+ PLUGIN_SETTINGS_DIR = join18(SESSION_DIR, ".claude");
8597
+ PLUGIN_SETTINGS_PATH = join18(PLUGIN_SETTINGS_DIR, "settings.json");
8598
+ PROJECT_MCP_PATH = join18(SESSION_DIR, ".mcp.json");
8599
+ CLAUDE_JSON_PATH = join18(homedir18(), ".claude.json");
8600
+ RUN_SCRIPT_PATH = join18(SESSION_DIR, "run-claude.sh");
8663
8601
  TMUX_SESSION_NAME = "synkro-local-cc";
8664
8602
  CHANNEL_1_PORT = 8941;
8665
- SESSION_DIR_2 = join19(homedir19(), ".synkro", "cc_sessions_2");
8666
- PLUGIN_PATH_2 = join19(SESSION_DIR_2, "synkro-channel.ts");
8667
- PLUGIN_PKG_PATH_2 = join19(SESSION_DIR_2, "package.json");
8668
- PLUGIN_SETTINGS_DIR_2 = join19(SESSION_DIR_2, ".claude");
8669
- PLUGIN_SETTINGS_PATH_2 = join19(PLUGIN_SETTINGS_DIR_2, "settings.json");
8670
- PROJECT_MCP_PATH_2 = join19(SESSION_DIR_2, ".mcp.json");
8671
- RUN_SCRIPT_PATH_2 = join19(SESSION_DIR_2, "run-claude.sh");
8603
+ SESSION_DIR_2 = join18(homedir18(), ".synkro", "cc_sessions_2");
8604
+ PLUGIN_PATH_2 = join18(SESSION_DIR_2, "synkro-channel.ts");
8605
+ PLUGIN_PKG_PATH_2 = join18(SESSION_DIR_2, "package.json");
8606
+ PLUGIN_SETTINGS_DIR_2 = join18(SESSION_DIR_2, ".claude");
8607
+ PLUGIN_SETTINGS_PATH_2 = join18(PLUGIN_SETTINGS_DIR_2, "settings.json");
8608
+ PROJECT_MCP_PATH_2 = join18(SESSION_DIR_2, ".mcp.json");
8609
+ RUN_SCRIPT_PATH_2 = join18(SESSION_DIR_2, "run-claude.sh");
8672
8610
  TMUX_SESSION_NAME_2 = "synkro-local-cc-2";
8673
8611
  CHANNEL_2_PORT = 8951;
8674
- SESSION_DIR_3 = join19(homedir19(), ".synkro", "cc_sessions_3");
8675
- PLUGIN_PATH_3 = join19(SESSION_DIR_3, "synkro-channel.ts");
8676
- PLUGIN_PKG_PATH_3 = join19(SESSION_DIR_3, "package.json");
8677
- PLUGIN_SETTINGS_DIR_3 = join19(SESSION_DIR_3, ".claude");
8678
- PLUGIN_SETTINGS_PATH_3 = join19(PLUGIN_SETTINGS_DIR_3, "settings.json");
8679
- PROJECT_MCP_PATH_3 = join19(SESSION_DIR_3, ".mcp.json");
8680
- RUN_SCRIPT_PATH_3 = join19(SESSION_DIR_3, "run-claude.sh");
8612
+ SESSION_DIR_3 = join18(homedir18(), ".synkro", "cc_sessions_3");
8613
+ PLUGIN_PATH_3 = join18(SESSION_DIR_3, "synkro-channel.ts");
8614
+ PLUGIN_PKG_PATH_3 = join18(SESSION_DIR_3, "package.json");
8615
+ PLUGIN_SETTINGS_DIR_3 = join18(SESSION_DIR_3, ".claude");
8616
+ PLUGIN_SETTINGS_PATH_3 = join18(PLUGIN_SETTINGS_DIR_3, "settings.json");
8617
+ PROJECT_MCP_PATH_3 = join18(SESSION_DIR_3, ".mcp.json");
8618
+ RUN_SCRIPT_PATH_3 = join18(SESSION_DIR_3, "run-claude.sh");
8681
8619
  TMUX_SESSION_NAME_3 = "synkro-local-cc-3";
8682
8620
  CHANNEL_3_PORT = 8942;
8683
- SESSION_DIR_4 = join19(homedir19(), ".synkro", "cc_sessions_4");
8684
- PLUGIN_PATH_4 = join19(SESSION_DIR_4, "synkro-channel.ts");
8685
- PLUGIN_PKG_PATH_4 = join19(SESSION_DIR_4, "package.json");
8686
- PLUGIN_SETTINGS_DIR_4 = join19(SESSION_DIR_4, ".claude");
8687
- PLUGIN_SETTINGS_PATH_4 = join19(PLUGIN_SETTINGS_DIR_4, "settings.json");
8688
- PROJECT_MCP_PATH_4 = join19(SESSION_DIR_4, ".mcp.json");
8689
- RUN_SCRIPT_PATH_4 = join19(SESSION_DIR_4, "run-claude.sh");
8621
+ SESSION_DIR_4 = join18(homedir18(), ".synkro", "cc_sessions_4");
8622
+ PLUGIN_PATH_4 = join18(SESSION_DIR_4, "synkro-channel.ts");
8623
+ PLUGIN_PKG_PATH_4 = join18(SESSION_DIR_4, "package.json");
8624
+ PLUGIN_SETTINGS_DIR_4 = join18(SESSION_DIR_4, ".claude");
8625
+ PLUGIN_SETTINGS_PATH_4 = join18(PLUGIN_SETTINGS_DIR_4, "settings.json");
8626
+ PROJECT_MCP_PATH_4 = join18(SESSION_DIR_4, ".mcp.json");
8627
+ RUN_SCRIPT_PATH_4 = join18(SESSION_DIR_4, "run-claude.sh");
8690
8628
  TMUX_SESSION_NAME_4 = "synkro-local-cc-4";
8691
8629
  CHANNEL_4_PORT = 8952;
8692
8630
  RUN_SCRIPT_SOURCE = `#!/usr/bin/env bash
@@ -8960,10 +8898,10 @@ var disconnect_exports = {};
8960
8898
  __export(disconnect_exports, {
8961
8899
  disconnectCommand: () => disconnectCommand
8962
8900
  });
8963
- import { existsSync as existsSync23, rmSync as rmSync2, readdirSync as readdirSync4 } from "fs";
8964
- import { homedir as homedir20 } from "os";
8965
- import { join as join20 } from "path";
8966
- import { spawnSync as spawnSync7 } from "child_process";
8901
+ import { existsSync as existsSync22, rmSync, readdirSync as readdirSync4 } from "fs";
8902
+ import { homedir as homedir19 } from "os";
8903
+ import { join as join19 } from "path";
8904
+ import { spawnSync as spawnSync6 } from "child_process";
8967
8905
  import { createInterface as createInterface4 } from "readline";
8968
8906
  async function tearDownLocalCC() {
8969
8907
  const docker = dockerStatus();
@@ -8977,7 +8915,7 @@ async function tearDownLocalCC() {
8977
8915
  console.log("\u2713 removed synkro-server container");
8978
8916
  try {
8979
8917
  const image = imageTag();
8980
- const r = spawnSync7("docker", ["rmi", "-f", image], { encoding: "utf-8", timeout: 3e4 });
8918
+ const r = spawnSync6("docker", ["rmi", "-f", image], { encoding: "utf-8", timeout: 3e4 });
8981
8919
  console.log(r.status === 0 ? `\u2713 removed Docker image ${image}` : "\xB7 no Docker image to remove");
8982
8920
  } catch {
8983
8921
  }
@@ -9048,7 +8986,7 @@ async function disconnectCommand(args2 = [], opts = {}) {
9048
8986
  if (desktopMcpRemoved) removed.mcp = true;
9049
8987
  console.log(`${desktopMcpRemoved ? "\u2713" : "\xB7"} MCP guardrails (Claude Desktop): ${desktopMcpRemoved ? "removed from claude_desktop_config.json" : "no entry found"}`);
9050
8988
  }
9051
- if (existsSync23(SYNKRO_DIR12)) {
8989
+ if (existsSync22(SYNKRO_DIR11)) {
9052
8990
  if (purge2) {
9053
8991
  emit("uninstall", {
9054
8992
  flavor,
@@ -9057,41 +8995,41 @@ async function disconnectCommand(args2 = [], opts = {}) {
9057
8995
  duration_ms: Date.now() - startedAt
9058
8996
  });
9059
8997
  await flush({ force: true });
9060
- rmSync2(SYNKRO_DIR12, { recursive: true, force: true });
8998
+ rmSync(SYNKRO_DIR11, { recursive: true, force: true });
9061
8999
  removed.config = true;
9062
- console.log(`\u2713 wiped ${SYNKRO_DIR12} entirely \u2014 including all scan data and backups`);
9000
+ console.log(`\u2713 wiped ${SYNKRO_DIR11} entirely \u2014 including all scan data and backups`);
9063
9001
  } else {
9064
9002
  const keep = /* @__PURE__ */ new Set([
9065
- join20(SYNKRO_DIR12, "pgdata"),
9066
- join20(SYNKRO_DIR12, "pgdata-backups"),
9067
- join20(SYNKRO_DIR12, ".transcript-offsets")
9003
+ join19(SYNKRO_DIR11, "pgdata"),
9004
+ join19(SYNKRO_DIR11, "pgdata-backups"),
9005
+ join19(SYNKRO_DIR11, ".transcript-offsets")
9068
9006
  ]);
9069
9007
  const preserved = [];
9070
- for (const entry of readdirSync4(SYNKRO_DIR12)) {
9071
- const full = join20(SYNKRO_DIR12, entry);
9008
+ for (const entry of readdirSync4(SYNKRO_DIR11)) {
9009
+ const full = join19(SYNKRO_DIR11, entry);
9072
9010
  if (keep.has(full)) {
9073
9011
  preserved.push(entry);
9074
9012
  continue;
9075
9013
  }
9076
- rmSync2(full, { recursive: true, force: true });
9014
+ rmSync(full, { recursive: true, force: true });
9077
9015
  }
9078
9016
  removed.config = true;
9079
9017
  if (preserved.length > 0) {
9080
- console.log(`\u2713 removed Synkro config from ${SYNKRO_DIR12} (kept your scan data: ${preserved.join(", ")})`);
9018
+ console.log(`\u2713 removed Synkro config from ${SYNKRO_DIR11} (kept your scan data: ${preserved.join(", ")})`);
9081
9019
  console.log(" run `synkro uninstall --purge` to delete that too");
9082
9020
  } else {
9083
- console.log(`\u2713 removed ${SYNKRO_DIR12}`);
9021
+ console.log(`\u2713 removed ${SYNKRO_DIR11}`);
9084
9022
  }
9085
9023
  }
9086
9024
  } else {
9087
- console.log(`\xB7 ${SYNKRO_DIR12} already gone`);
9025
+ console.log(`\xB7 ${SYNKRO_DIR11} already gone`);
9088
9026
  }
9089
9027
  if (!purge2) {
9090
9028
  emit("uninstall", { flavor, purge: purge2, removed, duration_ms: Date.now() - startedAt });
9091
9029
  }
9092
9030
  console.log(purge2 ? "\nSynkro fully removed \u2014 this machine is clean, as if it was never installed." : "\nSynkro uninstalled. Your scan data is preserved.");
9093
9031
  }
9094
- var SYNKRO_DIR12;
9032
+ var SYNKRO_DIR11;
9095
9033
  var init_disconnect = __esm({
9096
9034
  "cli/commands/disconnect.ts"() {
9097
9035
  "use strict";
@@ -9103,14 +9041,14 @@ var init_disconnect = __esm({
9103
9041
  init_dockerInstall();
9104
9042
  init_macKeychain();
9105
9043
  init_telemetry();
9106
- SYNKRO_DIR12 = join20(homedir20(), ".synkro");
9044
+ SYNKRO_DIR11 = join19(homedir19(), ".synkro");
9107
9045
  }
9108
9046
  });
9109
9047
 
9110
9048
  // cli/local-cc/turnLog.ts
9111
- import { appendFileSync as appendFileSync3, existsSync as existsSync24, mkdirSync as mkdirSync15, openSync as openSync3, readFileSync as readFileSync20, readSync, closeSync as closeSync3, statSync as statSync3, watchFile, unwatchFile } from "fs";
9112
- import { dirname as dirname6, join as join21 } from "path";
9113
- import { homedir as homedir21 } from "os";
9049
+ import { appendFileSync as appendFileSync3, existsSync as existsSync23, mkdirSync as mkdirSync14, openSync as openSync3, readFileSync as readFileSync19, readSync, closeSync as closeSync3, statSync as statSync3, watchFile, unwatchFile } from "fs";
9050
+ import { dirname as dirname6, join as join20 } from "path";
9051
+ import { homedir as homedir20 } from "os";
9114
9052
  function truncate(s, max = PREVIEW_MAX) {
9115
9053
  if (s.length <= max) return s;
9116
9054
  return s.slice(0, max) + "\u2026 [+" + (s.length - max) + " chars]";
@@ -9130,7 +9068,7 @@ function extractSeverity(result) {
9130
9068
  }
9131
9069
  function appendTurn(args2) {
9132
9070
  try {
9133
- mkdirSync15(dirname6(TURN_LOG_PATH), { recursive: true });
9071
+ mkdirSync14(dirname6(TURN_LOG_PATH), { recursive: true });
9134
9072
  const entry = {
9135
9073
  ts: new Date(args2.startedAt).toISOString(),
9136
9074
  role: args2.role,
@@ -9146,11 +9084,11 @@ function appendTurn(args2) {
9146
9084
  }
9147
9085
  }
9148
9086
  function readRecentTurns(n = 20) {
9149
- if (!existsSync24(TURN_LOG_PATH)) return [];
9087
+ if (!existsSync23(TURN_LOG_PATH)) return [];
9150
9088
  try {
9151
9089
  const size = statSync3(TURN_LOG_PATH).size;
9152
9090
  if (size === 0) return [];
9153
- const text = readFileSync20(TURN_LOG_PATH, "utf-8");
9091
+ const text = readFileSync19(TURN_LOG_PATH, "utf-8");
9154
9092
  const lines = text.split("\n").filter(Boolean);
9155
9093
  const lastN = lines.slice(-n).reverse();
9156
9094
  return lastN.map((line) => {
@@ -9166,8 +9104,8 @@ function readRecentTurns(n = 20) {
9166
9104
  }
9167
9105
  function followTurns(onEntry) {
9168
9106
  try {
9169
- mkdirSync15(dirname6(TURN_LOG_PATH), { recursive: true });
9170
- if (!existsSync24(TURN_LOG_PATH)) {
9107
+ mkdirSync14(dirname6(TURN_LOG_PATH), { recursive: true });
9108
+ if (!existsSync23(TURN_LOG_PATH)) {
9171
9109
  appendFileSync3(TURN_LOG_PATH, "", "utf-8");
9172
9110
  }
9173
9111
  } catch {
@@ -9229,7 +9167,7 @@ var TURN_LOG_PATH, PREVIEW_MAX;
9229
9167
  var init_turnLog = __esm({
9230
9168
  "cli/local-cc/turnLog.ts"() {
9231
9169
  "use strict";
9232
- TURN_LOG_PATH = join21(homedir21(), ".synkro", "cc_sessions", "turns.log");
9170
+ TURN_LOG_PATH = join20(homedir20(), ".synkro", "cc_sessions", "turns.log");
9233
9171
  PREVIEW_MAX = 400;
9234
9172
  }
9235
9173
  });
@@ -9378,19 +9316,19 @@ var init_grade = __esm({
9378
9316
  });
9379
9317
 
9380
9318
  // cli/local-cc/pueue.ts
9381
- import { execFileSync as execFileSync4, spawnSync as spawnSync8, spawn as spawn3 } from "child_process";
9382
- import { homedir as homedir22 } from "os";
9383
- import { join as join22 } from "path";
9319
+ import { execFileSync as execFileSync4, spawnSync as spawnSync7, spawn as spawn3 } from "child_process";
9320
+ import { homedir as homedir21 } from "os";
9321
+ import { join as join21 } from "path";
9384
9322
  import { connect as connect2 } from "net";
9385
9323
  function pueueAvailable() {
9386
- const r = spawnSync8("pueue", ["--version"], { encoding: "utf-8" });
9324
+ const r = spawnSync7("pueue", ["--version"], { encoding: "utf-8" });
9387
9325
  if (r.status !== 0) {
9388
9326
  throw new PueueError("pueue CLI not found on PATH. Install pueue (https://github.com/Nukesor/pueue) and start `pueued`.");
9389
9327
  }
9390
9328
  }
9391
9329
  function statusJson() {
9392
9330
  pueueAvailable();
9393
- const r = spawnSync8("pueue", ["status", "--json"], { encoding: "utf-8" });
9331
+ const r = spawnSync7("pueue", ["status", "--json"], { encoding: "utf-8" });
9394
9332
  if (r.status !== 0) {
9395
9333
  throw new PueueError(`pueue status failed: ${r.stderr || r.stdout || "unknown error"} \u2014 is pueued running?`);
9396
9334
  }
@@ -9435,18 +9373,18 @@ function startTask(opts = {}) {
9435
9373
  let existing = findTask(ch);
9436
9374
  while (existing) {
9437
9375
  if (existing.status === "Running" || existing.status === "Queued") {
9438
- spawnSync8("tmux", ["kill-session", "-t", `=${ch.tmuxSession}`], { encoding: "utf-8" });
9439
- spawnSync8("pueue", ["kill", String(existing.id)], { encoding: "utf-8" });
9376
+ spawnSync7("tmux", ["kill-session", "-t", `=${ch.tmuxSession}`], { encoding: "utf-8" });
9377
+ spawnSync7("pueue", ["kill", String(existing.id)], { encoding: "utf-8" });
9440
9378
  for (let i = 0; i < 10; i++) {
9441
9379
  const check = findTask(ch);
9442
9380
  if (!check || check.id !== existing.id || check.status !== "Running" && check.status !== "Queued") break;
9443
- spawnSync8("sleep", ["0.5"], { encoding: "utf-8" });
9381
+ spawnSync7("sleep", ["0.5"], { encoding: "utf-8" });
9444
9382
  }
9445
9383
  }
9446
- spawnSync8("pueue", ["remove", String(existing.id)], { encoding: "utf-8" });
9384
+ spawnSync7("pueue", ["remove", String(existing.id)], { encoding: "utf-8" });
9447
9385
  existing = findTask(ch);
9448
9386
  }
9449
- const runScript = join22(cwd, "run-claude.sh");
9387
+ const runScript = join21(cwd, "run-claude.sh");
9450
9388
  const args2 = [
9451
9389
  "add",
9452
9390
  "--label",
@@ -9457,7 +9395,7 @@ function startTask(opts = {}) {
9457
9395
  "bash",
9458
9396
  runScript
9459
9397
  ];
9460
- const r = spawnSync8("pueue", args2, { encoding: "utf-8" });
9398
+ const r = spawnSync7("pueue", args2, { encoding: "utf-8" });
9461
9399
  if (r.status !== 0) {
9462
9400
  throw new PueueError(`pueue add failed: ${r.stderr || r.stdout}`);
9463
9401
  }
@@ -9468,25 +9406,25 @@ function startTask(opts = {}) {
9468
9406
  return created;
9469
9407
  }
9470
9408
  function stopTask(channel = CHANNEL_PRIMARY) {
9471
- spawnSync8("tmux", ["kill-session", "-t", `=${channel.tmuxSession}`], { encoding: "utf-8" });
9409
+ spawnSync7("tmux", ["kill-session", "-t", `=${channel.tmuxSession}`], { encoding: "utf-8" });
9472
9410
  let t = findTask(channel);
9473
9411
  while (t) {
9474
9412
  if (t.status === "Running" || t.status === "Queued") {
9475
- spawnSync8("pueue", ["kill", String(t.id)], { encoding: "utf-8" });
9413
+ spawnSync7("pueue", ["kill", String(t.id)], { encoding: "utf-8" });
9476
9414
  for (let i = 0; i < 10; i++) {
9477
9415
  const check = findTask(channel);
9478
9416
  if (!check || check.id !== t.id || check.status !== "Running" && check.status !== "Queued") break;
9479
- spawnSync8("sleep", ["0.5"], { encoding: "utf-8" });
9417
+ spawnSync7("sleep", ["0.5"], { encoding: "utf-8" });
9480
9418
  }
9481
9419
  }
9482
- spawnSync8("pueue", ["remove", String(t.id)], { encoding: "utf-8" });
9420
+ spawnSync7("pueue", ["remove", String(t.id)], { encoding: "utf-8" });
9483
9421
  t = findTask(channel);
9484
9422
  }
9485
9423
  }
9486
9424
  function tailLogs(lines = 80, channel = CHANNEL_PRIMARY) {
9487
9425
  const t = findTask(channel);
9488
9426
  if (!t) return `(no ${channel.taskLabel} task)`;
9489
- const r = spawnSync8("pueue", ["log", "--lines", String(lines), String(t.id)], { encoding: "utf-8" });
9427
+ const r = spawnSync7("pueue", ["log", "--lines", String(lines), String(t.id)], { encoding: "utf-8" });
9490
9428
  return r.stdout || r.stderr || "(no output)";
9491
9429
  }
9492
9430
  function ensureRunning(opts = {}) {
@@ -9511,8 +9449,8 @@ function probePort(host, port, timeoutMs = 500) {
9511
9449
  });
9512
9450
  }
9513
9451
  function tmuxDismissPrompts(tmuxSession = TMUX_SESSION) {
9514
- spawnSync8("tmux", ["send-keys", "-t", tmuxSession, "1"], { encoding: "utf-8" });
9515
- spawnSync8("tmux", ["send-keys", "-t", tmuxSession, "Enter"], { encoding: "utf-8" });
9452
+ spawnSync7("tmux", ["send-keys", "-t", tmuxSession, "1"], { encoding: "utf-8" });
9453
+ spawnSync7("tmux", ["send-keys", "-t", tmuxSession, "Enter"], { encoding: "utf-8" });
9516
9454
  }
9517
9455
  async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tmuxSession = TMUX_SESSION) {
9518
9456
  const deadline = Date.now() + timeoutMs;
@@ -9524,46 +9462,46 @@ async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tm
9524
9462
  return probePort(host, port);
9525
9463
  }
9526
9464
  function brewInstall(pkg) {
9527
- const brew = spawnSync8("brew", ["--version"], { encoding: "utf-8" });
9465
+ const brew = spawnSync7("brew", ["--version"], { encoding: "utf-8" });
9528
9466
  if (brew.status !== 0) return false;
9529
9467
  console.log(` Installing ${pkg} via brew...`);
9530
- const r = spawnSync8("brew", ["install", pkg], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
9468
+ const r = spawnSync7("brew", ["install", pkg], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
9531
9469
  return r.status === 0;
9532
9470
  }
9533
9471
  function assertPueueInstalled() {
9534
- let r = spawnSync8("pueue", ["--version"], { encoding: "utf-8" });
9472
+ let r = spawnSync7("pueue", ["--version"], { encoding: "utf-8" });
9535
9473
  if (r.status !== 0) {
9536
9474
  if (process.platform === "darwin" && brewInstall("pueue")) {
9537
- r = spawnSync8("pueue", ["--version"], { encoding: "utf-8" });
9475
+ r = spawnSync7("pueue", ["--version"], { encoding: "utf-8" });
9538
9476
  if (r.status !== 0) throw new PueueError("pueue install succeeded but binary not found on PATH.");
9539
9477
  } else {
9540
9478
  throw new PueueError("pueue not found. Install it: brew install pueue (macOS) or https://github.com/Nukesor/pueue");
9541
9479
  }
9542
9480
  }
9543
- const status = spawnSync8("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
9481
+ const status = spawnSync7("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
9544
9482
  if (status.status !== 0) {
9545
9483
  console.log(" Starting pueued daemon...");
9546
9484
  const child = spawn3("pueued", ["-d"], { stdio: "ignore", detached: true });
9547
9485
  child.unref();
9548
- spawnSync8("sleep", ["1"]);
9549
- const retry = spawnSync8("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
9486
+ spawnSync7("sleep", ["1"]);
9487
+ const retry = spawnSync7("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
9550
9488
  if (retry.status !== 0) {
9551
9489
  throw new PueueError("pueue daemon not reachable after starting pueued. Check `pueued` manually.");
9552
9490
  }
9553
9491
  }
9554
- spawnSync8("pueue", ["parallel", "2"], { encoding: "utf-8" });
9492
+ spawnSync7("pueue", ["parallel", "2"], { encoding: "utf-8" });
9555
9493
  }
9556
9494
  function assertClaudeInstalled() {
9557
- const r = spawnSync8("claude", ["--version"], { encoding: "utf-8" });
9495
+ const r = spawnSync7("claude", ["--version"], { encoding: "utf-8" });
9558
9496
  if (r.status !== 0) {
9559
9497
  throw new PueueError("claude CLI not found on PATH. Install Claude Code first: https://docs.claude.com/claude-code");
9560
9498
  }
9561
9499
  }
9562
9500
  function assertTmuxInstalled() {
9563
- let r = spawnSync8("tmux", ["-V"], { encoding: "utf-8" });
9501
+ let r = spawnSync7("tmux", ["-V"], { encoding: "utf-8" });
9564
9502
  if (r.status !== 0) {
9565
9503
  if (process.platform === "darwin" && brewInstall("tmux")) {
9566
- r = spawnSync8("tmux", ["-V"], { encoding: "utf-8" });
9504
+ r = spawnSync7("tmux", ["-V"], { encoding: "utf-8" });
9567
9505
  if (r.status !== 0) throw new PueueError("tmux install succeeded but binary not found on PATH.");
9568
9506
  } else {
9569
9507
  throw new PueueError("tmux not found. Install it: brew install tmux (macOS) or apt install tmux (Linux)");
@@ -9576,12 +9514,12 @@ var init_pueue = __esm({
9576
9514
  "use strict";
9577
9515
  TASK_LABEL = "synkro-local-cc";
9578
9516
  TMUX_SESSION = "synkro-local-cc";
9579
- SESSION_DIR2 = join22(homedir22(), ".synkro", "cc_sessions");
9517
+ SESSION_DIR2 = join21(homedir21(), ".synkro", "cc_sessions");
9580
9518
  TASK_LABEL_2 = "synkro-local-cc-2";
9581
9519
  TMUX_SESSION_2 = "synkro-local-cc-2";
9582
- SESSION_DIR_22 = join22(homedir22(), ".synkro", "cc_sessions_2");
9583
- SESSION_DIR_32 = join22(homedir22(), ".synkro", "cc_sessions_3");
9584
- SESSION_DIR_42 = join22(homedir22(), ".synkro", "cc_sessions_4");
9520
+ SESSION_DIR_22 = join21(homedir21(), ".synkro", "cc_sessions_2");
9521
+ SESSION_DIR_32 = join21(homedir21(), ".synkro", "cc_sessions_3");
9522
+ SESSION_DIR_42 = join21(homedir21(), ".synkro", "cc_sessions_4");
9585
9523
  PueueError = class extends Error {
9586
9524
  constructor(message, cause) {
9587
9525
  super(message);
@@ -9596,13 +9534,13 @@ var init_pueue = __esm({
9596
9534
  });
9597
9535
 
9598
9536
  // cli/local-cc/settings.ts
9599
- import { existsSync as existsSync25, readFileSync as readFileSync21 } from "fs";
9600
- import { homedir as homedir23 } from "os";
9601
- import { join as join23 } from "path";
9537
+ import { existsSync as existsSync24, readFileSync as readFileSync20 } from "fs";
9538
+ import { homedir as homedir22 } from "os";
9539
+ import { join as join22 } from "path";
9602
9540
  function isLocalCCEnabled() {
9603
- if (!existsSync25(CONFIG_PATH5)) return false;
9541
+ if (!existsSync24(CONFIG_PATH5)) return false;
9604
9542
  try {
9605
- const content = readFileSync21(CONFIG_PATH5, "utf-8");
9543
+ const content = readFileSync20(CONFIG_PATH5, "utf-8");
9606
9544
  const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);
9607
9545
  return match?.[1] === "yes";
9608
9546
  } catch {
@@ -9613,7 +9551,7 @@ var CONFIG_PATH5;
9613
9551
  var init_settings = __esm({
9614
9552
  "cli/local-cc/settings.ts"() {
9615
9553
  "use strict";
9616
- CONFIG_PATH5 = join23(homedir23(), ".synkro", "config.env");
9554
+ CONFIG_PATH5 = join22(homedir22(), ".synkro", "config.env");
9617
9555
  }
9618
9556
  });
9619
9557
 
@@ -9622,11 +9560,11 @@ var localCc_exports = {};
9622
9560
  __export(localCc_exports, {
9623
9561
  localCcCommand: () => localCcCommand
9624
9562
  });
9625
- import { spawnSync as spawnSync9 } from "child_process";
9626
- import { homedir as homedir24 } from "os";
9627
- import { join as join24 } from "path";
9563
+ import { spawnSync as spawnSync8 } from "child_process";
9564
+ import { homedir as homedir23 } from "os";
9565
+ import { join as join23 } from "path";
9628
9566
  import { readFileSync as fsReadFileSync, existsSync as fsExistsSync } from "fs";
9629
- import { existsSync as existsSync26, readFileSync as readFileSync22, writeFileSync as writeFileSync15 } from "fs";
9567
+ import { existsSync as existsSync25, readFileSync as readFileSync21, writeFileSync as writeFileSync15 } from "fs";
9630
9568
  function deploymentMode() {
9631
9569
  const env = (process.env.SYNKRO_DEPLOYMENT_MODE || "").toLowerCase();
9632
9570
  if (env === "docker") return "docker";
@@ -9732,15 +9670,15 @@ TROUBLESHOOTING
9732
9670
  `);
9733
9671
  }
9734
9672
  function readGatewayUrl() {
9735
- if (existsSync26(CONFIG_PATH6)) {
9736
- const m = readFileSync22(CONFIG_PATH6, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
9673
+ if (existsSync25(CONFIG_PATH6)) {
9674
+ const m = readFileSync21(CONFIG_PATH6, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
9737
9675
  if (m) return m[1];
9738
9676
  }
9739
9677
  return "https://api.synkro.sh";
9740
9678
  }
9741
9679
  function updateLocalInferenceFlag(enabled) {
9742
- if (!existsSync26(CONFIG_PATH6)) return;
9743
- let content = readFileSync22(CONFIG_PATH6, "utf-8");
9680
+ if (!existsSync25(CONFIG_PATH6)) return;
9681
+ let content = readFileSync21(CONFIG_PATH6, "utf-8");
9744
9682
  const flag = enabled ? "yes" : "no";
9745
9683
  if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
9746
9684
  content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);
@@ -9803,7 +9741,7 @@ async function cmdStatus() {
9803
9741
  }
9804
9742
  const ch1Up = await isChannelAvailable();
9805
9743
  console.log(`Channel 1 ${CHANNEL_HOST}:${CHANNEL_PORT}: ${ch1Up ? "reachable" : "unreachable"}`);
9806
- const tmux1 = spawnSync9("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
9744
+ const tmux1 = spawnSync8("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
9807
9745
  console.log(`tmux '${TMUX_SESSION_NAME}': ${tmux1.status === 0 ? "live" : "absent"}`);
9808
9746
  const t2 = findTask(CHANNEL_SECONDARY);
9809
9747
  if (!t2) {
@@ -9813,7 +9751,7 @@ async function cmdStatus() {
9813
9751
  }
9814
9752
  const ch2Up = await isChannelAvailable(CHANNEL_2_PORT);
9815
9753
  console.log(`Channel 2 ${CHANNEL_HOST}:${CHANNEL_2_PORT}: ${ch2Up ? "reachable" : "unreachable"}`);
9816
- const tmux2 = spawnSync9("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME_2}`], { encoding: "utf-8" });
9754
+ const tmux2 = spawnSync8("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME_2}`], { encoding: "utf-8" });
9817
9755
  console.log(`tmux '${TMUX_SESSION_NAME_2}': ${tmux2.status === 0 ? "live" : "absent"}`);
9818
9756
  }
9819
9757
  async function cmdEnable() {
@@ -10019,7 +9957,7 @@ function cmdLogs(rest) {
10019
9957
  }
10020
9958
  return "200";
10021
9959
  })();
10022
- spawnSync9("docker", ["logs", "--tail", tailArg, ...followFlag, "synkro-server"], { stdio: "inherit" });
9960
+ spawnSync8("docker", ["logs", "--tail", tailArg, ...followFlag, "synkro-server"], { stdio: "inherit" });
10023
9961
  return;
10024
9962
  }
10025
9963
  for (const arg of rest) {
@@ -10067,7 +10005,7 @@ function cmdLogs(rest) {
10067
10005
  function cmdAttach(rest) {
10068
10006
  assertTmuxInstalled();
10069
10007
  const readonly = rest.some((a) => a === "--readonly" || a === "-r");
10070
- const has = spawnSync9("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
10008
+ const has = spawnSync8("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
10071
10009
  if (has.status !== 0) {
10072
10010
  console.error(`No tmux session '${TMUX_SESSION_NAME}' running. Start it with: synkro local-cc start`);
10073
10011
  process.exit(1);
@@ -10080,7 +10018,7 @@ function cmdAttach(rest) {
10080
10018
  console.log("Detach with Ctrl-B then D. (Do not press Ctrl-C \u2014 that would interrupt claude.)");
10081
10019
  console.log();
10082
10020
  const args2 = readonly ? ["attach-session", "-r", "-t", TMUX_SESSION_NAME] : ["attach-session", "-t", TMUX_SESSION_NAME];
10083
- const r = spawnSync9("tmux", args2, { stdio: "inherit" });
10021
+ const r = spawnSync8("tmux", args2, { stdio: "inherit" });
10084
10022
  process.exit(r.status ?? 0);
10085
10023
  }
10086
10024
  async function cmdTest() {
@@ -10181,8 +10119,8 @@ var init_localCc = __esm({
10181
10119
  init_install();
10182
10120
  init_client2();
10183
10121
  init_stub();
10184
- SYNKRO_CONFIG_PATH = join24(homedir24(), ".synkro", "config.env");
10185
- CONFIG_PATH6 = join24(homedir24(), ".synkro", "config.env");
10122
+ SYNKRO_CONFIG_PATH = join23(homedir23(), ".synkro", "config.env");
10123
+ CONFIG_PATH6 = join23(homedir23(), ".synkro", "config.env");
10186
10124
  }
10187
10125
  });
10188
10126
 
@@ -10191,15 +10129,15 @@ var import_exports = {};
10191
10129
  __export(import_exports, {
10192
10130
  importCommand: () => importCommand
10193
10131
  });
10194
- import { existsSync as existsSync27, readFileSync as readFileSync23, readdirSync as readdirSync5 } from "fs";
10195
- import { homedir as homedir25 } from "os";
10196
- import { join as join25 } from "path";
10132
+ import { existsSync as existsSync26, readFileSync as readFileSync22, readdirSync as readdirSync5 } from "fs";
10133
+ import { homedir as homedir24 } from "os";
10134
+ import { join as join24 } from "path";
10197
10135
  import { execSync as execSync7 } from "child_process";
10198
10136
  import { createInterface as createInterface5 } from "readline";
10199
10137
  function readConfigEnv2() {
10200
10138
  const out = {};
10201
10139
  try {
10202
- for (const line of readFileSync23(CONFIG_PATH7, "utf-8").split("\n")) {
10140
+ for (const line of readFileSync22(CONFIG_PATH7, "utf-8").split("\n")) {
10203
10141
  const t = line.trim();
10204
10142
  if (!t || t.startsWith("#")) continue;
10205
10143
  const eq = t.indexOf("=");
@@ -10211,8 +10149,8 @@ function readConfigEnv2() {
10211
10149
  }
10212
10150
  function projectsFolder() {
10213
10151
  const sanitized = process.cwd().replace(/\//g, "-");
10214
- const dir = join25(homedir25(), ".claude", "projects", sanitized);
10215
- return existsSync27(dir) ? dir : null;
10152
+ const dir = join24(homedir24(), ".claude", "projects", sanitized);
10153
+ return existsSync26(dir) ? dir : null;
10216
10154
  }
10217
10155
  function repoName() {
10218
10156
  try {
@@ -10235,7 +10173,7 @@ function extractText(content) {
10235
10173
  return "";
10236
10174
  }
10237
10175
  function parseSession(filePath, sessionId) {
10238
- const lines = readFileSync23(filePath, "utf-8").split("\n").filter(Boolean);
10176
+ const lines = readFileSync22(filePath, "utf-8").split("\n").filter(Boolean);
10239
10177
  const messages = [];
10240
10178
  const actions = [];
10241
10179
  let step = 0;
@@ -10308,7 +10246,7 @@ async function importCommand() {
10308
10246
  return;
10309
10247
  }
10310
10248
  }
10311
- const sessions = files.map((f) => parseSession(join25(dir, f), f.replace(".jsonl", ""))).filter((s) => s.messages.length > 0);
10249
+ const sessions = files.map((f) => parseSession(join24(dir, f), f.replace(".jsonl", ""))).filter((s) => s.messages.length > 0);
10312
10250
  const totalMsgs = sessions.reduce((n, s) => n + s.messages.length, 0);
10313
10251
  let ok = 0, fail = 0;
10314
10252
  if (isCloud) {
@@ -10368,7 +10306,7 @@ var init_import = __esm({
10368
10306
  "cli/commands/import.ts"() {
10369
10307
  "use strict";
10370
10308
  init_stub();
10371
- CONFIG_PATH7 = join25(homedir25(), ".synkro", "config.env");
10309
+ CONFIG_PATH7 = join24(homedir24(), ".synkro", "config.env");
10372
10310
  }
10373
10311
  });
10374
10312
 
@@ -10410,10 +10348,10 @@ var init_packVerify = __esm({
10410
10348
  });
10411
10349
 
10412
10350
  // cli/installer/lockfile.ts
10413
- import { existsSync as existsSync28, readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "fs";
10414
- import { join as join26 } from "path";
10351
+ import { existsSync as existsSync27, readFileSync as readFileSync23, writeFileSync as writeFileSync16 } from "fs";
10352
+ import { join as join25 } from "path";
10415
10353
  function lockPath(repoRoot) {
10416
- return join26(repoRoot, LOCK_FILE);
10354
+ return join25(repoRoot, LOCK_FILE);
10417
10355
  }
10418
10356
  function writeLockfile(repoRoot, entries) {
10419
10357
  const sorted = [...entries].sort((a, b) => a.ref.localeCompare(b.ref));
@@ -10446,9 +10384,9 @@ var sync_exports = {};
10446
10384
  __export(sync_exports, {
10447
10385
  syncCommand: () => syncCommand
10448
10386
  });
10449
- import { existsSync as existsSync29, mkdirSync as mkdirSync16, readdirSync as readdirSync6, rmSync as rmSync3, writeFileSync as writeFileSync17 } from "fs";
10450
- import { homedir as homedir26 } from "os";
10451
- import { join as join27 } from "path";
10387
+ import { existsSync as existsSync28, mkdirSync as mkdirSync15, readdirSync as readdirSync6, rmSync as rmSync2, writeFileSync as writeFileSync17 } from "fs";
10388
+ import { homedir as homedir25 } from "os";
10389
+ import { join as join26 } from "path";
10452
10390
  function cacheKey(ref, version) {
10453
10391
  return ref.replace(/\//g, "__").replace(/[^\w.@-]/g, "_") + "@" + version + ".json";
10454
10392
  }
@@ -10479,8 +10417,8 @@ async function syncCommand(_args = []) {
10479
10417
  }
10480
10418
  const gateway = (process.env.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/\/$/, "");
10481
10419
  const cloud = process.env.SYNKRO_DEPLOY_LOCATION === "cloud";
10482
- const cacheDir = join27(homedir26(), ".synkro", "cache", "packs");
10483
- if (!cloud) mkdirSync16(cacheDir, { recursive: true });
10420
+ const cacheDir = join26(homedir25(), ".synkro", "cache", "packs");
10421
+ if (!cloud) mkdirSync15(cacheDir, { recursive: true });
10484
10422
  console.log(`Syncing ${refs.length} standard(s) from the registry\u2026`);
10485
10423
  const lock = [];
10486
10424
  const keptCacheFiles = /* @__PURE__ */ new Set();
@@ -10508,7 +10446,7 @@ async function syncCommand(_args = []) {
10508
10446
  if (!cloud) {
10509
10447
  const fname = cacheKey(ref, data.version);
10510
10448
  keptCacheFiles.add(fname);
10511
- writeFileSync17(join27(cacheDir, fname), JSON.stringify({
10449
+ writeFileSync17(join26(cacheDir, fname), JSON.stringify({
10512
10450
  ref,
10513
10451
  version: data.version,
10514
10452
  digest: data.digest,
@@ -10520,11 +10458,11 @@ async function syncCommand(_args = []) {
10520
10458
  const ruleCount = Array.isArray(pack.rules) ? pack.rules.length : 0;
10521
10459
  console.log(` \u2713 ${ref}:${data.version} \u2014 verified (${ruleCount} rule${ruleCount === 1 ? "" : "s"})`);
10522
10460
  }
10523
- if (!cloud && existsSync29(cacheDir)) {
10461
+ if (!cloud && existsSync28(cacheDir)) {
10524
10462
  for (const f of readdirSync6(cacheDir)) {
10525
10463
  if (f.endsWith(".json") && !keptCacheFiles.has(f)) {
10526
10464
  try {
10527
- rmSync3(join27(cacheDir, f));
10465
+ rmSync2(join26(cacheDir, f));
10528
10466
  } catch {
10529
10467
  }
10530
10468
  }
@@ -10660,13 +10598,13 @@ var config_exports = {};
10660
10598
  __export(config_exports, {
10661
10599
  configCommand: () => configCommand
10662
10600
  });
10663
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync18, existsSync as existsSync30 } from "fs";
10664
- import { join as join28 } from "path";
10665
- import { homedir as homedir27 } from "os";
10601
+ import { readFileSync as readFileSync24, writeFileSync as writeFileSync18, existsSync as existsSync29 } from "fs";
10602
+ import { join as join27 } from "path";
10603
+ import { homedir as homedir26 } from "os";
10666
10604
  function readConfigEnv3() {
10667
- if (!existsSync30(CONFIG_PATH8)) return {};
10605
+ if (!existsSync29(CONFIG_PATH8)) return {};
10668
10606
  const out = {};
10669
- for (const line of readFileSync25(CONFIG_PATH8, "utf-8").split("\n")) {
10607
+ for (const line of readFileSync24(CONFIG_PATH8, "utf-8").split("\n")) {
10670
10608
  const t = line.trim();
10671
10609
  if (!t || t.startsWith("#")) continue;
10672
10610
  const eq = t.indexOf("=");
@@ -10675,11 +10613,11 @@ function readConfigEnv3() {
10675
10613
  return out;
10676
10614
  }
10677
10615
  function updateConfigValue(key, value) {
10678
- if (!existsSync30(CONFIG_PATH8)) {
10616
+ if (!existsSync29(CONFIG_PATH8)) {
10679
10617
  console.error("No config found. Run `synkro install` first.");
10680
10618
  process.exit(1);
10681
10619
  }
10682
- const lines = readFileSync25(CONFIG_PATH8, "utf-8").split("\n");
10620
+ const lines = readFileSync24(CONFIG_PATH8, "utf-8").split("\n");
10683
10621
  const pattern = new RegExp(`^${key}=`);
10684
10622
  let found = false;
10685
10623
  const updated = lines.map((line) => {
@@ -10842,14 +10780,14 @@ To change:`);
10842
10780
  }
10843
10781
  if (inferenceValue !== "cloud") await reconcileContainer();
10844
10782
  }
10845
- var SYNKRO_DIR13, CONFIG_PATH8;
10783
+ var SYNKRO_DIR12, CONFIG_PATH8;
10846
10784
  var init_config = __esm({
10847
10785
  "cli/commands/config.ts"() {
10848
10786
  "use strict";
10849
10787
  init_stub();
10850
10788
  init_optout();
10851
- SYNKRO_DIR13 = join28(homedir27(), ".synkro");
10852
- CONFIG_PATH8 = join28(SYNKRO_DIR13, "config.env");
10789
+ SYNKRO_DIR12 = join27(homedir26(), ".synkro");
10790
+ CONFIG_PATH8 = join27(SYNKRO_DIR12, "config.env");
10853
10791
  }
10854
10792
  });
10855
10793
 
@@ -11038,14 +10976,14 @@ Usage:
11038
10976
  });
11039
10977
 
11040
10978
  // cli/bootstrap.js
11041
- import { readFileSync as readFileSync26, existsSync as existsSync31 } from "fs";
10979
+ import { readFileSync as readFileSync25, existsSync as existsSync30 } from "fs";
11042
10980
  import { resolve as resolve3 } from "path";
11043
10981
  var envCandidates = [
11044
10982
  resolve3(process.env.HOME ?? "", ".synkro", "config.env")
11045
10983
  ];
11046
10984
  for (const envPath of envCandidates) {
11047
- if (!existsSync31(envPath)) continue;
11048
- const envContent = readFileSync26(envPath, "utf-8");
10985
+ if (!existsSync30(envPath)) continue;
10986
+ const envContent = readFileSync25(envPath, "utf-8");
11049
10987
  for (const line of envContent.split("\n")) {
11050
10988
  const trimmed = line.trim();
11051
10989
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -11062,7 +11000,7 @@ var subArgs = args.slice(1);
11062
11000
  var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
11063
11001
  var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "version", "--version", "-v", "help", "--help", "-h", ""]);
11064
11002
  function printVersion() {
11065
- console.log("1.7.63");
11003
+ console.log("1.7.65");
11066
11004
  }
11067
11005
  function printHelp2() {
11068
11006
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
@@ -11122,7 +11060,7 @@ async function main() {
11122
11060
  case "uninstall":
11123
11061
  case "disconnect": {
11124
11062
  const { disconnectCommand: disconnectCommand2 } = await Promise.resolve().then(() => (init_disconnect(), disconnect_exports));
11125
- disconnectCommand2(subArgs);
11063
+ await disconnectCommand2(subArgs);
11126
11064
  break;
11127
11065
  }
11128
11066
  case "login": {