@synkro-sh/cli 1.6.33 → 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 +359 -122
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
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,9 +1208,58 @@ 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
|
|
|
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
|
+
|
|
1161
1263
|
let _synkroFileCache: SynkroFileConfig | undefined;
|
|
1162
1264
|
|
|
1163
1265
|
export function loadSynkroFile(cwd?: string): SynkroFileConfig {
|
|
@@ -1167,7 +1269,8 @@ export function loadSynkroFile(cwd?: string): SynkroFileConfig {
|
|
|
1167
1269
|
const fp = root + '/.synkro';
|
|
1168
1270
|
try {
|
|
1169
1271
|
if (!existsSync(fp)) { _synkroFileCache = SYNKRO_FILE_DEFAULTS; return _synkroFileCache; }
|
|
1170
|
-
const
|
|
1272
|
+
const raw = readFileSync(fp, 'utf-8');
|
|
1273
|
+
const parsed = raw.trimStart().startsWith('{') ? JSON.parse(raw) : parseSynkroYaml(raw);
|
|
1171
1274
|
const validHarness = ['claude-code', 'cursor'] as const;
|
|
1172
1275
|
const harness = Array.isArray(parsed.harness)
|
|
1173
1276
|
? parsed.harness.filter((h: string) => validHarness.includes(h as any))
|
|
@@ -1180,6 +1283,7 @@ export function loadSynkroFile(cwd?: string): SynkroFileConfig {
|
|
|
1180
1283
|
mode: ['local', 'byok'].includes(parsed.grader?.mode) ? parsed.grader.mode : undefined,
|
|
1181
1284
|
},
|
|
1182
1285
|
ruleset: typeof parsed.ruleset === 'string' ? parsed.ruleset : 'default',
|
|
1286
|
+
skills: Array.isArray(parsed.skills) ? parsed.skills.filter((s: unknown) => typeof s === 'string') : [],
|
|
1183
1287
|
scanning: {
|
|
1184
1288
|
cwe: parsed.scanning?.cwe !== false,
|
|
1185
1289
|
cve: parsed.scanning?.cve !== false,
|
|
@@ -5946,7 +6050,7 @@ __export(stub_exports, {
|
|
|
5946
6050
|
saveCredentials: () => saveCredentials
|
|
5947
6051
|
});
|
|
5948
6052
|
import { createServer } from "http";
|
|
5949
|
-
import { writeFileSync as writeFileSync4, readFileSync as
|
|
6053
|
+
import { writeFileSync as writeFileSync4, readFileSync as readFileSync5, existsSync as existsSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "fs";
|
|
5950
6054
|
import { homedir as homedir4, platform } from "os";
|
|
5951
6055
|
import { join as join3, dirname as dirname4 } from "path";
|
|
5952
6056
|
import { execFile } from "child_process";
|
|
@@ -5977,17 +6081,17 @@ function openBrowser(url) {
|
|
|
5977
6081
|
}
|
|
5978
6082
|
function saveCredentials(data) {
|
|
5979
6083
|
const dir = dirname4(AUTH_FILE);
|
|
5980
|
-
if (!
|
|
6084
|
+
if (!existsSync5(dir)) {
|
|
5981
6085
|
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
5982
6086
|
}
|
|
5983
6087
|
writeFileSync4(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 384 });
|
|
5984
6088
|
}
|
|
5985
6089
|
function loadCredentials() {
|
|
5986
|
-
if (!
|
|
6090
|
+
if (!existsSync5(AUTH_FILE)) {
|
|
5987
6091
|
return null;
|
|
5988
6092
|
}
|
|
5989
6093
|
try {
|
|
5990
|
-
const content =
|
|
6094
|
+
const content = readFileSync5(AUTH_FILE, "utf8");
|
|
5991
6095
|
return JSON.parse(content);
|
|
5992
6096
|
} catch (error) {
|
|
5993
6097
|
return null;
|
|
@@ -6000,7 +6104,7 @@ function createCallbackServer() {
|
|
|
6000
6104
|
"Access-Control-Allow-Headers": "Content-Type",
|
|
6001
6105
|
"Vary": "Origin"
|
|
6002
6106
|
};
|
|
6003
|
-
return new Promise((
|
|
6107
|
+
return new Promise((resolve4, reject) => {
|
|
6004
6108
|
const server = createServer((req, res) => {
|
|
6005
6109
|
if (req.method === "OPTIONS") {
|
|
6006
6110
|
const origin = req.headers.origin;
|
|
@@ -6089,7 +6193,7 @@ function createCallbackServer() {
|
|
|
6089
6193
|
res.end(JSON.stringify({ ok: true }));
|
|
6090
6194
|
setTimeout(() => {
|
|
6091
6195
|
server.close();
|
|
6092
|
-
|
|
6196
|
+
resolve4(authData);
|
|
6093
6197
|
}, 200);
|
|
6094
6198
|
});
|
|
6095
6199
|
req.on("error", (e) => {
|
|
@@ -6242,7 +6346,7 @@ async function ensureValidToken() {
|
|
|
6242
6346
|
return true;
|
|
6243
6347
|
}
|
|
6244
6348
|
function clearCredentials() {
|
|
6245
|
-
if (
|
|
6349
|
+
if (existsSync5(AUTH_FILE)) {
|
|
6246
6350
|
unlinkSync2(AUTH_FILE);
|
|
6247
6351
|
}
|
|
6248
6352
|
}
|
|
@@ -6422,7 +6526,7 @@ jobs:
|
|
|
6422
6526
|
});
|
|
6423
6527
|
|
|
6424
6528
|
// cli/installer/githubSetup.ts
|
|
6425
|
-
import { existsSync as
|
|
6529
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
6426
6530
|
import { execSync as execSync2 } from "child_process";
|
|
6427
6531
|
import { join as join4 } from "path";
|
|
6428
6532
|
function ghSecretSet(token, owner, repo, name, value) {
|
|
@@ -6479,7 +6583,7 @@ function writeWorkflowFile(repoRootPath) {
|
|
|
6479
6583
|
function findGitRoot(startCwd) {
|
|
6480
6584
|
let cur = startCwd;
|
|
6481
6585
|
while (cur && cur !== "/") {
|
|
6482
|
-
if (
|
|
6586
|
+
if (existsSync6(join4(cur, ".git"))) return cur;
|
|
6483
6587
|
const parent = join4(cur, "..");
|
|
6484
6588
|
if (parent === cur) break;
|
|
6485
6589
|
cur = parent;
|
|
@@ -6545,10 +6649,10 @@ function detectSubdirRepos() {
|
|
|
6545
6649
|
}
|
|
6546
6650
|
}
|
|
6547
6651
|
function ask(rl, question) {
|
|
6548
|
-
return new Promise((
|
|
6652
|
+
return new Promise((resolve4) => rl.question(question, resolve4));
|
|
6549
6653
|
}
|
|
6550
6654
|
function waitForGithubToken() {
|
|
6551
|
-
return new Promise((
|
|
6655
|
+
return new Promise((resolve4, reject) => {
|
|
6552
6656
|
const server = createServer2((req, res) => {
|
|
6553
6657
|
if (req.method === "OPTIONS") {
|
|
6554
6658
|
res.writeHead(204, {
|
|
@@ -6585,7 +6689,7 @@ function waitForGithubToken() {
|
|
|
6585
6689
|
});
|
|
6586
6690
|
res.end(JSON.stringify({ ok: true }));
|
|
6587
6691
|
setTimeout(() => server.close(), 200);
|
|
6588
|
-
|
|
6692
|
+
resolve4(parsed.github_token);
|
|
6589
6693
|
} catch {
|
|
6590
6694
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
6591
6695
|
res.end(JSON.stringify({ error: "invalid json" }));
|
|
@@ -6845,7 +6949,7 @@ __export(macKeychain_exports, {
|
|
|
6845
6949
|
writeCursorApiKey: () => writeCursorApiKey,
|
|
6846
6950
|
writeRefreshAgent: () => writeRefreshAgent
|
|
6847
6951
|
});
|
|
6848
|
-
import { existsSync as
|
|
6952
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6, chmodSync, readFileSync as readFileSync6, statSync } from "fs";
|
|
6849
6953
|
import { homedir as homedir5, platform as platform2 } from "os";
|
|
6850
6954
|
import { join as join5 } from "path";
|
|
6851
6955
|
import { spawnSync } from "child_process";
|
|
@@ -6873,7 +6977,7 @@ function exportKeychainCreds() {
|
|
|
6873
6977
|
}
|
|
6874
6978
|
function cursorApiKeyConfigured() {
|
|
6875
6979
|
try {
|
|
6876
|
-
return
|
|
6980
|
+
return existsSync7(CURSOR_API_KEY_FILE) && readFileSync6(CURSOR_API_KEY_FILE, "utf-8").trim().length > 0;
|
|
6877
6981
|
} catch {
|
|
6878
6982
|
return false;
|
|
6879
6983
|
}
|
|
@@ -6889,7 +6993,7 @@ function writeCursorApiKey(key) {
|
|
|
6889
6993
|
async function validateCursorApiKey() {
|
|
6890
6994
|
let key;
|
|
6891
6995
|
try {
|
|
6892
|
-
key =
|
|
6996
|
+
key = readFileSync6(CURSOR_API_KEY_FILE, "utf-8").trim();
|
|
6893
6997
|
} catch {
|
|
6894
6998
|
return null;
|
|
6895
6999
|
}
|
|
@@ -6908,7 +7012,7 @@ async function validateCursorApiKey() {
|
|
|
6908
7012
|
}
|
|
6909
7013
|
}
|
|
6910
7014
|
function credsAreStale() {
|
|
6911
|
-
if (!
|
|
7015
|
+
if (!existsSync7(CLAUDE_CREDS_FILE)) return true;
|
|
6912
7016
|
try {
|
|
6913
7017
|
const ageMs = Date.now() - statSync(CLAUDE_CREDS_FILE).mtimeMs;
|
|
6914
7018
|
return ageMs > REFRESH_INTERVAL_SECONDS * 1e3;
|
|
@@ -6971,7 +7075,7 @@ function uninstallRefreshAgent() {
|
|
|
6971
7075
|
timeout: 5e3
|
|
6972
7076
|
});
|
|
6973
7077
|
try {
|
|
6974
|
-
if (
|
|
7078
|
+
if (existsSync7(LAUNCHD_PLIST)) {
|
|
6975
7079
|
__require("fs").unlinkSync(LAUNCHD_PLIST);
|
|
6976
7080
|
}
|
|
6977
7081
|
} catch {
|
|
@@ -6983,7 +7087,7 @@ function refreshCreds() {
|
|
|
6983
7087
|
}
|
|
6984
7088
|
function readExportedCreds() {
|
|
6985
7089
|
try {
|
|
6986
|
-
return
|
|
7090
|
+
return readFileSync6(CLAUDE_CREDS_FILE, "utf-8");
|
|
6987
7091
|
} catch {
|
|
6988
7092
|
return null;
|
|
6989
7093
|
}
|
|
@@ -7032,7 +7136,7 @@ __export(dockerInstall_exports, {
|
|
|
7032
7136
|
splitWorkers: () => splitWorkers,
|
|
7033
7137
|
waitForContainerReady: () => waitForContainerReady
|
|
7034
7138
|
});
|
|
7035
|
-
import { copyFileSync, existsSync as
|
|
7139
|
+
import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync7, readdirSync as readdirSync2 } from "fs";
|
|
7036
7140
|
import { homedir as homedir6 } from "os";
|
|
7037
7141
|
import { join as join6 } from "path";
|
|
7038
7142
|
import { execSync as execSync4, spawnSync as spawnSync2 } from "child_process";
|
|
@@ -7053,13 +7157,57 @@ function normalizeProvider(p) {
|
|
|
7053
7157
|
if (v === "cursor") return "cursor";
|
|
7054
7158
|
return null;
|
|
7055
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
|
+
}
|
|
7056
7203
|
function readSynkroFilePool() {
|
|
7057
7204
|
try {
|
|
7058
7205
|
const root = execSync4("git rev-parse --show-toplevel 2>/dev/null", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
7059
7206
|
if (!root) return "auto";
|
|
7060
7207
|
const fp = join6(root, ".synkro");
|
|
7061
|
-
if (!
|
|
7062
|
-
const
|
|
7208
|
+
if (!existsSync8(fp)) return "auto";
|
|
7209
|
+
const raw = readFileSync7(fp, "utf-8");
|
|
7210
|
+
const parsed = raw.trimStart().startsWith("{") ? JSON.parse(raw) : parseSynkroYaml(raw);
|
|
7063
7211
|
const pool = parsed?.grader?.pool;
|
|
7064
7212
|
if (pool === "cursor" || pool === "claude") return pool;
|
|
7065
7213
|
} catch {
|
|
@@ -7186,10 +7334,10 @@ async function dockerInstall(opts = {}) {
|
|
|
7186
7334
|
mkdirSync7(BACKUP_DIR, { recursive: true });
|
|
7187
7335
|
mkdirSync7(CLAUDE_HOST_STATE_DIR, { recursive: true });
|
|
7188
7336
|
const hostClaudeJson = join6(homedir6(), ".claude.json");
|
|
7189
|
-
if (
|
|
7337
|
+
if (existsSync8(hostClaudeJson)) {
|
|
7190
7338
|
copyFileSync(hostClaudeJson, CLAUDE_HOST_STATE_FILE);
|
|
7191
7339
|
}
|
|
7192
|
-
if (!
|
|
7340
|
+
if (!existsSync8(MCP_JWT_PATH)) {
|
|
7193
7341
|
throw new DockerInstallError(
|
|
7194
7342
|
`MCP JWT missing at ${MCP_JWT_PATH}. The installer should mint this before calling dockerInstall.`
|
|
7195
7343
|
);
|
|
@@ -7424,7 +7572,7 @@ async function dockerSafeStart() {
|
|
|
7424
7572
|
return { ok: false, pgdataState: "no_container", error: "No synkro-server container found. Run `synkro install` first." };
|
|
7425
7573
|
}
|
|
7426
7574
|
const pgCheck = checkPgdata();
|
|
7427
|
-
if (
|
|
7575
|
+
if (existsSync8(PGDATA_PATH) && readdirSync2(PGDATA_PATH).length > 0) {
|
|
7428
7576
|
if (pgCheck.healthy) {
|
|
7429
7577
|
console.log(` pgdata: existing data found \u2014 ${pgCheck.details}`);
|
|
7430
7578
|
} else {
|
|
@@ -7464,7 +7612,7 @@ async function dockerSafeRestart() {
|
|
|
7464
7612
|
return { ok: startResult.ok, stop: stopResult, start: startResult };
|
|
7465
7613
|
}
|
|
7466
7614
|
function checkPgdata() {
|
|
7467
|
-
if (!
|
|
7615
|
+
if (!existsSync8(PGDATA_PATH)) return { healthy: false, details: "pgdata directory does not exist" };
|
|
7468
7616
|
const entries = readdirSync2(PGDATA_PATH);
|
|
7469
7617
|
if (entries.length === 0) return { healthy: true, details: "empty (fresh start)" };
|
|
7470
7618
|
const hasPidFile = entries.includes("postmaster.pid");
|
|
@@ -7513,14 +7661,14 @@ __export(setupGithub_exports, {
|
|
|
7513
7661
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
7514
7662
|
import { stdin as input, stdout as output } from "process";
|
|
7515
7663
|
import { execSync as execSync5, spawn as nodeSpawn } from "child_process";
|
|
7516
|
-
import { existsSync as
|
|
7664
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, unlinkSync as unlinkSync3 } from "fs";
|
|
7517
7665
|
import { homedir as homedir7, platform as platform3 } from "os";
|
|
7518
7666
|
import { join as join7 } from "path";
|
|
7519
7667
|
import { execFile as execFile2 } from "child_process";
|
|
7520
7668
|
function readConfig() {
|
|
7521
|
-
if (!
|
|
7669
|
+
if (!existsSync9(CONFIG_PATH)) return {};
|
|
7522
7670
|
const out = {};
|
|
7523
|
-
for (const line of
|
|
7671
|
+
for (const line of readFileSync8(CONFIG_PATH, "utf-8").split("\n")) {
|
|
7524
7672
|
const t = line.trim();
|
|
7525
7673
|
if (!t || t.startsWith("#")) continue;
|
|
7526
7674
|
const eq = t.indexOf("=");
|
|
@@ -7533,7 +7681,7 @@ async function prompt(rl, q, opts = {}) {
|
|
|
7533
7681
|
process.stdout.write(q);
|
|
7534
7682
|
const wasRaw = process.stdin.isRaw;
|
|
7535
7683
|
if (process.stdin.setRawMode) process.stdin.setRawMode(true);
|
|
7536
|
-
return await new Promise((
|
|
7684
|
+
return await new Promise((resolve4) => {
|
|
7537
7685
|
let chunk = "";
|
|
7538
7686
|
const onData = (data) => {
|
|
7539
7687
|
const s = data.toString("utf-8");
|
|
@@ -7541,7 +7689,7 @@ async function prompt(rl, q, opts = {}) {
|
|
|
7541
7689
|
process.stdin.removeListener("data", onData);
|
|
7542
7690
|
if (process.stdin.setRawMode) process.stdin.setRawMode(wasRaw ?? false);
|
|
7543
7691
|
process.stdout.write("\n");
|
|
7544
|
-
|
|
7692
|
+
resolve4(chunk);
|
|
7545
7693
|
return;
|
|
7546
7694
|
}
|
|
7547
7695
|
if (s === "") process.exit(130);
|
|
@@ -7582,7 +7730,7 @@ function sleep(ms) {
|
|
|
7582
7730
|
}
|
|
7583
7731
|
function captureClaudeSetupToken() {
|
|
7584
7732
|
const tmpFile = join7(SYNKRO_DIR3, `token-capture-${Date.now()}.raw`);
|
|
7585
|
-
return new Promise((
|
|
7733
|
+
return new Promise((resolve4, reject) => {
|
|
7586
7734
|
const proc = nodeSpawn("script", ["-q", tmpFile, "claude", "setup-token"], {
|
|
7587
7735
|
stdio: "inherit"
|
|
7588
7736
|
});
|
|
@@ -7590,7 +7738,7 @@ function captureClaudeSetupToken() {
|
|
|
7590
7738
|
proc.on("close", (code) => {
|
|
7591
7739
|
let raw = "";
|
|
7592
7740
|
try {
|
|
7593
|
-
raw =
|
|
7741
|
+
raw = readFileSync8(tmpFile, "utf-8");
|
|
7594
7742
|
} catch (e) {
|
|
7595
7743
|
reject(new Error(`Could not read script output file: ${e.message}`));
|
|
7596
7744
|
return;
|
|
@@ -7612,7 +7760,7 @@ function captureClaudeSetupToken() {
|
|
|
7612
7760
|
reject(new Error(`Could not find token in claude setup-token output (file=${raw.length}b, yellow=${yellow.length}b)`));
|
|
7613
7761
|
return;
|
|
7614
7762
|
}
|
|
7615
|
-
|
|
7763
|
+
resolve4(token[0]);
|
|
7616
7764
|
});
|
|
7617
7765
|
});
|
|
7618
7766
|
}
|
|
@@ -7874,9 +8022,10 @@ __export(install_exports, {
|
|
|
7874
8022
|
installCommand: () => installCommand,
|
|
7875
8023
|
parseArgs: () => parseArgs,
|
|
7876
8024
|
reconcileHarness: () => reconcileHarness,
|
|
8025
|
+
syncSkillFiles: () => syncSkillFiles,
|
|
7877
8026
|
writeHookScripts: () => writeHookScripts
|
|
7878
8027
|
});
|
|
7879
|
-
import { existsSync as
|
|
8028
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, chmodSync as chmodSync2, readFileSync as readFileSync9, readdirSync as readdirSync3 } from "fs";
|
|
7880
8029
|
import { homedir as homedir8 } from "os";
|
|
7881
8030
|
import { join as join8 } from "path";
|
|
7882
8031
|
import { execSync as execSync6, spawnSync as spawnSync3 } from "child_process";
|
|
@@ -7908,20 +8057,20 @@ async function promptAgentSelection(detected) {
|
|
|
7908
8057
|
detected.forEach((a, i) => console.log(` ${i + 1}. ${a.name}`));
|
|
7909
8058
|
console.log(` ${detected.length + 1}. Both / all (default)`);
|
|
7910
8059
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
7911
|
-
const ask2 = () => new Promise((
|
|
8060
|
+
const ask2 = () => new Promise((resolve4) => {
|
|
7912
8061
|
rl.question(`Pick [1-${detected.length + 1}] (default: all): `, (answer) => {
|
|
7913
8062
|
const t = answer.trim().toLowerCase();
|
|
7914
8063
|
if (t === "" || t === String(detected.length + 1) || t === "both" || t === "all") {
|
|
7915
8064
|
rl.close();
|
|
7916
|
-
return
|
|
8065
|
+
return resolve4(detected);
|
|
7917
8066
|
}
|
|
7918
8067
|
const n = parseInt(t, 10);
|
|
7919
8068
|
if (Number.isInteger(n) && n >= 1 && n <= detected.length) {
|
|
7920
8069
|
rl.close();
|
|
7921
|
-
return
|
|
8070
|
+
return resolve4([detected[n - 1]]);
|
|
7922
8071
|
}
|
|
7923
8072
|
console.log("Invalid choice. Try again.");
|
|
7924
|
-
|
|
8073
|
+
resolve4(ask2());
|
|
7925
8074
|
});
|
|
7926
8075
|
});
|
|
7927
8076
|
return ask2();
|
|
@@ -7944,12 +8093,12 @@ async function promptCursorApiKey(opts) {
|
|
|
7944
8093
|
return;
|
|
7945
8094
|
}
|
|
7946
8095
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
7947
|
-
const key = await new Promise((
|
|
8096
|
+
const key = await new Promise((resolve4) => {
|
|
7948
8097
|
rl.question(
|
|
7949
8098
|
"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
8099
|
(answer) => {
|
|
7951
8100
|
rl.close();
|
|
7952
|
-
|
|
8101
|
+
resolve4(answer.trim());
|
|
7953
8102
|
}
|
|
7954
8103
|
);
|
|
7955
8104
|
});
|
|
@@ -7963,12 +8112,12 @@ async function promptCursorApiKey(opts) {
|
|
|
7963
8112
|
async function promptGradingMode() {
|
|
7964
8113
|
if (!process.stdin.isTTY) return "local";
|
|
7965
8114
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
7966
|
-
return new Promise((
|
|
8115
|
+
return new Promise((resolve4) => {
|
|
7967
8116
|
rl.question(
|
|
7968
8117
|
"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
8118
|
(answer) => {
|
|
7970
8119
|
rl.close();
|
|
7971
|
-
|
|
8120
|
+
resolve4(answer.trim().toLowerCase() === "byok" ? "byok" : "local");
|
|
7972
8121
|
}
|
|
7973
8122
|
);
|
|
7974
8123
|
});
|
|
@@ -7976,25 +8125,25 @@ async function promptGradingMode() {
|
|
|
7976
8125
|
async function promptStorageMode() {
|
|
7977
8126
|
if (!process.stdin.isTTY) return "local";
|
|
7978
8127
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
7979
|
-
return new Promise((
|
|
8128
|
+
return new Promise((resolve4) => {
|
|
7980
8129
|
rl.question(
|
|
7981
8130
|
"Where should telemetry be stored?\n local \u2014 on this machine only (default)\n cloud \u2014 sent to Synkro cloud\nChoose [local] / cloud: ",
|
|
7982
8131
|
(answer) => {
|
|
7983
8132
|
rl.close();
|
|
7984
|
-
|
|
8133
|
+
resolve4(answer.trim().toLowerCase() === "cloud" ? "cloud" : "local");
|
|
7985
8134
|
}
|
|
7986
8135
|
);
|
|
7987
8136
|
});
|
|
7988
8137
|
}
|
|
7989
8138
|
async function promptTranscriptConsent() {
|
|
7990
8139
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
7991
|
-
return new Promise((
|
|
8140
|
+
return new Promise((resolve4) => {
|
|
7992
8141
|
rl.question(
|
|
7993
8142
|
"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
8143
|
(answer) => {
|
|
7995
8144
|
rl.close();
|
|
7996
8145
|
const trimmed = answer.trim().toLowerCase();
|
|
7997
|
-
|
|
8146
|
+
resolve4(trimmed === "" || trimmed === "y" || trimmed === "yes");
|
|
7998
8147
|
}
|
|
7999
8148
|
);
|
|
8000
8149
|
});
|
|
@@ -8102,7 +8251,7 @@ function shellQuoteSingle(value) {
|
|
|
8102
8251
|
}
|
|
8103
8252
|
function resolveSynkroBundle() {
|
|
8104
8253
|
const scriptPath = process.argv[1];
|
|
8105
|
-
if (scriptPath &&
|
|
8254
|
+
if (scriptPath && existsSync10(scriptPath)) return scriptPath;
|
|
8106
8255
|
return null;
|
|
8107
8256
|
}
|
|
8108
8257
|
function writeConfigEnv(opts) {
|
|
@@ -8122,7 +8271,7 @@ function writeConfigEnv(opts) {
|
|
|
8122
8271
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
8123
8272
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
8124
8273
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
8125
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.6.
|
|
8274
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.6.35")}`
|
|
8126
8275
|
];
|
|
8127
8276
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
8128
8277
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -8144,8 +8293,8 @@ function resolveDeploymentMode() {
|
|
|
8144
8293
|
const envOverride = process.env.SYNKRO_DEPLOYMENT_MODE?.toLowerCase();
|
|
8145
8294
|
if (envOverride === "bare-host" || envOverride === "docker") return envOverride;
|
|
8146
8295
|
try {
|
|
8147
|
-
if (
|
|
8148
|
-
const m =
|
|
8296
|
+
if (existsSync10(CONFIG_PATH2)) {
|
|
8297
|
+
const m = readFileSync9(CONFIG_PATH2, "utf-8").match(/^SYNKRO_DEPLOYMENT_MODE='([^']*)'/m);
|
|
8149
8298
|
const val = m?.[1]?.toLowerCase();
|
|
8150
8299
|
if (val === "bare-host" || val === "docker") return val;
|
|
8151
8300
|
}
|
|
@@ -8174,14 +8323,14 @@ function collectLocalMetadata(includeClaudeCode = true) {
|
|
|
8174
8323
|
}
|
|
8175
8324
|
const claudeDir = join8(homedir8(), ".claude");
|
|
8176
8325
|
try {
|
|
8177
|
-
const settings = JSON.parse(
|
|
8326
|
+
const settings = JSON.parse(readFileSync9(join8(claudeDir, "settings.json"), "utf-8"));
|
|
8178
8327
|
const plugins = Object.keys(settings.enabledPlugins ?? {}).filter((k) => settings.enabledPlugins[k]);
|
|
8179
8328
|
if (plugins.length) meta.enabled_plugins = plugins;
|
|
8180
8329
|
if (settings.permissions?.defaultMode) meta.permissions_mode = settings.permissions.defaultMode;
|
|
8181
8330
|
} catch {
|
|
8182
8331
|
}
|
|
8183
8332
|
try {
|
|
8184
|
-
const mcpCache = JSON.parse(
|
|
8333
|
+
const mcpCache = JSON.parse(readFileSync9(join8(claudeDir, "mcp-needs-auth-cache.json"), "utf-8"));
|
|
8185
8334
|
const mcpNames = Object.keys(mcpCache);
|
|
8186
8335
|
if (mcpNames.length) meta.mcp_servers = mcpNames;
|
|
8187
8336
|
} catch {
|
|
@@ -8196,7 +8345,7 @@ function collectLocalMetadata(includeClaudeCode = true) {
|
|
|
8196
8345
|
const sessionsDir = join8(claudeDir, "sessions");
|
|
8197
8346
|
const files = readdirSync3(sessionsDir).filter((f) => f.endsWith(".json")).slice(-5);
|
|
8198
8347
|
for (const f of files) {
|
|
8199
|
-
const s = JSON.parse(
|
|
8348
|
+
const s = JSON.parse(readFileSync9(join8(sessionsDir, f), "utf-8"));
|
|
8200
8349
|
if (s.version) {
|
|
8201
8350
|
meta.cc_version = meta.cc_version || s.version;
|
|
8202
8351
|
break;
|
|
@@ -8328,7 +8477,7 @@ async function installCommand(opts = {}) {
|
|
|
8328
8477
|
for (const mode of ["edit", "bash"]) {
|
|
8329
8478
|
const pidFile = join8(SYNKRO_DIR4, "daemon", mode, "daemon.pid");
|
|
8330
8479
|
try {
|
|
8331
|
-
const pid = parseInt(
|
|
8480
|
+
const pid = parseInt(readFileSync9(pidFile, "utf-8").trim(), 10);
|
|
8332
8481
|
if (pid > 0) {
|
|
8333
8482
|
process.kill(pid, "SIGTERM");
|
|
8334
8483
|
console.log(`Stopped stale ${mode} grader daemon (pid ${pid})`);
|
|
@@ -8462,7 +8611,7 @@ async function installCommand(opts = {}) {
|
|
|
8462
8611
|
try {
|
|
8463
8612
|
if (useLocalMcp) {
|
|
8464
8613
|
const jwtPath = join8(SYNKRO_DIR4, ".mcp-jwt");
|
|
8465
|
-
if (!
|
|
8614
|
+
if (!existsSync10(jwtPath)) {
|
|
8466
8615
|
const mintResp = await fetch(`${gatewayUrl}/api/v1/cli/mcp-token`, {
|
|
8467
8616
|
method: "POST",
|
|
8468
8617
|
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
@@ -8552,7 +8701,7 @@ async function installCommand(opts = {}) {
|
|
|
8552
8701
|
const ready = await waitForContainerReady(6e4);
|
|
8553
8702
|
if (ready) {
|
|
8554
8703
|
console.log(" \u2713 container ready");
|
|
8555
|
-
const mcpJwt =
|
|
8704
|
+
const mcpJwt = readFileSync9(join8(SYNKRO_DIR4, ".mcp-jwt"), "utf-8").trim();
|
|
8556
8705
|
try {
|
|
8557
8706
|
const ingestResp = await fetch(`http://127.0.0.1:${hostMcpPort}/api/ingest`, {
|
|
8558
8707
|
method: "POST",
|
|
@@ -8569,6 +8718,7 @@ async function installCommand(opts = {}) {
|
|
|
8569
8718
|
} catch {
|
|
8570
8719
|
console.warn(" \u26A0 ingest endpoint unreachable \u2014 telemetry spool may not drain.");
|
|
8571
8720
|
}
|
|
8721
|
+
await syncSkillFiles();
|
|
8572
8722
|
} else {
|
|
8573
8723
|
console.error(" \u2717 container did not become healthy within 60s");
|
|
8574
8724
|
console.error(" Run `docker logs synkro-server` to debug.");
|
|
@@ -8583,7 +8733,7 @@ async function installCommand(opts = {}) {
|
|
|
8583
8733
|
try {
|
|
8584
8734
|
let mcpToken = "";
|
|
8585
8735
|
try {
|
|
8586
|
-
mcpToken =
|
|
8736
|
+
mcpToken = readFileSync9(join8(SYNKRO_DIR4, ".mcp-jwt"), "utf-8").trim();
|
|
8587
8737
|
} catch {
|
|
8588
8738
|
}
|
|
8589
8739
|
if (mcpToken) {
|
|
@@ -8647,12 +8797,58 @@ async function installCommand(opts = {}) {
|
|
|
8647
8797
|
}
|
|
8648
8798
|
console.log("\u2713 Synkro installed.");
|
|
8649
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
|
+
}
|
|
8650
8846
|
function writeSynkroFileIfMissing(opts) {
|
|
8651
8847
|
try {
|
|
8652
8848
|
const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
8653
8849
|
if (!root) return;
|
|
8654
8850
|
const fp = join8(root, ".synkro");
|
|
8655
|
-
if (
|
|
8851
|
+
if (existsSync10(fp)) {
|
|
8656
8852
|
console.log(` .synkro: ${fp} (existing, respected)`);
|
|
8657
8853
|
return;
|
|
8658
8854
|
}
|
|
@@ -8666,18 +8862,28 @@ function writeSynkroFileIfMissing(opts) {
|
|
|
8666
8862
|
harness.push("cursor");
|
|
8667
8863
|
if (!opts.hasClaudeCode) pool = "cursor";
|
|
8668
8864
|
}
|
|
8669
|
-
const
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8680
|
-
|
|
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})`);
|
|
8681
8887
|
} catch {
|
|
8682
8888
|
}
|
|
8683
8889
|
}
|
|
@@ -8686,8 +8892,8 @@ function readSynkroFilePool2() {
|
|
|
8686
8892
|
const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
8687
8893
|
if (!root) return "auto";
|
|
8688
8894
|
const fp = join8(root, ".synkro");
|
|
8689
|
-
if (!
|
|
8690
|
-
const parsed =
|
|
8895
|
+
if (!existsSync10(fp)) return "auto";
|
|
8896
|
+
const parsed = parseSynkroFileRaw(readFileSync9(fp, "utf-8"));
|
|
8691
8897
|
const pool = parsed?.grader?.pool;
|
|
8692
8898
|
if (pool === "cursor" || pool === "claude") return pool;
|
|
8693
8899
|
} catch {
|
|
@@ -8699,8 +8905,8 @@ function readFullSynkroFile() {
|
|
|
8699
8905
|
const root = execSync6("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
8700
8906
|
if (!root) return null;
|
|
8701
8907
|
const fp = join8(root, ".synkro");
|
|
8702
|
-
if (!
|
|
8703
|
-
const parsed =
|
|
8908
|
+
if (!existsSync10(fp)) return null;
|
|
8909
|
+
const parsed = parseSynkroFileRaw(readFileSync9(fp, "utf-8"));
|
|
8704
8910
|
const valid = ["claude-code", "cursor"];
|
|
8705
8911
|
const harness = Array.isArray(parsed.harness) ? parsed.harness.filter((h) => valid.includes(h)) : ["claude-code", "cursor"];
|
|
8706
8912
|
return {
|
|
@@ -8710,7 +8916,9 @@ function readFullSynkroFile() {
|
|
|
8710
8916
|
mode: ["local", "byok"].includes(parsed.grader?.mode) ? parsed.grader.mode : "local"
|
|
8711
8917
|
},
|
|
8712
8918
|
ruleset: parsed.ruleset || "default",
|
|
8713
|
-
|
|
8919
|
+
skills: Array.isArray(parsed.skills) ? parsed.skills.filter((s) => typeof s === "string" && s.endsWith(".md")) : [],
|
|
8920
|
+
scanning: { cwe: parsed.scanning?.cwe !== false, cve: parsed.scanning?.cve !== false },
|
|
8921
|
+
_repoRoot: root
|
|
8714
8922
|
};
|
|
8715
8923
|
} catch {
|
|
8716
8924
|
return null;
|
|
@@ -8745,7 +8953,7 @@ function reconcileHarness() {
|
|
|
8745
8953
|
});
|
|
8746
8954
|
console.log(" \u2713 Claude Code hooks registered");
|
|
8747
8955
|
try {
|
|
8748
|
-
const mcpJwt =
|
|
8956
|
+
const mcpJwt = readFileSync9(join8(SYNKRO_DIR4, ".mcp-jwt"), "utf-8").trim();
|
|
8749
8957
|
if (mcpJwt) {
|
|
8750
8958
|
installMcpConfig({ gatewayUrl: "", bearerToken: mcpJwt, local: true });
|
|
8751
8959
|
console.log(" \u2713 Claude Code MCP registered");
|
|
@@ -8797,6 +9005,33 @@ function reconcileHarness() {
|
|
|
8797
9005
|
if (providers.length === 0) providers.push("claude_code");
|
|
8798
9006
|
return splitWorkers(total, providers);
|
|
8799
9007
|
}
|
|
9008
|
+
async function syncSkillFiles() {
|
|
9009
|
+
const sf = readFullSynkroFile();
|
|
9010
|
+
if (!sf || sf.skills.length === 0) return;
|
|
9011
|
+
const resolved = resolveSkillPaths(sf.skills, sf._repoRoot);
|
|
9012
|
+
if (resolved.length === 0) return;
|
|
9013
|
+
const mcpPort = process.env.SYNKRO_MCP_PORT || "18931";
|
|
9014
|
+
for (const fp of resolved) {
|
|
9015
|
+
const skill = parseSkillFile(fp);
|
|
9016
|
+
if (!skill || skill.rules.length === 0) continue;
|
|
9017
|
+
try {
|
|
9018
|
+
const resp = await fetch(`http://127.0.0.1:${mcpPort}/api/local/skills/sync`, {
|
|
9019
|
+
method: "POST",
|
|
9020
|
+
headers: { "Content-Type": "application/json" },
|
|
9021
|
+
body: JSON.stringify(skill),
|
|
9022
|
+
signal: AbortSignal.timeout(1e4)
|
|
9023
|
+
});
|
|
9024
|
+
if (resp.ok) {
|
|
9025
|
+
const result = await resp.json();
|
|
9026
|
+
console.log(` \u2713 skill ${skill.source}: ${result.created} rules synced (${result.removed} removed)`);
|
|
9027
|
+
} else {
|
|
9028
|
+
console.warn(` \u26A0 skill ${skill.source}: sync failed (${resp.status})`);
|
|
9029
|
+
}
|
|
9030
|
+
} catch (e) {
|
|
9031
|
+
console.warn(` \u26A0 skill ${skill.source}: ${e.message}`);
|
|
9032
|
+
}
|
|
9033
|
+
}
|
|
9034
|
+
}
|
|
8800
9035
|
function detectGitRepo2() {
|
|
8801
9036
|
const run = (cmd2) => {
|
|
8802
9037
|
try {
|
|
@@ -8816,7 +9051,7 @@ function getClaudeProjectsFolder() {
|
|
|
8816
9051
|
const cwd = process.cwd();
|
|
8817
9052
|
const sanitized = "-" + cwd.replace(/\//g, "-");
|
|
8818
9053
|
const projectsDir = join8(homedir8(), ".claude", "projects", sanitized);
|
|
8819
|
-
return
|
|
9054
|
+
return existsSync10(projectsDir) ? projectsDir : null;
|
|
8820
9055
|
}
|
|
8821
9056
|
function extractSessionInsights(projectsDir) {
|
|
8822
9057
|
const insights = [];
|
|
@@ -8825,7 +9060,7 @@ function extractSessionInsights(projectsDir) {
|
|
|
8825
9060
|
const sessionId = file.replace(".jsonl", "");
|
|
8826
9061
|
const filePath = join8(projectsDir, file);
|
|
8827
9062
|
try {
|
|
8828
|
-
const content =
|
|
9063
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
8829
9064
|
const lines = content.split("\n").filter(Boolean);
|
|
8830
9065
|
for (let i = 0; i < lines.length; i++) {
|
|
8831
9066
|
try {
|
|
@@ -8901,7 +9136,7 @@ function extractTextContent(content) {
|
|
|
8901
9136
|
return "";
|
|
8902
9137
|
}
|
|
8903
9138
|
function parseTranscriptFile(filePath) {
|
|
8904
|
-
const content =
|
|
9139
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
8905
9140
|
const lines = content.split("\n").filter(Boolean);
|
|
8906
9141
|
const messages = [];
|
|
8907
9142
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -8971,7 +9206,7 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
|
8971
9206
|
process.stdout.write(`\r Progress: ${i + 1}/${files.length} sessions (${totalMessages} messages embedded) `);
|
|
8972
9207
|
}
|
|
8973
9208
|
try {
|
|
8974
|
-
const content =
|
|
9209
|
+
const content = readFileSync9(join8(projectsDir, file), "utf-8");
|
|
8975
9210
|
const lineCount = content.split("\n").filter(Boolean).length;
|
|
8976
9211
|
writeFileSync7(join8(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
|
|
8977
9212
|
} catch {
|
|
@@ -9025,7 +9260,7 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
9025
9260
|
const sessionId = file.replace(".jsonl", "");
|
|
9026
9261
|
const filePath = join8(projectsDir, file);
|
|
9027
9262
|
try {
|
|
9028
|
-
const content =
|
|
9263
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
9029
9264
|
const lineCount = content.split("\n").filter(Boolean).length;
|
|
9030
9265
|
writeFileSync7(join8(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
|
|
9031
9266
|
} catch {
|
|
@@ -9042,6 +9277,7 @@ var init_install = __esm({
|
|
|
9042
9277
|
init_ccHookConfig();
|
|
9043
9278
|
init_cursorHookConfig();
|
|
9044
9279
|
init_mcpConfig();
|
|
9280
|
+
init_skillParser();
|
|
9045
9281
|
init_hookScripts();
|
|
9046
9282
|
init_hookScriptsTs();
|
|
9047
9283
|
init_stub();
|
|
@@ -9104,7 +9340,7 @@ rl.on('line', async (line) => {
|
|
|
9104
9340
|
});
|
|
9105
9341
|
|
|
9106
9342
|
// cli/local-cc/install.ts
|
|
9107
|
-
import { existsSync as
|
|
9343
|
+
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
9344
|
import { join as join9 } from "path";
|
|
9109
9345
|
import { homedir as homedir9 } from "os";
|
|
9110
9346
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
@@ -9140,10 +9376,10 @@ function runBunInstall() {
|
|
|
9140
9376
|
}
|
|
9141
9377
|
}
|
|
9142
9378
|
function safelyMutateClaudeJson(mutator) {
|
|
9143
|
-
if (!
|
|
9379
|
+
if (!existsSync11(CLAUDE_JSON_PATH)) {
|
|
9144
9380
|
return;
|
|
9145
9381
|
}
|
|
9146
|
-
const originalText =
|
|
9382
|
+
const originalText = readFileSync10(CLAUDE_JSON_PATH, "utf-8");
|
|
9147
9383
|
let parsed;
|
|
9148
9384
|
try {
|
|
9149
9385
|
parsed = JSON.parse(originalText);
|
|
@@ -9594,7 +9830,7 @@ var disconnect_exports = {};
|
|
|
9594
9830
|
__export(disconnect_exports, {
|
|
9595
9831
|
disconnectCommand: () => disconnectCommand
|
|
9596
9832
|
});
|
|
9597
|
-
import { existsSync as
|
|
9833
|
+
import { existsSync as existsSync12, rmSync, readdirSync as readdirSync4 } from "fs";
|
|
9598
9834
|
import { homedir as homedir10 } from "os";
|
|
9599
9835
|
import { join as join10 } from "path";
|
|
9600
9836
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
@@ -9635,10 +9871,10 @@ function confirmPurge() {
|
|
|
9635
9871
|
return Promise.resolve(false);
|
|
9636
9872
|
}
|
|
9637
9873
|
const rl = createInterface4({ input: process.stdin, output: process.stdout });
|
|
9638
|
-
return new Promise((
|
|
9874
|
+
return new Promise((resolve4) => {
|
|
9639
9875
|
rl.question(" Type 'yes' to wipe everything (anything else cancels): ", (answer) => {
|
|
9640
9876
|
rl.close();
|
|
9641
|
-
|
|
9877
|
+
resolve4(answer.trim().toLowerCase() === "yes");
|
|
9642
9878
|
});
|
|
9643
9879
|
});
|
|
9644
9880
|
}
|
|
@@ -9670,7 +9906,7 @@ async function disconnectCommand(args2 = []) {
|
|
|
9670
9906
|
const cursorMcpRemoved = uninstallCursorMcpConfig();
|
|
9671
9907
|
console.log(`${cursorMcpRemoved ? "\u2713" : "\xB7"} MCP guardrails (Cursor): ${cursorMcpRemoved ? "removed from ~/.cursor/mcp.json" : "no entry found"}`);
|
|
9672
9908
|
}
|
|
9673
|
-
if (
|
|
9909
|
+
if (existsSync12(SYNKRO_DIR5)) {
|
|
9674
9910
|
if (purge) {
|
|
9675
9911
|
rmSync(SYNKRO_DIR5, { recursive: true, force: true });
|
|
9676
9912
|
console.log(`\u2713 wiped ${SYNKRO_DIR5} entirely \u2014 including all scan data and backups`);
|
|
@@ -9713,7 +9949,7 @@ var init_disconnect = __esm({
|
|
|
9713
9949
|
});
|
|
9714
9950
|
|
|
9715
9951
|
// cli/local-cc/turnLog.ts
|
|
9716
|
-
import { appendFileSync, existsSync as
|
|
9952
|
+
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
9953
|
import { dirname as dirname6, join as join11 } from "path";
|
|
9718
9954
|
import { homedir as homedir11 } from "os";
|
|
9719
9955
|
function truncate(s, max = PREVIEW_MAX) {
|
|
@@ -9751,11 +9987,11 @@ function appendTurn(args2) {
|
|
|
9751
9987
|
}
|
|
9752
9988
|
}
|
|
9753
9989
|
function readRecentTurns(n = 20) {
|
|
9754
|
-
if (!
|
|
9990
|
+
if (!existsSync13(TURN_LOG_PATH)) return [];
|
|
9755
9991
|
try {
|
|
9756
9992
|
const size = statSync2(TURN_LOG_PATH).size;
|
|
9757
9993
|
if (size === 0) return [];
|
|
9758
|
-
const text =
|
|
9994
|
+
const text = readFileSync11(TURN_LOG_PATH, "utf-8");
|
|
9759
9995
|
const lines = text.split("\n").filter(Boolean);
|
|
9760
9996
|
const lastN = lines.slice(-n).reverse();
|
|
9761
9997
|
return lastN.map((line) => {
|
|
@@ -9772,7 +10008,7 @@ function readRecentTurns(n = 20) {
|
|
|
9772
10008
|
function followTurns(onEntry) {
|
|
9773
10009
|
try {
|
|
9774
10010
|
mkdirSync10(dirname6(TURN_LOG_PATH), { recursive: true });
|
|
9775
|
-
if (!
|
|
10011
|
+
if (!existsSync13(TURN_LOG_PATH)) {
|
|
9776
10012
|
appendFileSync(TURN_LOG_PATH, "", "utf-8");
|
|
9777
10013
|
}
|
|
9778
10014
|
} catch {
|
|
@@ -9848,7 +10084,7 @@ async function submitToChannel(role, payload, opts = {}) {
|
|
|
9848
10084
|
const port = opts.port ?? CHANNEL_PORT;
|
|
9849
10085
|
const startedAt = Date.now();
|
|
9850
10086
|
try {
|
|
9851
|
-
const result = await new Promise((
|
|
10087
|
+
const result = await new Promise((resolve4, reject) => {
|
|
9852
10088
|
const req = httpRequest({
|
|
9853
10089
|
host: CHANNEL_HOST,
|
|
9854
10090
|
port,
|
|
@@ -9874,7 +10110,7 @@ async function submitToChannel(role, payload, opts = {}) {
|
|
|
9874
10110
|
reject(new LocalCCError(parsed.error));
|
|
9875
10111
|
return;
|
|
9876
10112
|
}
|
|
9877
|
-
|
|
10113
|
+
resolve4(String(parsed.result ?? ""));
|
|
9878
10114
|
} catch (err) {
|
|
9879
10115
|
reject(new LocalCCError(`malformed channel response: ${text.slice(0, 200)}`, err));
|
|
9880
10116
|
}
|
|
@@ -9900,14 +10136,14 @@ async function submitToChannel(role, payload, opts = {}) {
|
|
|
9900
10136
|
}
|
|
9901
10137
|
}
|
|
9902
10138
|
function isChannelAvailable(port = CHANNEL_PORT, timeoutMs = 500) {
|
|
9903
|
-
return new Promise((
|
|
10139
|
+
return new Promise((resolve4) => {
|
|
9904
10140
|
const sock = connect(port, CHANNEL_HOST);
|
|
9905
10141
|
const done = (ok) => {
|
|
9906
10142
|
try {
|
|
9907
10143
|
sock.destroy();
|
|
9908
10144
|
} catch {
|
|
9909
10145
|
}
|
|
9910
|
-
|
|
10146
|
+
resolve4(ok);
|
|
9911
10147
|
};
|
|
9912
10148
|
sock.once("connect", () => done(true));
|
|
9913
10149
|
sock.once("error", () => done(false));
|
|
@@ -9939,10 +10175,10 @@ __export(grade_exports, {
|
|
|
9939
10175
|
gradeCommand: () => gradeCommand
|
|
9940
10176
|
});
|
|
9941
10177
|
async function readStdin() {
|
|
9942
|
-
return new Promise((
|
|
10178
|
+
return new Promise((resolve4, reject) => {
|
|
9943
10179
|
const chunks = [];
|
|
9944
10180
|
process.stdin.on("data", (c) => chunks.push(c));
|
|
9945
|
-
process.stdin.on("end", () =>
|
|
10181
|
+
process.stdin.on("end", () => resolve4(Buffer.concat(chunks).toString("utf-8")));
|
|
9946
10182
|
process.stdin.on("error", reject);
|
|
9947
10183
|
});
|
|
9948
10184
|
}
|
|
@@ -10101,14 +10337,14 @@ function ensureRunning(opts = {}) {
|
|
|
10101
10337
|
return startTask(opts);
|
|
10102
10338
|
}
|
|
10103
10339
|
function probePort(host, port, timeoutMs = 500) {
|
|
10104
|
-
return new Promise((
|
|
10340
|
+
return new Promise((resolve4) => {
|
|
10105
10341
|
const sock = connect2(port, host);
|
|
10106
10342
|
const done = (ok) => {
|
|
10107
10343
|
try {
|
|
10108
10344
|
sock.destroy();
|
|
10109
10345
|
} catch {
|
|
10110
10346
|
}
|
|
10111
|
-
|
|
10347
|
+
resolve4(ok);
|
|
10112
10348
|
};
|
|
10113
10349
|
sock.once("connect", () => done(true));
|
|
10114
10350
|
sock.once("error", () => done(false));
|
|
@@ -10201,13 +10437,13 @@ var init_pueue = __esm({
|
|
|
10201
10437
|
});
|
|
10202
10438
|
|
|
10203
10439
|
// cli/local-cc/settings.ts
|
|
10204
|
-
import { existsSync as
|
|
10440
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
10205
10441
|
import { homedir as homedir13 } from "os";
|
|
10206
10442
|
import { join as join13 } from "path";
|
|
10207
10443
|
function isLocalCCEnabled() {
|
|
10208
|
-
if (!
|
|
10444
|
+
if (!existsSync14(CONFIG_PATH3)) return false;
|
|
10209
10445
|
try {
|
|
10210
|
-
const content =
|
|
10446
|
+
const content = readFileSync12(CONFIG_PATH3, "utf-8");
|
|
10211
10447
|
const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);
|
|
10212
10448
|
return match?.[1] === "yes";
|
|
10213
10449
|
} catch {
|
|
@@ -10231,7 +10467,7 @@ import { spawnSync as spawnSync7 } from "child_process";
|
|
|
10231
10467
|
import { homedir as homedir14 } from "os";
|
|
10232
10468
|
import { join as join14 } from "path";
|
|
10233
10469
|
import { readFileSync as fsReadFileSync, existsSync as fsExistsSync } from "fs";
|
|
10234
|
-
import { existsSync as
|
|
10470
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
|
|
10235
10471
|
function deploymentMode() {
|
|
10236
10472
|
const env = (process.env.SYNKRO_DEPLOYMENT_MODE || "").toLowerCase();
|
|
10237
10473
|
if (env === "docker") return "docker";
|
|
@@ -10337,15 +10573,15 @@ TROUBLESHOOTING
|
|
|
10337
10573
|
`);
|
|
10338
10574
|
}
|
|
10339
10575
|
function readGatewayUrl() {
|
|
10340
|
-
if (
|
|
10341
|
-
const m =
|
|
10576
|
+
if (existsSync15(CONFIG_PATH4)) {
|
|
10577
|
+
const m = readFileSync13(CONFIG_PATH4, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
|
|
10342
10578
|
if (m) return m[1];
|
|
10343
10579
|
}
|
|
10344
10580
|
return "https://api.synkro.sh";
|
|
10345
10581
|
}
|
|
10346
10582
|
function updateLocalInferenceFlag(enabled) {
|
|
10347
|
-
if (!
|
|
10348
|
-
let content =
|
|
10583
|
+
if (!existsSync15(CONFIG_PATH4)) return;
|
|
10584
|
+
let content = readFileSync13(CONFIG_PATH4, "utf-8");
|
|
10349
10585
|
const flag = enabled ? "yes" : "no";
|
|
10350
10586
|
if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
|
|
10351
10587
|
content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);
|
|
@@ -10537,6 +10773,7 @@ async function cmdRestart(rest = []) {
|
|
|
10537
10773
|
await dockerUpdate({ claudeWorkers, cursorWorkers });
|
|
10538
10774
|
const ready = await waitForContainerReady(6e4);
|
|
10539
10775
|
console.log(ready ? "\u2713 container ready" : "\u26A0 container did not pass /healthz within 60s");
|
|
10776
|
+
if (ready) await syncSkillFiles();
|
|
10540
10777
|
return;
|
|
10541
10778
|
}
|
|
10542
10779
|
stopTask(CHANNEL_PRIMARY);
|
|
@@ -10651,7 +10888,7 @@ function cmdLogs(rest) {
|
|
|
10651
10888
|
if (!raw) console.log(" " + colorize("(use --raw / -r to see full payloads, --live / -f to follow)", 90));
|
|
10652
10889
|
return;
|
|
10653
10890
|
}
|
|
10654
|
-
return new Promise((
|
|
10891
|
+
return new Promise((resolve4) => {
|
|
10655
10892
|
console.log(" " + colorize("\u2014 following new turns (Ctrl-C to exit) \u2014", 90));
|
|
10656
10893
|
const stop = followTurns((t) => {
|
|
10657
10894
|
console.log(" " + formatTurn(t, raw));
|
|
@@ -10659,7 +10896,7 @@ function cmdLogs(rest) {
|
|
|
10659
10896
|
const onSigint = () => {
|
|
10660
10897
|
stop();
|
|
10661
10898
|
process.removeListener("SIGINT", onSigint);
|
|
10662
|
-
|
|
10899
|
+
resolve4();
|
|
10663
10900
|
};
|
|
10664
10901
|
process.on("SIGINT", onSigint);
|
|
10665
10902
|
});
|
|
@@ -10887,13 +11124,13 @@ var config_exports = {};
|
|
|
10887
11124
|
__export(config_exports, {
|
|
10888
11125
|
configCommand: () => configCommand
|
|
10889
11126
|
});
|
|
10890
|
-
import { readFileSync as
|
|
11127
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync10, existsSync as existsSync16 } from "fs";
|
|
10891
11128
|
import { join as join15 } from "path";
|
|
10892
11129
|
import { homedir as homedir15 } from "os";
|
|
10893
11130
|
function readConfigEnv() {
|
|
10894
|
-
if (!
|
|
11131
|
+
if (!existsSync16(CONFIG_PATH5)) return {};
|
|
10895
11132
|
const out = {};
|
|
10896
|
-
for (const line of
|
|
11133
|
+
for (const line of readFileSync14(CONFIG_PATH5, "utf-8").split("\n")) {
|
|
10897
11134
|
const t = line.trim();
|
|
10898
11135
|
if (!t || t.startsWith("#")) continue;
|
|
10899
11136
|
const eq = t.indexOf("=");
|
|
@@ -10902,11 +11139,11 @@ function readConfigEnv() {
|
|
|
10902
11139
|
return out;
|
|
10903
11140
|
}
|
|
10904
11141
|
function updateConfigValue(key, value) {
|
|
10905
|
-
if (!
|
|
11142
|
+
if (!existsSync16(CONFIG_PATH5)) {
|
|
10906
11143
|
console.error("No config found. Run `synkro install` first.");
|
|
10907
11144
|
process.exit(1);
|
|
10908
11145
|
}
|
|
10909
|
-
const lines =
|
|
11146
|
+
const lines = readFileSync14(CONFIG_PATH5, "utf-8").split("\n");
|
|
10910
11147
|
const pattern = new RegExp(`^${key}=`);
|
|
10911
11148
|
let found = false;
|
|
10912
11149
|
const updated = lines.map((line) => {
|
|
@@ -11033,14 +11270,14 @@ var init_config = __esm({
|
|
|
11033
11270
|
});
|
|
11034
11271
|
|
|
11035
11272
|
// cli/bootstrap.js
|
|
11036
|
-
import { readFileSync as
|
|
11037
|
-
import { resolve as
|
|
11273
|
+
import { readFileSync as readFileSync15, existsSync as existsSync17 } from "fs";
|
|
11274
|
+
import { resolve as resolve3 } from "path";
|
|
11038
11275
|
var envCandidates = [
|
|
11039
|
-
|
|
11276
|
+
resolve3(process.env.HOME ?? "", ".synkro", "config.env")
|
|
11040
11277
|
];
|
|
11041
11278
|
for (const envPath of envCandidates) {
|
|
11042
|
-
if (!
|
|
11043
|
-
const envContent =
|
|
11279
|
+
if (!existsSync17(envPath)) continue;
|
|
11280
|
+
const envContent = readFileSync15(envPath, "utf-8");
|
|
11044
11281
|
for (const line of envContent.split("\n")) {
|
|
11045
11282
|
const trimmed = line.trim();
|
|
11046
11283
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -11055,7 +11292,7 @@ var args = process.argv.slice(2);
|
|
|
11055
11292
|
var cmd = args[0] || "";
|
|
11056
11293
|
var subArgs = args.slice(1);
|
|
11057
11294
|
function printVersion() {
|
|
11058
|
-
console.log("1.6.
|
|
11295
|
+
console.log("1.6.35");
|
|
11059
11296
|
}
|
|
11060
11297
|
function printHelp2() {
|
|
11061
11298
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|