@synkro-sh/cli 1.4.66 → 1.4.67

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
@@ -283,22 +283,32 @@ var init_ccHookConfig = __esm({
283
283
  });
284
284
 
285
285
  // cli/installer/cursorHookConfig.ts
286
- import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync as renameSync2, mkdirSync as mkdirSync2 } from "fs";
287
- import { dirname as dirname2 } from "path";
288
- function readHooksFile(path) {
289
- if (!existsSync3(path)) return { version: 1, hooks: {} };
286
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync as renameSync2, mkdirSync as mkdirSync2 } from "fs";
287
+ import { dirname as dirname2, resolve, normalize } from "path";
288
+ import { homedir as homedir2 } from "os";
289
+ function validateHooksPath(path) {
290
+ const resolved = resolve(normalize(path));
291
+ if (!ALLOWED_PARENT_DIRS.some((dir) => resolved.startsWith(dir + "/") || resolved === dir)) {
292
+ throw new Error(`Hooks path must be under ~/.cursor or ~/.config/cursor, got: ${resolved}`);
293
+ }
294
+ return resolved;
295
+ }
296
+ function readHooksFile(rawPath) {
297
+ const safePath = validateHooksPath(rawPath);
290
298
  try {
291
- const raw = readFileSync2(path, "utf-8");
299
+ const raw = readFileSync2(safePath, "utf-8");
292
300
  return JSON.parse(raw);
293
301
  } catch (err) {
294
- throw new Error(`Failed to parse ${path}: ${err.message}`);
302
+ if (err?.code === "ENOENT") return { version: 1, hooks: {} };
303
+ throw new Error(`Failed to parse ${safePath}: ${err.message}`);
295
304
  }
296
305
  }
297
- function writeHooksFileAtomic(path, data) {
298
- mkdirSync2(dirname2(path), { recursive: true });
299
- const tmpPath = `${path}.synkro.tmp`;
300
- writeFileSync2(tmpPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
301
- renameSync2(tmpPath, path);
306
+ function writeHooksFileAtomic(rawPath, data) {
307
+ const safePath = validateHooksPath(rawPath);
308
+ mkdirSync2(dirname2(safePath), { recursive: true });
309
+ const tmpPath = `${safePath}.synkro.tmp`;
310
+ writeFileSync2(tmpPath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
311
+ renameSync2(tmpPath, safePath);
302
312
  }
303
313
  function isSynkroEntry2(entry) {
304
314
  if (entry?.[SYNKRO_MARKER2]) return true;
@@ -314,10 +324,15 @@ function installCursorHooks(hooksJsonPath, config) {
314
324
  const file = readHooksFile(hooksJsonPath);
315
325
  file.version = file.version ?? 1;
316
326
  file.hooks = file.hooks ?? {};
317
- const events = ["beforeShellExecution", "preToolUse", "afterFileEdit", "postToolUse"];
318
- for (const evt of events) {
327
+ for (const evt of ALL_EVENTS) {
319
328
  removeSynkroEntries2(file.hooks, evt);
320
329
  }
330
+ file.hooks.sessionStart = file.hooks.sessionStart ?? [];
331
+ file.hooks.sessionStart.push({
332
+ command: config.sessionStartScriptPath,
333
+ timeout: 5,
334
+ [SYNKRO_MARKER2]: true
335
+ });
321
336
  file.hooks.beforeShellExecution = file.hooks.beforeShellExecution ?? [];
322
337
  file.hooks.beforeShellExecution.push({
323
338
  command: config.bashJudgeScriptPath,
@@ -346,14 +361,17 @@ function installCursorHooks(hooksJsonPath, config) {
346
361
  writeHooksFileAtomic(hooksJsonPath, file);
347
362
  }
348
363
  function uninstallCursorHooks(hooksJsonPath) {
349
- if (!existsSync3(hooksJsonPath)) return false;
350
- const file = readHooksFile(hooksJsonPath);
364
+ let file;
365
+ try {
366
+ file = readHooksFile(hooksJsonPath);
367
+ } catch {
368
+ return false;
369
+ }
351
370
  if (!file.hooks) return false;
352
- const events = ["beforeShellExecution", "preToolUse", "afterFileEdit", "postToolUse"];
353
- for (const evt of events) {
371
+ for (const evt of ALL_EVENTS) {
354
372
  removeSynkroEntries2(file.hooks, evt);
355
373
  }
356
- for (const evt of events) {
374
+ for (const evt of ALL_EVENTS) {
357
375
  if (Array.isArray(file.hooks[evt]) && file.hooks[evt].length === 0) {
358
376
  delete file.hooks[evt];
359
377
  }
@@ -365,37 +383,46 @@ function uninstallCursorHooks(hooksJsonPath) {
365
383
  return true;
366
384
  }
367
385
  function inspectCursorHooks(hooksJsonPath) {
368
- if (!existsSync3(hooksJsonPath)) {
369
- return { installed: false, beforeShellExecution: false, preToolUse: false, afterFileEdit: false, postToolUse: false };
386
+ let file;
387
+ try {
388
+ file = readHooksFile(hooksJsonPath);
389
+ } catch {
390
+ return { installed: false, sessionStart: false, beforeShellExecution: false, preToolUse: false, afterFileEdit: false, postToolUse: false };
370
391
  }
371
- const file = readHooksFile(hooksJsonPath);
372
392
  const h = file.hooks ?? {};
393
+ const sessionStart = (h.sessionStart ?? []).some((e) => isSynkroEntry2(e));
373
394
  const beforeShellExecution = (h.beforeShellExecution ?? []).some((e) => isSynkroEntry2(e));
374
395
  const preToolUse = (h.preToolUse ?? []).some((e) => isSynkroEntry2(e));
375
396
  const afterFileEdit = (h.afterFileEdit ?? []).some((e) => isSynkroEntry2(e));
376
397
  const postToolUse = (h.postToolUse ?? []).some((e) => isSynkroEntry2(e));
377
398
  return {
378
- installed: beforeShellExecution || preToolUse || afterFileEdit || postToolUse,
399
+ installed: sessionStart || beforeShellExecution || preToolUse || afterFileEdit || postToolUse,
400
+ sessionStart,
379
401
  beforeShellExecution,
380
402
  preToolUse,
381
403
  afterFileEdit,
382
404
  postToolUse
383
405
  };
384
406
  }
385
- var SYNKRO_MARKER2;
407
+ var SYNKRO_MARKER2, ALLOWED_PARENT_DIRS, ALL_EVENTS;
386
408
  var init_cursorHookConfig = __esm({
387
409
  "cli/installer/cursorHookConfig.ts"() {
388
410
  "use strict";
389
411
  SYNKRO_MARKER2 = "__synkro_managed__";
412
+ ALLOWED_PARENT_DIRS = [
413
+ resolve(homedir2(), ".cursor"),
414
+ resolve(homedir2(), ".config", "cursor")
415
+ ];
416
+ ALL_EVENTS = ["sessionStart", "beforeShellExecution", "preToolUse", "afterFileEdit", "postToolUse"];
390
417
  }
391
418
  });
392
419
 
393
420
  // cli/installer/mcpConfig.ts
394
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3, renameSync as renameSync3, mkdirSync as mkdirSync3 } from "fs";
395
- import { homedir as homedir2 } from "os";
421
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3, renameSync as renameSync3, mkdirSync as mkdirSync3 } from "fs";
422
+ import { homedir as homedir3 } from "os";
396
423
  import { dirname as dirname3, join as join2 } from "path";
397
424
  function readClaudeJson() {
398
- if (!existsSync4(CC_CONFIG_PATH)) return {};
425
+ if (!existsSync3(CC_CONFIG_PATH)) return {};
399
426
  try {
400
427
  const raw = readFileSync3(CC_CONFIG_PATH, "utf-8");
401
428
  return JSON.parse(raw);
@@ -417,7 +444,7 @@ function installMcpConfig(opts) {
417
444
  }
418
445
  if (opts.local) {
419
446
  const url2 = "http://127.0.0.1:8931/";
420
- const tokenPath = join2(homedir2(), ".synkro", ".mcp-local-token");
447
+ const tokenPath = join2(homedir3(), ".synkro", ".mcp-local-token");
421
448
  let localToken = "";
422
449
  try {
423
450
  localToken = readFileSync3(tokenPath, "utf-8").trim();
@@ -443,7 +470,7 @@ function installMcpConfig(opts) {
443
470
  return { path: CC_CONFIG_PATH, url };
444
471
  }
445
472
  function uninstallMcpConfig() {
446
- if (!existsSync4(CC_CONFIG_PATH)) return false;
473
+ if (!existsSync3(CC_CONFIG_PATH)) return false;
447
474
  const config = readClaudeJson();
448
475
  if (!config.mcpServers || Object.keys(config.mcpServers).length === 0) return false;
449
476
  let removed = false;
@@ -459,7 +486,7 @@ function uninstallMcpConfig() {
459
486
  return true;
460
487
  }
461
488
  function inspectMcpConfig() {
462
- if (!existsSync4(CC_CONFIG_PATH)) {
489
+ if (!existsSync3(CC_CONFIG_PATH)) {
463
490
  return { installed: false, configPath: CC_CONFIG_PATH };
464
491
  }
465
492
  const config = readClaudeJson();
@@ -475,7 +502,7 @@ var init_mcpConfig = __esm({
475
502
  "use strict";
476
503
  SYNKRO_MARKER3 = "__synkro_managed__";
477
504
  SYNKRO_SERVER_NAME = "synkro-guardrails";
478
- CC_CONFIG_PATH = join2(homedir2(), ".claude.json");
505
+ CC_CONFIG_PATH = join2(homedir3(), ".claude.json");
479
506
  }
480
507
  });
481
508
 
@@ -675,7 +702,7 @@ synkro_post_with_retry() {
675
702
  });
676
703
 
677
704
  // cli/installer/hookScriptsTs.ts
678
- var SYNKRO_COMMON_TS, EDIT_PRECHECK_TS, CWE_PRECHECK_TS, CVE_PRECHECK_TS, BASH_JUDGE_TS, AGENT_JUDGE_TS, PLAN_JUDGE_TS, STOP_SUMMARY_TS, SESSION_START_TS, BASH_FOLLOWUP_TS, TRANSCRIPT_SYNC_TS, USER_PROMPT_SUBMIT_TS, CURSOR_BASH_JUDGE_TS, CURSOR_EDIT_PRECHECK_TS, CURSOR_EDIT_CAPTURE_TS, CURSOR_BASH_FOLLOWUP_TS;
705
+ var SYNKRO_COMMON_TS, EDIT_PRECHECK_TS, CWE_PRECHECK_TS, CVE_PRECHECK_TS, BASH_JUDGE_TS, AGENT_JUDGE_TS, PLAN_JUDGE_TS, STOP_SUMMARY_TS, SESSION_START_TS, BASH_FOLLOWUP_TS, TRANSCRIPT_SYNC_TS, USER_PROMPT_SUBMIT_TS, CURSOR_BASH_JUDGE_TS, CURSOR_EDIT_PRECHECK_TS, CURSOR_EDIT_CAPTURE_TS, CURSOR_BASH_FOLLOWUP_TS, CURSOR_SESSION_START_TS;
679
706
  var init_hookScriptsTs = __esm({
680
707
  "cli/installer/hookScriptsTs.ts"() {
681
708
  "use strict";
@@ -855,7 +882,7 @@ export async function ensureFreshJwt(jwt: string): Promise<string> {
855
882
 
856
883
  export function detectRepo(cwd: string): string {
857
884
  try {
858
- const url = execSync('git remote get-url origin', { cwd, timeout: 3000, encoding: 'utf-8' }).trim();
885
+ const url = execSync('git remote get-url origin 2>/dev/null', { cwd, timeout: 3000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
859
886
  if (!url) return '';
860
887
  return url
861
888
  .replace(/^git@[^:]+:/, '')
@@ -2261,7 +2288,7 @@ async function main() {
2261
2288
  let installScanMsg = '';
2262
2289
  if (toolName === 'Bash') {
2263
2290
  const pkgInstallMatch = command.match(
2264
- /(?:npm\\s+(?:install|i|add)|pnpm\\s+(?:add|install|i)|yarn\\s+add|bun\\s+(?:add|install|i)|(?:uv\\s+)?pip3?\\s+install|go\\s+get|cargo\\s+add|gem\\s+install|composer\\s+require)\\s+(.+)/
2291
+ /(?:npm\\s+(?:install|i|add)|pnpm\\s+(?:add|install|i)|yarn\\s+add|bun\\s+(?:add|install|i)|(?:uv\\s+)?pip3?\\s+install|go\\s+get|cargo\\s+add|gem\\s+install|composer\\s+require)\\s+([^|;&><]+)/
2265
2292
  );
2266
2293
  const isPip = /(?:uv\\s+)?pip3?\\s+install/.test(command);
2267
2294
  const isGo = command.match(/^go\\s+get/);
@@ -2275,6 +2302,7 @@ async function main() {
2275
2302
  let skipNext = false;
2276
2303
  for (const token of tokens) {
2277
2304
  if (skipNext) { skipNext = false; continue; }
2305
+ if (!token || !/^[@a-zA-Z]/.test(token)) continue;
2278
2306
  if (token.startsWith('-')) {
2279
2307
  if (/^--(python|target|prefix|root|constraint|requirement|index-url|extra-index-url|find-links|build|src|cache-dir|filter|workspace)$/.test(token)) skipNext = true;
2280
2308
  continue;
@@ -2313,8 +2341,9 @@ async function main() {
2313
2341
  warnings.push('\\u26a0 ' + pkg + ': package not found on PyPI \\u2014 may not exist');
2314
2342
  }
2315
2343
  } else {
2344
+ const verSlug = deps[pkg] !== '*' ? deps[pkg] : 'latest';
2316
2345
  const [metaResp, dlResp] = await Promise.all([
2317
- fetch('https://registry.npmjs.org/' + encodeURIComponent(pkg) + '/latest', { signal: AbortSignal.timeout(4000) }),
2346
+ fetch('https://registry.npmjs.org/' + encodeURIComponent(pkg) + '/' + verSlug, { signal: AbortSignal.timeout(4000) }),
2318
2347
  fetch('https://api.npmjs.org/downloads/point/last-week/' + encodeURIComponent(pkg), { signal: AbortSignal.timeout(4000) }),
2319
2348
  ]);
2320
2349
  if (metaResp.ok) {
@@ -3737,6 +3766,42 @@ async function main() {
3737
3766
  }
3738
3767
  }
3739
3768
 
3769
+ main();
3770
+ `;
3771
+ CURSOR_SESSION_START_TS = `#!/usr/bin/env bun
3772
+ import {
3773
+ loadJwt, loadConfig, readStdin,
3774
+ type HookConfig,
3775
+ } from './_synkro-common.ts';
3776
+
3777
+ async function main() {
3778
+ try {
3779
+ const input = await readStdin();
3780
+
3781
+ let jwt = loadJwt();
3782
+ const config: HookConfig = jwt ? await loadConfig(jwt) : {
3783
+ captureDepth: 'local_only', tier: 'standard', silent: false,
3784
+ policyName: '', rules: [], scanExemptions: [],
3785
+ };
3786
+
3787
+ const policyName = config.policyName || 'default';
3788
+ const ruleCount = config.rules.length;
3789
+ const mode = config.silent ? 'silent' : 'active';
3790
+
3791
+ const context = [
3792
+ 'This session is monitored by Synkro (' + mode + ' mode, policy: "' + policyName + '", ' + ruleCount + ' rules).',
3793
+ 'Synkro enforces security and compliance rules on tool calls (shell commands, file edits).',
3794
+ 'If a tool call is blocked, Synkro will explain which rule was violated and why.',
3795
+ 'Do not suggest workarounds to bypass Synkro hooks \u2014 fix the underlying issue instead.',
3796
+ ].join(' ');
3797
+
3798
+ const result = { additional_context: context };
3799
+ process.stdout.write(JSON.stringify(result) + '\\n');
3800
+ } catch {
3801
+ process.stdout.write('{}\\n');
3802
+ }
3803
+ }
3804
+
3740
3805
  main();
3741
3806
  `;
3742
3807
  }
@@ -3744,8 +3809,8 @@ main();
3744
3809
 
3745
3810
  // cli/auth/stub.ts
3746
3811
  import { createServer } from "http";
3747
- import { writeFileSync as writeFileSync4, readFileSync as readFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "fs";
3748
- import { homedir as homedir3, platform } from "os";
3812
+ import { writeFileSync as writeFileSync4, readFileSync as readFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "fs";
3813
+ import { homedir as homedir4, platform } from "os";
3749
3814
  import { join as join3, dirname as dirname4 } from "path";
3750
3815
  import { execFile } from "child_process";
3751
3816
  import jwt from "jsonwebtoken";
@@ -3775,13 +3840,13 @@ function openBrowser(url) {
3775
3840
  }
3776
3841
  function saveCredentials(data) {
3777
3842
  const dir = dirname4(AUTH_FILE);
3778
- if (!existsSync5(dir)) {
3843
+ if (!existsSync4(dir)) {
3779
3844
  mkdirSync4(dir, { recursive: true, mode: 448 });
3780
3845
  }
3781
3846
  writeFileSync4(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 384 });
3782
3847
  }
3783
3848
  function loadCredentials() {
3784
- if (!existsSync5(AUTH_FILE)) {
3849
+ if (!existsSync4(AUTH_FILE)) {
3785
3850
  return null;
3786
3851
  }
3787
3852
  try {
@@ -3798,7 +3863,7 @@ function createCallbackServer() {
3798
3863
  "Access-Control-Allow-Headers": "Content-Type",
3799
3864
  "Vary": "Origin"
3800
3865
  };
3801
- return new Promise((resolve2, reject) => {
3866
+ return new Promise((resolve3, reject) => {
3802
3867
  const server = createServer((req, res) => {
3803
3868
  if (req.method === "OPTIONS") {
3804
3869
  const origin = req.headers.origin;
@@ -3887,7 +3952,7 @@ function createCallbackServer() {
3887
3952
  res.end(JSON.stringify({ ok: true }));
3888
3953
  setTimeout(() => {
3889
3954
  server.close();
3890
- resolve2(authData);
3955
+ resolve3(authData);
3891
3956
  }, 200);
3892
3957
  });
3893
3958
  req.on("error", (e) => {
@@ -4029,7 +4094,7 @@ async function ensureValidToken() {
4029
4094
  return true;
4030
4095
  }
4031
4096
  function clearCredentials() {
4032
- if (existsSync5(AUTH_FILE)) {
4097
+ if (existsSync4(AUTH_FILE)) {
4033
4098
  unlinkSync2(AUTH_FILE);
4034
4099
  }
4035
4100
  }
@@ -4040,7 +4105,7 @@ var init_stub = __esm({
4040
4105
  PORT = 8100;
4041
4106
  RAW_WEB_AUTH_URL = process.env.SYNKRO_WEB_AUTH_URL;
4042
4107
  SYNKRO_WEB_AUTH_URL = RAW_WEB_AUTH_URL && /^https?:\/\//.test(RAW_WEB_AUTH_URL) ? RAW_WEB_AUTH_URL : "https://app.synkro.sh";
4043
- AUTH_FILE = process.env.SYNKRO_AUTH_FILE || join3(homedir3(), ".synkro", "credentials.json");
4108
+ AUTH_FILE = process.env.SYNKRO_AUTH_FILE || join3(homedir4(), ".synkro", "credentials.json");
4044
4109
  RAW_API_URL = process.env.SYNKRO_CRUD_URL || process.env.SYNKRO_API_URL;
4045
4110
  SYNKRO_API_URL = RAW_API_URL && /^https?:\/\//.test(RAW_API_URL) ? RAW_API_URL : "https://api.synkro.sh";
4046
4111
  ERROR_HTML = `
@@ -4203,7 +4268,7 @@ jobs:
4203
4268
  });
4204
4269
 
4205
4270
  // cli/installer/githubSetup.ts
4206
- import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
4271
+ import { existsSync as existsSync5, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
4207
4272
  import { execSync as execSync2 } from "child_process";
4208
4273
  import { join as join4 } from "path";
4209
4274
  function ghSecretSet(token, owner, repo, name, value) {
@@ -4260,7 +4325,7 @@ function writeWorkflowFile(repoRootPath) {
4260
4325
  function findGitRoot(startCwd) {
4261
4326
  let cur = startCwd;
4262
4327
  while (cur && cur !== "/") {
4263
- if (existsSync6(join4(cur, ".git"))) return cur;
4328
+ if (existsSync5(join4(cur, ".git"))) return cur;
4264
4329
  const parent = join4(cur, "..");
4265
4330
  if (parent === cur) break;
4266
4331
  cur = parent;
@@ -4298,10 +4363,10 @@ function detectGitRepo() {
4298
4363
  }
4299
4364
  }
4300
4365
  function ask(rl, question) {
4301
- return new Promise((resolve2) => rl.question(question, resolve2));
4366
+ return new Promise((resolve3) => rl.question(question, resolve3));
4302
4367
  }
4303
4368
  function waitForGithubToken() {
4304
- return new Promise((resolve2, reject) => {
4369
+ return new Promise((resolve3, reject) => {
4305
4370
  const server = createServer2((req, res) => {
4306
4371
  if (req.method === "OPTIONS") {
4307
4372
  res.writeHead(204, {
@@ -4338,7 +4403,7 @@ function waitForGithubToken() {
4338
4403
  });
4339
4404
  res.end(JSON.stringify({ ok: true }));
4340
4405
  setTimeout(() => server.close(), 200);
4341
- resolve2(parsed.github_token);
4406
+ resolve3(parsed.github_token);
4342
4407
  } catch {
4343
4408
  res.writeHead(400, { "Content-Type": "application/json" });
4344
4409
  res.end(JSON.stringify({ error: "invalid json" }));
@@ -4503,12 +4568,12 @@ __export(setupGithub_exports, {
4503
4568
  import { createInterface as createInterface2 } from "readline/promises";
4504
4569
  import { stdin as input, stdout as output } from "process";
4505
4570
  import { execSync as execSync4, spawn as nodeSpawn } from "child_process";
4506
- import { existsSync as existsSync7, readFileSync as readFileSync5, unlinkSync as unlinkSync3 } from "fs";
4507
- import { homedir as homedir4, platform as platform2 } from "os";
4571
+ import { existsSync as existsSync6, readFileSync as readFileSync5, unlinkSync as unlinkSync3 } from "fs";
4572
+ import { homedir as homedir5, platform as platform2 } from "os";
4508
4573
  import { join as join5 } from "path";
4509
4574
  import { execFile as execFile2 } from "child_process";
4510
4575
  function readConfig() {
4511
- if (!existsSync7(CONFIG_PATH)) return {};
4576
+ if (!existsSync6(CONFIG_PATH)) return {};
4512
4577
  const out = {};
4513
4578
  for (const line of readFileSync5(CONFIG_PATH, "utf-8").split("\n")) {
4514
4579
  const t = line.trim();
@@ -4523,7 +4588,7 @@ async function prompt(rl, q, opts = {}) {
4523
4588
  process.stdout.write(q);
4524
4589
  const wasRaw = process.stdin.isRaw;
4525
4590
  if (process.stdin.setRawMode) process.stdin.setRawMode(true);
4526
- return await new Promise((resolve2) => {
4591
+ return await new Promise((resolve3) => {
4527
4592
  let chunk = "";
4528
4593
  const onData = (data) => {
4529
4594
  const s = data.toString("utf-8");
@@ -4531,7 +4596,7 @@ async function prompt(rl, q, opts = {}) {
4531
4596
  process.stdin.removeListener("data", onData);
4532
4597
  if (process.stdin.setRawMode) process.stdin.setRawMode(wasRaw ?? false);
4533
4598
  process.stdout.write("\n");
4534
- resolve2(chunk);
4599
+ resolve3(chunk);
4535
4600
  return;
4536
4601
  }
4537
4602
  if (s === "") process.exit(130);
@@ -4572,7 +4637,7 @@ function sleep(ms) {
4572
4637
  }
4573
4638
  function captureClaudeSetupToken() {
4574
4639
  const tmpFile = join5(SYNKRO_DIR, `token-capture-${Date.now()}.raw`);
4575
- return new Promise((resolve2, reject) => {
4640
+ return new Promise((resolve3, reject) => {
4576
4641
  const proc = nodeSpawn("script", ["-q", tmpFile, "claude", "setup-token"], {
4577
4642
  stdio: "inherit"
4578
4643
  });
@@ -4602,7 +4667,7 @@ function captureClaudeSetupToken() {
4602
4667
  reject(new Error(`Could not find token in claude setup-token output (file=${raw.length}b, yellow=${yellow.length}b)`));
4603
4668
  return;
4604
4669
  }
4605
- resolve2(token[0]);
4670
+ resolve3(token[0]);
4606
4671
  });
4607
4672
  });
4608
4673
  }
@@ -4852,7 +4917,7 @@ var init_setupGithub = __esm({
4852
4917
  "use strict";
4853
4918
  init_githubSetup();
4854
4919
  init_stub();
4855
- SYNKRO_DIR = join5(homedir4(), ".synkro");
4920
+ SYNKRO_DIR = join5(homedir5(), ".synkro");
4856
4921
  CONFIG_PATH = join5(SYNKRO_DIR, "config.env");
4857
4922
  }
4858
4923
  });
@@ -4881,11 +4946,11 @@ var init_promptFetcher = __esm({
4881
4946
  });
4882
4947
 
4883
4948
  // cli/local-cc/settings.ts
4884
- import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
4885
- import { homedir as homedir5 } from "os";
4949
+ import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
4950
+ import { homedir as homedir6 } from "os";
4886
4951
  import { join as join6 } from "path";
4887
4952
  function isLocalCCEnabled() {
4888
- if (!existsSync8(CONFIG_PATH2)) return false;
4953
+ if (!existsSync7(CONFIG_PATH2)) return false;
4889
4954
  try {
4890
4955
  const content = readFileSync6(CONFIG_PATH2, "utf-8");
4891
4956
  const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);
@@ -4898,7 +4963,7 @@ var CONFIG_PATH2;
4898
4963
  var init_settings = __esm({
4899
4964
  "cli/local-cc/settings.ts"() {
4900
4965
  "use strict";
4901
- CONFIG_PATH2 = join6(homedir5(), ".synkro", "config.env");
4966
+ CONFIG_PATH2 = join6(homedir6(), ".synkro", "config.env");
4902
4967
  }
4903
4968
  });
4904
4969
 
@@ -5052,9 +5117,9 @@ await mcp.connect(new StdioServerTransport());
5052
5117
  });
5053
5118
 
5054
5119
  // cli/local-cc/install.ts
5055
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6, readFileSync as readFileSync7, chmodSync, copyFileSync, renameSync as renameSync4, unlinkSync as unlinkSync4, openSync, fsyncSync, closeSync } from "fs";
5120
+ import { existsSync as existsSync8, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6, readFileSync as readFileSync7, chmodSync, copyFileSync, renameSync as renameSync4, unlinkSync as unlinkSync4, openSync, fsyncSync, closeSync } from "fs";
5056
5121
  import { join as join7 } from "path";
5057
- import { homedir as homedir6 } from "os";
5122
+ import { homedir as homedir7 } from "os";
5058
5123
  import { spawnSync } from "child_process";
5059
5124
  function writePluginFiles() {
5060
5125
  mkdirSync6(SESSION_DIR, { recursive: true });
@@ -5103,7 +5168,7 @@ function runBunInstall() {
5103
5168
  }
5104
5169
  }
5105
5170
  function safelyMutateClaudeJson(mutator) {
5106
- if (!existsSync9(CLAUDE_JSON_PATH)) {
5171
+ if (!existsSync8(CLAUDE_JSON_PATH)) {
5107
5172
  return;
5108
5173
  }
5109
5174
  const originalText = readFileSync7(CLAUDE_JSON_PATH, "utf-8");
@@ -5256,17 +5321,17 @@ var init_install = __esm({
5256
5321
  "cli/local-cc/install.ts"() {
5257
5322
  "use strict";
5258
5323
  init_channelSource();
5259
- CLAUDE_JSON_BACKUP_PATH = join7(homedir6(), ".claude.json.synkro-bak");
5260
- SESSION_DIR = join7(homedir6(), ".synkro", "cc_sessions");
5324
+ CLAUDE_JSON_BACKUP_PATH = join7(homedir7(), ".claude.json.synkro-bak");
5325
+ SESSION_DIR = join7(homedir7(), ".synkro", "cc_sessions");
5261
5326
  PLUGIN_PATH = join7(SESSION_DIR, "synkro-channel.ts");
5262
5327
  PLUGIN_PKG_PATH = join7(SESSION_DIR, "package.json");
5263
5328
  PLUGIN_SETTINGS_DIR = join7(SESSION_DIR, ".claude");
5264
5329
  PLUGIN_SETTINGS_PATH = join7(PLUGIN_SETTINGS_DIR, "settings.json");
5265
5330
  PROJECT_MCP_PATH = join7(SESSION_DIR, ".mcp.json");
5266
- CLAUDE_JSON_PATH = join7(homedir6(), ".claude.json");
5331
+ CLAUDE_JSON_PATH = join7(homedir7(), ".claude.json");
5267
5332
  RUN_SCRIPT_PATH = join7(SESSION_DIR, "run-claude.sh");
5268
5333
  TMUX_SESSION_NAME = "synkro-local-cc";
5269
- SESSION_DIR_2 = join7(homedir6(), ".synkro", "cc_sessions_2");
5334
+ SESSION_DIR_2 = join7(homedir7(), ".synkro", "cc_sessions_2");
5270
5335
  PLUGIN_PATH_2 = join7(SESSION_DIR_2, "synkro-channel.ts");
5271
5336
  PLUGIN_PKG_PATH_2 = join7(SESSION_DIR_2, "package.json");
5272
5337
  PLUGIN_SETTINGS_DIR_2 = join7(SESSION_DIR_2, ".claude");
@@ -5425,7 +5490,7 @@ log "tmux session ended."
5425
5490
 
5426
5491
  // cli/local-cc/pueue.ts
5427
5492
  import { execFileSync, spawnSync as spawnSync2, spawn } from "child_process";
5428
- import { homedir as homedir7 } from "os";
5493
+ import { homedir as homedir8 } from "os";
5429
5494
  import { join as join8 } from "path";
5430
5495
  import { connect } from "net";
5431
5496
  function pueueAvailable() {
@@ -5542,14 +5607,14 @@ function ensureRunning(opts = {}) {
5542
5607
  return startTask(opts);
5543
5608
  }
5544
5609
  function probePort(host, port, timeoutMs = 500) {
5545
- return new Promise((resolve2) => {
5610
+ return new Promise((resolve3) => {
5546
5611
  const sock = connect(port, host);
5547
5612
  const done = (ok) => {
5548
5613
  try {
5549
5614
  sock.destroy();
5550
5615
  } catch {
5551
5616
  }
5552
- resolve2(ok);
5617
+ resolve3(ok);
5553
5618
  };
5554
5619
  sock.once("connect", () => done(true));
5555
5620
  sock.once("error", () => done(false));
@@ -5622,10 +5687,10 @@ var init_pueue = __esm({
5622
5687
  "use strict";
5623
5688
  TASK_LABEL = "synkro-local-cc";
5624
5689
  TMUX_SESSION = "synkro-local-cc";
5625
- SESSION_DIR2 = join8(homedir7(), ".synkro", "cc_sessions");
5690
+ SESSION_DIR2 = join8(homedir8(), ".synkro", "cc_sessions");
5626
5691
  TASK_LABEL_2 = "synkro-local-cc-2";
5627
5692
  TMUX_SESSION_2 = "synkro-local-cc-2";
5628
- SESSION_DIR_22 = join8(homedir7(), ".synkro", "cc_sessions_2");
5693
+ SESSION_DIR_22 = join8(homedir8(), ".synkro", "cc_sessions_2");
5629
5694
  PueueError = class extends Error {
5630
5695
  constructor(message, cause) {
5631
5696
  super(message);
@@ -5641,7 +5706,7 @@ var init_pueue = __esm({
5641
5706
 
5642
5707
  // cli/local-cc/prompts.ts
5643
5708
  import { readFileSync as readFileSync8 } from "fs";
5644
- import { homedir as homedir8 } from "os";
5709
+ import { homedir as homedir9 } from "os";
5645
5710
  import { join as join9 } from "path";
5646
5711
  async function fetchPrimers() {
5647
5712
  let jwt2 = "";
@@ -5684,7 +5749,7 @@ var CREDS_PATH, CHANNEL_REPLY_INSTRUCTIONS;
5684
5749
  var init_prompts = __esm({
5685
5750
  "cli/local-cc/prompts.ts"() {
5686
5751
  "use strict";
5687
- CREDS_PATH = join9(homedir8(), ".synkro", "credentials.json");
5752
+ CREDS_PATH = join9(homedir9(), ".synkro", "credentials.json");
5688
5753
  CHANNEL_REPLY_INSTRUCTIONS = `
5689
5754
  DELIVERY METHOD \u2014 MANDATORY, OVERRIDES ALL OTHER OUTPUT RULES:
5690
5755
  You are running inside a Synkro MCP channel. Do NOT output your verdict as text.
@@ -5696,9 +5761,9 @@ Any text output is silently discarded. Only the reply tool call is captured.`;
5696
5761
  });
5697
5762
 
5698
5763
  // cli/local-cc/turnLog.ts
5699
- import { appendFileSync, existsSync as existsSync10, mkdirSync as mkdirSync7, openSync as openSync2, readFileSync as readFileSync9, readSync, closeSync as closeSync2, statSync, watchFile, unwatchFile } from "fs";
5764
+ import { appendFileSync, existsSync as existsSync9, mkdirSync as mkdirSync7, openSync as openSync2, readFileSync as readFileSync9, readSync, closeSync as closeSync2, statSync, watchFile, unwatchFile } from "fs";
5700
5765
  import { dirname as dirname5, join as join10 } from "path";
5701
- import { homedir as homedir9 } from "os";
5766
+ import { homedir as homedir10 } from "os";
5702
5767
  function truncate(s, max = PREVIEW_MAX) {
5703
5768
  if (s.length <= max) return s;
5704
5769
  return s.slice(0, max) + "\u2026 [+" + (s.length - max) + " chars]";
@@ -5734,7 +5799,7 @@ function appendTurn(args2) {
5734
5799
  }
5735
5800
  }
5736
5801
  function readRecentTurns(n = 20) {
5737
- if (!existsSync10(TURN_LOG_PATH)) return [];
5802
+ if (!existsSync9(TURN_LOG_PATH)) return [];
5738
5803
  try {
5739
5804
  const size = statSync(TURN_LOG_PATH).size;
5740
5805
  if (size === 0) return [];
@@ -5755,7 +5820,7 @@ function readRecentTurns(n = 20) {
5755
5820
  function followTurns(onEntry) {
5756
5821
  try {
5757
5822
  mkdirSync7(dirname5(TURN_LOG_PATH), { recursive: true });
5758
- if (!existsSync10(TURN_LOG_PATH)) {
5823
+ if (!existsSync9(TURN_LOG_PATH)) {
5759
5824
  appendFileSync(TURN_LOG_PATH, "", "utf-8");
5760
5825
  }
5761
5826
  } catch {
@@ -5817,7 +5882,7 @@ var TURN_LOG_PATH, PREVIEW_MAX;
5817
5882
  var init_turnLog = __esm({
5818
5883
  "cli/local-cc/turnLog.ts"() {
5819
5884
  "use strict";
5820
- TURN_LOG_PATH = join10(homedir9(), ".synkro", "cc_sessions", "turns.log");
5885
+ TURN_LOG_PATH = join10(homedir10(), ".synkro", "cc_sessions", "turns.log");
5821
5886
  PREVIEW_MAX = 400;
5822
5887
  }
5823
5888
  });
@@ -5832,7 +5897,7 @@ async function submitToChannel(role, payload, opts = {}) {
5832
5897
  const port = opts.port ?? CHANNEL_PORT;
5833
5898
  const startedAt = Date.now();
5834
5899
  try {
5835
- const result = await new Promise((resolve2, reject) => {
5900
+ const result = await new Promise((resolve3, reject) => {
5836
5901
  const req = httpRequest({
5837
5902
  host: CHANNEL_HOST,
5838
5903
  port,
@@ -5858,7 +5923,7 @@ async function submitToChannel(role, payload, opts = {}) {
5858
5923
  reject(new LocalCCError(parsed.error));
5859
5924
  return;
5860
5925
  }
5861
- resolve2(String(parsed.result ?? ""));
5926
+ resolve3(String(parsed.result ?? ""));
5862
5927
  } catch (err) {
5863
5928
  reject(new LocalCCError(`malformed channel response: ${text.slice(0, 200)}`, err));
5864
5929
  }
@@ -5884,14 +5949,14 @@ async function submitToChannel(role, payload, opts = {}) {
5884
5949
  }
5885
5950
  }
5886
5951
  function isChannelAvailable(port = CHANNEL_PORT, timeoutMs = 500) {
5887
- return new Promise((resolve2) => {
5952
+ return new Promise((resolve3) => {
5888
5953
  const sock = connect2(port, CHANNEL_HOST);
5889
5954
  const done = (ok) => {
5890
5955
  try {
5891
5956
  sock.destroy();
5892
5957
  } catch {
5893
5958
  }
5894
- resolve2(ok);
5959
+ resolve3(ok);
5895
5960
  };
5896
5961
  sock.once("connect", () => done(true));
5897
5962
  sock.once("error", () => done(false));
@@ -5924,8 +5989,8 @@ __export(install_exports, {
5924
5989
  installCommand: () => installCommand,
5925
5990
  parseArgs: () => parseArgs
5926
5991
  });
5927
- import { existsSync as existsSync11, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, chmodSync as chmodSync2, readFileSync as readFileSync10, readdirSync, appendFileSync as appendFileSync2, renameSync as renameSync5 } from "fs";
5928
- import { homedir as homedir10 } from "os";
5992
+ import { existsSync as existsSync10, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, chmodSync as chmodSync2, readFileSync as readFileSync10, readdirSync, appendFileSync as appendFileSync2, renameSync as renameSync5 } from "fs";
5993
+ import { homedir as homedir11 } from "os";
5929
5994
  import { join as join11 } from "path";
5930
5995
  import { execSync as execSync5, spawnSync as spawnSync3, spawn as spawn2 } from "child_process";
5931
5996
  import { createInterface as createInterface3 } from "readline";
@@ -5951,13 +6016,13 @@ function parseArgs(argv) {
5951
6016
  }
5952
6017
  async function promptTranscriptConsent() {
5953
6018
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
5954
- return new Promise((resolve2) => {
6019
+ return new Promise((resolve3) => {
5955
6020
  rl.question(
5956
6021
  "Would you like Synkro to use Claude Code session transcripts\nto generate guardrail rules and policies for your team? (Y/n) ",
5957
6022
  (answer) => {
5958
6023
  rl.close();
5959
6024
  const trimmed = answer.trim().toLowerCase();
5960
- resolve2(trimmed === "" || trimmed === "y" || trimmed === "yes");
6025
+ resolve3(trimmed === "" || trimmed === "y" || trimmed === "yes");
5961
6026
  }
5962
6027
  );
5963
6028
  });
@@ -5986,6 +6051,7 @@ function writeHookScripts() {
5986
6051
  const cursorEditPrecheckPath = join11(HOOKS_DIR, "cursor-edit-precheck.ts");
5987
6052
  const cursorEditCapturePath = join11(HOOKS_DIR, "cursor-edit-capture.ts");
5988
6053
  const cursorBashFollowupPath = join11(HOOKS_DIR, "cursor-bash-followup.ts");
6054
+ const cursorSessionStartPath = join11(HOOKS_DIR, "cursor-session-start.ts");
5989
6055
  const mcpLocalServerPath = join11(HOOKS_DIR, "mcp-local-server.ts");
5990
6056
  writeFileSync7(bashScriptPath, BASH_JUDGE_TS, "utf-8");
5991
6057
  writeFileSync7(bashFollowupScriptPath, BASH_FOLLOWUP_TS, "utf-8");
@@ -6004,6 +6070,7 @@ function writeHookScripts() {
6004
6070
  writeFileSync7(cursorEditPrecheckPath, CURSOR_EDIT_PRECHECK_TS, "utf-8");
6005
6071
  writeFileSync7(cursorEditCapturePath, CURSOR_EDIT_CAPTURE_TS, "utf-8");
6006
6072
  writeFileSync7(cursorBashFollowupPath, CURSOR_BASH_FOLLOWUP_TS, "utf-8");
6073
+ writeFileSync7(cursorSessionStartPath, CURSOR_SESSION_START_TS, "utf-8");
6007
6074
  writeFileSync7(mcpLocalServerPath, `#!/usr/bin/env bun
6008
6075
  /**
6009
6076
  * Local MCP guardrails server \u2014 runs on port 8931, stores rules in ~/.synkro/rules.json.
@@ -6827,6 +6894,7 @@ console.log(\`[synkro] local MCP guardrails server listening on http://127.0.0.1
6827
6894
  chmodSync2(cursorEditPrecheckPath, 493);
6828
6895
  chmodSync2(cursorEditCapturePath, 493);
6829
6896
  chmodSync2(cursorBashFollowupPath, 493);
6897
+ chmodSync2(cursorSessionStartPath, 493);
6830
6898
  chmodSync2(mcpLocalServerPath, 493);
6831
6899
  return {
6832
6900
  bashScript: bashScriptPath,
@@ -6844,6 +6912,7 @@ console.log(\`[synkro] local MCP guardrails server listening on http://127.0.0.1
6844
6912
  cursorEditPrecheckScript: cursorEditPrecheckPath,
6845
6913
  cursorEditCaptureScript: cursorEditCapturePath,
6846
6914
  cursorBashFollowupScript: cursorBashFollowupPath,
6915
+ cursorSessionStartScript: cursorSessionStartPath,
6847
6916
  mcpLocalServerScript: mcpLocalServerPath
6848
6917
  };
6849
6918
  }
@@ -6856,7 +6925,7 @@ function shellQuoteSingle(value) {
6856
6925
  }
6857
6926
  function resolveSynkroBundle() {
6858
6927
  const scriptPath = process.argv[1];
6859
- if (scriptPath && existsSync11(scriptPath)) return scriptPath;
6928
+ if (scriptPath && existsSync10(scriptPath)) return scriptPath;
6860
6929
  return null;
6861
6930
  }
6862
6931
  function writeConfigEnv(opts) {
@@ -6876,7 +6945,7 @@ function writeConfigEnv(opts) {
6876
6945
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
6877
6946
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
6878
6947
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
6879
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.66")}`
6948
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.67")}`
6880
6949
  ];
6881
6950
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
6882
6951
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -6891,7 +6960,7 @@ function writeConfigEnv(opts) {
6891
6960
  chmodSync2(CONFIG_PATH3, 384);
6892
6961
  }
6893
6962
  function updateLocalInferenceFlag(enabled) {
6894
- if (!existsSync11(CONFIG_PATH3)) return;
6963
+ if (!existsSync10(CONFIG_PATH3)) return;
6895
6964
  let content = readFileSync10(CONFIG_PATH3, "utf-8");
6896
6965
  const flag = enabled ? "yes" : "no";
6897
6966
  if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
@@ -6921,7 +6990,7 @@ function collectLocalMetadata() {
6921
6990
  meta.cc_version = execSync5("claude --version", { encoding: "utf-8", timeout: 5e3 }).trim().split("\n")[0];
6922
6991
  } catch {
6923
6992
  }
6924
- const claudeDir = join11(homedir10(), ".claude");
6993
+ const claudeDir = join11(homedir11(), ".claude");
6925
6994
  try {
6926
6995
  const settings = JSON.parse(readFileSync10(join11(claudeDir, "settings.json"), "utf-8"));
6927
6996
  const plugins = Object.keys(settings.enabledPlugins ?? {}).filter((k) => settings.enabledPlugins[k]);
@@ -7011,10 +7080,10 @@ function isAlreadyInstalled() {
7011
7080
  join11(HOOKS_DIR, "cc-stop-summary.ts"),
7012
7081
  join11(HOOKS_DIR, "cc-session-start.ts")
7013
7082
  ];
7014
- if (!requiredScripts.every((p) => existsSync11(p))) return false;
7015
- if (!existsSync11(CONFIG_PATH3)) return false;
7016
- const settingsPath = join11(homedir10(), ".claude", "settings.json");
7017
- if (!existsSync11(settingsPath)) return false;
7083
+ if (!requiredScripts.every((p) => existsSync10(p))) return false;
7084
+ if (!existsSync10(CONFIG_PATH3)) return false;
7085
+ const settingsPath = join11(homedir11(), ".claude", "settings.json");
7086
+ if (!existsSync10(settingsPath)) return false;
7018
7087
  try {
7019
7088
  const settings = JSON.parse(readFileSync10(settingsPath, "utf-8"));
7020
7089
  const hooks = settings?.hooks;
@@ -7048,8 +7117,8 @@ function printChannelDiagnostics() {
7048
7117
  }
7049
7118
  }
7050
7119
  }
7051
- const logPath = join11(homedir10(), ".synkro", "cc_sessions", "run-claude.log");
7052
- if (existsSync11(logPath)) {
7120
+ const logPath = join11(homedir11(), ".synkro", "cc_sessions", "run-claude.log");
7121
+ if (existsSync10(logPath)) {
7053
7122
  const logContent = readFileSync10(logPath, "utf-8").trim().split("\n").slice(-10);
7054
7123
  console.warn(` run-claude.log:`);
7055
7124
  for (const line of logContent) console.warn(` ${line}`);
@@ -7059,7 +7128,7 @@ function printChannelDiagnostics() {
7059
7128
  console.warn(` Run \`synkro local-cc status\` and \`synkro local-cc logs --tmux\` to debug.`);
7060
7129
  }
7061
7130
  async function backfillLocalRules(gatewayUrl, token) {
7062
- if (existsSync11(RULES_PATH)) {
7131
+ if (existsSync10(RULES_PATH)) {
7063
7132
  console.log(" Local rules already exist \u2014 skipping cloud backfill.");
7064
7133
  return;
7065
7134
  }
@@ -7123,7 +7192,7 @@ async function backfillLocalRules(gatewayUrl, token) {
7123
7192
  }
7124
7193
  async function startLocalMcpServer() {
7125
7194
  const serverScript = join11(HOOKS_DIR, "mcp-local-server.ts");
7126
- if (!existsSync11(serverScript)) {
7195
+ if (!existsSync10(serverScript)) {
7127
7196
  console.warn(" \u26A0 Local MCP server script not found \u2014 skipping.");
7128
7197
  return;
7129
7198
  }
@@ -7329,7 +7398,8 @@ async function installCommand(opts = {}) {
7329
7398
  bashJudgeScriptPath: scripts.cursorBashJudgeScript,
7330
7399
  editPrecheckScriptPath: scripts.cursorEditPrecheckScript,
7331
7400
  editCaptureScriptPath: scripts.cursorEditCaptureScript,
7332
- bashFollowupScriptPath: scripts.cursorBashFollowupScript
7401
+ bashFollowupScriptPath: scripts.cursorBashFollowupScript,
7402
+ sessionStartScriptPath: scripts.cursorSessionStartScript
7333
7403
  });
7334
7404
  console.log(`Configured ${agent.name} hooks at ${agent.settingsPath}`);
7335
7405
  }
@@ -7542,8 +7612,8 @@ function detectGitRepo2() {
7542
7612
  function getClaudeProjectsFolder() {
7543
7613
  const cwd = process.cwd();
7544
7614
  const sanitized = "-" + cwd.replace(/\//g, "-");
7545
- const projectsDir = join11(homedir10(), ".claude", "projects", sanitized);
7546
- return existsSync11(projectsDir) ? projectsDir : null;
7615
+ const projectsDir = join11(homedir11(), ".claude", "projects", sanitized);
7616
+ return existsSync10(projectsDir) ? projectsDir : null;
7547
7617
  }
7548
7618
  function extractSessionInsights(projectsDir) {
7549
7619
  const insights = [];
@@ -7738,7 +7808,7 @@ var init_install2 = __esm({
7738
7808
  init_install();
7739
7809
  init_pueue();
7740
7810
  init_client();
7741
- SYNKRO_DIR2 = join11(homedir10(), ".synkro");
7811
+ SYNKRO_DIR2 = join11(homedir11(), ".synkro");
7742
7812
  HOOKS_DIR = join11(SYNKRO_DIR2, "hooks");
7743
7813
  BIN_DIR = join11(SYNKRO_DIR2, "bin");
7744
7814
  CONFIG_PATH3 = join11(SYNKRO_DIR2, "config.env");
@@ -7820,11 +7890,11 @@ var status_exports = {};
7820
7890
  __export(status_exports, {
7821
7891
  statusCommand: () => statusCommand
7822
7892
  });
7823
- import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
7824
- import { homedir as homedir11 } from "os";
7893
+ import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
7894
+ import { homedir as homedir12 } from "os";
7825
7895
  import { join as join12 } from "path";
7826
7896
  function readConfigEnv() {
7827
- if (!existsSync12(CONFIG_PATH4)) return {};
7897
+ if (!existsSync11(CONFIG_PATH4)) return {};
7828
7898
  const out = {};
7829
7899
  const raw = readFileSync11(CONFIG_PATH4, "utf-8");
7830
7900
  for (const line of raw.split("\n")) {
@@ -7933,12 +8003,12 @@ async function statusCommand() {
7933
8003
  console.log("Hook scripts (Claude Code):");
7934
8004
  for (const f of ccHooks) {
7935
8005
  const p = join12(HOOKS_DIR2, f);
7936
- console.log(` ${existsSync12(p) ? "\u2713" : "\u2717"} ${p}`);
8006
+ console.log(` ${existsSync11(p) ? "\u2713" : "\u2717"} ${p}`);
7937
8007
  }
7938
8008
  console.log("Hook scripts (Cursor):");
7939
8009
  for (const f of cursorHooks) {
7940
8010
  const p = join12(HOOKS_DIR2, f);
7941
- console.log(` ${existsSync12(p) ? "\u2713" : "\u2717"} ${p}`);
8011
+ console.log(` ${existsSync11(p) ? "\u2713" : "\u2717"} ${p}`);
7942
8012
  }
7943
8013
  console.log();
7944
8014
  if (localInference) {
@@ -7981,7 +8051,7 @@ var init_status = __esm({
7981
8051
  init_cursorHookConfig();
7982
8052
  init_mcpConfig();
7983
8053
  init_pueue();
7984
- SYNKRO_DIR3 = join12(homedir11(), ".synkro");
8054
+ SYNKRO_DIR3 = join12(homedir12(), ".synkro");
7985
8055
  CONFIG_PATH4 = join12(SYNKRO_DIR3, "config.env");
7986
8056
  }
7987
8057
  });
@@ -8014,7 +8084,7 @@ __export(unlink_exports, {
8014
8084
  });
8015
8085
  import { createInterface as createInterface4 } from "readline";
8016
8086
  function ask2(rl, question) {
8017
- return new Promise((resolve2) => rl.question(question, resolve2));
8087
+ return new Promise((resolve3) => rl.question(question, resolve3));
8018
8088
  }
8019
8089
  async function unlinkCommand() {
8020
8090
  if (!isAuthenticated()) {
@@ -8071,11 +8141,11 @@ var config_exports = {};
8071
8141
  __export(config_exports, {
8072
8142
  configCommand: () => configCommand
8073
8143
  });
8074
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync13 } from "fs";
8144
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync12 } from "fs";
8075
8145
  import { join as join13 } from "path";
8076
- import { homedir as homedir12 } from "os";
8146
+ import { homedir as homedir13 } from "os";
8077
8147
  function readConfigEnv2() {
8078
- if (!existsSync13(CONFIG_PATH5)) return {};
8148
+ if (!existsSync12(CONFIG_PATH5)) return {};
8079
8149
  const out = {};
8080
8150
  for (const line of readFileSync12(CONFIG_PATH5, "utf-8").split("\n")) {
8081
8151
  const t = line.trim();
@@ -8086,7 +8156,7 @@ function readConfigEnv2() {
8086
8156
  return out;
8087
8157
  }
8088
8158
  function updateConfigValue(key, value) {
8089
- if (!existsSync13(CONFIG_PATH5)) {
8159
+ if (!existsSync12(CONFIG_PATH5)) {
8090
8160
  console.error("No config found. Run `synkro install` first.");
8091
8161
  process.exit(1);
8092
8162
  }
@@ -8157,7 +8227,7 @@ var init_config = __esm({
8157
8227
  "cli/commands/config.ts"() {
8158
8228
  "use strict";
8159
8229
  init_stub();
8160
- SYNKRO_DIR4 = join13(homedir12(), ".synkro");
8230
+ SYNKRO_DIR4 = join13(homedir13(), ".synkro");
8161
8231
  CONFIG_PATH5 = join13(SYNKRO_DIR4, "config.env");
8162
8232
  }
8163
8233
  });
@@ -8168,7 +8238,7 @@ __export(scanPr_exports, {
8168
8238
  scanPrCommand: () => scanPrCommand
8169
8239
  });
8170
8240
  import { execSync as execSync6, spawn as spawn3 } from "child_process";
8171
- import { readFileSync as readFileSync13, existsSync as existsSync14 } from "fs";
8241
+ import { readFileSync as readFileSync13, existsSync as existsSync13 } from "fs";
8172
8242
  import { join as join14 } from "path";
8173
8243
  function parseMatchSpec(condition) {
8174
8244
  if (!condition.startsWith("match_spec:")) return null;
@@ -8376,7 +8446,7 @@ function spawnClaudeJudge(file, claudeToken, promptHeader) {
8376
8446
  Diff:
8377
8447
  ${hunks}`;
8378
8448
  const fullPrompt = promptHeader + userMessage;
8379
- return new Promise((resolve2) => {
8449
+ return new Promise((resolve3) => {
8380
8450
  const t0 = Date.now();
8381
8451
  const proc = spawn3(
8382
8452
  "claude",
@@ -8404,7 +8474,7 @@ ${hunks}`;
8404
8474
  const latencyMs = Date.now() - t0;
8405
8475
  if (code !== 0) {
8406
8476
  console.warn(` claude exited ${code}: ${(stderr || stdout).slice(0, 500)}`);
8407
- resolve2({ findings: [], latencyMs });
8477
+ resolve3({ findings: [], latencyMs });
8408
8478
  return;
8409
8479
  }
8410
8480
  try {
@@ -8423,10 +8493,10 @@ ${hunks}`;
8423
8493
  description: f.description,
8424
8494
  fix: f.fix
8425
8495
  }));
8426
- resolve2({ findings, latencyMs });
8496
+ resolve3({ findings, latencyMs });
8427
8497
  } catch (parseErr) {
8428
8498
  console.warn(` failed to parse claude response: ${stdout.slice(0, 300)}`);
8429
- resolve2({ findings: [], latencyMs });
8499
+ resolve3({ findings: [], latencyMs });
8430
8500
  }
8431
8501
  });
8432
8502
  });
@@ -8475,7 +8545,7 @@ ${JSON.stringify(findings, null, 2)}
8475
8545
  `;
8476
8546
  }
8477
8547
  function spawnOpusConsolidator(findings, claudeToken) {
8478
- return new Promise((resolve2) => {
8548
+ return new Promise((resolve3) => {
8479
8549
  const prompt2 = buildConsolidationPrompt(findings);
8480
8550
  const proc = spawn3(
8481
8551
  "claude",
@@ -8502,7 +8572,7 @@ function spawnOpusConsolidator(findings, claudeToken) {
8502
8572
  proc.on("close", (code) => {
8503
8573
  if (code !== 0) {
8504
8574
  console.warn(` opus consolidation exited ${code}: ${(stderr || stdout).slice(0, 300)}`);
8505
- resolve2(fallbackReview(findings));
8575
+ resolve3(fallbackReview(findings));
8506
8576
  return;
8507
8577
  }
8508
8578
  try {
@@ -8523,10 +8593,10 @@ function spawnOpusConsolidator(findings, claudeToken) {
8523
8593
  const order = ["low", "medium", "high", "critical"];
8524
8594
  return order.indexOf(f.severity) > order.indexOf(max) ? f.severity : max;
8525
8595
  }, "low");
8526
- resolve2({ summary: review.summary || "", comments, severity: maxSeverity });
8596
+ resolve3({ summary: review.summary || "", comments, severity: maxSeverity });
8527
8597
  } catch {
8528
8598
  console.warn(` failed to parse opus response, using fallback`);
8529
- resolve2(fallbackReview(findings));
8599
+ resolve3(fallbackReview(findings));
8530
8600
  }
8531
8601
  });
8532
8602
  });
@@ -8649,7 +8719,7 @@ function shouldFail(findings, threshold) {
8649
8719
  }
8650
8720
  function readRepoDeps() {
8651
8721
  const pkgPath = join14(process.cwd(), "package.json");
8652
- if (!existsSync14(pkgPath)) return {};
8722
+ if (!existsSync13(pkgPath)) return {};
8653
8723
  try {
8654
8724
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
8655
8725
  return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
@@ -8913,8 +8983,8 @@ var disconnect_exports = {};
8913
8983
  __export(disconnect_exports, {
8914
8984
  disconnectCommand: () => disconnectCommand
8915
8985
  });
8916
- import { existsSync as existsSync15, rmSync } from "fs";
8917
- import { homedir as homedir13 } from "os";
8986
+ import { existsSync as existsSync14, rmSync } from "fs";
8987
+ import { homedir as homedir14 } from "os";
8918
8988
  import { join as join15 } from "path";
8919
8989
  function tearDownLocalCC() {
8920
8990
  let hadTask = false;
@@ -8951,13 +9021,13 @@ function disconnectCommand(args2 = []) {
8951
9021
  console.log(`${mcpRemoved ? "\u2713" : "\xB7"} MCP guardrails server: ${mcpRemoved ? "removed entry from ~/.claude.json" : "no Synkro MCP entry found"}`);
8952
9022
  }
8953
9023
  if (purge) {
8954
- if (existsSync15(SYNKRO_DIR5)) {
9024
+ if (existsSync14(SYNKRO_DIR5)) {
8955
9025
  rmSync(SYNKRO_DIR5, { recursive: true, force: true });
8956
9026
  console.log(`\u2713 Removed ${SYNKRO_DIR5}`);
8957
9027
  } else {
8958
9028
  console.log(`\xB7 ${SYNKRO_DIR5} already gone, nothing to remove`);
8959
9029
  }
8960
- } else if (existsSync15(SYNKRO_DIR5)) {
9030
+ } else if (existsSync14(SYNKRO_DIR5)) {
8961
9031
  console.log(`Config preserved at ${SYNKRO_DIR5}. Run with --purge to remove.`);
8962
9032
  }
8963
9033
  console.log("\nSynkro disconnected.");
@@ -8972,7 +9042,7 @@ var init_disconnect = __esm({
8972
9042
  init_mcpConfig();
8973
9043
  init_pueue();
8974
9044
  init_install();
8975
- SYNKRO_DIR5 = join15(homedir13(), ".synkro");
9045
+ SYNKRO_DIR5 = join15(homedir14(), ".synkro");
8976
9046
  }
8977
9047
  });
8978
9048
 
@@ -9019,9 +9089,9 @@ __export(localCc_exports, {
9019
9089
  localCcCommand: () => localCcCommand
9020
9090
  });
9021
9091
  import { spawnSync as spawnSync4 } from "child_process";
9022
- import { homedir as homedir14 } from "os";
9092
+ import { homedir as homedir15 } from "os";
9023
9093
  import { join as join16 } from "path";
9024
- import { existsSync as existsSync16, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
9094
+ import { existsSync as existsSync15, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
9025
9095
  function printHelp() {
9026
9096
  console.log(`synkro local-cc \u2014 manage the local Claude Code inference session
9027
9097
 
@@ -9111,14 +9181,14 @@ TROUBLESHOOTING
9111
9181
  `);
9112
9182
  }
9113
9183
  function readGatewayUrl() {
9114
- if (existsSync16(CONFIG_PATH6)) {
9184
+ if (existsSync15(CONFIG_PATH6)) {
9115
9185
  const m = readFileSync14(CONFIG_PATH6, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
9116
9186
  if (m) return m[1];
9117
9187
  }
9118
9188
  return "https://api.synkro.sh";
9119
9189
  }
9120
9190
  function updateLocalInferenceFlag2(enabled) {
9121
- if (!existsSync16(CONFIG_PATH6)) return;
9191
+ if (!existsSync15(CONFIG_PATH6)) return;
9122
9192
  let content = readFileSync14(CONFIG_PATH6, "utf-8");
9123
9193
  const flag = enabled ? "yes" : "no";
9124
9194
  if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
@@ -9348,7 +9418,7 @@ function cmdLogs(rest) {
9348
9418
  if (!raw) console.log(" " + colorize("(use --raw / -r to see full payloads, --live / -f to follow)", 90));
9349
9419
  return;
9350
9420
  }
9351
- return new Promise((resolve2) => {
9421
+ return new Promise((resolve3) => {
9352
9422
  console.log(" " + colorize("\u2014 following new turns (Ctrl-C to exit) \u2014", 90));
9353
9423
  const stop = followTurns((t) => {
9354
9424
  console.log(" " + formatTurn(t, raw));
@@ -9356,7 +9426,7 @@ function cmdLogs(rest) {
9356
9426
  const onSigint = () => {
9357
9427
  stop();
9358
9428
  process.removeListener("SIGINT", onSigint);
9359
- resolve2();
9429
+ resolve3();
9360
9430
  };
9361
9431
  process.on("SIGINT", onSigint);
9362
9432
  });
@@ -9453,7 +9523,7 @@ var init_localCc = __esm({
9453
9523
  init_settings();
9454
9524
  init_client();
9455
9525
  init_stub();
9456
- CONFIG_PATH6 = join16(homedir14(), ".synkro", "config.env");
9526
+ CONFIG_PATH6 = join16(homedir15(), ".synkro", "config.env");
9457
9527
  }
9458
9528
  });
9459
9529
 
@@ -9463,10 +9533,10 @@ __export(grade_exports, {
9463
9533
  gradeCommand: () => gradeCommand
9464
9534
  });
9465
9535
  async function readStdin() {
9466
- return new Promise((resolve2, reject) => {
9536
+ return new Promise((resolve3, reject) => {
9467
9537
  const chunks = [];
9468
9538
  process.stdin.on("data", (c) => chunks.push(c));
9469
- process.stdin.on("end", () => resolve2(Buffer.concat(chunks).toString("utf-8")));
9539
+ process.stdin.on("end", () => resolve3(Buffer.concat(chunks).toString("utf-8")));
9470
9540
  process.stdin.on("error", reject);
9471
9541
  });
9472
9542
  }
@@ -9507,14 +9577,14 @@ var init_grade = __esm({
9507
9577
  });
9508
9578
 
9509
9579
  // cli/bootstrap.js
9510
- import { readFileSync as readFileSync15, existsSync as existsSync17 } from "fs";
9511
- import { resolve } from "path";
9580
+ import { readFileSync as readFileSync15, existsSync as existsSync16 } from "fs";
9581
+ import { resolve as resolve2 } from "path";
9512
9582
  var envCandidates = [
9513
- resolve(process.cwd(), ".env"),
9514
- resolve(process.env.HOME ?? "", ".synkro", "config.env")
9583
+ resolve2(process.cwd(), ".env"),
9584
+ resolve2(process.env.HOME ?? "", ".synkro", "config.env")
9515
9585
  ];
9516
9586
  for (const envPath of envCandidates) {
9517
- if (!existsSync17(envPath)) continue;
9587
+ if (!existsSync16(envPath)) continue;
9518
9588
  const envContent = readFileSync15(envPath, "utf-8");
9519
9589
  for (const line of envContent.split("\n")) {
9520
9590
  const trimmed = line.trim();