@synkro-sh/cli 1.7.9 → 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
 
@@ -11311,6 +11408,7 @@ var init_setupGithub = __esm({
11311
11408
  var install_exports = {};
11312
11409
  __export(install_exports, {
11313
11410
  detectGitRepo: () => detectGitRepo2,
11411
+ discoverAndIngestSkills: () => discoverAndIngestSkills,
11314
11412
  getOrMintCloudToken: () => getOrMintCloudToken,
11315
11413
  installCommand: () => installCommand,
11316
11414
  parseArgs: () => parseArgs,
@@ -11321,16 +11419,17 @@ __export(install_exports, {
11321
11419
  syncSkillFiles: () => syncSkillFiles,
11322
11420
  writeHookScripts: () => writeHookScripts
11323
11421
  });
11324
- 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";
11325
- import { homedir as homedir10 } from "os";
11326
- 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";
11327
11425
  import { execSync as execSync6 } from "child_process";
11328
11426
  import { createInterface as createInterface3 } from "readline";
11427
+ import { createHash } from "crypto";
11329
11428
  function resolvePersistedHookMode() {
11330
11429
  if (process.env.SYNKRO_HOOK_MODE === "full") return "full";
11331
11430
  if (process.env.SYNKRO_HOOK_MODE === "stub") return "stub";
11332
11431
  try {
11333
- const env = readFileSync10(CONFIG_PATH2, "utf-8");
11432
+ const env = readFileSync11(CONFIG_PATH2, "utf-8");
11334
11433
  if (/^SYNKRO_HOOK_MODE=['"]?full['"]?\s*$/m.test(env)) return "full";
11335
11434
  } catch {
11336
11435
  }
@@ -11466,37 +11565,51 @@ coding patterns and the dashboard shows full history. (Y/n) `
11466
11565
  rl.close();
11467
11566
  }
11468
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
+ }
11469
11582
  function ensureSynkroDir() {
11470
- mkdirSync9(SYNKRO_DIR5, { recursive: true });
11471
- mkdirSync9(HOOKS_DIR, { recursive: true });
11472
- mkdirSync9(BIN_DIR, { recursive: true });
11473
- mkdirSync9(OFFSETS_DIR, { recursive: true });
11474
- 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 });
11475
11588
  }
11476
11589
  function writeHookScripts(mode = "stub") {
11477
- const installExtractCorePath = join10(HOOKS_DIR, "installExtractCore.ts");
11478
- const bashScriptPath = join10(HOOKS_DIR, "cc-bash-judge.ts");
11479
- const bashFollowupScriptPath = join10(HOOKS_DIR, "cc-bash-followup.ts");
11480
- const editPrecheckScriptPath = join10(HOOKS_DIR, "cc-edit-precheck.ts");
11481
- const cwePrecheckScriptPath = join10(HOOKS_DIR, "cc-cwe-precheck.ts");
11482
- const cvePrecheckScriptPath = join10(HOOKS_DIR, "cc-cve-precheck.ts");
11483
- const planJudgeScriptPath = join10(HOOKS_DIR, "cc-plan-judge.ts");
11484
- const agentJudgeScriptPath = join10(HOOKS_DIR, "cc-agent-judge.ts");
11485
- const stopSummaryScriptPath = join10(HOOKS_DIR, "cc-stop-summary.ts");
11486
- const sessionStartScriptPath = join10(HOOKS_DIR, "cc-session-start.ts");
11487
- const transcriptSyncScriptPath = join10(HOOKS_DIR, "cc-transcript-sync.ts");
11488
- const userPromptSubmitScriptPath = join10(HOOKS_DIR, "cc-user-prompt-submit.ts");
11489
- const commonScriptPath = join10(HOOKS_DIR, "_synkro-common.ts");
11490
- const commonBashScriptPath = join10(HOOKS_DIR, "_synkro-common.sh");
11491
- const installScanScriptPath = join10(HOOKS_DIR, "cc-install-scan.ts");
11492
- const cursorBashJudgePath = join10(HOOKS_DIR, "cursor-bash-judge.ts");
11493
- const cursorEditCapturePath = join10(HOOKS_DIR, "cursor-edit-capture.ts");
11494
- const cursorAgentCapturePath = join10(HOOKS_DIR, "cursor-agent-capture.ts");
11495
- const mcpStdioProxyPath = join10(HOOKS_DIR, "mcp-stdio-proxy.ts");
11496
- const taskActivateIntentScriptPath = join10(HOOKS_DIR, "cc-task-activate-intent.ts");
11497
- 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");
11498
11611
  if (mode === "stub") {
11499
- const stubCommonPath2 = join10(HOOKS_DIR, "_synkro-stub-common.ts");
11612
+ const stubCommonPath2 = join11(HOOKS_DIR, "_synkro-stub-common.ts");
11500
11613
  const stubFiles = [
11501
11614
  [stubCommonPath2, STUB_COMMON_TS],
11502
11615
  [bashScriptPath, STUB_BASH_JUDGE_TS],
@@ -11525,7 +11638,7 @@ function writeHookScripts(mode = "stub") {
11525
11638
  chmodSync2(mcpStdioProxyPath, 493);
11526
11639
  for (const stale of ["_synkro-common.ts", "_synkro-common.sh", "installExtractCore.ts"]) {
11527
11640
  try {
11528
- unlinkSync4(join10(HOOKS_DIR, stale));
11641
+ unlinkSync4(join11(HOOKS_DIR, stale));
11529
11642
  } catch {
11530
11643
  }
11531
11644
  }
@@ -11569,7 +11682,7 @@ function writeHookScripts(mode = "stub") {
11569
11682
  writeFileSync8(mcpStdioProxyPath, MCP_STDIO_PROXY_SRC, "utf-8");
11570
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");
11571
11684
  writeFileSync8(taskActivateIntentScriptPath, STUB_TASK_ACTIVATE_INTENT_TS, "utf-8");
11572
- const stubCommonPath = join10(HOOKS_DIR, "_synkro-stub-common.ts");
11685
+ const stubCommonPath = join11(HOOKS_DIR, "_synkro-stub-common.ts");
11573
11686
  writeFileSync8(stubCommonPath, STUB_COMMON_TS, "utf-8");
11574
11687
  chmodSync2(stubCommonPath, 493);
11575
11688
  writeFileSync8(mcpGateScriptPath, STUB_MCP_GATE_TS, "utf-8");
@@ -11623,11 +11736,11 @@ function shellQuoteSingle(value) {
11623
11736
  }
11624
11737
  function resolveSynkroBundle() {
11625
11738
  const scriptPath = process.argv[1];
11626
- if (scriptPath && existsSync11(scriptPath)) return scriptPath;
11739
+ if (scriptPath && existsSync12(scriptPath)) return scriptPath;
11627
11740
  return null;
11628
11741
  }
11629
11742
  function writeConfigEnv(opts) {
11630
- const credsPath = join10(SYNKRO_DIR5, "credentials.json");
11743
+ const credsPath = join11(SYNKRO_DIR6, "credentials.json");
11631
11744
  const safeGateway = sanitizeConfigValue(opts.gatewayUrl);
11632
11745
  const safeUserId = sanitizeConfigValue(opts.userId);
11633
11746
  const safeOrgId = sanitizeConfigValue(opts.orgId);
@@ -11643,7 +11756,7 @@ function writeConfigEnv(opts) {
11643
11756
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
11644
11757
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
11645
11758
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
11646
- `SYNKRO_VERSION=${shellQuoteSingle("1.7.9")}`
11759
+ `SYNKRO_VERSION=${shellQuoteSingle("1.7.24")}`
11647
11760
  ];
11648
11761
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
11649
11762
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -11676,7 +11789,7 @@ async function getOrMintCloudToken(gatewayUrl) {
11676
11789
  assertGatewayAllowed(gatewayUrl);
11677
11790
  let stored = "";
11678
11791
  try {
11679
- stored = readFileSync10(CLOUD_JWT_PATH, "utf-8").trim();
11792
+ stored = readFileSync11(CLOUD_JWT_PATH, "utf-8").trim();
11680
11793
  } catch {
11681
11794
  }
11682
11795
  if (stored && !jwtExpired(stored)) return stored;
@@ -11735,7 +11848,7 @@ async function provisionCloudContainer(opts) {
11735
11848
  let cursorApiKey = "";
11736
11849
  if (cursorTotal > 0) {
11737
11850
  try {
11738
- 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();
11739
11852
  } catch {
11740
11853
  }
11741
11854
  }
@@ -11892,8 +12005,8 @@ async function verifyCloudGrader(jwt2) {
11892
12005
  }
11893
12006
  function readPersistedDeployLocation() {
11894
12007
  try {
11895
- if (existsSync11(CONFIG_PATH2)) {
11896
- 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);
11897
12010
  if (m?.[1] === "cloud") return "cloud";
11898
12011
  }
11899
12012
  } catch {
@@ -11901,8 +12014,8 @@ function readPersistedDeployLocation() {
11901
12014
  return "local";
11902
12015
  }
11903
12016
  function updateConfigEnvLocation(location) {
11904
- if (!existsSync11(CONFIG_PATH2)) return;
11905
- let env = readFileSync10(CONFIG_PATH2, "utf-8");
12017
+ if (!existsSync12(CONFIG_PATH2)) return;
12018
+ let env = readFileSync11(CONFIG_PATH2, "utf-8");
11906
12019
  const set = (k, v) => {
11907
12020
  const re = new RegExp(`^${k}=.*$`, "m");
11908
12021
  const line = `${k}='${v}'`;
@@ -11917,7 +12030,7 @@ async function applyMcpConfig(opts) {
11917
12030
  if (!opts.hasClaudeCode && !opts.hasCursor) return;
11918
12031
  let mcpJwt = "";
11919
12032
  try {
11920
- mcpJwt = readFileSync10(join10(SYNKRO_DIR5, ".mcp-jwt"), "utf-8").trim();
12033
+ mcpJwt = readFileSync11(join11(SYNKRO_DIR6, ".mcp-jwt"), "utf-8").trim();
11921
12034
  } catch {
11922
12035
  }
11923
12036
  if (!mcpJwt) {
@@ -12035,8 +12148,8 @@ function resolveDeploymentMode() {
12035
12148
  const envOverride = process.env.SYNKRO_DEPLOYMENT_MODE?.toLowerCase();
12036
12149
  if (envOverride === "bare-host" || envOverride === "docker") return envOverride;
12037
12150
  try {
12038
- if (existsSync11(CONFIG_PATH2)) {
12039
- 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);
12040
12153
  const val = m?.[1]?.toLowerCase();
12041
12154
  if (val === "bare-host" || val === "docker") return val;
12042
12155
  }
@@ -12063,16 +12176,16 @@ function collectLocalMetadata(includeClaudeCode = true) {
12063
12176
  meta.cc_version = execSync6("claude --version", { encoding: "utf-8", timeout: 5e3 }).trim().split("\n")[0];
12064
12177
  } catch {
12065
12178
  }
12066
- const claudeDir = join10(homedir10(), ".claude");
12179
+ const claudeDir = join11(homedir11(), ".claude");
12067
12180
  try {
12068
- const settings = JSON.parse(readFileSync10(join10(claudeDir, "settings.json"), "utf-8"));
12181
+ const settings = JSON.parse(readFileSync11(join11(claudeDir, "settings.json"), "utf-8"));
12069
12182
  const plugins = Object.keys(settings.enabledPlugins ?? {}).filter((k) => settings.enabledPlugins[k]);
12070
12183
  if (plugins.length) meta.enabled_plugins = plugins;
12071
12184
  if (settings.permissions?.defaultMode) meta.permissions_mode = settings.permissions.defaultMode;
12072
12185
  } catch {
12073
12186
  }
12074
12187
  try {
12075
- 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"));
12076
12189
  const mcpNames = Object.keys(mcpCache);
12077
12190
  if (mcpNames.length) meta.mcp_servers = mcpNames;
12078
12191
  } catch {
@@ -12084,10 +12197,10 @@ function collectLocalMetadata(includeClaudeCode = true) {
12084
12197
  } catch {
12085
12198
  }
12086
12199
  try {
12087
- const sessionsDir = join10(claudeDir, "sessions");
12200
+ const sessionsDir = join11(claudeDir, "sessions");
12088
12201
  const files = readdirSync3(sessionsDir).filter((f) => f.endsWith(".json")).slice(-5);
12089
12202
  for (const f of files) {
12090
- const s = JSON.parse(readFileSync10(join10(sessionsDir, f), "utf-8"));
12203
+ const s = JSON.parse(readFileSync11(join11(sessionsDir, f), "utf-8"));
12091
12204
  if (s.version) {
12092
12205
  meta.cc_version = meta.cc_version || s.version;
12093
12206
  break;
@@ -12275,9 +12388,9 @@ async function installCommand(opts = {}) {
12275
12388
  const scripts = writeHookScripts(hookMode);
12276
12389
  console.log("Wrote hook scripts to ~/.synkro/hooks/\n");
12277
12390
  for (const mode of ["edit", "bash"]) {
12278
- const pidFile = join10(SYNKRO_DIR5, "daemon", mode, "daemon.pid");
12391
+ const pidFile = join11(SYNKRO_DIR6, "daemon", mode, "daemon.pid");
12279
12392
  try {
12280
- const pid = parseInt(readFileSync10(pidFile, "utf-8").trim(), 10);
12393
+ const pid = parseInt(readFileSync11(pidFile, "utf-8").trim(), 10);
12281
12394
  if (pid > 0) {
12282
12395
  process.kill(pid, "SIGTERM");
12283
12396
  console.log(`Stopped stale ${mode} grader daemon (pid ${pid})`);
@@ -12360,7 +12473,7 @@ async function installCommand(opts = {}) {
12360
12473
  if (mintResp.ok) {
12361
12474
  const minted = await mintResp.json();
12362
12475
  mcpJwt = minted.token;
12363
- writeFileSync8(join10(SYNKRO_DIR5, ".mcp-jwt"), mcpJwt + "\n", { mode: 384 });
12476
+ writeFileSync8(join11(SYNKRO_DIR6, ".mcp-jwt"), mcpJwt + "\n", { mode: 384 });
12364
12477
  } else {
12365
12478
  console.warn(" \u26A0 Could not mint MCP token \u2014 local server will reject requests until re-installed.");
12366
12479
  }
@@ -12388,7 +12501,7 @@ async function installCommand(opts = {}) {
12388
12501
  throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);
12389
12502
  }
12390
12503
  const minted = await mintResp.json();
12391
- writeFileSync8(join10(SYNKRO_DIR5, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
12504
+ writeFileSync8(join11(SYNKRO_DIR6, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
12392
12505
  const mcp = installMcpConfig({ gatewayUrl, bearerToken: minted.token });
12393
12506
  console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);
12394
12507
  console.log(` url: ${mcp.url}`);
@@ -12405,8 +12518,8 @@ async function installCommand(opts = {}) {
12405
12518
  if (hasCursor && !opts.noMcp) {
12406
12519
  try {
12407
12520
  if (useLocalMcp) {
12408
- const jwtPath = join10(SYNKRO_DIR5, ".mcp-jwt");
12409
- if (!existsSync11(jwtPath)) {
12521
+ const jwtPath = join11(SYNKRO_DIR6, ".mcp-jwt");
12522
+ if (!existsSync12(jwtPath)) {
12410
12523
  const mintResp = await fetch(`${gatewayUrl}/api/v1/cli/mcp-token`, {
12411
12524
  method: "POST",
12412
12525
  headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
@@ -12434,7 +12547,7 @@ async function installCommand(opts = {}) {
12434
12547
  throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);
12435
12548
  }
12436
12549
  const minted = await mintResp.json();
12437
- writeFileSync8(join10(SYNKRO_DIR5, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
12550
+ writeFileSync8(join11(SYNKRO_DIR6, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
12438
12551
  const mcp = installCursorMcpConfig({ gatewayUrl, bearerToken: minted.token });
12439
12552
  console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);
12440
12553
  console.log(` url: ${mcp.url}`);
@@ -12516,7 +12629,7 @@ async function installCommand(opts = {}) {
12516
12629
  const ready = await waitForContainerReady(6e4);
12517
12630
  if (ready) {
12518
12631
  console.log(" \u2713 container ready");
12519
- const mcpJwt = readFileSync10(join10(SYNKRO_DIR5, ".mcp-jwt"), "utf-8").trim();
12632
+ const mcpJwt = readFileSync11(join11(SYNKRO_DIR6, ".mcp-jwt"), "utf-8").trim();
12520
12633
  try {
12521
12634
  const ingestResp = await fetch(`http://127.0.0.1:${hostMcpPort}/api/ingest`, {
12522
12635
  method: "POST",
@@ -12537,6 +12650,7 @@ async function installCommand(opts = {}) {
12537
12650
  if (workersUp) {
12538
12651
  console.log(" \u2713 workers ready");
12539
12652
  await syncSkillFiles();
12653
+ await discoverAndIngestSkills();
12540
12654
  } else {
12541
12655
  console.warn(" \u26A0 workers did not register within 30s \u2014 skill sync skipped");
12542
12656
  }
@@ -12550,6 +12664,11 @@ async function installCommand(opts = {}) {
12550
12664
  if (hasCursor) await promptCursorApiKey(opts);
12551
12665
  await provisionCloudContainer({ gatewayUrl, jwt: token, orgId, userId, email, hasClaudeCode, hasCursor });
12552
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
+ }
12553
12672
  }
12554
12673
  if (transcriptConsent) {
12555
12674
  const repo = detectGitRepo2();
@@ -12558,7 +12677,7 @@ async function installCommand(opts = {}) {
12558
12677
  try {
12559
12678
  let mcpToken = "";
12560
12679
  try {
12561
- mcpToken = readFileSync10(join10(SYNKRO_DIR5, ".mcp-jwt"), "utf-8").trim();
12680
+ mcpToken = readFileSync11(join11(SYNKRO_DIR6, ".mcp-jwt"), "utf-8").trim();
12562
12681
  } catch {
12563
12682
  }
12564
12683
  if (mcpToken) {
@@ -12694,8 +12813,8 @@ function writeSynkroFileIfMissing(opts) {
12694
12813
  try {
12695
12814
  const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
12696
12815
  if (!root) return;
12697
- if (root === homedir10()) return;
12698
- const fp = join10(root, "synkro.toml");
12816
+ if (root === homedir11()) return;
12817
+ const fp = join11(root, "synkro.toml");
12699
12818
  let hasFile = false;
12700
12819
  try {
12701
12820
  hasFile = statSync(fp).isFile();
@@ -12740,9 +12859,9 @@ function readFullSynkroFile() {
12740
12859
  try {
12741
12860
  const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
12742
12861
  if (!root) return null;
12743
- const fp = join10(root, "synkro.toml");
12744
- if (!existsSync11(fp)) return null;
12745
- 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"));
12746
12865
  const valid = ["claude-code", "cursor"];
12747
12866
  const harness = Array.isArray(parsed.harness) ? parsed.harness.filter((h) => valid.includes(h)) : ["claude-code", "cursor"];
12748
12867
  const resolved = resolveGraderPool(parsed);
@@ -12780,7 +12899,7 @@ function reconcileHarness() {
12780
12899
  console.log(`synkro.toml: harness=[${sf.harness.join(", ")}] pool=${poolLabel(sf.grader.pool)} mode=${sf.grader.mode}`);
12781
12900
  const scripts = writeHookScripts(resolvePersistedHookMode());
12782
12901
  console.log("Wrote hook scripts to ~/.synkro/hooks/");
12783
- const ccSettings = join10(homedir10(), ".claude", "settings.json");
12902
+ const ccSettings = join11(homedir11(), ".claude", "settings.json");
12784
12903
  if (wantCC) {
12785
12904
  installCCHooks(ccSettings, {
12786
12905
  bashJudgeScriptPath: scripts.bashScript,
@@ -12800,7 +12919,7 @@ function reconcileHarness() {
12800
12919
  });
12801
12920
  console.log(" \u2713 Claude Code hooks registered");
12802
12921
  try {
12803
- const mcpJwt = readFileSync10(join10(SYNKRO_DIR5, ".mcp-jwt"), "utf-8").trim();
12922
+ const mcpJwt = readFileSync11(join11(SYNKRO_DIR6, ".mcp-jwt"), "utf-8").trim();
12804
12923
  if (mcpJwt) {
12805
12924
  installMcpConfig({ gatewayUrl: "", bearerToken: mcpJwt, local: true });
12806
12925
  console.log(" \u2713 Claude Code MCP registered");
@@ -12812,7 +12931,7 @@ function reconcileHarness() {
12812
12931
  if (uninstallMcpConfig()) console.log(" \u2717 Claude Code MCP removed");
12813
12932
  if (uninstallClaudeDesktopMcpConfig()) console.log(" \u2717 Claude Desktop MCP removed");
12814
12933
  }
12815
- const cursorHooks = join10(homedir10(), ".cursor", "hooks.json");
12934
+ const cursorHooks = join11(homedir11(), ".cursor", "hooks.json");
12816
12935
  if (wantCursor) {
12817
12936
  installCursorHooks(cursorHooks, {
12818
12937
  bashJudgeScriptPath: scripts.cursorBashJudgeScript,
@@ -12859,6 +12978,34 @@ function reconcileHarness() {
12859
12978
  if (providers.length === 0) providers.push("claude_code");
12860
12979
  return splitWorkers(total, providers);
12861
12980
  }
12981
+ async function ingestSkillTasks(tasks, mcpPort) {
12982
+ const summary = { ingested: 0, rules: 0, rejected: [] };
12983
+ for (const { source, content } of tasks) {
12984
+ try {
12985
+ const resp = await fetch(`http://127.0.0.1:${mcpPort}/api/local/skills/sync`, {
12986
+ method: "POST",
12987
+ headers: { "Content-Type": "application/json" },
12988
+ body: JSON.stringify({ source, content }),
12989
+ signal: AbortSignal.timeout(65e3)
12990
+ });
12991
+ if (!resp.ok) throw new Error(`${resp.status}`);
12992
+ const r = await resp.json();
12993
+ if (r.status === "rejected") {
12994
+ const reason = (r.reasons || []).slice(0, 2).join("; ") || r.message || "blocked by security vetting";
12995
+ summary.rejected.push({ source, reason });
12996
+ console.log(` \u26D4 skill ${source}: BLOCKED \u2014 ${reason}`);
12997
+ } else {
12998
+ const created = r.created || 0;
12999
+ summary.ingested++;
13000
+ summary.rules += created;
13001
+ console.log(created > 0 ? ` \u2713 skill ${source}: ${created} rule${created === 1 ? "" : "s"} \u2192 its own pack` : ` \u2713 skill ${source}: ${r.message || "up to date"}`);
13002
+ }
13003
+ } catch (e) {
13004
+ console.warn(` \u26A0 skill ${source}: sync failed (${e instanceof Error ? e.message : String(e)})`);
13005
+ }
13006
+ }
13007
+ return summary;
13008
+ }
12862
13009
  async function syncSkillFiles() {
12863
13010
  const sf = readFullSynkroFile();
12864
13011
  if (!sf || sf.skills.length === 0) return;
@@ -12866,7 +13013,7 @@ async function syncSkillFiles() {
12866
13013
  if (resolved.length === 0) return;
12867
13014
  const mcpPort = process.env.SYNKRO_MCP_PORT || "18931";
12868
13015
  const tasks = resolved.map((fp) => {
12869
- const content = readFileSync10(fp, "utf-8");
13016
+ const content = readFileSync11(fp, "utf-8");
12870
13017
  const source = `skill:${fp.split("/").pop()}`;
12871
13018
  if (!content.trim()) {
12872
13019
  console.log(` \u2298 skill ${source}: empty file, skipped`);
@@ -12878,20 +13025,128 @@ async function syncSkillFiles() {
12878
13025
  }).filter(Boolean);
12879
13026
  if (tasks.length === 0) return;
12880
13027
  console.log(` indexing ${tasks.length} skill file${tasks.length === 1 ? "" : "s"} \u2014 comparing against existing ruleset (this may take a few seconds)...`);
12881
- for (const { source, content } of tasks) {
13028
+ await ingestSkillTasks(tasks, mcpPort);
13029
+ }
13030
+ function discoverSkillFiles(repoRoot, excludeHashes) {
13031
+ const roots = [];
13032
+ const add = (p) => {
12882
13033
  try {
12883
- const resp = await fetch(`http://127.0.0.1:${mcpPort}/api/local/skills/sync`, {
12884
- method: "POST",
12885
- headers: { "Content-Type": "application/json" },
12886
- body: JSON.stringify({ source, content }),
12887
- signal: AbortSignal.timeout(65e3)
12888
- });
12889
- if (!resp.ok) throw new Error(`${resp.status}`);
12890
- const { created, message } = await resp.json();
12891
- console.log(created > 0 ? ` \u2713 skill ${source}: ${created} new rule${created === 1 ? "" : "s"} added to index` : ` \u2713 skill ${source}: ${message || "up to date"} \u2014 index unchanged`);
12892
- } catch (e) {
12893
- console.warn(` \u26A0 skill ${source}: sync failed (${e instanceof Error ? e.message : String(e)})`);
13034
+ if (p && existsSync12(p) && statSync(p).isDirectory()) roots.push(p);
13035
+ } catch {
13036
+ }
13037
+ };
13038
+ add(join11(homedir11(), ".claude", "skills"));
13039
+ add(join11(homedir11(), ".agents", "skills"));
13040
+ if (repoRoot) {
13041
+ add(join11(repoRoot, ".claude", "skills"));
13042
+ add(join11(repoRoot, ".agents", "skills"));
13043
+ }
13044
+ const out = [];
13045
+ const seen = /* @__PURE__ */ new Set();
13046
+ const MAX = 200;
13047
+ const consider = (file, name) => {
13048
+ if (out.length >= MAX) return;
13049
+ let content = "";
13050
+ try {
13051
+ const st = statSync(file);
13052
+ if (!st.isFile() || st.size > 2e5) return;
13053
+ content = readFileSync11(file, "utf-8");
13054
+ } catch {
13055
+ return;
13056
+ }
13057
+ if (!content.trim()) return;
13058
+ const hash = createHash("sha256").update(content).digest("hex");
13059
+ if (seen.has(hash) || excludeHashes.has(hash)) return;
13060
+ seen.add(hash);
13061
+ out.push({ source: `skill:${name}`, content, name, hash });
13062
+ };
13063
+ for (const root of roots) {
13064
+ let entries = [];
13065
+ try {
13066
+ entries = readdirSync3(root);
13067
+ } catch {
13068
+ continue;
13069
+ }
13070
+ for (const entry of entries) {
13071
+ if (entry.startsWith(".")) continue;
13072
+ const full = join11(root, entry);
13073
+ let st;
13074
+ try {
13075
+ st = statSync(full);
13076
+ } catch {
13077
+ continue;
13078
+ }
13079
+ if (st.isDirectory()) {
13080
+ const skillMd = join11(full, "SKILL.md");
13081
+ if (existsSync12(skillMd)) consider(skillMd, entry);
13082
+ } else if (/\.mdx?$/i.test(entry) && entry.toUpperCase() !== "README.MD") {
13083
+ consider(full, entry.replace(/\.mdx?$/i, ""));
13084
+ }
13085
+ }
13086
+ }
13087
+ return out;
13088
+ }
13089
+ function discoverySetHash(found) {
13090
+ return createHash("sha256").update(found.map((f) => f.hash).sort().join(",")).digest("hex");
13091
+ }
13092
+ async function promptSkillDiscovery(found) {
13093
+ if (!process.stdin.isTTY || found.length === 0) return [];
13094
+ const rl = createInterface3({ input: process.stdin, output: process.stdout });
13095
+ const ask3 = (q) => new Promise((res) => rl.question(q, (a) => res(a.trim())));
13096
+ try {
13097
+ console.log(`
13098
+ Found ${found.length} skill${found.length === 1 ? "" : "s"} in your Claude Code / skills.sh folders:`);
13099
+ found.forEach((s, i) => console.log(` ${i + 1}. ${s.name}`));
13100
+ console.log("Ingest into Synkro as enforceable rules? Each is security-vetted first \u2014 anything");
13101
+ console.log("malicious (prompt injection, guardrail-weakening, secret exfil) is blocked and reported.");
13102
+ const a = (await ask3("Ingest [all] / type numbers (e.g. 1,3) / [skip]: ")).toLowerCase();
13103
+ if (a === "skip" || a === "n" || a === "no" || a === "s") return [];
13104
+ if (a === "" || a === "all" || a === "y" || a === "yes" || a === "a") return found.map((_, i) => i);
13105
+ const picks = a.split(/[\s,]+/).map((x) => parseInt(x, 10) - 1).filter((i) => Number.isInteger(i) && i >= 0 && i < found.length);
13106
+ return [...new Set(picks)];
13107
+ } finally {
13108
+ rl.close();
13109
+ }
13110
+ }
13111
+ async function discoverAndIngestSkills() {
13112
+ try {
13113
+ const sf = readFullSynkroFile();
13114
+ const repoRoot = sf?._repoRoot || detectGitRepo2();
13115
+ const excludeHashes = /* @__PURE__ */ new Set();
13116
+ if (sf?.skills?.length) {
13117
+ for (const fp of resolveSkillPaths(sf.skills, sf._repoRoot)) {
13118
+ try {
13119
+ excludeHashes.add(createHash("sha256").update(readFileSync11(fp, "utf-8")).digest("hex"));
13120
+ } catch {
13121
+ }
13122
+ }
13123
+ }
13124
+ const found = discoverSkillFiles(repoRoot, excludeHashes);
13125
+ if (found.length === 0) return;
13126
+ const setHash = discoverySetHash(found);
13127
+ let prev = "";
13128
+ try {
13129
+ prev = readFileSync11(SKILLS_DISCOVERED_PATH, "utf-8").trim();
13130
+ } catch {
13131
+ }
13132
+ if (prev === setHash) return;
13133
+ const picks = await promptSkillDiscovery(found);
13134
+ try {
13135
+ writeFileSync8(SKILLS_DISCOVERED_PATH, setHash);
13136
+ } catch {
12894
13137
  }
13138
+ if (picks.length === 0) {
13139
+ console.log(" Skipped \u2014 you can ingest skills anytime from the Synkro dashboard or via the MCP tool.");
13140
+ return;
13141
+ }
13142
+ const mcpPort = process.env.SYNKRO_MCP_PORT || "18931";
13143
+ console.log(` Ingesting ${picks.length} skill${picks.length === 1 ? "" : "s"} (security-vetting each first)\u2026`);
13144
+ const summary = await ingestSkillTasks(picks.map((i) => ({ source: found[i].source, content: found[i].content })), mcpPort);
13145
+ const parts = [`${summary.ingested} ingested (${summary.rules} rule${summary.rules === 1 ? "" : "s"})`];
13146
+ if (summary.rejected.length) parts.push(`${summary.rejected.length} blocked`);
13147
+ console.log(` Skills: ${parts.join(", ")} \u2014 each in its own toggleable pack.`);
13148
+ } catch (e) {
13149
+ console.warn(` \u26A0 skill discovery skipped (${e instanceof Error ? e.message : String(e)})`);
12895
13150
  }
12896
13151
  }
12897
13152
  function detectGitRepo2() {
@@ -12912,17 +13167,17 @@ function detectGitRepo2() {
12912
13167
  function getClaudeProjectsFolder() {
12913
13168
  const cwd = process.cwd();
12914
13169
  const sanitized = "-" + cwd.replace(/\//g, "-");
12915
- const projectsDir = join10(homedir10(), ".claude", "projects", sanitized);
12916
- return existsSync11(projectsDir) ? projectsDir : null;
13170
+ const projectsDir = join11(homedir11(), ".claude", "projects", sanitized);
13171
+ return existsSync12(projectsDir) ? projectsDir : null;
12917
13172
  }
12918
13173
  function extractSessionInsights(projectsDir) {
12919
13174
  const insights = [];
12920
13175
  const files = readdirSync3(projectsDir).filter((f) => f.endsWith(".jsonl"));
12921
13176
  for (const file of files) {
12922
13177
  const sessionId = file.replace(".jsonl", "");
12923
- const filePath = join10(projectsDir, file);
13178
+ const filePath = join11(projectsDir, file);
12924
13179
  try {
12925
- const content = readFileSync10(filePath, "utf-8");
13180
+ const content = readFileSync11(filePath, "utf-8");
12926
13181
  const lines = content.split("\n").filter(Boolean);
12927
13182
  for (let i = 0; i < lines.length; i++) {
12928
13183
  try {
@@ -13001,14 +13256,14 @@ function cursorProjectSlug(workspaceRoot) {
13001
13256
  return workspaceRoot.replace(/^[/]+/, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "");
13002
13257
  }
13003
13258
  function getCursorTranscriptsDir() {
13004
- const dir = join10(homedir10(), ".cursor", "projects", cursorProjectSlug(process.cwd()), "agent-transcripts");
13005
- return existsSync11(dir) ? dir : null;
13259
+ const dir = join11(homedir11(), ".cursor", "projects", cursorProjectSlug(process.cwd()), "agent-transcripts");
13260
+ return existsSync12(dir) ? dir : null;
13006
13261
  }
13007
13262
  function isSafeConvId(id) {
13008
13263
  return /^[A-Za-z0-9_-]+$/.test(id);
13009
13264
  }
13010
13265
  function parseCursorTranscriptFile(filePath) {
13011
- const content = readFileSync10(filePath, "utf-8");
13266
+ const content = readFileSync11(filePath, "utf-8");
13012
13267
  const lines = content.split("\n").filter(Boolean);
13013
13268
  const messages = [];
13014
13269
  for (let i = 0; i < lines.length; i++) {
@@ -13040,8 +13295,8 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
13040
13295
  for (let i = 0; i < convDirs.length; i++) {
13041
13296
  const convId = convDirs[i];
13042
13297
  if (!isSafeConvId(convId)) continue;
13043
- const filePath = join10(dir, convId, `${convId}.jsonl`);
13044
- if (!existsSync11(filePath)) continue;
13298
+ const filePath = join11(dir, convId, `${convId}.jsonl`);
13299
+ if (!existsSync12(filePath)) continue;
13045
13300
  try {
13046
13301
  const all = parseCursorTranscriptFile(filePath);
13047
13302
  const messages = all.length > 500 ? all.slice(-500) : all;
@@ -13063,8 +13318,8 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
13063
13318
  process.stdout.write(`\r Progress: ${i + 1}/${convDirs.length} sessions (${totalMessages} messages embedded) `);
13064
13319
  }
13065
13320
  try {
13066
- const lc = readFileSync10(filePath, "utf-8").split("\n").filter(Boolean).length;
13067
- 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");
13068
13323
  } catch {
13069
13324
  }
13070
13325
  }
@@ -13072,7 +13327,7 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
13072
13327
  return { sessions: totalSessions, messages: totalMessages };
13073
13328
  }
13074
13329
  function parseTranscriptFile(filePath) {
13075
- const content = readFileSync10(filePath, "utf-8");
13330
+ const content = readFileSync11(filePath, "utf-8");
13076
13331
  const lines = content.split("\n").filter(Boolean);
13077
13332
  const messages = [];
13078
13333
  for (let i = 0; i < lines.length; i++) {
@@ -13120,7 +13375,7 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
13120
13375
  for (let i = 0; i < files.length; i++) {
13121
13376
  const file = files[i];
13122
13377
  const sessionId = file.replace(".jsonl", "");
13123
- const filePath = join10(projectsDir, file);
13378
+ const filePath = join11(projectsDir, file);
13124
13379
  try {
13125
13380
  const allMessages = parseTranscriptFile(filePath);
13126
13381
  const messages = allMessages.length > 500 ? allMessages.slice(-500) : allMessages;
@@ -13142,9 +13397,9 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
13142
13397
  process.stdout.write(`\r Progress: ${i + 1}/${files.length} sessions (${totalMessages} messages embedded) `);
13143
13398
  }
13144
13399
  try {
13145
- const content = readFileSync10(join10(projectsDir, file), "utf-8");
13400
+ const content = readFileSync11(join11(projectsDir, file), "utf-8");
13146
13401
  const lineCount = content.split("\n").filter(Boolean).length;
13147
- writeFileSync8(join10(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
13402
+ writeFileSync8(join11(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
13148
13403
  } catch {
13149
13404
  }
13150
13405
  }
@@ -13165,7 +13420,7 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
13165
13420
  const sessions = [];
13166
13421
  for (const file of batch) {
13167
13422
  const sessionId = file.replace(".jsonl", "");
13168
- const filePath = join10(projectsDir, file);
13423
+ const filePath = join11(projectsDir, file);
13169
13424
  try {
13170
13425
  const allMessages = parseTranscriptFile(filePath);
13171
13426
  const messages = allMessages.length > maxMessagesPerSession ? allMessages.slice(-maxMessagesPerSession) : allMessages;
@@ -13194,18 +13449,18 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
13194
13449
  }
13195
13450
  for (const file of batch) {
13196
13451
  const sessionId = file.replace(".jsonl", "");
13197
- const filePath = join10(projectsDir, file);
13452
+ const filePath = join11(projectsDir, file);
13198
13453
  try {
13199
- const content = readFileSync10(filePath, "utf-8");
13454
+ const content = readFileSync11(filePath, "utf-8");
13200
13455
  const lineCount = content.split("\n").filter(Boolean).length;
13201
- writeFileSync8(join10(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
13456
+ writeFileSync8(join11(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
13202
13457
  } catch {
13203
13458
  }
13204
13459
  }
13205
13460
  }
13206
13461
  return { sessions: totalSessions, messages: totalMessages };
13207
13462
  }
13208
- var SYNKRO_DIR5, HOOKS_DIR, BIN_DIR, CONFIG_PATH2, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, CLOUD_JWT_PATH;
13463
+ var SYNKRO_DIR6, HOOKS_DIR, BIN_DIR, CONFIG_PATH2, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, CLOUD_JWT_PATH, SKILLS_DISCOVERED_PATH;
13209
13464
  var init_install = __esm({
13210
13465
  "cli/commands/install.ts"() {
13211
13466
  "use strict";
@@ -13224,10 +13479,10 @@ var init_install = __esm({
13224
13479
  init_claudeDesktopTap();
13225
13480
  init_dockerInstall();
13226
13481
  init_setupToken();
13227
- SYNKRO_DIR5 = join10(homedir10(), ".synkro");
13228
- HOOKS_DIR = join10(SYNKRO_DIR5, "hooks");
13229
- BIN_DIR = join10(SYNKRO_DIR5, "bin");
13230
- 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");
13231
13486
  MCP_STDIO_PROXY_SRC = `#!/usr/bin/env bun
13232
13487
  import { readFileSync } from 'node:fs';
13233
13488
  import { homedir } from 'node:os';
@@ -13338,20 +13593,21 @@ rl.on('line', async (line) => {
13338
13593
  }
13339
13594
  });
13340
13595
  `;
13341
- OFFSETS_DIR = join10(SYNKRO_DIR5, ".transcript-offsets");
13342
- CLOUD_JWT_PATH = join10(SYNKRO_DIR5, ".cloud-jwt");
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");
13343
13599
  }
13344
13600
  });
13345
13601
 
13346
13602
  // cli/local-cc/install.ts
13347
- 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";
13348
- import { join as join11 } from "path";
13349
- import { homedir as homedir11 } from "os";
13350
- 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";
13351
13607
  function writePluginFiles() {
13352
13608
  for (const c of CHANNELS) {
13353
- mkdirSync10(c.sessionDir, { recursive: true });
13354
- mkdirSync10(c.pluginSettingsDir, { recursive: true });
13609
+ mkdirSync11(c.sessionDir, { recursive: true });
13610
+ mkdirSync11(c.pluginSettingsDir, { recursive: true });
13355
13611
  writeFileSync9(c.pluginPkgPath, PLUGIN_PACKAGE_JSON, "utf-8");
13356
13612
  writeFileSync9(
13357
13613
  c.pluginSettingsPath,
@@ -13374,7 +13630,7 @@ function writePluginFiles() {
13374
13630
  }
13375
13631
  function runBunInstall() {
13376
13632
  for (const c of CHANNELS) {
13377
- const r = spawnSync5("bun", ["install", "--silent"], {
13633
+ const r = spawnSync6("bun", ["install", "--silent"], {
13378
13634
  cwd: c.sessionDir,
13379
13635
  encoding: "utf-8",
13380
13636
  timeout: 12e4
@@ -13387,10 +13643,10 @@ function runBunInstall() {
13387
13643
  }
13388
13644
  }
13389
13645
  function safelyMutateClaudeJson(mutator) {
13390
- if (!existsSync12(CLAUDE_JSON_PATH)) {
13646
+ if (!existsSync13(CLAUDE_JSON_PATH)) {
13391
13647
  return;
13392
13648
  }
13393
- const originalText = readFileSync11(CLAUDE_JSON_PATH, "utf-8");
13649
+ const originalText = readFileSync12(CLAUDE_JSON_PATH, "utf-8");
13394
13650
  let parsed;
13395
13651
  try {
13396
13652
  parsed = JSON.parse(originalText);
@@ -13490,15 +13746,15 @@ function patchClaudeJson() {
13490
13746
  });
13491
13747
  }
13492
13748
  function installLocalCC() {
13493
- let bunCheck = spawnSync5("bun", ["--version"], { encoding: "utf-8" });
13749
+ let bunCheck = spawnSync6("bun", ["--version"], { encoding: "utf-8" });
13494
13750
  if (bunCheck.status !== 0) {
13495
13751
  if (process.platform === "darwin") {
13496
13752
  console.log(" Installing bun via brew...");
13497
- 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 });
13498
13754
  if (brewR.status !== 0) {
13499
13755
  throw new LocalCCInstallError("bun auto-install failed. Install manually: curl -fsSL https://bun.sh/install | bash");
13500
13756
  }
13501
- bunCheck = spawnSync5("bun", ["--version"], { encoding: "utf-8" });
13757
+ bunCheck = spawnSync6("bun", ["--version"], { encoding: "utf-8" });
13502
13758
  if (bunCheck.status !== 0) {
13503
13759
  throw new LocalCCInstallError("bun installed but not found on PATH. Restart your terminal and re-run install.");
13504
13760
  }
@@ -13532,42 +13788,42 @@ var CLAUDE_JSON_BACKUP_PATH, SESSION_DIR, PLUGIN_PATH, PLUGIN_PKG_PATH, PLUGIN_S
13532
13788
  var init_install2 = __esm({
13533
13789
  "cli/local-cc/install.ts"() {
13534
13790
  "use strict";
13535
- CLAUDE_JSON_BACKUP_PATH = join11(homedir11(), ".claude.json.synkro-bak");
13536
- SESSION_DIR = join11(homedir11(), ".synkro", "cc_sessions");
13537
- PLUGIN_PATH = join11(SESSION_DIR, "synkro-channel.ts");
13538
- PLUGIN_PKG_PATH = join11(SESSION_DIR, "package.json");
13539
- PLUGIN_SETTINGS_DIR = join11(SESSION_DIR, ".claude");
13540
- PLUGIN_SETTINGS_PATH = join11(PLUGIN_SETTINGS_DIR, "settings.json");
13541
- PROJECT_MCP_PATH = join11(SESSION_DIR, ".mcp.json");
13542
- CLAUDE_JSON_PATH = join11(homedir11(), ".claude.json");
13543
- 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");
13544
13800
  TMUX_SESSION_NAME = "synkro-local-cc";
13545
13801
  CHANNEL_1_PORT = 8941;
13546
- SESSION_DIR_2 = join11(homedir11(), ".synkro", "cc_sessions_2");
13547
- PLUGIN_PATH_2 = join11(SESSION_DIR_2, "synkro-channel.ts");
13548
- PLUGIN_PKG_PATH_2 = join11(SESSION_DIR_2, "package.json");
13549
- PLUGIN_SETTINGS_DIR_2 = join11(SESSION_DIR_2, ".claude");
13550
- PLUGIN_SETTINGS_PATH_2 = join11(PLUGIN_SETTINGS_DIR_2, "settings.json");
13551
- PROJECT_MCP_PATH_2 = join11(SESSION_DIR_2, ".mcp.json");
13552
- 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");
13553
13809
  TMUX_SESSION_NAME_2 = "synkro-local-cc-2";
13554
13810
  CHANNEL_2_PORT = 8951;
13555
- SESSION_DIR_3 = join11(homedir11(), ".synkro", "cc_sessions_3");
13556
- PLUGIN_PATH_3 = join11(SESSION_DIR_3, "synkro-channel.ts");
13557
- PLUGIN_PKG_PATH_3 = join11(SESSION_DIR_3, "package.json");
13558
- PLUGIN_SETTINGS_DIR_3 = join11(SESSION_DIR_3, ".claude");
13559
- PLUGIN_SETTINGS_PATH_3 = join11(PLUGIN_SETTINGS_DIR_3, "settings.json");
13560
- PROJECT_MCP_PATH_3 = join11(SESSION_DIR_3, ".mcp.json");
13561
- 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");
13562
13818
  TMUX_SESSION_NAME_3 = "synkro-local-cc-3";
13563
13819
  CHANNEL_3_PORT = 8942;
13564
- SESSION_DIR_4 = join11(homedir11(), ".synkro", "cc_sessions_4");
13565
- PLUGIN_PATH_4 = join11(SESSION_DIR_4, "synkro-channel.ts");
13566
- PLUGIN_PKG_PATH_4 = join11(SESSION_DIR_4, "package.json");
13567
- PLUGIN_SETTINGS_DIR_4 = join11(SESSION_DIR_4, ".claude");
13568
- PLUGIN_SETTINGS_PATH_4 = join11(PLUGIN_SETTINGS_DIR_4, "settings.json");
13569
- PROJECT_MCP_PATH_4 = join11(SESSION_DIR_4, ".mcp.json");
13570
- 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");
13571
13827
  TMUX_SESSION_NAME_4 = "synkro-local-cc-4";
13572
13828
  CHANNEL_4_PORT = 8952;
13573
13829
  RUN_SCRIPT_SOURCE = `#!/usr/bin/env bash
@@ -13841,10 +14097,10 @@ var disconnect_exports = {};
13841
14097
  __export(disconnect_exports, {
13842
14098
  disconnectCommand: () => disconnectCommand
13843
14099
  });
13844
- import { existsSync as existsSync13, rmSync, readdirSync as readdirSync4 } from "fs";
13845
- import { homedir as homedir12 } from "os";
13846
- import { join as join12 } from "path";
13847
- 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";
13848
14104
  import { createInterface as createInterface4 } from "readline";
13849
14105
  async function tearDownLocalCC() {
13850
14106
  const docker = dockerStatus();
@@ -13858,7 +14114,7 @@ async function tearDownLocalCC() {
13858
14114
  console.log("\u2713 removed synkro-server container");
13859
14115
  try {
13860
14116
  const image = imageTag();
13861
- 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 });
13862
14118
  console.log(r.status === 0 ? `\u2713 removed Docker image ${image}` : "\xB7 no Docker image to remove");
13863
14119
  } catch {
13864
14120
  }
@@ -13921,34 +14177,34 @@ async function disconnectCommand(args2 = []) {
13921
14177
  const desktopMcpRemoved = uninstallClaudeDesktopMcpConfig();
13922
14178
  console.log(`${desktopMcpRemoved ? "\u2713" : "\xB7"} MCP guardrails (Claude Desktop): ${desktopMcpRemoved ? "removed from claude_desktop_config.json" : "no entry found"}`);
13923
14179
  }
13924
- if (existsSync13(SYNKRO_DIR6)) {
14180
+ if (existsSync14(SYNKRO_DIR7)) {
13925
14181
  if (purge) {
13926
- rmSync(SYNKRO_DIR6, { recursive: true, force: true });
13927
- 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`);
13928
14184
  } else {
13929
- 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")]);
13930
14186
  const preserved = [];
13931
- for (const entry of readdirSync4(SYNKRO_DIR6)) {
13932
- const full = join12(SYNKRO_DIR6, entry);
14187
+ for (const entry of readdirSync4(SYNKRO_DIR7)) {
14188
+ const full = join13(SYNKRO_DIR7, entry);
13933
14189
  if (keep.has(full)) {
13934
14190
  preserved.push(entry);
13935
14191
  continue;
13936
14192
  }
13937
- rmSync(full, { recursive: true, force: true });
14193
+ rmSync2(full, { recursive: true, force: true });
13938
14194
  }
13939
14195
  if (preserved.length > 0) {
13940
- 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(", ")})`);
13941
14197
  console.log(" run `synkro uninstall --purge` to delete that too");
13942
14198
  } else {
13943
- console.log(`\u2713 removed ${SYNKRO_DIR6}`);
14199
+ console.log(`\u2713 removed ${SYNKRO_DIR7}`);
13944
14200
  }
13945
14201
  }
13946
14202
  } else {
13947
- console.log(`\xB7 ${SYNKRO_DIR6} already gone`);
14203
+ console.log(`\xB7 ${SYNKRO_DIR7} already gone`);
13948
14204
  }
13949
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.");
13950
14206
  }
13951
- var SYNKRO_DIR6;
14207
+ var SYNKRO_DIR7;
13952
14208
  var init_disconnect = __esm({
13953
14209
  "cli/commands/disconnect.ts"() {
13954
14210
  "use strict";
@@ -13959,14 +14215,14 @@ var init_disconnect = __esm({
13959
14215
  init_install2();
13960
14216
  init_dockerInstall();
13961
14217
  init_macKeychain();
13962
- SYNKRO_DIR6 = join12(homedir12(), ".synkro");
14218
+ SYNKRO_DIR7 = join13(homedir13(), ".synkro");
13963
14219
  }
13964
14220
  });
13965
14221
 
13966
14222
  // cli/local-cc/turnLog.ts
13967
- 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";
13968
- import { dirname as dirname6, join as join13 } from "path";
13969
- 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";
13970
14226
  function truncate(s, max = PREVIEW_MAX) {
13971
14227
  if (s.length <= max) return s;
13972
14228
  return s.slice(0, max) + "\u2026 [+" + (s.length - max) + " chars]";
@@ -13986,7 +14242,7 @@ function extractSeverity(result) {
13986
14242
  }
13987
14243
  function appendTurn(args2) {
13988
14244
  try {
13989
- mkdirSync11(dirname6(TURN_LOG_PATH), { recursive: true });
14245
+ mkdirSync12(dirname6(TURN_LOG_PATH), { recursive: true });
13990
14246
  const entry = {
13991
14247
  ts: new Date(args2.startedAt).toISOString(),
13992
14248
  role: args2.role,
@@ -14002,11 +14258,11 @@ function appendTurn(args2) {
14002
14258
  }
14003
14259
  }
14004
14260
  function readRecentTurns(n = 20) {
14005
- if (!existsSync14(TURN_LOG_PATH)) return [];
14261
+ if (!existsSync15(TURN_LOG_PATH)) return [];
14006
14262
  try {
14007
14263
  const size = statSync2(TURN_LOG_PATH).size;
14008
14264
  if (size === 0) return [];
14009
- const text = readFileSync12(TURN_LOG_PATH, "utf-8");
14265
+ const text = readFileSync13(TURN_LOG_PATH, "utf-8");
14010
14266
  const lines = text.split("\n").filter(Boolean);
14011
14267
  const lastN = lines.slice(-n).reverse();
14012
14268
  return lastN.map((line) => {
@@ -14022,8 +14278,8 @@ function readRecentTurns(n = 20) {
14022
14278
  }
14023
14279
  function followTurns(onEntry) {
14024
14280
  try {
14025
- mkdirSync11(dirname6(TURN_LOG_PATH), { recursive: true });
14026
- if (!existsSync14(TURN_LOG_PATH)) {
14281
+ mkdirSync12(dirname6(TURN_LOG_PATH), { recursive: true });
14282
+ if (!existsSync15(TURN_LOG_PATH)) {
14027
14283
  appendFileSync(TURN_LOG_PATH, "", "utf-8");
14028
14284
  }
14029
14285
  } catch {
@@ -14085,7 +14341,7 @@ var TURN_LOG_PATH, PREVIEW_MAX;
14085
14341
  var init_turnLog = __esm({
14086
14342
  "cli/local-cc/turnLog.ts"() {
14087
14343
  "use strict";
14088
- TURN_LOG_PATH = join13(homedir13(), ".synkro", "cc_sessions", "turns.log");
14344
+ TURN_LOG_PATH = join14(homedir14(), ".synkro", "cc_sessions", "turns.log");
14089
14345
  PREVIEW_MAX = 400;
14090
14346
  }
14091
14347
  });
@@ -14234,19 +14490,19 @@ var init_grade = __esm({
14234
14490
  });
14235
14491
 
14236
14492
  // cli/local-cc/pueue.ts
14237
- import { execFileSync as execFileSync3, spawnSync as spawnSync7, spawn as spawn2 } from "child_process";
14238
- import { homedir as homedir14 } from "os";
14239
- 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";
14240
14496
  import { connect as connect2 } from "net";
14241
14497
  function pueueAvailable() {
14242
- const r = spawnSync7("pueue", ["--version"], { encoding: "utf-8" });
14498
+ const r = spawnSync8("pueue", ["--version"], { encoding: "utf-8" });
14243
14499
  if (r.status !== 0) {
14244
14500
  throw new PueueError("pueue CLI not found on PATH. Install pueue (https://github.com/Nukesor/pueue) and start `pueued`.");
14245
14501
  }
14246
14502
  }
14247
14503
  function statusJson() {
14248
14504
  pueueAvailable();
14249
- const r = spawnSync7("pueue", ["status", "--json"], { encoding: "utf-8" });
14505
+ const r = spawnSync8("pueue", ["status", "--json"], { encoding: "utf-8" });
14250
14506
  if (r.status !== 0) {
14251
14507
  throw new PueueError(`pueue status failed: ${r.stderr || r.stdout || "unknown error"} \u2014 is pueued running?`);
14252
14508
  }
@@ -14291,18 +14547,18 @@ function startTask(opts = {}) {
14291
14547
  let existing = findTask(ch);
14292
14548
  while (existing) {
14293
14549
  if (existing.status === "Running" || existing.status === "Queued") {
14294
- spawnSync7("tmux", ["kill-session", "-t", `=${ch.tmuxSession}`], { encoding: "utf-8" });
14295
- 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" });
14296
14552
  for (let i = 0; i < 10; i++) {
14297
14553
  const check = findTask(ch);
14298
14554
  if (!check || check.id !== existing.id || check.status !== "Running" && check.status !== "Queued") break;
14299
- spawnSync7("sleep", ["0.5"], { encoding: "utf-8" });
14555
+ spawnSync8("sleep", ["0.5"], { encoding: "utf-8" });
14300
14556
  }
14301
14557
  }
14302
- spawnSync7("pueue", ["remove", String(existing.id)], { encoding: "utf-8" });
14558
+ spawnSync8("pueue", ["remove", String(existing.id)], { encoding: "utf-8" });
14303
14559
  existing = findTask(ch);
14304
14560
  }
14305
- const runScript = join14(cwd, "run-claude.sh");
14561
+ const runScript = join15(cwd, "run-claude.sh");
14306
14562
  const args2 = [
14307
14563
  "add",
14308
14564
  "--label",
@@ -14313,7 +14569,7 @@ function startTask(opts = {}) {
14313
14569
  "bash",
14314
14570
  runScript
14315
14571
  ];
14316
- const r = spawnSync7("pueue", args2, { encoding: "utf-8" });
14572
+ const r = spawnSync8("pueue", args2, { encoding: "utf-8" });
14317
14573
  if (r.status !== 0) {
14318
14574
  throw new PueueError(`pueue add failed: ${r.stderr || r.stdout}`);
14319
14575
  }
@@ -14324,25 +14580,25 @@ function startTask(opts = {}) {
14324
14580
  return created;
14325
14581
  }
14326
14582
  function stopTask(channel = CHANNEL_PRIMARY) {
14327
- spawnSync7("tmux", ["kill-session", "-t", `=${channel.tmuxSession}`], { encoding: "utf-8" });
14583
+ spawnSync8("tmux", ["kill-session", "-t", `=${channel.tmuxSession}`], { encoding: "utf-8" });
14328
14584
  let t = findTask(channel);
14329
14585
  while (t) {
14330
14586
  if (t.status === "Running" || t.status === "Queued") {
14331
- spawnSync7("pueue", ["kill", String(t.id)], { encoding: "utf-8" });
14587
+ spawnSync8("pueue", ["kill", String(t.id)], { encoding: "utf-8" });
14332
14588
  for (let i = 0; i < 10; i++) {
14333
14589
  const check = findTask(channel);
14334
14590
  if (!check || check.id !== t.id || check.status !== "Running" && check.status !== "Queued") break;
14335
- spawnSync7("sleep", ["0.5"], { encoding: "utf-8" });
14591
+ spawnSync8("sleep", ["0.5"], { encoding: "utf-8" });
14336
14592
  }
14337
14593
  }
14338
- spawnSync7("pueue", ["remove", String(t.id)], { encoding: "utf-8" });
14594
+ spawnSync8("pueue", ["remove", String(t.id)], { encoding: "utf-8" });
14339
14595
  t = findTask(channel);
14340
14596
  }
14341
14597
  }
14342
14598
  function tailLogs(lines = 80, channel = CHANNEL_PRIMARY) {
14343
14599
  const t = findTask(channel);
14344
14600
  if (!t) return `(no ${channel.taskLabel} task)`;
14345
- 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" });
14346
14602
  return r.stdout || r.stderr || "(no output)";
14347
14603
  }
14348
14604
  function ensureRunning(opts = {}) {
@@ -14367,8 +14623,8 @@ function probePort(host, port, timeoutMs = 500) {
14367
14623
  });
14368
14624
  }
14369
14625
  function tmuxDismissPrompts(tmuxSession = TMUX_SESSION) {
14370
- spawnSync7("tmux", ["send-keys", "-t", tmuxSession, "1"], { encoding: "utf-8" });
14371
- 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" });
14372
14628
  }
14373
14629
  async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tmuxSession = TMUX_SESSION) {
14374
14630
  const deadline = Date.now() + timeoutMs;
@@ -14380,46 +14636,46 @@ async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tm
14380
14636
  return probePort(host, port);
14381
14637
  }
14382
14638
  function brewInstall(pkg) {
14383
- const brew = spawnSync7("brew", ["--version"], { encoding: "utf-8" });
14639
+ const brew = spawnSync8("brew", ["--version"], { encoding: "utf-8" });
14384
14640
  if (brew.status !== 0) return false;
14385
14641
  console.log(` Installing ${pkg} via brew...`);
14386
- 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 });
14387
14643
  return r.status === 0;
14388
14644
  }
14389
14645
  function assertPueueInstalled() {
14390
- let r = spawnSync7("pueue", ["--version"], { encoding: "utf-8" });
14646
+ let r = spawnSync8("pueue", ["--version"], { encoding: "utf-8" });
14391
14647
  if (r.status !== 0) {
14392
14648
  if (process.platform === "darwin" && brewInstall("pueue")) {
14393
- r = spawnSync7("pueue", ["--version"], { encoding: "utf-8" });
14649
+ r = spawnSync8("pueue", ["--version"], { encoding: "utf-8" });
14394
14650
  if (r.status !== 0) throw new PueueError("pueue install succeeded but binary not found on PATH.");
14395
14651
  } else {
14396
14652
  throw new PueueError("pueue not found. Install it: brew install pueue (macOS) or https://github.com/Nukesor/pueue");
14397
14653
  }
14398
14654
  }
14399
- const status = spawnSync7("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
14655
+ const status = spawnSync8("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
14400
14656
  if (status.status !== 0) {
14401
14657
  console.log(" Starting pueued daemon...");
14402
14658
  const child = spawn2("pueued", ["-d"], { stdio: "ignore", detached: true });
14403
14659
  child.unref();
14404
- spawnSync7("sleep", ["1"]);
14405
- 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 });
14406
14662
  if (retry.status !== 0) {
14407
14663
  throw new PueueError("pueue daemon not reachable after starting pueued. Check `pueued` manually.");
14408
14664
  }
14409
14665
  }
14410
- spawnSync7("pueue", ["parallel", "2"], { encoding: "utf-8" });
14666
+ spawnSync8("pueue", ["parallel", "2"], { encoding: "utf-8" });
14411
14667
  }
14412
14668
  function assertClaudeInstalled() {
14413
- const r = spawnSync7("claude", ["--version"], { encoding: "utf-8" });
14669
+ const r = spawnSync8("claude", ["--version"], { encoding: "utf-8" });
14414
14670
  if (r.status !== 0) {
14415
14671
  throw new PueueError("claude CLI not found on PATH. Install Claude Code first: https://docs.claude.com/claude-code");
14416
14672
  }
14417
14673
  }
14418
14674
  function assertTmuxInstalled() {
14419
- let r = spawnSync7("tmux", ["-V"], { encoding: "utf-8" });
14675
+ let r = spawnSync8("tmux", ["-V"], { encoding: "utf-8" });
14420
14676
  if (r.status !== 0) {
14421
14677
  if (process.platform === "darwin" && brewInstall("tmux")) {
14422
- r = spawnSync7("tmux", ["-V"], { encoding: "utf-8" });
14678
+ r = spawnSync8("tmux", ["-V"], { encoding: "utf-8" });
14423
14679
  if (r.status !== 0) throw new PueueError("tmux install succeeded but binary not found on PATH.");
14424
14680
  } else {
14425
14681
  throw new PueueError("tmux not found. Install it: brew install tmux (macOS) or apt install tmux (Linux)");
@@ -14432,12 +14688,12 @@ var init_pueue = __esm({
14432
14688
  "use strict";
14433
14689
  TASK_LABEL = "synkro-local-cc";
14434
14690
  TMUX_SESSION = "synkro-local-cc";
14435
- SESSION_DIR2 = join14(homedir14(), ".synkro", "cc_sessions");
14691
+ SESSION_DIR2 = join15(homedir15(), ".synkro", "cc_sessions");
14436
14692
  TASK_LABEL_2 = "synkro-local-cc-2";
14437
14693
  TMUX_SESSION_2 = "synkro-local-cc-2";
14438
- SESSION_DIR_22 = join14(homedir14(), ".synkro", "cc_sessions_2");
14439
- SESSION_DIR_32 = join14(homedir14(), ".synkro", "cc_sessions_3");
14440
- 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");
14441
14697
  PueueError = class extends Error {
14442
14698
  constructor(message, cause) {
14443
14699
  super(message);
@@ -14452,13 +14708,13 @@ var init_pueue = __esm({
14452
14708
  });
14453
14709
 
14454
14710
  // cli/local-cc/settings.ts
14455
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
14456
- import { homedir as homedir15 } from "os";
14457
- 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";
14458
14714
  function isLocalCCEnabled() {
14459
- if (!existsSync15(CONFIG_PATH3)) return false;
14715
+ if (!existsSync16(CONFIG_PATH3)) return false;
14460
14716
  try {
14461
- const content = readFileSync13(CONFIG_PATH3, "utf-8");
14717
+ const content = readFileSync14(CONFIG_PATH3, "utf-8");
14462
14718
  const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);
14463
14719
  return match?.[1] === "yes";
14464
14720
  } catch {
@@ -14469,7 +14725,7 @@ var CONFIG_PATH3;
14469
14725
  var init_settings = __esm({
14470
14726
  "cli/local-cc/settings.ts"() {
14471
14727
  "use strict";
14472
- CONFIG_PATH3 = join15(homedir15(), ".synkro", "config.env");
14728
+ CONFIG_PATH3 = join16(homedir16(), ".synkro", "config.env");
14473
14729
  }
14474
14730
  });
14475
14731
 
@@ -14478,11 +14734,11 @@ var localCc_exports = {};
14478
14734
  __export(localCc_exports, {
14479
14735
  localCcCommand: () => localCcCommand
14480
14736
  });
14481
- import { spawnSync as spawnSync8 } from "child_process";
14482
- import { homedir as homedir16 } from "os";
14483
- 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";
14484
14740
  import { readFileSync as fsReadFileSync, existsSync as fsExistsSync } from "fs";
14485
- 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";
14486
14742
  function deploymentMode() {
14487
14743
  const env = (process.env.SYNKRO_DEPLOYMENT_MODE || "").toLowerCase();
14488
14744
  if (env === "docker") return "docker";
@@ -14588,15 +14844,15 @@ TROUBLESHOOTING
14588
14844
  `);
14589
14845
  }
14590
14846
  function readGatewayUrl() {
14591
- if (existsSync16(CONFIG_PATH4)) {
14592
- 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);
14593
14849
  if (m) return m[1];
14594
14850
  }
14595
14851
  return "https://api.synkro.sh";
14596
14852
  }
14597
14853
  function updateLocalInferenceFlag(enabled) {
14598
- if (!existsSync16(CONFIG_PATH4)) return;
14599
- let content = readFileSync14(CONFIG_PATH4, "utf-8");
14854
+ if (!existsSync17(CONFIG_PATH4)) return;
14855
+ let content = readFileSync15(CONFIG_PATH4, "utf-8");
14600
14856
  const flag = enabled ? "yes" : "no";
14601
14857
  if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
14602
14858
  content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);
@@ -14659,7 +14915,7 @@ async function cmdStatus() {
14659
14915
  }
14660
14916
  const ch1Up = await isChannelAvailable();
14661
14917
  console.log(`Channel 1 ${CHANNEL_HOST}:${CHANNEL_PORT}: ${ch1Up ? "reachable" : "unreachable"}`);
14662
- 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" });
14663
14919
  console.log(`tmux '${TMUX_SESSION_NAME}': ${tmux1.status === 0 ? "live" : "absent"}`);
14664
14920
  const t2 = findTask(CHANNEL_SECONDARY);
14665
14921
  if (!t2) {
@@ -14669,7 +14925,7 @@ async function cmdStatus() {
14669
14925
  }
14670
14926
  const ch2Up = await isChannelAvailable(CHANNEL_2_PORT);
14671
14927
  console.log(`Channel 2 ${CHANNEL_HOST}:${CHANNEL_2_PORT}: ${ch2Up ? "reachable" : "unreachable"}`);
14672
- 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" });
14673
14929
  console.log(`tmux '${TMUX_SESSION_NAME_2}': ${tmux2.status === 0 ? "live" : "absent"}`);
14674
14930
  }
14675
14931
  async function cmdEnable() {
@@ -14875,7 +15131,7 @@ function cmdLogs(rest) {
14875
15131
  }
14876
15132
  return "200";
14877
15133
  })();
14878
- spawnSync8("docker", ["logs", "--tail", tailArg, ...followFlag, "synkro-server"], { stdio: "inherit" });
15134
+ spawnSync9("docker", ["logs", "--tail", tailArg, ...followFlag, "synkro-server"], { stdio: "inherit" });
14879
15135
  return;
14880
15136
  }
14881
15137
  for (const arg of rest) {
@@ -14923,7 +15179,7 @@ function cmdLogs(rest) {
14923
15179
  function cmdAttach(rest) {
14924
15180
  assertTmuxInstalled();
14925
15181
  const readonly = rest.some((a) => a === "--readonly" || a === "-r");
14926
- 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" });
14927
15183
  if (has.status !== 0) {
14928
15184
  console.error(`No tmux session '${TMUX_SESSION_NAME}' running. Start it with: synkro local-cc start`);
14929
15185
  process.exit(1);
@@ -14936,7 +15192,7 @@ function cmdAttach(rest) {
14936
15192
  console.log("Detach with Ctrl-B then D. (Do not press Ctrl-C \u2014 that would interrupt claude.)");
14937
15193
  console.log();
14938
15194
  const args2 = readonly ? ["attach-session", "-r", "-t", TMUX_SESSION_NAME] : ["attach-session", "-t", TMUX_SESSION_NAME];
14939
- const r = spawnSync8("tmux", args2, { stdio: "inherit" });
15195
+ const r = spawnSync9("tmux", args2, { stdio: "inherit" });
14940
15196
  process.exit(r.status ?? 0);
14941
15197
  }
14942
15198
  async function cmdTest() {
@@ -15037,8 +15293,8 @@ var init_localCc = __esm({
15037
15293
  init_install();
15038
15294
  init_client();
15039
15295
  init_stub();
15040
- SYNKRO_CONFIG_PATH = join16(homedir16(), ".synkro", "config.env");
15041
- CONFIG_PATH4 = join16(homedir16(), ".synkro", "config.env");
15296
+ SYNKRO_CONFIG_PATH = join17(homedir17(), ".synkro", "config.env");
15297
+ CONFIG_PATH4 = join17(homedir17(), ".synkro", "config.env");
15042
15298
  }
15043
15299
  });
15044
15300
 
@@ -15047,15 +15303,15 @@ var import_exports = {};
15047
15303
  __export(import_exports, {
15048
15304
  importCommand: () => importCommand
15049
15305
  });
15050
- import { existsSync as existsSync17, readFileSync as readFileSync15, readdirSync as readdirSync5 } from "fs";
15051
- import { homedir as homedir17 } from "os";
15052
- 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";
15053
15309
  import { execSync as execSync7 } from "child_process";
15054
15310
  import { createInterface as createInterface5 } from "readline";
15055
15311
  function readConfigEnv() {
15056
15312
  const out = {};
15057
15313
  try {
15058
- for (const line of readFileSync15(CONFIG_PATH5, "utf-8").split("\n")) {
15314
+ for (const line of readFileSync16(CONFIG_PATH5, "utf-8").split("\n")) {
15059
15315
  const t = line.trim();
15060
15316
  if (!t || t.startsWith("#")) continue;
15061
15317
  const eq = t.indexOf("=");
@@ -15067,8 +15323,8 @@ function readConfigEnv() {
15067
15323
  }
15068
15324
  function projectsFolder() {
15069
15325
  const sanitized = process.cwd().replace(/\//g, "-");
15070
- const dir = join17(homedir17(), ".claude", "projects", sanitized);
15071
- return existsSync17(dir) ? dir : null;
15326
+ const dir = join18(homedir18(), ".claude", "projects", sanitized);
15327
+ return existsSync18(dir) ? dir : null;
15072
15328
  }
15073
15329
  function repoName() {
15074
15330
  try {
@@ -15091,7 +15347,7 @@ function extractText(content) {
15091
15347
  return "";
15092
15348
  }
15093
15349
  function parseSession(filePath, sessionId) {
15094
- const lines = readFileSync15(filePath, "utf-8").split("\n").filter(Boolean);
15350
+ const lines = readFileSync16(filePath, "utf-8").split("\n").filter(Boolean);
15095
15351
  const messages = [];
15096
15352
  const actions = [];
15097
15353
  let step = 0;
@@ -15164,7 +15420,7 @@ async function importCommand() {
15164
15420
  return;
15165
15421
  }
15166
15422
  }
15167
- 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);
15168
15424
  const totalMsgs = sessions.reduce((n, s) => n + s.messages.length, 0);
15169
15425
  let ok = 0, fail = 0;
15170
15426
  if (isCloud) {
@@ -15224,7 +15480,7 @@ var init_import = __esm({
15224
15480
  "cli/commands/import.ts"() {
15225
15481
  "use strict";
15226
15482
  init_stub();
15227
- CONFIG_PATH5 = join17(homedir17(), ".synkro", "config.env");
15483
+ CONFIG_PATH5 = join18(homedir18(), ".synkro", "config.env");
15228
15484
  }
15229
15485
  });
15230
15486
 
@@ -15266,10 +15522,10 @@ var init_packVerify = __esm({
15266
15522
  });
15267
15523
 
15268
15524
  // cli/installer/lockfile.ts
15269
- import { existsSync as existsSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "fs";
15270
- 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";
15271
15527
  function lockPath(repoRoot) {
15272
- return join18(repoRoot, LOCK_FILE);
15528
+ return join19(repoRoot, LOCK_FILE);
15273
15529
  }
15274
15530
  function writeLockfile(repoRoot, entries) {
15275
15531
  const sorted = [...entries].sort((a, b) => a.ref.localeCompare(b.ref));
@@ -15302,9 +15558,9 @@ var sync_exports = {};
15302
15558
  __export(sync_exports, {
15303
15559
  syncCommand: () => syncCommand
15304
15560
  });
15305
- import { existsSync as existsSync19, mkdirSync as mkdirSync12, readdirSync as readdirSync6, rmSync as rmSync2, writeFileSync as writeFileSync12 } from "fs";
15306
- import { homedir as homedir18 } from "os";
15307
- 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";
15308
15564
  function cacheKey(ref, version) {
15309
15565
  return ref.replace(/\//g, "__").replace(/[^\w.@-]/g, "_") + "@" + version + ".json";
15310
15566
  }
@@ -15335,8 +15591,8 @@ async function syncCommand(_args = []) {
15335
15591
  }
15336
15592
  const gateway = (process.env.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/\/$/, "");
15337
15593
  const cloud = process.env.SYNKRO_DEPLOY_LOCATION === "cloud";
15338
- const cacheDir = join19(homedir18(), ".synkro", "cache", "packs");
15339
- if (!cloud) mkdirSync12(cacheDir, { recursive: true });
15594
+ const cacheDir = join20(homedir19(), ".synkro", "cache", "packs");
15595
+ if (!cloud) mkdirSync13(cacheDir, { recursive: true });
15340
15596
  console.log(`Syncing ${refs.length} standard(s) from the registry\u2026`);
15341
15597
  const lock = [];
15342
15598
  const keptCacheFiles = /* @__PURE__ */ new Set();
@@ -15364,7 +15620,7 @@ async function syncCommand(_args = []) {
15364
15620
  if (!cloud) {
15365
15621
  const fname = cacheKey(ref, data.version);
15366
15622
  keptCacheFiles.add(fname);
15367
- writeFileSync12(join19(cacheDir, fname), JSON.stringify({
15623
+ writeFileSync12(join20(cacheDir, fname), JSON.stringify({
15368
15624
  ref,
15369
15625
  version: data.version,
15370
15626
  digest: data.digest,
@@ -15376,11 +15632,11 @@ async function syncCommand(_args = []) {
15376
15632
  const ruleCount = Array.isArray(pack.rules) ? pack.rules.length : 0;
15377
15633
  console.log(` \u2713 ${ref}:${data.version} \u2014 verified (${ruleCount} rule${ruleCount === 1 ? "" : "s"})`);
15378
15634
  }
15379
- if (!cloud && existsSync19(cacheDir)) {
15635
+ if (!cloud && existsSync20(cacheDir)) {
15380
15636
  for (const f of readdirSync6(cacheDir)) {
15381
15637
  if (f.endsWith(".json") && !keptCacheFiles.has(f)) {
15382
15638
  try {
15383
- rmSync2(join19(cacheDir, f));
15639
+ rmSync3(join20(cacheDir, f));
15384
15640
  } catch {
15385
15641
  }
15386
15642
  }
@@ -15516,13 +15772,13 @@ var config_exports = {};
15516
15772
  __export(config_exports, {
15517
15773
  configCommand: () => configCommand
15518
15774
  });
15519
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync13, existsSync as existsSync20 } from "fs";
15520
- import { join as join20 } from "path";
15521
- 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";
15522
15778
  function readConfigEnv2() {
15523
- if (!existsSync20(CONFIG_PATH6)) return {};
15779
+ if (!existsSync21(CONFIG_PATH6)) return {};
15524
15780
  const out = {};
15525
- for (const line of readFileSync17(CONFIG_PATH6, "utf-8").split("\n")) {
15781
+ for (const line of readFileSync18(CONFIG_PATH6, "utf-8").split("\n")) {
15526
15782
  const t = line.trim();
15527
15783
  if (!t || t.startsWith("#")) continue;
15528
15784
  const eq = t.indexOf("=");
@@ -15531,11 +15787,11 @@ function readConfigEnv2() {
15531
15787
  return out;
15532
15788
  }
15533
15789
  function updateConfigValue(key, value) {
15534
- if (!existsSync20(CONFIG_PATH6)) {
15790
+ if (!existsSync21(CONFIG_PATH6)) {
15535
15791
  console.error("No config found. Run `synkro install` first.");
15536
15792
  process.exit(1);
15537
15793
  }
15538
- const lines = readFileSync17(CONFIG_PATH6, "utf-8").split("\n");
15794
+ const lines = readFileSync18(CONFIG_PATH6, "utf-8").split("\n");
15539
15795
  const pattern = new RegExp(`^${key}=`);
15540
15796
  let found = false;
15541
15797
  const updated = lines.map((line) => {
@@ -15673,25 +15929,25 @@ To change:`);
15673
15929
  }
15674
15930
  if (inferenceValue !== "cloud") await reconcileContainer();
15675
15931
  }
15676
- var SYNKRO_DIR7, CONFIG_PATH6;
15932
+ var SYNKRO_DIR8, CONFIG_PATH6;
15677
15933
  var init_config = __esm({
15678
15934
  "cli/commands/config.ts"() {
15679
15935
  "use strict";
15680
15936
  init_stub();
15681
- SYNKRO_DIR7 = join20(homedir19(), ".synkro");
15682
- CONFIG_PATH6 = join20(SYNKRO_DIR7, "config.env");
15937
+ SYNKRO_DIR8 = join21(homedir20(), ".synkro");
15938
+ CONFIG_PATH6 = join21(SYNKRO_DIR8, "config.env");
15683
15939
  }
15684
15940
  });
15685
15941
 
15686
15942
  // cli/bootstrap.js
15687
- import { readFileSync as readFileSync18, existsSync as existsSync21 } from "fs";
15943
+ import { readFileSync as readFileSync19, existsSync as existsSync22 } from "fs";
15688
15944
  import { resolve as resolve3 } from "path";
15689
15945
  var envCandidates = [
15690
15946
  resolve3(process.env.HOME ?? "", ".synkro", "config.env")
15691
15947
  ];
15692
15948
  for (const envPath of envCandidates) {
15693
- if (!existsSync21(envPath)) continue;
15694
- const envContent = readFileSync18(envPath, "utf-8");
15949
+ if (!existsSync22(envPath)) continue;
15950
+ const envContent = readFileSync19(envPath, "utf-8");
15695
15951
  for (const line of envContent.split("\n")) {
15696
15952
  const trimmed = line.trim();
15697
15953
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -15706,7 +15962,7 @@ var args = process.argv.slice(2);
15706
15962
  var cmd = args[0] || "";
15707
15963
  var subArgs = args.slice(1);
15708
15964
  function printVersion() {
15709
- console.log("1.7.9");
15965
+ console.log("1.7.24");
15710
15966
  }
15711
15967
  function printHelp2() {
15712
15968
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents