@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 +197 -109
- 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,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
|
|
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 (!
|
|
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 (!
|
|
6041
|
+
if (!existsSync5(AUTH_FILE)) {
|
|
5987
6042
|
return null;
|
|
5988
6043
|
}
|
|
5989
6044
|
try {
|
|
5990
|
-
const content =
|
|
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((
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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 (
|
|
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((
|
|
6603
|
+
return new Promise((resolve4) => rl.question(question, resolve4));
|
|
6549
6604
|
}
|
|
6550
6605
|
function waitForGithubToken() {
|
|
6551
|
-
return new Promise((
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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 (!
|
|
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 (
|
|
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
|
|
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
|
|
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 (!
|
|
7062
|
-
const parsed = JSON.parse(
|
|
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 (
|
|
7244
|
+
if (existsSync8(hostClaudeJson)) {
|
|
7190
7245
|
copyFileSync(hostClaudeJson, CLAUDE_HOST_STATE_FILE);
|
|
7191
7246
|
}
|
|
7192
|
-
if (!
|
|
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 (
|
|
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 (!
|
|
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
|
|
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 (!
|
|
7576
|
+
if (!existsSync9(CONFIG_PATH)) return {};
|
|
7522
7577
|
const out = {};
|
|
7523
|
-
for (const line of
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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((
|
|
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
|
|
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
|
|
7977
|
+
return resolve4([detected[n - 1]]);
|
|
7922
7978
|
}
|
|
7923
7979
|
console.log("Invalid choice. Try again.");
|
|
7924
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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 &&
|
|
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.
|
|
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 (
|
|
8148
|
-
const 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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 (!
|
|
8690
|
-
const parsed = JSON.parse(
|
|
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 (!
|
|
8703
|
-
const parsed = JSON.parse(
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 (!
|
|
9230
|
+
if (!existsSync11(CLAUDE_JSON_PATH)) {
|
|
9144
9231
|
return;
|
|
9145
9232
|
}
|
|
9146
|
-
const originalText =
|
|
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
|
|
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((
|
|
9725
|
+
return new Promise((resolve4) => {
|
|
9639
9726
|
rl.question(" Type 'yes' to wipe everything (anything else cancels): ", (answer) => {
|
|
9640
9727
|
rl.close();
|
|
9641
|
-
|
|
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 (
|
|
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
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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", () =>
|
|
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((
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
10295
|
+
if (!existsSync14(CONFIG_PATH3)) return false;
|
|
10209
10296
|
try {
|
|
10210
|
-
const content =
|
|
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
|
|
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 (
|
|
10341
|
-
const 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 (!
|
|
10348
|
-
let content =
|
|
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((
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
10982
|
+
if (!existsSync16(CONFIG_PATH5)) return {};
|
|
10895
10983
|
const out = {};
|
|
10896
|
-
for (const line of
|
|
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 (!
|
|
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 =
|
|
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
|
|
11037
|
-
import { resolve as
|
|
11124
|
+
import { readFileSync as readFileSync15, existsSync as existsSync17 } from "fs";
|
|
11125
|
+
import { resolve as resolve3 } from "path";
|
|
11038
11126
|
var envCandidates = [
|
|
11039
|
-
|
|
11127
|
+
resolve3(process.env.HOME ?? "", ".synkro", "config.env")
|
|
11040
11128
|
];
|
|
11041
11129
|
for (const envPath of envCandidates) {
|
|
11042
|
-
if (!
|
|
11043
|
-
const envContent =
|
|
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.
|
|
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
|