@synkro-sh/cli 1.6.33 → 1.6.34

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
@@ -601,6 +601,58 @@ var init_mcpConfig = __esm({
601
601
  }
602
602
  });
603
603
 
604
+ // cli/installer/skillParser.ts
605
+ import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
606
+ import { resolve as resolve2, basename } from "path";
607
+ function parseSection(heading, body) {
608
+ const lines = body.split("\n");
609
+ const meta = {};
610
+ const textLines = [];
611
+ for (const line of lines) {
612
+ const m = line.match(/^(mode|severity|category)\s*:\s*(.+)/i);
613
+ if (m && META_KEYS.has(m[1].toLowerCase())) {
614
+ meta[m[1].toLowerCase()] = m[2].trim();
615
+ } else if (line.trim()) {
616
+ textLines.push(line.trim());
617
+ }
618
+ }
619
+ const description = textLines.join(" ").trim();
620
+ const text = description ? `${heading}: ${description}` : heading;
621
+ return {
622
+ text,
623
+ mode: meta.mode || "ask",
624
+ severity: meta.severity || "medium",
625
+ category: meta.category || "custom"
626
+ };
627
+ }
628
+ function parseSkillFile(filePath) {
629
+ if (!existsSync4(filePath)) return null;
630
+ const content = readFileSync4(filePath, "utf-8");
631
+ const source = `skill:${basename(filePath)}`;
632
+ const sections = content.split(/^## /m).slice(1);
633
+ if (sections.length === 0) return null;
634
+ const rules = [];
635
+ for (const section of sections) {
636
+ const newlineIdx = section.indexOf("\n");
637
+ if (newlineIdx === -1) continue;
638
+ const heading = section.slice(0, newlineIdx).trim();
639
+ const body = section.slice(newlineIdx + 1);
640
+ if (!heading) continue;
641
+ rules.push(parseSection(heading, body));
642
+ }
643
+ return rules.length > 0 ? { source, rules } : null;
644
+ }
645
+ function resolveSkillPaths(skills, repoRoot) {
646
+ return skills.filter((s) => s.endsWith(".md")).map((s) => resolve2(repoRoot, s)).filter((p) => existsSync4(p));
647
+ }
648
+ var META_KEYS;
649
+ var init_skillParser = __esm({
650
+ "cli/installer/skillParser.ts"() {
651
+ "use strict";
652
+ META_KEYS = /* @__PURE__ */ new Set(["mode", "severity", "category"]);
653
+ }
654
+ });
655
+
604
656
  // cli/installer/hookScripts.ts
605
657
  var SYNKRO_COMMON_SCRIPT;
606
658
  var init_hookScripts = __esm({
@@ -1147,6 +1199,7 @@ export interface SynkroFileConfig {
1147
1199
  harness: ('claude-code' | 'cursor')[];
1148
1200
  grader: { pool: 'auto' | 'claude' | 'cursor'; mode?: string };
1149
1201
  ruleset: string;
1202
+ skills: string[];
1150
1203
  scanning: { cwe: boolean; cve: boolean };
1151
1204
  }
1152
1205
 
@@ -1155,6 +1208,7 @@ const SYNKRO_FILE_DEFAULTS: SynkroFileConfig = {
1155
1208
  harness: ['claude-code', 'cursor'],
1156
1209
  grader: { pool: 'auto' },
1157
1210
  ruleset: 'default',
1211
+ skills: [],
1158
1212
  scanning: { cwe: true, cve: true },
1159
1213
  };
1160
1214
 
@@ -1180,6 +1234,7 @@ export function loadSynkroFile(cwd?: string): SynkroFileConfig {
1180
1234
  mode: ['local', 'byok'].includes(parsed.grader?.mode) ? parsed.grader.mode : undefined,
1181
1235
  },
1182
1236
  ruleset: typeof parsed.ruleset === 'string' ? parsed.ruleset : 'default',
1237
+ skills: Array.isArray(parsed.skills) ? parsed.skills.filter((s: unknown) => typeof s === 'string') : [],
1183
1238
  scanning: {
1184
1239
  cwe: parsed.scanning?.cwe !== false,
1185
1240
  cve: parsed.scanning?.cve !== false,
@@ -5946,7 +6001,7 @@ __export(stub_exports, {
5946
6001
  saveCredentials: () => saveCredentials
5947
6002
  });
5948
6003
  import { createServer } from "http";
5949
- import { writeFileSync as writeFileSync4, readFileSync as readFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "fs";
6004
+ import { writeFileSync as writeFileSync4, readFileSync as readFileSync5, existsSync as existsSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "fs";
5950
6005
  import { homedir as homedir4, platform } from "os";
5951
6006
  import { join as join3, dirname as dirname4 } from "path";
5952
6007
  import { execFile } from "child_process";
@@ -5977,17 +6032,17 @@ function openBrowser(url) {
5977
6032
  }
5978
6033
  function saveCredentials(data) {
5979
6034
  const dir = dirname4(AUTH_FILE);
5980
- if (!existsSync4(dir)) {
6035
+ if (!existsSync5(dir)) {
5981
6036
  mkdirSync4(dir, { recursive: true, mode: 448 });
5982
6037
  }
5983
6038
  writeFileSync4(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 384 });
5984
6039
  }
5985
6040
  function loadCredentials() {
5986
- if (!existsSync4(AUTH_FILE)) {
6041
+ if (!existsSync5(AUTH_FILE)) {
5987
6042
  return null;
5988
6043
  }
5989
6044
  try {
5990
- const content = readFileSync4(AUTH_FILE, "utf8");
6045
+ const content = readFileSync5(AUTH_FILE, "utf8");
5991
6046
  return JSON.parse(content);
5992
6047
  } catch (error) {
5993
6048
  return null;
@@ -6000,7 +6055,7 @@ function createCallbackServer() {
6000
6055
  "Access-Control-Allow-Headers": "Content-Type",
6001
6056
  "Vary": "Origin"
6002
6057
  };
6003
- return new Promise((resolve3, reject) => {
6058
+ return new Promise((resolve4, reject) => {
6004
6059
  const server = createServer((req, res) => {
6005
6060
  if (req.method === "OPTIONS") {
6006
6061
  const origin = req.headers.origin;
@@ -6089,7 +6144,7 @@ function createCallbackServer() {
6089
6144
  res.end(JSON.stringify({ ok: true }));
6090
6145
  setTimeout(() => {
6091
6146
  server.close();
6092
- resolve3(authData);
6147
+ resolve4(authData);
6093
6148
  }, 200);
6094
6149
  });
6095
6150
  req.on("error", (e) => {
@@ -6242,7 +6297,7 @@ async function ensureValidToken() {
6242
6297
  return true;
6243
6298
  }
6244
6299
  function clearCredentials() {
6245
- if (existsSync4(AUTH_FILE)) {
6300
+ if (existsSync5(AUTH_FILE)) {
6246
6301
  unlinkSync2(AUTH_FILE);
6247
6302
  }
6248
6303
  }
@@ -6422,7 +6477,7 @@ jobs:
6422
6477
  });
6423
6478
 
6424
6479
  // cli/installer/githubSetup.ts
6425
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
6480
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
6426
6481
  import { execSync as execSync2 } from "child_process";
6427
6482
  import { join as join4 } from "path";
6428
6483
  function ghSecretSet(token, owner, repo, name, value) {
@@ -6479,7 +6534,7 @@ function writeWorkflowFile(repoRootPath) {
6479
6534
  function findGitRoot(startCwd) {
6480
6535
  let cur = startCwd;
6481
6536
  while (cur && cur !== "/") {
6482
- if (existsSync5(join4(cur, ".git"))) return cur;
6537
+ if (existsSync6(join4(cur, ".git"))) return cur;
6483
6538
  const parent = join4(cur, "..");
6484
6539
  if (parent === cur) break;
6485
6540
  cur = parent;
@@ -6545,10 +6600,10 @@ function detectSubdirRepos() {
6545
6600
  }
6546
6601
  }
6547
6602
  function ask(rl, question) {
6548
- return new Promise((resolve3) => rl.question(question, resolve3));
6603
+ return new Promise((resolve4) => rl.question(question, resolve4));
6549
6604
  }
6550
6605
  function waitForGithubToken() {
6551
- return new Promise((resolve3, reject) => {
6606
+ return new Promise((resolve4, reject) => {
6552
6607
  const server = createServer2((req, res) => {
6553
6608
  if (req.method === "OPTIONS") {
6554
6609
  res.writeHead(204, {
@@ -6585,7 +6640,7 @@ function waitForGithubToken() {
6585
6640
  });
6586
6641
  res.end(JSON.stringify({ ok: true }));
6587
6642
  setTimeout(() => server.close(), 200);
6588
- resolve3(parsed.github_token);
6643
+ resolve4(parsed.github_token);
6589
6644
  } catch {
6590
6645
  res.writeHead(400, { "Content-Type": "application/json" });
6591
6646
  res.end(JSON.stringify({ error: "invalid json" }));
@@ -6845,7 +6900,7 @@ __export(macKeychain_exports, {
6845
6900
  writeCursorApiKey: () => writeCursorApiKey,
6846
6901
  writeRefreshAgent: () => writeRefreshAgent
6847
6902
  });
6848
- import { existsSync as existsSync6, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6, chmodSync, readFileSync as readFileSync5, statSync } from "fs";
6903
+ import { existsSync as existsSync7, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6, chmodSync, readFileSync as readFileSync6, statSync } from "fs";
6849
6904
  import { homedir as homedir5, platform as platform2 } from "os";
6850
6905
  import { join as join5 } from "path";
6851
6906
  import { spawnSync } from "child_process";
@@ -6873,7 +6928,7 @@ function exportKeychainCreds() {
6873
6928
  }
6874
6929
  function cursorApiKeyConfigured() {
6875
6930
  try {
6876
- return existsSync6(CURSOR_API_KEY_FILE) && readFileSync5(CURSOR_API_KEY_FILE, "utf-8").trim().length > 0;
6931
+ return existsSync7(CURSOR_API_KEY_FILE) && readFileSync6(CURSOR_API_KEY_FILE, "utf-8").trim().length > 0;
6877
6932
  } catch {
6878
6933
  return false;
6879
6934
  }
@@ -6889,7 +6944,7 @@ function writeCursorApiKey(key) {
6889
6944
  async function validateCursorApiKey() {
6890
6945
  let key;
6891
6946
  try {
6892
- key = readFileSync5(CURSOR_API_KEY_FILE, "utf-8").trim();
6947
+ key = readFileSync6(CURSOR_API_KEY_FILE, "utf-8").trim();
6893
6948
  } catch {
6894
6949
  return null;
6895
6950
  }
@@ -6908,7 +6963,7 @@ async function validateCursorApiKey() {
6908
6963
  }
6909
6964
  }
6910
6965
  function credsAreStale() {
6911
- if (!existsSync6(CLAUDE_CREDS_FILE)) return true;
6966
+ if (!existsSync7(CLAUDE_CREDS_FILE)) return true;
6912
6967
  try {
6913
6968
  const ageMs = Date.now() - statSync(CLAUDE_CREDS_FILE).mtimeMs;
6914
6969
  return ageMs > REFRESH_INTERVAL_SECONDS * 1e3;
@@ -6971,7 +7026,7 @@ function uninstallRefreshAgent() {
6971
7026
  timeout: 5e3
6972
7027
  });
6973
7028
  try {
6974
- if (existsSync6(LAUNCHD_PLIST)) {
7029
+ if (existsSync7(LAUNCHD_PLIST)) {
6975
7030
  __require("fs").unlinkSync(LAUNCHD_PLIST);
6976
7031
  }
6977
7032
  } catch {
@@ -6983,7 +7038,7 @@ function refreshCreds() {
6983
7038
  }
6984
7039
  function readExportedCreds() {
6985
7040
  try {
6986
- return readFileSync5(CLAUDE_CREDS_FILE, "utf-8");
7041
+ return readFileSync6(CLAUDE_CREDS_FILE, "utf-8");
6987
7042
  } catch {
6988
7043
  return null;
6989
7044
  }
@@ -7032,7 +7087,7 @@ __export(dockerInstall_exports, {
7032
7087
  splitWorkers: () => splitWorkers,
7033
7088
  waitForContainerReady: () => waitForContainerReady
7034
7089
  });
7035
- import { copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync6, readdirSync as readdirSync2 } from "fs";
7090
+ import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync7, readdirSync as readdirSync2 } from "fs";
7036
7091
  import { homedir as homedir6 } from "os";
7037
7092
  import { join as join6 } from "path";
7038
7093
  import { execSync as execSync4, spawnSync as spawnSync2 } from "child_process";
@@ -7058,8 +7113,8 @@ function readSynkroFilePool() {
7058
7113
  const root = execSync4("git rev-parse --show-toplevel 2>/dev/null", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
7059
7114
  if (!root) return "auto";
7060
7115
  const fp = join6(root, ".synkro");
7061
- if (!existsSync7(fp)) return "auto";
7062
- const parsed = JSON.parse(readFileSync6(fp, "utf-8"));
7116
+ if (!existsSync8(fp)) return "auto";
7117
+ const parsed = JSON.parse(readFileSync7(fp, "utf-8"));
7063
7118
  const pool = parsed?.grader?.pool;
7064
7119
  if (pool === "cursor" || pool === "claude") return pool;
7065
7120
  } catch {
@@ -7186,10 +7241,10 @@ async function dockerInstall(opts = {}) {
7186
7241
  mkdirSync7(BACKUP_DIR, { recursive: true });
7187
7242
  mkdirSync7(CLAUDE_HOST_STATE_DIR, { recursive: true });
7188
7243
  const hostClaudeJson = join6(homedir6(), ".claude.json");
7189
- if (existsSync7(hostClaudeJson)) {
7244
+ if (existsSync8(hostClaudeJson)) {
7190
7245
  copyFileSync(hostClaudeJson, CLAUDE_HOST_STATE_FILE);
7191
7246
  }
7192
- if (!existsSync7(MCP_JWT_PATH)) {
7247
+ if (!existsSync8(MCP_JWT_PATH)) {
7193
7248
  throw new DockerInstallError(
7194
7249
  `MCP JWT missing at ${MCP_JWT_PATH}. The installer should mint this before calling dockerInstall.`
7195
7250
  );
@@ -7424,7 +7479,7 @@ async function dockerSafeStart() {
7424
7479
  return { ok: false, pgdataState: "no_container", error: "No synkro-server container found. Run `synkro install` first." };
7425
7480
  }
7426
7481
  const pgCheck = checkPgdata();
7427
- if (existsSync7(PGDATA_PATH) && readdirSync2(PGDATA_PATH).length > 0) {
7482
+ if (existsSync8(PGDATA_PATH) && readdirSync2(PGDATA_PATH).length > 0) {
7428
7483
  if (pgCheck.healthy) {
7429
7484
  console.log(` pgdata: existing data found \u2014 ${pgCheck.details}`);
7430
7485
  } else {
@@ -7464,7 +7519,7 @@ async function dockerSafeRestart() {
7464
7519
  return { ok: startResult.ok, stop: stopResult, start: startResult };
7465
7520
  }
7466
7521
  function checkPgdata() {
7467
- if (!existsSync7(PGDATA_PATH)) return { healthy: false, details: "pgdata directory does not exist" };
7522
+ if (!existsSync8(PGDATA_PATH)) return { healthy: false, details: "pgdata directory does not exist" };
7468
7523
  const entries = readdirSync2(PGDATA_PATH);
7469
7524
  if (entries.length === 0) return { healthy: true, details: "empty (fresh start)" };
7470
7525
  const hasPidFile = entries.includes("postmaster.pid");
@@ -7513,14 +7568,14 @@ __export(setupGithub_exports, {
7513
7568
  import { createInterface as createInterface2 } from "readline/promises";
7514
7569
  import { stdin as input, stdout as output } from "process";
7515
7570
  import { execSync as execSync5, spawn as nodeSpawn } from "child_process";
7516
- import { existsSync as existsSync8, readFileSync as readFileSync7, unlinkSync as unlinkSync3 } from "fs";
7571
+ import { existsSync as existsSync9, readFileSync as readFileSync8, unlinkSync as unlinkSync3 } from "fs";
7517
7572
  import { homedir as homedir7, platform as platform3 } from "os";
7518
7573
  import { join as join7 } from "path";
7519
7574
  import { execFile as execFile2 } from "child_process";
7520
7575
  function readConfig() {
7521
- if (!existsSync8(CONFIG_PATH)) return {};
7576
+ if (!existsSync9(CONFIG_PATH)) return {};
7522
7577
  const out = {};
7523
- for (const line of readFileSync7(CONFIG_PATH, "utf-8").split("\n")) {
7578
+ for (const line of readFileSync8(CONFIG_PATH, "utf-8").split("\n")) {
7524
7579
  const t = line.trim();
7525
7580
  if (!t || t.startsWith("#")) continue;
7526
7581
  const eq = t.indexOf("=");
@@ -7533,7 +7588,7 @@ async function prompt(rl, q, opts = {}) {
7533
7588
  process.stdout.write(q);
7534
7589
  const wasRaw = process.stdin.isRaw;
7535
7590
  if (process.stdin.setRawMode) process.stdin.setRawMode(true);
7536
- return await new Promise((resolve3) => {
7591
+ return await new Promise((resolve4) => {
7537
7592
  let chunk = "";
7538
7593
  const onData = (data) => {
7539
7594
  const s = data.toString("utf-8");
@@ -7541,7 +7596,7 @@ async function prompt(rl, q, opts = {}) {
7541
7596
  process.stdin.removeListener("data", onData);
7542
7597
  if (process.stdin.setRawMode) process.stdin.setRawMode(wasRaw ?? false);
7543
7598
  process.stdout.write("\n");
7544
- resolve3(chunk);
7599
+ resolve4(chunk);
7545
7600
  return;
7546
7601
  }
7547
7602
  if (s === "") process.exit(130);
@@ -7582,7 +7637,7 @@ function sleep(ms) {
7582
7637
  }
7583
7638
  function captureClaudeSetupToken() {
7584
7639
  const tmpFile = join7(SYNKRO_DIR3, `token-capture-${Date.now()}.raw`);
7585
- return new Promise((resolve3, reject) => {
7640
+ return new Promise((resolve4, reject) => {
7586
7641
  const proc = nodeSpawn("script", ["-q", tmpFile, "claude", "setup-token"], {
7587
7642
  stdio: "inherit"
7588
7643
  });
@@ -7590,7 +7645,7 @@ function captureClaudeSetupToken() {
7590
7645
  proc.on("close", (code) => {
7591
7646
  let raw = "";
7592
7647
  try {
7593
- raw = readFileSync7(tmpFile, "utf-8");
7648
+ raw = readFileSync8(tmpFile, "utf-8");
7594
7649
  } catch (e) {
7595
7650
  reject(new Error(`Could not read script output file: ${e.message}`));
7596
7651
  return;
@@ -7612,7 +7667,7 @@ function captureClaudeSetupToken() {
7612
7667
  reject(new Error(`Could not find token in claude setup-token output (file=${raw.length}b, yellow=${yellow.length}b)`));
7613
7668
  return;
7614
7669
  }
7615
- resolve3(token[0]);
7670
+ resolve4(token[0]);
7616
7671
  });
7617
7672
  });
7618
7673
  }
@@ -7874,9 +7929,10 @@ __export(install_exports, {
7874
7929
  installCommand: () => installCommand,
7875
7930
  parseArgs: () => parseArgs,
7876
7931
  reconcileHarness: () => reconcileHarness,
7932
+ syncSkillFiles: () => syncSkillFiles,
7877
7933
  writeHookScripts: () => writeHookScripts
7878
7934
  });
7879
- import { existsSync as existsSync9, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, chmodSync as chmodSync2, readFileSync as readFileSync8, readdirSync as readdirSync3 } from "fs";
7935
+ import { existsSync as existsSync10, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, chmodSync as chmodSync2, readFileSync as readFileSync9, readdirSync as readdirSync3 } from "fs";
7880
7936
  import { homedir as homedir8 } from "os";
7881
7937
  import { join as join8 } from "path";
7882
7938
  import { execSync as execSync6, spawnSync as spawnSync3 } from "child_process";
@@ -7908,20 +7964,20 @@ async function promptAgentSelection(detected) {
7908
7964
  detected.forEach((a, i) => console.log(` ${i + 1}. ${a.name}`));
7909
7965
  console.log(` ${detected.length + 1}. Both / all (default)`);
7910
7966
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
7911
- const ask2 = () => new Promise((resolve3) => {
7967
+ const ask2 = () => new Promise((resolve4) => {
7912
7968
  rl.question(`Pick [1-${detected.length + 1}] (default: all): `, (answer) => {
7913
7969
  const t = answer.trim().toLowerCase();
7914
7970
  if (t === "" || t === String(detected.length + 1) || t === "both" || t === "all") {
7915
7971
  rl.close();
7916
- return resolve3(detected);
7972
+ return resolve4(detected);
7917
7973
  }
7918
7974
  const n = parseInt(t, 10);
7919
7975
  if (Number.isInteger(n) && n >= 1 && n <= detected.length) {
7920
7976
  rl.close();
7921
- return resolve3([detected[n - 1]]);
7977
+ return resolve4([detected[n - 1]]);
7922
7978
  }
7923
7979
  console.log("Invalid choice. Try again.");
7924
- resolve3(ask2());
7980
+ resolve4(ask2());
7925
7981
  });
7926
7982
  });
7927
7983
  return ask2();
@@ -7944,12 +8000,12 @@ async function promptCursorApiKey(opts) {
7944
8000
  return;
7945
8001
  }
7946
8002
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
7947
- const key = await new Promise((resolve3) => {
8003
+ const key = await new Promise((resolve4) => {
7948
8004
  rl.question(
7949
8005
  "Cursor grading needs a Cursor API key (cursor.com \u2192 Settings \u2192 API Keys).\nPaste it now, or press Enter to skip (Cursor workers stay idle until set): ",
7950
8006
  (answer) => {
7951
8007
  rl.close();
7952
- resolve3(answer.trim());
8008
+ resolve4(answer.trim());
7953
8009
  }
7954
8010
  );
7955
8011
  });
@@ -7963,12 +8019,12 @@ async function promptCursorApiKey(opts) {
7963
8019
  async function promptGradingMode() {
7964
8020
  if (!process.stdin.isTTY) return "local";
7965
8021
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
7966
- return new Promise((resolve3) => {
8022
+ return new Promise((resolve4) => {
7967
8023
  rl.question(
7968
8024
  "Where should grading run?\n local \u2014 on this machine, via the Synkro container (default)\n byok \u2014 via an LLM API using your own provider key\nChoose [local] / byok: ",
7969
8025
  (answer) => {
7970
8026
  rl.close();
7971
- resolve3(answer.trim().toLowerCase() === "byok" ? "byok" : "local");
8027
+ resolve4(answer.trim().toLowerCase() === "byok" ? "byok" : "local");
7972
8028
  }
7973
8029
  );
7974
8030
  });
@@ -7976,25 +8032,25 @@ async function promptGradingMode() {
7976
8032
  async function promptStorageMode() {
7977
8033
  if (!process.stdin.isTTY) return "local";
7978
8034
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
7979
- return new Promise((resolve3) => {
8035
+ return new Promise((resolve4) => {
7980
8036
  rl.question(
7981
8037
  "Where should telemetry be stored?\n local \u2014 on this machine only (default)\n cloud \u2014 sent to Synkro cloud\nChoose [local] / cloud: ",
7982
8038
  (answer) => {
7983
8039
  rl.close();
7984
- resolve3(answer.trim().toLowerCase() === "cloud" ? "cloud" : "local");
8040
+ resolve4(answer.trim().toLowerCase() === "cloud" ? "cloud" : "local");
7985
8041
  }
7986
8042
  );
7987
8043
  });
7988
8044
  }
7989
8045
  async function promptTranscriptConsent() {
7990
8046
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
7991
- return new Promise((resolve3) => {
8047
+ return new Promise((resolve4) => {
7992
8048
  rl.question(
7993
8049
  "Import and embed your Claude Code session history?\nThis indexes past sessions so Ask Synkro can answer questions\nabout your coding patterns and the dashboard shows full history. (Y/n) ",
7994
8050
  (answer) => {
7995
8051
  rl.close();
7996
8052
  const trimmed = answer.trim().toLowerCase();
7997
- resolve3(trimmed === "" || trimmed === "y" || trimmed === "yes");
8053
+ resolve4(trimmed === "" || trimmed === "y" || trimmed === "yes");
7998
8054
  }
7999
8055
  );
8000
8056
  });
@@ -8102,7 +8158,7 @@ function shellQuoteSingle(value) {
8102
8158
  }
8103
8159
  function resolveSynkroBundle() {
8104
8160
  const scriptPath = process.argv[1];
8105
- if (scriptPath && existsSync9(scriptPath)) return scriptPath;
8161
+ if (scriptPath && existsSync10(scriptPath)) return scriptPath;
8106
8162
  return null;
8107
8163
  }
8108
8164
  function writeConfigEnv(opts) {
@@ -8122,7 +8178,7 @@ function writeConfigEnv(opts) {
8122
8178
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
8123
8179
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
8124
8180
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
8125
- `SYNKRO_VERSION=${shellQuoteSingle("1.6.33")}`
8181
+ `SYNKRO_VERSION=${shellQuoteSingle("1.6.34")}`
8126
8182
  ];
8127
8183
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
8128
8184
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -8144,8 +8200,8 @@ function resolveDeploymentMode() {
8144
8200
  const envOverride = process.env.SYNKRO_DEPLOYMENT_MODE?.toLowerCase();
8145
8201
  if (envOverride === "bare-host" || envOverride === "docker") return envOverride;
8146
8202
  try {
8147
- if (existsSync9(CONFIG_PATH2)) {
8148
- const m = readFileSync8(CONFIG_PATH2, "utf-8").match(/^SYNKRO_DEPLOYMENT_MODE='([^']*)'/m);
8203
+ if (existsSync10(CONFIG_PATH2)) {
8204
+ const m = readFileSync9(CONFIG_PATH2, "utf-8").match(/^SYNKRO_DEPLOYMENT_MODE='([^']*)'/m);
8149
8205
  const val = m?.[1]?.toLowerCase();
8150
8206
  if (val === "bare-host" || val === "docker") return val;
8151
8207
  }
@@ -8174,14 +8230,14 @@ function collectLocalMetadata(includeClaudeCode = true) {
8174
8230
  }
8175
8231
  const claudeDir = join8(homedir8(), ".claude");
8176
8232
  try {
8177
- const settings = JSON.parse(readFileSync8(join8(claudeDir, "settings.json"), "utf-8"));
8233
+ const settings = JSON.parse(readFileSync9(join8(claudeDir, "settings.json"), "utf-8"));
8178
8234
  const plugins = Object.keys(settings.enabledPlugins ?? {}).filter((k) => settings.enabledPlugins[k]);
8179
8235
  if (plugins.length) meta.enabled_plugins = plugins;
8180
8236
  if (settings.permissions?.defaultMode) meta.permissions_mode = settings.permissions.defaultMode;
8181
8237
  } catch {
8182
8238
  }
8183
8239
  try {
8184
- const mcpCache = JSON.parse(readFileSync8(join8(claudeDir, "mcp-needs-auth-cache.json"), "utf-8"));
8240
+ const mcpCache = JSON.parse(readFileSync9(join8(claudeDir, "mcp-needs-auth-cache.json"), "utf-8"));
8185
8241
  const mcpNames = Object.keys(mcpCache);
8186
8242
  if (mcpNames.length) meta.mcp_servers = mcpNames;
8187
8243
  } catch {
@@ -8196,7 +8252,7 @@ function collectLocalMetadata(includeClaudeCode = true) {
8196
8252
  const sessionsDir = join8(claudeDir, "sessions");
8197
8253
  const files = readdirSync3(sessionsDir).filter((f) => f.endsWith(".json")).slice(-5);
8198
8254
  for (const f of files) {
8199
- const s = JSON.parse(readFileSync8(join8(sessionsDir, f), "utf-8"));
8255
+ const s = JSON.parse(readFileSync9(join8(sessionsDir, f), "utf-8"));
8200
8256
  if (s.version) {
8201
8257
  meta.cc_version = meta.cc_version || s.version;
8202
8258
  break;
@@ -8328,7 +8384,7 @@ async function installCommand(opts = {}) {
8328
8384
  for (const mode of ["edit", "bash"]) {
8329
8385
  const pidFile = join8(SYNKRO_DIR4, "daemon", mode, "daemon.pid");
8330
8386
  try {
8331
- const pid = parseInt(readFileSync8(pidFile, "utf-8").trim(), 10);
8387
+ const pid = parseInt(readFileSync9(pidFile, "utf-8").trim(), 10);
8332
8388
  if (pid > 0) {
8333
8389
  process.kill(pid, "SIGTERM");
8334
8390
  console.log(`Stopped stale ${mode} grader daemon (pid ${pid})`);
@@ -8462,7 +8518,7 @@ async function installCommand(opts = {}) {
8462
8518
  try {
8463
8519
  if (useLocalMcp) {
8464
8520
  const jwtPath = join8(SYNKRO_DIR4, ".mcp-jwt");
8465
- if (!existsSync9(jwtPath)) {
8521
+ if (!existsSync10(jwtPath)) {
8466
8522
  const mintResp = await fetch(`${gatewayUrl}/api/v1/cli/mcp-token`, {
8467
8523
  method: "POST",
8468
8524
  headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
@@ -8552,7 +8608,7 @@ async function installCommand(opts = {}) {
8552
8608
  const ready = await waitForContainerReady(6e4);
8553
8609
  if (ready) {
8554
8610
  console.log(" \u2713 container ready");
8555
- const mcpJwt = readFileSync8(join8(SYNKRO_DIR4, ".mcp-jwt"), "utf-8").trim();
8611
+ const mcpJwt = readFileSync9(join8(SYNKRO_DIR4, ".mcp-jwt"), "utf-8").trim();
8556
8612
  try {
8557
8613
  const ingestResp = await fetch(`http://127.0.0.1:${hostMcpPort}/api/ingest`, {
8558
8614
  method: "POST",
@@ -8569,6 +8625,7 @@ async function installCommand(opts = {}) {
8569
8625
  } catch {
8570
8626
  console.warn(" \u26A0 ingest endpoint unreachable \u2014 telemetry spool may not drain.");
8571
8627
  }
8628
+ await syncSkillFiles();
8572
8629
  } else {
8573
8630
  console.error(" \u2717 container did not become healthy within 60s");
8574
8631
  console.error(" Run `docker logs synkro-server` to debug.");
@@ -8583,7 +8640,7 @@ async function installCommand(opts = {}) {
8583
8640
  try {
8584
8641
  let mcpToken = "";
8585
8642
  try {
8586
- mcpToken = readFileSync8(join8(SYNKRO_DIR4, ".mcp-jwt"), "utf-8").trim();
8643
+ mcpToken = readFileSync9(join8(SYNKRO_DIR4, ".mcp-jwt"), "utf-8").trim();
8587
8644
  } catch {
8588
8645
  }
8589
8646
  if (mcpToken) {
@@ -8652,7 +8709,7 @@ function writeSynkroFileIfMissing(opts) {
8652
8709
  const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
8653
8710
  if (!root) return;
8654
8711
  const fp = join8(root, ".synkro");
8655
- if (existsSync9(fp)) {
8712
+ if (existsSync10(fp)) {
8656
8713
  console.log(` .synkro: ${fp} (existing, respected)`);
8657
8714
  return;
8658
8715
  }
@@ -8686,8 +8743,8 @@ function readSynkroFilePool2() {
8686
8743
  const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
8687
8744
  if (!root) return "auto";
8688
8745
  const fp = join8(root, ".synkro");
8689
- if (!existsSync9(fp)) return "auto";
8690
- const parsed = JSON.parse(readFileSync8(fp, "utf-8"));
8746
+ if (!existsSync10(fp)) return "auto";
8747
+ const parsed = JSON.parse(readFileSync9(fp, "utf-8"));
8691
8748
  const pool = parsed?.grader?.pool;
8692
8749
  if (pool === "cursor" || pool === "claude") return pool;
8693
8750
  } catch {
@@ -8699,8 +8756,8 @@ function readFullSynkroFile() {
8699
8756
  const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
8700
8757
  if (!root) return null;
8701
8758
  const fp = join8(root, ".synkro");
8702
- if (!existsSync9(fp)) return null;
8703
- const parsed = JSON.parse(readFileSync8(fp, "utf-8"));
8759
+ if (!existsSync10(fp)) return null;
8760
+ const parsed = JSON.parse(readFileSync9(fp, "utf-8"));
8704
8761
  const valid = ["claude-code", "cursor"];
8705
8762
  const harness = Array.isArray(parsed.harness) ? parsed.harness.filter((h) => valid.includes(h)) : ["claude-code", "cursor"];
8706
8763
  return {
@@ -8710,7 +8767,9 @@ function readFullSynkroFile() {
8710
8767
  mode: ["local", "byok"].includes(parsed.grader?.mode) ? parsed.grader.mode : "local"
8711
8768
  },
8712
8769
  ruleset: parsed.ruleset || "default",
8713
- scanning: { cwe: parsed.scanning?.cwe !== false, cve: parsed.scanning?.cve !== false }
8770
+ skills: Array.isArray(parsed.skills) ? parsed.skills.filter((s) => typeof s === "string" && s.endsWith(".md")) : [],
8771
+ scanning: { cwe: parsed.scanning?.cwe !== false, cve: parsed.scanning?.cve !== false },
8772
+ _repoRoot: root
8714
8773
  };
8715
8774
  } catch {
8716
8775
  return null;
@@ -8745,7 +8804,7 @@ function reconcileHarness() {
8745
8804
  });
8746
8805
  console.log(" \u2713 Claude Code hooks registered");
8747
8806
  try {
8748
- const mcpJwt = readFileSync8(join8(SYNKRO_DIR4, ".mcp-jwt"), "utf-8").trim();
8807
+ const mcpJwt = readFileSync9(join8(SYNKRO_DIR4, ".mcp-jwt"), "utf-8").trim();
8749
8808
  if (mcpJwt) {
8750
8809
  installMcpConfig({ gatewayUrl: "", bearerToken: mcpJwt, local: true });
8751
8810
  console.log(" \u2713 Claude Code MCP registered");
@@ -8797,6 +8856,33 @@ function reconcileHarness() {
8797
8856
  if (providers.length === 0) providers.push("claude_code");
8798
8857
  return splitWorkers(total, providers);
8799
8858
  }
8859
+ async function syncSkillFiles() {
8860
+ const sf = readFullSynkroFile();
8861
+ if (!sf || sf.skills.length === 0) return;
8862
+ const resolved = resolveSkillPaths(sf.skills, sf._repoRoot);
8863
+ if (resolved.length === 0) return;
8864
+ const mcpPort = process.env.SYNKRO_MCP_PORT || "18931";
8865
+ for (const fp of resolved) {
8866
+ const skill = parseSkillFile(fp);
8867
+ if (!skill || skill.rules.length === 0) continue;
8868
+ try {
8869
+ const resp = await fetch(`http://127.0.0.1:${mcpPort}/api/local/skills/sync`, {
8870
+ method: "POST",
8871
+ headers: { "Content-Type": "application/json" },
8872
+ body: JSON.stringify(skill),
8873
+ signal: AbortSignal.timeout(1e4)
8874
+ });
8875
+ if (resp.ok) {
8876
+ const result = await resp.json();
8877
+ console.log(` \u2713 skill ${skill.source}: ${result.created} rules synced (${result.removed} removed)`);
8878
+ } else {
8879
+ console.warn(` \u26A0 skill ${skill.source}: sync failed (${resp.status})`);
8880
+ }
8881
+ } catch (e) {
8882
+ console.warn(` \u26A0 skill ${skill.source}: ${e.message}`);
8883
+ }
8884
+ }
8885
+ }
8800
8886
  function detectGitRepo2() {
8801
8887
  const run = (cmd2) => {
8802
8888
  try {
@@ -8816,7 +8902,7 @@ function getClaudeProjectsFolder() {
8816
8902
  const cwd = process.cwd();
8817
8903
  const sanitized = "-" + cwd.replace(/\//g, "-");
8818
8904
  const projectsDir = join8(homedir8(), ".claude", "projects", sanitized);
8819
- return existsSync9(projectsDir) ? projectsDir : null;
8905
+ return existsSync10(projectsDir) ? projectsDir : null;
8820
8906
  }
8821
8907
  function extractSessionInsights(projectsDir) {
8822
8908
  const insights = [];
@@ -8825,7 +8911,7 @@ function extractSessionInsights(projectsDir) {
8825
8911
  const sessionId = file.replace(".jsonl", "");
8826
8912
  const filePath = join8(projectsDir, file);
8827
8913
  try {
8828
- const content = readFileSync8(filePath, "utf-8");
8914
+ const content = readFileSync9(filePath, "utf-8");
8829
8915
  const lines = content.split("\n").filter(Boolean);
8830
8916
  for (let i = 0; i < lines.length; i++) {
8831
8917
  try {
@@ -8901,7 +8987,7 @@ function extractTextContent(content) {
8901
8987
  return "";
8902
8988
  }
8903
8989
  function parseTranscriptFile(filePath) {
8904
- const content = readFileSync8(filePath, "utf-8");
8990
+ const content = readFileSync9(filePath, "utf-8");
8905
8991
  const lines = content.split("\n").filter(Boolean);
8906
8992
  const messages = [];
8907
8993
  for (let i = 0; i < lines.length; i++) {
@@ -8971,7 +9057,7 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
8971
9057
  process.stdout.write(`\r Progress: ${i + 1}/${files.length} sessions (${totalMessages} messages embedded) `);
8972
9058
  }
8973
9059
  try {
8974
- const content = readFileSync8(join8(projectsDir, file), "utf-8");
9060
+ const content = readFileSync9(join8(projectsDir, file), "utf-8");
8975
9061
  const lineCount = content.split("\n").filter(Boolean).length;
8976
9062
  writeFileSync7(join8(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
8977
9063
  } catch {
@@ -9025,7 +9111,7 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
9025
9111
  const sessionId = file.replace(".jsonl", "");
9026
9112
  const filePath = join8(projectsDir, file);
9027
9113
  try {
9028
- const content = readFileSync8(filePath, "utf-8");
9114
+ const content = readFileSync9(filePath, "utf-8");
9029
9115
  const lineCount = content.split("\n").filter(Boolean).length;
9030
9116
  writeFileSync7(join8(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
9031
9117
  } catch {
@@ -9042,6 +9128,7 @@ var init_install = __esm({
9042
9128
  init_ccHookConfig();
9043
9129
  init_cursorHookConfig();
9044
9130
  init_mcpConfig();
9131
+ init_skillParser();
9045
9132
  init_hookScripts();
9046
9133
  init_hookScriptsTs();
9047
9134
  init_stub();
@@ -9104,7 +9191,7 @@ rl.on('line', async (line) => {
9104
9191
  });
9105
9192
 
9106
9193
  // cli/local-cc/install.ts
9107
- import { existsSync as existsSync10, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, readFileSync as readFileSync9, chmodSync as chmodSync3, copyFileSync as copyFileSync2, renameSync as renameSync4, unlinkSync as unlinkSync4, openSync, fsyncSync, closeSync } from "fs";
9194
+ import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, readFileSync as readFileSync10, chmodSync as chmodSync3, copyFileSync as copyFileSync2, renameSync as renameSync4, unlinkSync as unlinkSync4, openSync, fsyncSync, closeSync } from "fs";
9108
9195
  import { join as join9 } from "path";
9109
9196
  import { homedir as homedir9 } from "os";
9110
9197
  import { spawnSync as spawnSync4 } from "child_process";
@@ -9140,10 +9227,10 @@ function runBunInstall() {
9140
9227
  }
9141
9228
  }
9142
9229
  function safelyMutateClaudeJson(mutator) {
9143
- if (!existsSync10(CLAUDE_JSON_PATH)) {
9230
+ if (!existsSync11(CLAUDE_JSON_PATH)) {
9144
9231
  return;
9145
9232
  }
9146
- const originalText = readFileSync9(CLAUDE_JSON_PATH, "utf-8");
9233
+ const originalText = readFileSync10(CLAUDE_JSON_PATH, "utf-8");
9147
9234
  let parsed;
9148
9235
  try {
9149
9236
  parsed = JSON.parse(originalText);
@@ -9594,7 +9681,7 @@ var disconnect_exports = {};
9594
9681
  __export(disconnect_exports, {
9595
9682
  disconnectCommand: () => disconnectCommand
9596
9683
  });
9597
- import { existsSync as existsSync11, rmSync, readdirSync as readdirSync4 } from "fs";
9684
+ import { existsSync as existsSync12, rmSync, readdirSync as readdirSync4 } from "fs";
9598
9685
  import { homedir as homedir10 } from "os";
9599
9686
  import { join as join10 } from "path";
9600
9687
  import { spawnSync as spawnSync5 } from "child_process";
@@ -9635,10 +9722,10 @@ function confirmPurge() {
9635
9722
  return Promise.resolve(false);
9636
9723
  }
9637
9724
  const rl = createInterface4({ input: process.stdin, output: process.stdout });
9638
- return new Promise((resolve3) => {
9725
+ return new Promise((resolve4) => {
9639
9726
  rl.question(" Type 'yes' to wipe everything (anything else cancels): ", (answer) => {
9640
9727
  rl.close();
9641
- resolve3(answer.trim().toLowerCase() === "yes");
9728
+ resolve4(answer.trim().toLowerCase() === "yes");
9642
9729
  });
9643
9730
  });
9644
9731
  }
@@ -9670,7 +9757,7 @@ async function disconnectCommand(args2 = []) {
9670
9757
  const cursorMcpRemoved = uninstallCursorMcpConfig();
9671
9758
  console.log(`${cursorMcpRemoved ? "\u2713" : "\xB7"} MCP guardrails (Cursor): ${cursorMcpRemoved ? "removed from ~/.cursor/mcp.json" : "no entry found"}`);
9672
9759
  }
9673
- if (existsSync11(SYNKRO_DIR5)) {
9760
+ if (existsSync12(SYNKRO_DIR5)) {
9674
9761
  if (purge) {
9675
9762
  rmSync(SYNKRO_DIR5, { recursive: true, force: true });
9676
9763
  console.log(`\u2713 wiped ${SYNKRO_DIR5} entirely \u2014 including all scan data and backups`);
@@ -9713,7 +9800,7 @@ var init_disconnect = __esm({
9713
9800
  });
9714
9801
 
9715
9802
  // cli/local-cc/turnLog.ts
9716
- import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync10, openSync as openSync2, readFileSync as readFileSync10, readSync, closeSync as closeSync2, statSync as statSync2, watchFile, unwatchFile } from "fs";
9803
+ import { appendFileSync, existsSync as existsSync13, mkdirSync as mkdirSync10, openSync as openSync2, readFileSync as readFileSync11, readSync, closeSync as closeSync2, statSync as statSync2, watchFile, unwatchFile } from "fs";
9717
9804
  import { dirname as dirname6, join as join11 } from "path";
9718
9805
  import { homedir as homedir11 } from "os";
9719
9806
  function truncate(s, max = PREVIEW_MAX) {
@@ -9751,11 +9838,11 @@ function appendTurn(args2) {
9751
9838
  }
9752
9839
  }
9753
9840
  function readRecentTurns(n = 20) {
9754
- if (!existsSync12(TURN_LOG_PATH)) return [];
9841
+ if (!existsSync13(TURN_LOG_PATH)) return [];
9755
9842
  try {
9756
9843
  const size = statSync2(TURN_LOG_PATH).size;
9757
9844
  if (size === 0) return [];
9758
- const text = readFileSync10(TURN_LOG_PATH, "utf-8");
9845
+ const text = readFileSync11(TURN_LOG_PATH, "utf-8");
9759
9846
  const lines = text.split("\n").filter(Boolean);
9760
9847
  const lastN = lines.slice(-n).reverse();
9761
9848
  return lastN.map((line) => {
@@ -9772,7 +9859,7 @@ function readRecentTurns(n = 20) {
9772
9859
  function followTurns(onEntry) {
9773
9860
  try {
9774
9861
  mkdirSync10(dirname6(TURN_LOG_PATH), { recursive: true });
9775
- if (!existsSync12(TURN_LOG_PATH)) {
9862
+ if (!existsSync13(TURN_LOG_PATH)) {
9776
9863
  appendFileSync(TURN_LOG_PATH, "", "utf-8");
9777
9864
  }
9778
9865
  } catch {
@@ -9848,7 +9935,7 @@ async function submitToChannel(role, payload, opts = {}) {
9848
9935
  const port = opts.port ?? CHANNEL_PORT;
9849
9936
  const startedAt = Date.now();
9850
9937
  try {
9851
- const result = await new Promise((resolve3, reject) => {
9938
+ const result = await new Promise((resolve4, reject) => {
9852
9939
  const req = httpRequest({
9853
9940
  host: CHANNEL_HOST,
9854
9941
  port,
@@ -9874,7 +9961,7 @@ async function submitToChannel(role, payload, opts = {}) {
9874
9961
  reject(new LocalCCError(parsed.error));
9875
9962
  return;
9876
9963
  }
9877
- resolve3(String(parsed.result ?? ""));
9964
+ resolve4(String(parsed.result ?? ""));
9878
9965
  } catch (err) {
9879
9966
  reject(new LocalCCError(`malformed channel response: ${text.slice(0, 200)}`, err));
9880
9967
  }
@@ -9900,14 +9987,14 @@ async function submitToChannel(role, payload, opts = {}) {
9900
9987
  }
9901
9988
  }
9902
9989
  function isChannelAvailable(port = CHANNEL_PORT, timeoutMs = 500) {
9903
- return new Promise((resolve3) => {
9990
+ return new Promise((resolve4) => {
9904
9991
  const sock = connect(port, CHANNEL_HOST);
9905
9992
  const done = (ok) => {
9906
9993
  try {
9907
9994
  sock.destroy();
9908
9995
  } catch {
9909
9996
  }
9910
- resolve3(ok);
9997
+ resolve4(ok);
9911
9998
  };
9912
9999
  sock.once("connect", () => done(true));
9913
10000
  sock.once("error", () => done(false));
@@ -9939,10 +10026,10 @@ __export(grade_exports, {
9939
10026
  gradeCommand: () => gradeCommand
9940
10027
  });
9941
10028
  async function readStdin() {
9942
- return new Promise((resolve3, reject) => {
10029
+ return new Promise((resolve4, reject) => {
9943
10030
  const chunks = [];
9944
10031
  process.stdin.on("data", (c) => chunks.push(c));
9945
- process.stdin.on("end", () => resolve3(Buffer.concat(chunks).toString("utf-8")));
10032
+ process.stdin.on("end", () => resolve4(Buffer.concat(chunks).toString("utf-8")));
9946
10033
  process.stdin.on("error", reject);
9947
10034
  });
9948
10035
  }
@@ -10101,14 +10188,14 @@ function ensureRunning(opts = {}) {
10101
10188
  return startTask(opts);
10102
10189
  }
10103
10190
  function probePort(host, port, timeoutMs = 500) {
10104
- return new Promise((resolve3) => {
10191
+ return new Promise((resolve4) => {
10105
10192
  const sock = connect2(port, host);
10106
10193
  const done = (ok) => {
10107
10194
  try {
10108
10195
  sock.destroy();
10109
10196
  } catch {
10110
10197
  }
10111
- resolve3(ok);
10198
+ resolve4(ok);
10112
10199
  };
10113
10200
  sock.once("connect", () => done(true));
10114
10201
  sock.once("error", () => done(false));
@@ -10201,13 +10288,13 @@ var init_pueue = __esm({
10201
10288
  });
10202
10289
 
10203
10290
  // cli/local-cc/settings.ts
10204
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
10291
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
10205
10292
  import { homedir as homedir13 } from "os";
10206
10293
  import { join as join13 } from "path";
10207
10294
  function isLocalCCEnabled() {
10208
- if (!existsSync13(CONFIG_PATH3)) return false;
10295
+ if (!existsSync14(CONFIG_PATH3)) return false;
10209
10296
  try {
10210
- const content = readFileSync11(CONFIG_PATH3, "utf-8");
10297
+ const content = readFileSync12(CONFIG_PATH3, "utf-8");
10211
10298
  const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);
10212
10299
  return match?.[1] === "yes";
10213
10300
  } catch {
@@ -10231,7 +10318,7 @@ import { spawnSync as spawnSync7 } from "child_process";
10231
10318
  import { homedir as homedir14 } from "os";
10232
10319
  import { join as join14 } from "path";
10233
10320
  import { readFileSync as fsReadFileSync, existsSync as fsExistsSync } from "fs";
10234
- import { existsSync as existsSync14, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "fs";
10321
+ import { existsSync as existsSync15, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
10235
10322
  function deploymentMode() {
10236
10323
  const env = (process.env.SYNKRO_DEPLOYMENT_MODE || "").toLowerCase();
10237
10324
  if (env === "docker") return "docker";
@@ -10337,15 +10424,15 @@ TROUBLESHOOTING
10337
10424
  `);
10338
10425
  }
10339
10426
  function readGatewayUrl() {
10340
- if (existsSync14(CONFIG_PATH4)) {
10341
- const m = readFileSync12(CONFIG_PATH4, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
10427
+ if (existsSync15(CONFIG_PATH4)) {
10428
+ const m = readFileSync13(CONFIG_PATH4, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
10342
10429
  if (m) return m[1];
10343
10430
  }
10344
10431
  return "https://api.synkro.sh";
10345
10432
  }
10346
10433
  function updateLocalInferenceFlag(enabled) {
10347
- if (!existsSync14(CONFIG_PATH4)) return;
10348
- let content = readFileSync12(CONFIG_PATH4, "utf-8");
10434
+ if (!existsSync15(CONFIG_PATH4)) return;
10435
+ let content = readFileSync13(CONFIG_PATH4, "utf-8");
10349
10436
  const flag = enabled ? "yes" : "no";
10350
10437
  if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
10351
10438
  content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);
@@ -10537,6 +10624,7 @@ async function cmdRestart(rest = []) {
10537
10624
  await dockerUpdate({ claudeWorkers, cursorWorkers });
10538
10625
  const ready = await waitForContainerReady(6e4);
10539
10626
  console.log(ready ? "\u2713 container ready" : "\u26A0 container did not pass /healthz within 60s");
10627
+ if (ready) await syncSkillFiles();
10540
10628
  return;
10541
10629
  }
10542
10630
  stopTask(CHANNEL_PRIMARY);
@@ -10651,7 +10739,7 @@ function cmdLogs(rest) {
10651
10739
  if (!raw) console.log(" " + colorize("(use --raw / -r to see full payloads, --live / -f to follow)", 90));
10652
10740
  return;
10653
10741
  }
10654
- return new Promise((resolve3) => {
10742
+ return new Promise((resolve4) => {
10655
10743
  console.log(" " + colorize("\u2014 following new turns (Ctrl-C to exit) \u2014", 90));
10656
10744
  const stop = followTurns((t) => {
10657
10745
  console.log(" " + formatTurn(t, raw));
@@ -10659,7 +10747,7 @@ function cmdLogs(rest) {
10659
10747
  const onSigint = () => {
10660
10748
  stop();
10661
10749
  process.removeListener("SIGINT", onSigint);
10662
- resolve3();
10750
+ resolve4();
10663
10751
  };
10664
10752
  process.on("SIGINT", onSigint);
10665
10753
  });
@@ -10887,13 +10975,13 @@ var config_exports = {};
10887
10975
  __export(config_exports, {
10888
10976
  configCommand: () => configCommand
10889
10977
  });
10890
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync15 } from "fs";
10978
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync10, existsSync as existsSync16 } from "fs";
10891
10979
  import { join as join15 } from "path";
10892
10980
  import { homedir as homedir15 } from "os";
10893
10981
  function readConfigEnv() {
10894
- if (!existsSync15(CONFIG_PATH5)) return {};
10982
+ if (!existsSync16(CONFIG_PATH5)) return {};
10895
10983
  const out = {};
10896
- for (const line of readFileSync13(CONFIG_PATH5, "utf-8").split("\n")) {
10984
+ for (const line of readFileSync14(CONFIG_PATH5, "utf-8").split("\n")) {
10897
10985
  const t = line.trim();
10898
10986
  if (!t || t.startsWith("#")) continue;
10899
10987
  const eq = t.indexOf("=");
@@ -10902,11 +10990,11 @@ function readConfigEnv() {
10902
10990
  return out;
10903
10991
  }
10904
10992
  function updateConfigValue(key, value) {
10905
- if (!existsSync15(CONFIG_PATH5)) {
10993
+ if (!existsSync16(CONFIG_PATH5)) {
10906
10994
  console.error("No config found. Run `synkro install` first.");
10907
10995
  process.exit(1);
10908
10996
  }
10909
- const lines = readFileSync13(CONFIG_PATH5, "utf-8").split("\n");
10997
+ const lines = readFileSync14(CONFIG_PATH5, "utf-8").split("\n");
10910
10998
  const pattern = new RegExp(`^${key}=`);
10911
10999
  let found = false;
10912
11000
  const updated = lines.map((line) => {
@@ -11033,14 +11121,14 @@ var init_config = __esm({
11033
11121
  });
11034
11122
 
11035
11123
  // cli/bootstrap.js
11036
- import { readFileSync as readFileSync14, existsSync as existsSync16 } from "fs";
11037
- import { resolve as resolve2 } from "path";
11124
+ import { readFileSync as readFileSync15, existsSync as existsSync17 } from "fs";
11125
+ import { resolve as resolve3 } from "path";
11038
11126
  var envCandidates = [
11039
- resolve2(process.env.HOME ?? "", ".synkro", "config.env")
11127
+ resolve3(process.env.HOME ?? "", ".synkro", "config.env")
11040
11128
  ];
11041
11129
  for (const envPath of envCandidates) {
11042
- if (!existsSync16(envPath)) continue;
11043
- const envContent = readFileSync14(envPath, "utf-8");
11130
+ if (!existsSync17(envPath)) continue;
11131
+ const envContent = readFileSync15(envPath, "utf-8");
11044
11132
  for (const line of envContent.split("\n")) {
11045
11133
  const trimmed = line.trim();
11046
11134
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -11055,7 +11143,7 @@ var args = process.argv.slice(2);
11055
11143
  var cmd = args[0] || "";
11056
11144
  var subArgs = args.slice(1);
11057
11145
  function printVersion() {
11058
- console.log("1.6.33");
11146
+ console.log("1.6.34");
11059
11147
  }
11060
11148
  function printHelp2() {
11061
11149
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents