@synkro-sh/cli 1.6.48 → 1.6.49
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 +158 -40
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -8451,18 +8451,35 @@ async function promptStorageMode() {
|
|
|
8451
8451
|
);
|
|
8452
8452
|
});
|
|
8453
8453
|
}
|
|
8454
|
-
async function
|
|
8454
|
+
async function promptTranscriptSources(wantCC, wantCursor) {
|
|
8455
|
+
if (!wantCC && !wantCursor) return { cc: false, cursor: false };
|
|
8455
8456
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
(
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8457
|
+
const ask2 = (q) => new Promise((res) => rl.question(q, (a) => res(a.trim().toLowerCase())));
|
|
8458
|
+
try {
|
|
8459
|
+
if (wantCC && wantCursor) {
|
|
8460
|
+
console.log("Import and embed past session history? This indexes sessions so Ask Synkro");
|
|
8461
|
+
console.log("can answer questions about your patterns and the dashboard shows full history.");
|
|
8462
|
+
console.log(" 1. Cursor only");
|
|
8463
|
+
console.log(" 2. Claude Code only");
|
|
8464
|
+
console.log(" 3. Both");
|
|
8465
|
+
console.log(" n. Skip");
|
|
8466
|
+
const a2 = await ask2("Choose [1/2/3/n] (default: 3): ");
|
|
8467
|
+
if (a2 === "n" || a2 === "no") return { cc: false, cursor: false };
|
|
8468
|
+
if (a2 === "1") return { cc: false, cursor: true };
|
|
8469
|
+
if (a2 === "2") return { cc: true, cursor: false };
|
|
8470
|
+
return { cc: true, cursor: true };
|
|
8471
|
+
}
|
|
8472
|
+
const which2 = wantCursor ? "Cursor" : "Claude Code";
|
|
8473
|
+
const a = await ask2(
|
|
8474
|
+
`Import and embed your ${which2} session history?
|
|
8475
|
+
This indexes past sessions so Ask Synkro can answer questions about your
|
|
8476
|
+
coding patterns and the dashboard shows full history. (Y/n) `
|
|
8464
8477
|
);
|
|
8465
|
-
|
|
8478
|
+
const yes = a === "" || a === "y" || a === "yes";
|
|
8479
|
+
return { cc: wantCC && yes, cursor: wantCursor && yes };
|
|
8480
|
+
} finally {
|
|
8481
|
+
rl.close();
|
|
8482
|
+
}
|
|
8466
8483
|
}
|
|
8467
8484
|
function ensureSynkroDir() {
|
|
8468
8485
|
mkdirSync8(SYNKRO_DIR4, { recursive: true });
|
|
@@ -8630,7 +8647,7 @@ function writeConfigEnv(opts) {
|
|
|
8630
8647
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
8631
8648
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
8632
8649
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
8633
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.6.
|
|
8650
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.6.49")}`
|
|
8634
8651
|
];
|
|
8635
8652
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
8636
8653
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -8813,7 +8830,8 @@ async function installCommand(opts = {}) {
|
|
|
8813
8830
|
let agents;
|
|
8814
8831
|
let gradingMode;
|
|
8815
8832
|
let storageMode;
|
|
8816
|
-
let
|
|
8833
|
+
let transcriptCC = true;
|
|
8834
|
+
let transcriptCursor = true;
|
|
8817
8835
|
if (existingSynkro) {
|
|
8818
8836
|
const wantCC = existingSynkro.harness.includes("claude-code");
|
|
8819
8837
|
const wantCursor = existingSynkro.harness.includes("cursor");
|
|
@@ -8823,6 +8841,8 @@ async function installCommand(opts = {}) {
|
|
|
8823
8841
|
if (agents.length === 0 && detected.length > 0) agents = detected;
|
|
8824
8842
|
gradingMode = existingSynkro.grader.mode === "byok" ? "byok" : "local";
|
|
8825
8843
|
storageMode = "local";
|
|
8844
|
+
transcriptCC = agents.some((a) => a.kind === "claude_code");
|
|
8845
|
+
transcriptCursor = agents.some((a) => a.kind === "cursor");
|
|
8826
8846
|
console.log(`Using .synkro config:`);
|
|
8827
8847
|
console.log(` harness: ${existingSynkro.harness.join(", ")}`);
|
|
8828
8848
|
console.log(` grading: ${gradingMode} pool: ${existingSynkro.grader.pool}`);
|
|
@@ -8853,15 +8873,21 @@ async function installCommand(opts = {}) {
|
|
|
8853
8873
|
console.log(" BYOK grading uses your own provider key \u2014 register one in the");
|
|
8854
8874
|
console.log(" dashboard under Settings \u2192 Provider Keys if you have not already.\n");
|
|
8855
8875
|
}
|
|
8876
|
+
const wantCC = agents.some((a) => a.kind === "claude_code");
|
|
8877
|
+
const wantCursor = agents.some((a) => a.kind === "cursor");
|
|
8856
8878
|
if (process.stdin.isTTY) {
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8879
|
+
const src = await promptTranscriptSources(wantCC, wantCursor);
|
|
8880
|
+
transcriptCC = src.cc;
|
|
8881
|
+
transcriptCursor = src.cursor;
|
|
8882
|
+
const chosen = [src.cc && "Claude Code", src.cursor && "Cursor"].filter(Boolean).join(" + ");
|
|
8883
|
+
console.log(chosen ? ` \u2713 Session import: ${chosen}
|
|
8884
|
+
` : " \u2717 Session import skipped\n");
|
|
8885
|
+
} else {
|
|
8886
|
+
transcriptCC = wantCC;
|
|
8887
|
+
transcriptCursor = wantCursor;
|
|
8863
8888
|
}
|
|
8864
8889
|
}
|
|
8890
|
+
const transcriptConsent = transcriptCC || transcriptCursor;
|
|
8865
8891
|
ensureSynkroDir();
|
|
8866
8892
|
const hookMode = opts.hookMode || resolvePersistedHookMode();
|
|
8867
8893
|
const scripts = writeHookScripts(hookMode);
|
|
@@ -9128,7 +9154,7 @@ async function installCommand(opts = {}) {
|
|
|
9128
9154
|
}
|
|
9129
9155
|
console.log();
|
|
9130
9156
|
}
|
|
9131
|
-
if (transcriptConsent
|
|
9157
|
+
if (transcriptConsent) {
|
|
9132
9158
|
const repo = detectGitRepo2();
|
|
9133
9159
|
if (repo) {
|
|
9134
9160
|
if (storageMode === "local") {
|
|
@@ -9140,21 +9166,34 @@ async function installCommand(opts = {}) {
|
|
|
9140
9166
|
}
|
|
9141
9167
|
if (mcpToken) {
|
|
9142
9168
|
const mcpPort = parseInt(process.env.SYNKRO_MCP_PORT || "18931", 10);
|
|
9143
|
-
|
|
9144
|
-
if (
|
|
9145
|
-
|
|
9169
|
+
let imported = 0;
|
|
9170
|
+
if (transcriptCC && hasClaudeCode) {
|
|
9171
|
+
const r = await syncTranscriptsLocal(mcpPort, mcpToken, repo);
|
|
9172
|
+
if (r.messages > 0) {
|
|
9173
|
+
console.log(` \u2713 Imported ${r.sessions} Claude Code sessions (${r.messages} messages) into local store.`);
|
|
9174
|
+
imported += r.messages;
|
|
9175
|
+
}
|
|
9176
|
+
}
|
|
9177
|
+
if (transcriptCursor && hasCursor) {
|
|
9178
|
+
const r = await syncCursorTranscriptsLocal(mcpPort, mcpToken, repo);
|
|
9179
|
+
if (r.messages > 0) {
|
|
9180
|
+
console.log(` \u2713 Imported ${r.sessions} Cursor sessions (${r.messages} messages) into local store.`);
|
|
9181
|
+
imported += r.messages;
|
|
9182
|
+
}
|
|
9183
|
+
}
|
|
9184
|
+
if (imported > 0) {
|
|
9146
9185
|
console.log(" Embeddings generated. Analyzing for rule suggestions...");
|
|
9147
9186
|
try {
|
|
9148
9187
|
const suggestResp = await fetch(`http://127.0.0.1:${mcpPort}/api/local/suggest-rules`, {
|
|
9149
9188
|
method: "POST",
|
|
9150
|
-
headers: { "Content-Type": "application/json" },
|
|
9189
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${mcpToken}` },
|
|
9151
9190
|
signal: AbortSignal.timeout(9e4)
|
|
9152
9191
|
});
|
|
9153
9192
|
if (suggestResp.ok) {
|
|
9154
9193
|
const suggestResult = await suggestResp.json();
|
|
9155
9194
|
if (suggestResult.suggested && suggestResult.suggested > 0) {
|
|
9156
9195
|
console.log(` \u2713 Generated ${suggestResult.suggested} rule suggestions from your session history.`);
|
|
9157
|
-
console.log(' Ask
|
|
9196
|
+
console.log(' Ask Synkro: "show me suggested rules" to review them.\n');
|
|
9158
9197
|
} else {
|
|
9159
9198
|
console.log(" No rule suggestions generated yet \u2014 more session data needed.\n");
|
|
9160
9199
|
}
|
|
@@ -9171,24 +9210,29 @@ async function installCommand(opts = {}) {
|
|
|
9171
9210
|
`);
|
|
9172
9211
|
}
|
|
9173
9212
|
} else {
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9213
|
+
if (transcriptCC && hasClaudeCode) {
|
|
9214
|
+
try {
|
|
9215
|
+
const ingested = await ingestSessionTranscripts(gatewayUrl, token, repo);
|
|
9216
|
+
if (ingested > 0) {
|
|
9217
|
+
console.log(` \u2713 Indexed ${ingested} session insights from Claude Code history.`);
|
|
9218
|
+
}
|
|
9219
|
+
} catch (err) {
|
|
9220
|
+
console.warn(` \u26A0 Session indexing skipped: ${err.message}
|
|
9181
9221
|
`);
|
|
9182
|
-
}
|
|
9183
|
-
try {
|
|
9184
|
-
const result = await syncTranscriptsBulk(gatewayUrl, token, repo);
|
|
9185
|
-
if (result.messages > 0) {
|
|
9186
|
-
console.log(` \u2713 Synced ${result.sessions} sessions (${result.messages} messages) to cloud.`);
|
|
9187
|
-
console.log(" Embeddings use your configured inference provider.\n");
|
|
9188
9222
|
}
|
|
9189
|
-
|
|
9190
|
-
|
|
9223
|
+
try {
|
|
9224
|
+
const result = await syncTranscriptsBulk(gatewayUrl, token, repo);
|
|
9225
|
+
if (result.messages > 0) {
|
|
9226
|
+
console.log(` \u2713 Synced ${result.sessions} sessions (${result.messages} messages) to cloud.`);
|
|
9227
|
+
console.log(" Embeddings use your configured inference provider.\n");
|
|
9228
|
+
}
|
|
9229
|
+
} catch (err) {
|
|
9230
|
+
console.warn(` \u26A0 Transcript sync skipped: ${err.message}
|
|
9191
9231
|
`);
|
|
9232
|
+
}
|
|
9233
|
+
}
|
|
9234
|
+
if (transcriptCursor && hasCursor) {
|
|
9235
|
+
console.log(" \u2139 Cursor transcript import requires local storage mode \u2014 skipped under cloud storage.\n");
|
|
9192
9236
|
}
|
|
9193
9237
|
}
|
|
9194
9238
|
}
|
|
@@ -9552,6 +9596,80 @@ function extractTextContent(content) {
|
|
|
9552
9596
|
}
|
|
9553
9597
|
return "";
|
|
9554
9598
|
}
|
|
9599
|
+
function cursorProjectSlug(workspaceRoot) {
|
|
9600
|
+
return workspaceRoot.replace(/^[/]+/, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
9601
|
+
}
|
|
9602
|
+
function getCursorTranscriptsDir() {
|
|
9603
|
+
const dir = join8(homedir8(), ".cursor", "projects", cursorProjectSlug(process.cwd()), "agent-transcripts");
|
|
9604
|
+
return existsSync10(dir) ? dir : null;
|
|
9605
|
+
}
|
|
9606
|
+
function isSafeConvId(id) {
|
|
9607
|
+
return /^[A-Za-z0-9_-]+$/.test(id);
|
|
9608
|
+
}
|
|
9609
|
+
function parseCursorTranscriptFile(filePath) {
|
|
9610
|
+
const content = readFileSync8(filePath, "utf-8");
|
|
9611
|
+
const lines = content.split("\n").filter(Boolean);
|
|
9612
|
+
const messages = [];
|
|
9613
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9614
|
+
try {
|
|
9615
|
+
const entry = JSON.parse(lines[i]);
|
|
9616
|
+
const role = entry.role || entry.message?.role;
|
|
9617
|
+
if (role !== "user" && role !== "assistant") continue;
|
|
9618
|
+
const text = extractTextContent(entry.message?.content ?? entry.content);
|
|
9619
|
+
if (!text) continue;
|
|
9620
|
+
messages.push({ message_index: i, type: role, content: text });
|
|
9621
|
+
} catch {
|
|
9622
|
+
}
|
|
9623
|
+
}
|
|
9624
|
+
return messages;
|
|
9625
|
+
}
|
|
9626
|
+
async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
9627
|
+
const dir = getCursorTranscriptsDir();
|
|
9628
|
+
if (!dir) return { sessions: 0, messages: 0 };
|
|
9629
|
+
let convDirs = [];
|
|
9630
|
+
try {
|
|
9631
|
+
convDirs = readdirSync3(dir, { withFileTypes: true }).filter((d) => d.isDirectory() && isSafeConvId(d.name)).map((d) => d.name);
|
|
9632
|
+
} catch {
|
|
9633
|
+
return { sessions: 0, messages: 0 };
|
|
9634
|
+
}
|
|
9635
|
+
if (convDirs.length === 0) return { sessions: 0, messages: 0 };
|
|
9636
|
+
console.log(` Found ${convDirs.length} Cursor session transcripts, importing + embedding...`);
|
|
9637
|
+
let totalSessions = 0;
|
|
9638
|
+
let totalMessages = 0;
|
|
9639
|
+
for (let i = 0; i < convDirs.length; i++) {
|
|
9640
|
+
const convId = convDirs[i];
|
|
9641
|
+
if (!isSafeConvId(convId)) continue;
|
|
9642
|
+
const filePath = join8(dir, convId, `${convId}.jsonl`);
|
|
9643
|
+
if (!existsSync10(filePath)) continue;
|
|
9644
|
+
try {
|
|
9645
|
+
const all = parseCursorTranscriptFile(filePath);
|
|
9646
|
+
const messages = all.length > 500 ? all.slice(-500) : all;
|
|
9647
|
+
if (messages.length === 0) continue;
|
|
9648
|
+
const resp = await fetch(`http://127.0.0.1:${mcpPort}/api/conversation-sync`, {
|
|
9649
|
+
method: "POST",
|
|
9650
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${mcpToken}` },
|
|
9651
|
+
body: JSON.stringify({ session_id: convId, repo, messages }),
|
|
9652
|
+
signal: AbortSignal.timeout(15e3)
|
|
9653
|
+
});
|
|
9654
|
+
if (resp.ok) {
|
|
9655
|
+
const result = await resp.json();
|
|
9656
|
+
totalSessions++;
|
|
9657
|
+
totalMessages += result.ingested ?? messages.length;
|
|
9658
|
+
}
|
|
9659
|
+
} catch {
|
|
9660
|
+
}
|
|
9661
|
+
if ((i + 1) % 10 === 0 || i === convDirs.length - 1) {
|
|
9662
|
+
process.stdout.write(`\r Progress: ${i + 1}/${convDirs.length} sessions (${totalMessages} messages embedded) `);
|
|
9663
|
+
}
|
|
9664
|
+
try {
|
|
9665
|
+
const lc = readFileSync8(filePath, "utf-8").split("\n").filter(Boolean).length;
|
|
9666
|
+
writeFileSync7(join8(OFFSETS_DIR, convId), String(lc), "utf-8");
|
|
9667
|
+
} catch {
|
|
9668
|
+
}
|
|
9669
|
+
}
|
|
9670
|
+
if (totalSessions > 0) process.stdout.write("\n");
|
|
9671
|
+
return { sessions: totalSessions, messages: totalMessages };
|
|
9672
|
+
}
|
|
9555
9673
|
function parseTranscriptFile(filePath) {
|
|
9556
9674
|
const content = readFileSync8(filePath, "utf-8");
|
|
9557
9675
|
const lines = content.split("\n").filter(Boolean);
|
|
@@ -11718,7 +11836,7 @@ var args = process.argv.slice(2);
|
|
|
11718
11836
|
var cmd = args[0] || "";
|
|
11719
11837
|
var subArgs = args.slice(1);
|
|
11720
11838
|
function printVersion() {
|
|
11721
|
-
console.log("1.6.
|
|
11839
|
+
console.log("1.6.49");
|
|
11722
11840
|
}
|
|
11723
11841
|
function printHelp2() {
|
|
11724
11842
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|