@synkro-sh/cli 1.6.34 → 1.6.35

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
@@ -1212,6 +1212,54 @@ const SYNKRO_FILE_DEFAULTS: SynkroFileConfig = {
1212
1212
  scanning: { cwe: true, cve: true },
1213
1213
  };
1214
1214
 
1215
+ function parseSynkroYaml(raw: string): Record<string, any> {
1216
+ const result: Record<string, any> = {};
1217
+ const lines = raw.split('\\n');
1218
+ let currentKey = '';
1219
+ let currentObj: Record<string, any> | null = null;
1220
+ let currentArr: string[] | null = null;
1221
+
1222
+ for (const line of lines) {
1223
+ if (!line.trim() || line.trim().startsWith('#')) continue;
1224
+
1225
+ if (line.match(/^\\S/) && line.includes(':')) {
1226
+ if (currentObj && currentKey) result[currentKey] = currentObj;
1227
+ if (currentArr && currentKey) result[currentKey] = currentArr;
1228
+ currentObj = null;
1229
+ currentArr = null;
1230
+
1231
+ const colonIdx = line.indexOf(':');
1232
+ const key = line.slice(0, colonIdx).trim();
1233
+ const val = line.slice(colonIdx + 1).trim();
1234
+ currentKey = key;
1235
+
1236
+ if (val) {
1237
+ if (val === '[]') result[key] = [];
1238
+ else if (val === 'true') result[key] = true;
1239
+ else if (val === 'false') result[key] = false;
1240
+ else if (/^\\d+$/.test(val)) result[key] = parseInt(val, 10);
1241
+ else result[key] = val;
1242
+ currentKey = '';
1243
+ }
1244
+ } else if (line.match(/^ - /)) {
1245
+ if (!currentArr) currentArr = [];
1246
+ currentArr.push(line.replace(/^ - /, '').trim());
1247
+ } else if (line.match(/^ \\S/) && line.includes(':')) {
1248
+ if (!currentObj) currentObj = {};
1249
+ const colonIdx = line.indexOf(':');
1250
+ const k = line.slice(0, colonIdx).trim();
1251
+ const v = line.slice(colonIdx + 1).trim();
1252
+ if (v === 'true') currentObj[k] = true;
1253
+ else if (v === 'false') currentObj[k] = false;
1254
+ else if (/^\\d+$/.test(v)) currentObj[k] = parseInt(v, 10);
1255
+ else currentObj[k] = v;
1256
+ }
1257
+ }
1258
+ if (currentObj && currentKey) result[currentKey] = currentObj;
1259
+ if (currentArr && currentKey) result[currentKey] = currentArr;
1260
+ return result;
1261
+ }
1262
+
1215
1263
  let _synkroFileCache: SynkroFileConfig | undefined;
1216
1264
 
1217
1265
  export function loadSynkroFile(cwd?: string): SynkroFileConfig {
@@ -1221,7 +1269,8 @@ export function loadSynkroFile(cwd?: string): SynkroFileConfig {
1221
1269
  const fp = root + '/.synkro';
1222
1270
  try {
1223
1271
  if (!existsSync(fp)) { _synkroFileCache = SYNKRO_FILE_DEFAULTS; return _synkroFileCache; }
1224
- const parsed = JSON.parse(readFileSync(fp, 'utf-8'));
1272
+ const raw = readFileSync(fp, 'utf-8');
1273
+ const parsed = raw.trimStart().startsWith('{') ? JSON.parse(raw) : parseSynkroYaml(raw);
1225
1274
  const validHarness = ['claude-code', 'cursor'] as const;
1226
1275
  const harness = Array.isArray(parsed.harness)
1227
1276
  ? parsed.harness.filter((h: string) => validHarness.includes(h as any))
@@ -7108,13 +7157,57 @@ function normalizeProvider(p) {
7108
7157
  if (v === "cursor") return "cursor";
7109
7158
  return null;
7110
7159
  }
7160
+ function parseSynkroYaml(raw) {
7161
+ const result = {};
7162
+ const lines = raw.split("\n");
7163
+ let currentKey = "";
7164
+ let currentObj = null;
7165
+ let currentArr = null;
7166
+ for (const line of lines) {
7167
+ if (!line.trim() || line.trim().startsWith("#")) continue;
7168
+ if (/^\S/.test(line) && line.includes(":")) {
7169
+ if (currentObj && currentKey) result[currentKey] = currentObj;
7170
+ if (currentArr && currentKey) result[currentKey] = currentArr;
7171
+ currentObj = null;
7172
+ currentArr = null;
7173
+ const ci = line.indexOf(":");
7174
+ const key = line.slice(0, ci).trim();
7175
+ const val = line.slice(ci + 1).trim();
7176
+ currentKey = key;
7177
+ if (val) {
7178
+ if (val === "[]") result[key] = [];
7179
+ else if (val === "true") result[key] = true;
7180
+ else if (val === "false") result[key] = false;
7181
+ else if (/^\d+$/.test(val)) result[key] = parseInt(val, 10);
7182
+ else result[key] = val;
7183
+ currentKey = "";
7184
+ }
7185
+ } else if (/^ - /.test(line)) {
7186
+ if (!currentArr) currentArr = [];
7187
+ currentArr.push(line.replace(/^ - /, "").trim());
7188
+ } else if (/^ \S/.test(line) && line.includes(":")) {
7189
+ if (!currentObj) currentObj = {};
7190
+ const ci = line.indexOf(":");
7191
+ const k = line.slice(0, ci).trim();
7192
+ const v = line.slice(ci + 1).trim();
7193
+ if (v === "true") currentObj[k] = true;
7194
+ else if (v === "false") currentObj[k] = false;
7195
+ else if (/^\d+$/.test(v)) currentObj[k] = parseInt(v, 10);
7196
+ else currentObj[k] = v;
7197
+ }
7198
+ }
7199
+ if (currentObj && currentKey) result[currentKey] = currentObj;
7200
+ if (currentArr && currentKey) result[currentKey] = currentArr;
7201
+ return result;
7202
+ }
7111
7203
  function readSynkroFilePool() {
7112
7204
  try {
7113
7205
  const root = execSync4("git rev-parse --show-toplevel 2>/dev/null", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
7114
7206
  if (!root) return "auto";
7115
7207
  const fp = join6(root, ".synkro");
7116
7208
  if (!existsSync8(fp)) return "auto";
7117
- const parsed = JSON.parse(readFileSync7(fp, "utf-8"));
7209
+ const raw = readFileSync7(fp, "utf-8");
7210
+ const parsed = raw.trimStart().startsWith("{") ? JSON.parse(raw) : parseSynkroYaml(raw);
7118
7211
  const pool = parsed?.grader?.pool;
7119
7212
  if (pool === "cursor" || pool === "claude") return pool;
7120
7213
  } catch {
@@ -8178,7 +8271,7 @@ function writeConfigEnv(opts) {
8178
8271
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
8179
8272
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
8180
8273
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
8181
- `SYNKRO_VERSION=${shellQuoteSingle("1.6.34")}`
8274
+ `SYNKRO_VERSION=${shellQuoteSingle("1.6.35")}`
8182
8275
  ];
8183
8276
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
8184
8277
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -8704,6 +8797,52 @@ async function installCommand(opts = {}) {
8704
8797
  }
8705
8798
  console.log("\u2713 Synkro installed.");
8706
8799
  }
8800
+ function parseSynkroYaml2(raw) {
8801
+ const result = {};
8802
+ const lines = raw.split("\n");
8803
+ let currentKey = "";
8804
+ let currentObj = null;
8805
+ let currentArr = null;
8806
+ for (const line of lines) {
8807
+ if (!line.trim() || line.trim().startsWith("#")) continue;
8808
+ if (/^\S/.test(line) && line.includes(":")) {
8809
+ if (currentObj && currentKey) result[currentKey] = currentObj;
8810
+ if (currentArr && currentKey) result[currentKey] = currentArr;
8811
+ currentObj = null;
8812
+ currentArr = null;
8813
+ const colonIdx = line.indexOf(":");
8814
+ const key = line.slice(0, colonIdx).trim();
8815
+ const val = line.slice(colonIdx + 1).trim();
8816
+ currentKey = key;
8817
+ if (val) {
8818
+ if (val === "[]") result[key] = [];
8819
+ else if (val === "true") result[key] = true;
8820
+ else if (val === "false") result[key] = false;
8821
+ else if (/^\d+$/.test(val)) result[key] = parseInt(val, 10);
8822
+ else result[key] = val;
8823
+ currentKey = "";
8824
+ }
8825
+ } else if (/^ - /.test(line)) {
8826
+ if (!currentArr) currentArr = [];
8827
+ currentArr.push(line.replace(/^ - /, "").trim());
8828
+ } else if (/^ \S/.test(line) && line.includes(":")) {
8829
+ if (!currentObj) currentObj = {};
8830
+ const colonIdx = line.indexOf(":");
8831
+ const k = line.slice(0, colonIdx).trim();
8832
+ const v = line.slice(colonIdx + 1).trim();
8833
+ if (v === "true") currentObj[k] = true;
8834
+ else if (v === "false") currentObj[k] = false;
8835
+ else if (/^\d+$/.test(v)) currentObj[k] = parseInt(v, 10);
8836
+ else currentObj[k] = v;
8837
+ }
8838
+ }
8839
+ if (currentObj && currentKey) result[currentKey] = currentObj;
8840
+ if (currentArr && currentKey) result[currentKey] = currentArr;
8841
+ return result;
8842
+ }
8843
+ function parseSynkroFileRaw(content) {
8844
+ return content.trimStart().startsWith("{") ? JSON.parse(content) : parseSynkroYaml2(content);
8845
+ }
8707
8846
  function writeSynkroFileIfMissing(opts) {
8708
8847
  try {
8709
8848
  const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
@@ -8723,18 +8862,28 @@ function writeSynkroFileIfMissing(opts) {
8723
8862
  harness.push("cursor");
8724
8863
  if (!opts.hasClaudeCode) pool = "cursor";
8725
8864
  }
8726
- const config = {
8727
- version: 1,
8728
- harness,
8729
- grader: {
8730
- pool,
8731
- mode: opts.gradingMode === "byok" ? "byok" : "local"
8732
- },
8733
- ruleset: "default",
8734
- scanning: { cwe: true, cve: true }
8735
- };
8736
- writeFileSync7(fp, JSON.stringify(config, null, 2) + "\n", "utf-8");
8737
- console.log(` .synkro: wrote ${fp} (pool=${pool}, mode=${config.grader.mode})`);
8865
+ const mode = opts.gradingMode === "byok" ? "byok" : "local";
8866
+ const yaml = [
8867
+ "version: 1",
8868
+ "",
8869
+ "harness:",
8870
+ ...harness.map((h) => ` - ${h}`),
8871
+ "",
8872
+ "grader:",
8873
+ ` pool: ${pool}`,
8874
+ ` mode: ${mode}`,
8875
+ "",
8876
+ "ruleset: default",
8877
+ "",
8878
+ "skills: []",
8879
+ "",
8880
+ "scanning:",
8881
+ " cwe: true",
8882
+ " cve: true",
8883
+ ""
8884
+ ].join("\n");
8885
+ writeFileSync7(fp, yaml, "utf-8");
8886
+ console.log(` .synkro: wrote ${fp} (pool=${pool}, mode=${mode})`);
8738
8887
  } catch {
8739
8888
  }
8740
8889
  }
@@ -8744,7 +8893,7 @@ function readSynkroFilePool2() {
8744
8893
  if (!root) return "auto";
8745
8894
  const fp = join8(root, ".synkro");
8746
8895
  if (!existsSync10(fp)) return "auto";
8747
- const parsed = JSON.parse(readFileSync9(fp, "utf-8"));
8896
+ const parsed = parseSynkroFileRaw(readFileSync9(fp, "utf-8"));
8748
8897
  const pool = parsed?.grader?.pool;
8749
8898
  if (pool === "cursor" || pool === "claude") return pool;
8750
8899
  } catch {
@@ -8757,7 +8906,7 @@ function readFullSynkroFile() {
8757
8906
  if (!root) return null;
8758
8907
  const fp = join8(root, ".synkro");
8759
8908
  if (!existsSync10(fp)) return null;
8760
- const parsed = JSON.parse(readFileSync9(fp, "utf-8"));
8909
+ const parsed = parseSynkroFileRaw(readFileSync9(fp, "utf-8"));
8761
8910
  const valid = ["claude-code", "cursor"];
8762
8911
  const harness = Array.isArray(parsed.harness) ? parsed.harness.filter((h) => valid.includes(h)) : ["claude-code", "cursor"];
8763
8912
  return {
@@ -11143,7 +11292,7 @@ var args = process.argv.slice(2);
11143
11292
  var cmd = args[0] || "";
11144
11293
  var subArgs = args.slice(1);
11145
11294
  function printVersion() {
11146
- console.log("1.6.34");
11295
+ console.log("1.6.35");
11147
11296
  }
11148
11297
  function printHelp2() {
11149
11298
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents