@synkro-sh/cli 1.6.35 → 1.6.36

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
@@ -1198,6 +1198,7 @@ export interface SynkroFileConfig {
1198
1198
  version: number;
1199
1199
  harness: ('claude-code' | 'cursor')[];
1200
1200
  grader: { pool: 'auto' | 'claude' | 'cursor'; mode?: string };
1201
+ workers: { claude?: number; cursor?: number };
1201
1202
  ruleset: string;
1202
1203
  skills: string[];
1203
1204
  scanning: { cwe: boolean; cve: boolean };
@@ -1207,6 +1208,7 @@ const SYNKRO_FILE_DEFAULTS: SynkroFileConfig = {
1207
1208
  version: 1,
1208
1209
  harness: ['claude-code', 'cursor'],
1209
1210
  grader: { pool: 'auto' },
1211
+ workers: {},
1210
1212
  ruleset: 'default',
1211
1213
  skills: [],
1212
1214
  scanning: { cwe: true, cve: true },
@@ -1282,6 +1284,10 @@ export function loadSynkroFile(cwd?: string): SynkroFileConfig {
1282
1284
  pool: ['auto', 'claude', 'cursor'].includes(parsed.grader?.pool) ? parsed.grader.pool : 'auto',
1283
1285
  mode: ['local', 'byok'].includes(parsed.grader?.mode) ? parsed.grader.mode : undefined,
1284
1286
  },
1287
+ workers: {
1288
+ ...(typeof parsed.workers?.claude === 'number' ? { claude: parsed.workers.claude } : {}),
1289
+ ...(typeof parsed.workers?.cursor === 'number' ? { cursor: parsed.workers.cursor } : {}),
1290
+ },
1285
1291
  ruleset: typeof parsed.ruleset === 'string' ? parsed.ruleset : 'default',
1286
1292
  skills: Array.isArray(parsed.skills) ? parsed.skills.filter((s: unknown) => typeof s === 'string') : [],
1287
1293
  scanning: {
@@ -7200,19 +7206,21 @@ function parseSynkroYaml(raw) {
7200
7206
  if (currentArr && currentKey) result[currentKey] = currentArr;
7201
7207
  return result;
7202
7208
  }
7203
- function readSynkroFilePool() {
7209
+ function readSynkroFileConfig() {
7204
7210
  try {
7205
7211
  const root = execSync4("git rev-parse --show-toplevel 2>/dev/null", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
7206
- if (!root) return "auto";
7212
+ if (!root) return { pool: "auto" };
7207
7213
  const fp = join6(root, ".synkro");
7208
- if (!existsSync8(fp)) return "auto";
7214
+ if (!existsSync8(fp)) return { pool: "auto" };
7209
7215
  const raw = readFileSync7(fp, "utf-8");
7210
7216
  const parsed = raw.trimStart().startsWith("{") ? JSON.parse(raw) : parseSynkroYaml(raw);
7211
- const pool = parsed?.grader?.pool;
7212
- if (pool === "cursor" || pool === "claude") return pool;
7217
+ const pool = ["auto", "claude", "cursor"].includes(parsed?.grader?.pool) ? parsed.grader.pool : "auto";
7218
+ const cw = typeof parsed?.workers?.claude === "number" ? Math.max(0, Math.floor(parsed.workers.claude)) : void 0;
7219
+ const curw = typeof parsed?.workers?.cursor === "number" ? Math.max(0, Math.floor(parsed.workers.cursor)) : void 0;
7220
+ return { pool, claudeWorkers: cw, cursorWorkers: curw };
7213
7221
  } catch {
7214
7222
  }
7215
- return "auto";
7223
+ return { pool: "auto" };
7216
7224
  }
7217
7225
  function resolveWorkerConfig(rest) {
7218
7226
  let workers = 8;
@@ -7247,10 +7255,15 @@ function resolveWorkerConfig(rest) {
7247
7255
  workers = Math.min(workers, 64);
7248
7256
  let provs = providers;
7249
7257
  if (provs.length === 0) {
7250
- const synkroPool = readSynkroFilePool();
7251
- if (synkroPool === "cursor") {
7258
+ const sc = readSynkroFileConfig();
7259
+ if (sc.claudeWorkers != null || sc.cursorWorkers != null) {
7260
+ const cw = sc.claudeWorkers || 0;
7261
+ const curw = sc.cursorWorkers || 0;
7262
+ if (cw + curw > 0) return { claudeWorkers: cw, cursorWorkers: curw, explicit };
7263
+ }
7264
+ if (sc.pool === "cursor") {
7252
7265
  provs = ["cursor"];
7253
- } else if (synkroPool === "claude") {
7266
+ } else if (sc.pool === "claude") {
7254
7267
  provs = ["claude_code"];
7255
7268
  } else {
7256
7269
  provs = detectAgents().map((a) => a.kind);
@@ -8271,7 +8284,7 @@ function writeConfigEnv(opts) {
8271
8284
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
8272
8285
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
8273
8286
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
8274
- `SYNKRO_VERSION=${shellQuoteSingle("1.6.35")}`
8287
+ `SYNKRO_VERSION=${shellQuoteSingle("1.6.36")}`
8275
8288
  ];
8276
8289
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
8277
8290
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -8679,19 +8692,32 @@ async function installCommand(opts = {}) {
8679
8692
  await promptCursorApiKey(opts);
8680
8693
  }
8681
8694
  console.log("Installing Synkro server container...");
8682
- const totalWorkers = parseInt(process.env.SYNKRO_WORKERS_PER_POOL || "8", 10);
8683
- const synkroFilePool = readSynkroFilePool2();
8684
- let providers = [];
8685
- if (synkroFilePool === "cursor") {
8686
- providers = ["cursor"];
8687
- } else if (synkroFilePool === "claude") {
8688
- providers = ["claude_code"];
8695
+ const sf = readFullSynkroFile();
8696
+ let claudeWorkers;
8697
+ let cursorWorkers;
8698
+ if (sf && (sf.workers.claude != null || sf.workers.cursor != null)) {
8699
+ claudeWorkers = Math.max(0, Math.floor(sf.workers.claude || 0));
8700
+ cursorWorkers = Math.max(0, Math.floor(sf.workers.cursor || 0));
8701
+ if (claudeWorkers + cursorWorkers === 0) {
8702
+ claudeWorkers = 0;
8703
+ cursorWorkers = 8;
8704
+ }
8705
+ console.log(` .synkro: explicit workers \u2014 ${claudeWorkers} claude + ${cursorWorkers} cursor`);
8689
8706
  } else {
8690
- if (hasClaudeCode) providers.push("claude_code");
8691
- if (hasCursor) providers.push("cursor");
8707
+ const totalWorkers = parseInt(process.env.SYNKRO_WORKERS_PER_POOL || "8", 10);
8708
+ const synkroFilePool = sf?.grader?.pool || readSynkroFilePool();
8709
+ let providers = [];
8710
+ if (synkroFilePool === "cursor") {
8711
+ providers = ["cursor"];
8712
+ } else if (synkroFilePool === "claude") {
8713
+ providers = ["claude_code"];
8714
+ } else {
8715
+ if (hasClaudeCode) providers.push("claude_code");
8716
+ if (hasCursor) providers.push("cursor");
8717
+ }
8718
+ ({ claudeWorkers, cursorWorkers } = splitWorkers(totalWorkers, providers));
8719
+ if (synkroFilePool !== "auto") console.log(` .synkro: grader pool set to ${synkroFilePool}`);
8692
8720
  }
8693
- const { claudeWorkers, cursorWorkers } = splitWorkers(totalWorkers, providers);
8694
- if (synkroFilePool !== "auto") console.log(` .synkro: grader pool set to ${synkroFilePool}`);
8695
8721
  console.log(` worker pool: ${claudeWorkers} claude + ${cursorWorkers} cursor`);
8696
8722
  const connectedRepo = detectGitRepo2() || void 0;
8697
8723
  const { image, hostMcpPort, hostGraderPort, hostCwePort, hostPglitePort } = await dockerInstall({ claudeWorkers, cursorWorkers, connectedRepo });
@@ -8887,7 +8913,7 @@ function writeSynkroFileIfMissing(opts) {
8887
8913
  } catch {
8888
8914
  }
8889
8915
  }
8890
- function readSynkroFilePool2() {
8916
+ function readSynkroFilePool() {
8891
8917
  try {
8892
8918
  const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
8893
8919
  if (!root) return "auto";
@@ -8915,6 +8941,10 @@ function readFullSynkroFile() {
8915
8941
  pool: ["auto", "claude", "cursor"].includes(parsed.grader?.pool) ? parsed.grader.pool : "auto",
8916
8942
  mode: ["local", "byok"].includes(parsed.grader?.mode) ? parsed.grader.mode : "local"
8917
8943
  },
8944
+ workers: {
8945
+ ...typeof parsed.workers?.claude === "number" ? { claude: parsed.workers.claude } : {},
8946
+ ...typeof parsed.workers?.cursor === "number" ? { cursor: parsed.workers.cursor } : {}
8947
+ },
8918
8948
  ruleset: parsed.ruleset || "default",
8919
8949
  skills: Array.isArray(parsed.skills) ? parsed.skills.filter((s) => typeof s === "string" && s.endsWith(".md")) : [],
8920
8950
  scanning: { cwe: parsed.scanning?.cwe !== false, cve: parsed.scanning?.cve !== false },
@@ -8992,6 +9022,11 @@ function reconcileHarness() {
8992
9022
  if (uninstallCursorHooks(cursorHooks)) console.log(" \u2717 Cursor hooks removed");
8993
9023
  if (uninstallCursorMcpConfig()) console.log(" \u2717 Cursor MCP removed");
8994
9024
  }
9025
+ if (sf.workers.claude != null || sf.workers.cursor != null) {
9026
+ const cw = Math.max(0, Math.floor(sf.workers.claude || 0));
9027
+ const curw = Math.max(0, Math.floor(sf.workers.cursor || 0));
9028
+ if (cw + curw > 0) return { claudeWorkers: cw, cursorWorkers: curw };
9029
+ }
8995
9030
  const total = parseInt(process.env.SYNKRO_WORKERS_PER_POOL || "8", 10);
8996
9031
  const providers = [];
8997
9032
  if (sf.grader.pool === "cursor") {
@@ -11292,7 +11327,7 @@ var args = process.argv.slice(2);
11292
11327
  var cmd = args[0] || "";
11293
11328
  var subArgs = args.slice(1);
11294
11329
  function printVersion() {
11295
- console.log("1.6.35");
11330
+ console.log("1.6.36");
11296
11331
  }
11297
11332
  function printHelp2() {
11298
11333
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents