@synkro-sh/cli 1.4.47 → 1.4.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bootstrap.js CHANGED
@@ -4465,9 +4465,14 @@ function findTask(channel = CHANNEL_PRIMARY) {
4465
4465
  function startTask(opts = {}) {
4466
4466
  const ch = opts.channel ?? CHANNEL_PRIMARY;
4467
4467
  const cwd = opts.cwd ?? ch.sessionDir;
4468
- const existing = findTask(ch);
4469
- if (existing) {
4468
+ let existing = findTask(ch);
4469
+ while (existing) {
4470
+ if (existing.status === "Running") {
4471
+ spawnSync2("tmux", ["kill-session", "-t", ch.tmuxSession], { encoding: "utf-8" });
4472
+ spawnSync2("pueue", ["kill", String(existing.id)], { encoding: "utf-8" });
4473
+ }
4470
4474
  spawnSync2("pueue", ["remove", String(existing.id)], { encoding: "utf-8" });
4475
+ existing = findTask(ch);
4471
4476
  }
4472
4477
  const runScript = join8(cwd, "run-claude.sh");
4473
4478
  const args2 = [
@@ -4895,7 +4900,7 @@ __export(install_exports, {
4895
4900
  import { existsSync as existsSync11, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, chmodSync as chmodSync2, readFileSync as readFileSync10, readdirSync } from "fs";
4896
4901
  import { homedir as homedir10 } from "os";
4897
4902
  import { join as join11 } from "path";
4898
- import { execSync as execSync5 } from "child_process";
4903
+ import { execSync as execSync5, spawnSync as spawnSync3 } from "child_process";
4899
4904
  import { createInterface as createInterface3 } from "readline";
4900
4905
  function sanitizeGatewayCandidate(raw) {
4901
4906
  if (!raw) return void 0;
@@ -4939,7 +4944,6 @@ function ensureSynkroDir() {
4939
4944
  function writeHookScripts() {
4940
4945
  const bashScriptPath = join11(HOOKS_DIR, "cc-bash-judge.ts");
4941
4946
  const bashFollowupScriptPath = join11(HOOKS_DIR, "cc-bash-followup.ts");
4942
- const editCaptureScriptPath = join11(HOOKS_DIR, "cc-edit-capture.sh");
4943
4947
  const editPrecheckScriptPath = join11(HOOKS_DIR, "cc-edit-precheck.ts");
4944
4948
  const cwePrecheckScriptPath = join11(HOOKS_DIR, "cc-cwe-precheck.ts");
4945
4949
  const cvePrecheckScriptPath = join11(HOOKS_DIR, "cc-cve-precheck.ts");
@@ -4956,7 +4960,6 @@ function writeHookScripts() {
4956
4960
  const cursorBashFollowupPath = join11(HOOKS_DIR, "cursor-bash-followup.sh");
4957
4961
  writeFileSync7(bashScriptPath, BASH_JUDGE_TS, "utf-8");
4958
4962
  writeFileSync7(bashFollowupScriptPath, BASH_FOLLOWUP_TS, "utf-8");
4959
- writeFileSync7(editCaptureScriptPath, "", "utf-8");
4960
4963
  writeFileSync7(editPrecheckScriptPath, EDIT_PRECHECK_TS, "utf-8");
4961
4964
  writeFileSync7(cwePrecheckScriptPath, CWE_PRECHECK_TS, "utf-8");
4962
4965
  writeFileSync7(cvePrecheckScriptPath, CVE_PRECHECK_TS, "utf-8");
@@ -4973,7 +4976,6 @@ function writeHookScripts() {
4973
4976
  writeFileSync7(cursorBashFollowupPath, CURSOR_BASH_FOLLOWUP_SCRIPT, "utf-8");
4974
4977
  chmodSync2(bashScriptPath, 493);
4975
4978
  chmodSync2(bashFollowupScriptPath, 493);
4976
- chmodSync2(editCaptureScriptPath, 493);
4977
4979
  chmodSync2(editPrecheckScriptPath, 493);
4978
4980
  chmodSync2(cwePrecheckScriptPath, 493);
4979
4981
  chmodSync2(cvePrecheckScriptPath, 493);
@@ -4991,7 +4993,6 @@ function writeHookScripts() {
4991
4993
  return {
4992
4994
  bashScript: bashScriptPath,
4993
4995
  bashFollowupScript: bashFollowupScriptPath,
4994
- editCaptureScript: editCaptureScriptPath,
4995
4996
  editPrecheckScript: editPrecheckScriptPath,
4996
4997
  cwePrecheckScript: cwePrecheckScriptPath,
4997
4998
  cvePrecheckScript: cvePrecheckScriptPath,
@@ -5035,7 +5036,7 @@ function writeConfigEnv(opts) {
5035
5036
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
5036
5037
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
5037
5038
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
5038
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.47")}`
5039
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.48")}`
5039
5040
  ];
5040
5041
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
5041
5042
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -5160,13 +5161,14 @@ function assertGatewayAllowed(gatewayUrl) {
5160
5161
  }
5161
5162
  function isAlreadyInstalled() {
5162
5163
  const requiredScripts = [
5163
- join11(HOOKS_DIR, "cc-bash-judge.sh"),
5164
- join11(HOOKS_DIR, "cc-bash-followup.sh"),
5165
- join11(HOOKS_DIR, "cc-edit-precheck.sh"),
5166
- join11(HOOKS_DIR, "cc-edit-capture.sh"),
5167
- join11(HOOKS_DIR, "cc-plan-judge.sh"),
5168
- join11(HOOKS_DIR, "cc-stop-summary.sh"),
5169
- join11(HOOKS_DIR, "cc-session-start.sh")
5164
+ join11(HOOKS_DIR, "cc-bash-judge.ts"),
5165
+ join11(HOOKS_DIR, "cc-bash-followup.ts"),
5166
+ join11(HOOKS_DIR, "cc-edit-precheck.ts"),
5167
+ join11(HOOKS_DIR, "cc-cwe-precheck.ts"),
5168
+ join11(HOOKS_DIR, "cc-cve-precheck.ts"),
5169
+ join11(HOOKS_DIR, "cc-plan-judge.ts"),
5170
+ join11(HOOKS_DIR, "cc-stop-summary.ts"),
5171
+ join11(HOOKS_DIR, "cc-session-start.ts")
5170
5172
  ];
5171
5173
  if (!requiredScripts.every((p) => existsSync11(p))) return false;
5172
5174
  if (!existsSync11(CONFIG_PATH3)) return false;
@@ -5186,6 +5188,35 @@ function isAlreadyInstalled() {
5186
5188
  }
5187
5189
  return true;
5188
5190
  }
5191
+ function printChannelDiagnostics() {
5192
+ try {
5193
+ const tmuxCheck = spawnSync3("tmux", ["has-session", "-t", "synkro-local-cc"], { encoding: "utf-8" });
5194
+ console.warn(` tmux session: ${tmuxCheck.status === 0 ? "running" : "not running"}`);
5195
+ const pueueTask = findTask();
5196
+ console.warn(` pueue task: ${pueueTask ? `id=${pueueTask.id} status=${pueueTask.status}` : "not found"}`);
5197
+ const bunCheck = spawnSync3("bun", ["--version"], { encoding: "utf-8" });
5198
+ console.warn(` bun: ${bunCheck.status === 0 ? bunCheck.stdout.trim() : "not found"}`);
5199
+ const claudeCheck = spawnSync3("claude", ["--version"], { encoding: "utf-8" });
5200
+ console.warn(` claude: ${claudeCheck.status === 0 ? claudeCheck.stdout.trim().split("\n")[0] : "not found"}`);
5201
+ if (pueueTask) {
5202
+ const logs = tailLogs(15);
5203
+ if (logs && logs !== "(no output)") {
5204
+ console.warn(` pueue logs (last 15 lines):`);
5205
+ for (const line of logs.split("\n").slice(0, 15)) {
5206
+ console.warn(` ${line}`);
5207
+ }
5208
+ }
5209
+ }
5210
+ const logPath = join11(homedir10(), ".synkro", "cc_sessions", "run-claude.log");
5211
+ if (existsSync11(logPath)) {
5212
+ const logContent = readFileSync10(logPath, "utf-8").trim().split("\n").slice(-10);
5213
+ console.warn(` run-claude.log:`);
5214
+ for (const line of logContent) console.warn(` ${line}`);
5215
+ }
5216
+ } catch {
5217
+ }
5218
+ console.warn(` Run \`synkro local-cc status\` and \`synkro local-cc logs --tmux\` to debug.`);
5219
+ }
5189
5220
  async function installCommand(opts = {}) {
5190
5221
  const gatewayUrl = opts.gatewayUrl || sanitizeGatewayCandidate(process.env.SYNKRO_GATEWAY_URL) || "https://api.synkro.sh";
5191
5222
  try {
@@ -5223,17 +5254,18 @@ async function installCommand(opts = {}) {
5223
5254
  assertPueueInstalled();
5224
5255
  assertTmuxInstalled();
5225
5256
  installLocalCC();
5226
- const t = ensureRunning();
5227
- console.log(` pueue task: id=${t.id} status=${t.status}`);
5228
- console.log(" Waiting for channel...");
5229
- const ready = await waitForChannelReady(CHANNEL_PORT, 6e4, CHANNEL_HOST);
5230
- if (ready) console.log(` channel ready at ${CHANNEL_HOST}:${CHANNEL_PORT}`);
5231
- else console.warn(" \u26A0 channel did not come up within 60s \u2014 check `synkro local-cc logs`");
5257
+ const t1 = ensureRunning();
5232
5258
  const t2 = ensureRunning({ channel: CHANNEL_SECONDARY });
5233
- console.log(` CWE channel: id=${t2.id} status=${t2.status}`);
5234
- const ready2 = await waitForChannelReady(CHANNEL_2_PORT, 6e4, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession);
5235
- if (ready2) console.log(` CWE channel ready at ${CHANNEL_HOST}:${CHANNEL_2_PORT}`);
5236
- else console.warn(" \u26A0 CWE channel did not come up within 60s");
5259
+ console.log(` channel 1: pueue id=${t1.id} channel 2: pueue id=${t2.id}`);
5260
+ console.log(" Waiting for both channels...");
5261
+ const [ready1, ready2] = await Promise.all([
5262
+ waitForChannelReady(CHANNEL_PORT, 6e4, CHANNEL_HOST),
5263
+ waitForChannelReady(CHANNEL_2_PORT, 6e4, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession)
5264
+ ]);
5265
+ if (ready1) console.log(` channel 1 ready at ${CHANNEL_HOST}:${CHANNEL_PORT}`);
5266
+ else console.warn(" \u26A0 channel 1 did not come up within 60s \u2014 check `synkro local-cc logs`");
5267
+ if (ready2) console.log(` channel 2 ready at ${CHANNEL_HOST}:${CHANNEL_2_PORT}`);
5268
+ else console.warn(" \u26A0 channel 2 did not come up within 60s");
5237
5269
  updateLocalInferenceFlag(true);
5238
5270
  } catch (err) {
5239
5271
  console.warn(` \u26A0 Local-CC setup skipped: ${err.message}`);
@@ -5306,7 +5338,6 @@ async function installCommand(opts = {}) {
5306
5338
  console.log("Wrote hook scripts:");
5307
5339
  console.log(` ${scripts.bashScript}`);
5308
5340
  console.log(` ${scripts.bashFollowupScript}`);
5309
- console.log(` ${scripts.editCaptureScript}`);
5310
5341
  console.log(` ${scripts.editPrecheckScript}`);
5311
5342
  console.log(` ${scripts.planJudgeScript}`);
5312
5343
  console.log(` ${scripts.stopSummaryScript}`);
@@ -5341,7 +5372,6 @@ async function installCommand(opts = {}) {
5341
5372
  installCCHooks(agent.settingsPath, {
5342
5373
  bashJudgeScriptPath: scripts.bashScript,
5343
5374
  bashFollowupScriptPath: scripts.bashFollowupScript,
5344
- editCaptureScriptPath: scripts.editCaptureScript,
5345
5375
  editPrecheckScriptPath: scripts.editPrecheckScript,
5346
5376
  cwePrecheckScriptPath: scripts.cwePrecheckScript,
5347
5377
  cvePrecheckScriptPath: scripts.cvePrecheckScript,
@@ -5443,72 +5473,62 @@ async function installCommand(opts = {}) {
5443
5473
  console.warn(` \u26A0 Some dependencies missing \u2014 \`synkro local-cc enable\` may not work until they're installed.`);
5444
5474
  }
5445
5475
  console.log();
5446
- if (profile.localInference) {
5476
+ const priorLocalFlag = (() => {
5477
+ try {
5478
+ const content = readFileSync10(CONFIG_PATH3, "utf-8");
5479
+ return content.includes("SYNKRO_LOCAL_INFERENCE='yes'");
5480
+ } catch {
5481
+ return false;
5482
+ }
5483
+ })();
5484
+ if (profile.localInference || priorLocalFlag && localCcDeps.length === 3) {
5447
5485
  try {
5448
5486
  const r = installLocalCC();
5449
5487
  console.log(`Installed local-CC channel plugin at ${r.pluginPath}`);
5450
- const t = ensureRunning();
5451
- console.log(`Local-CC pueue task: id=${t.id} status=${t.status}`);
5452
- console.log("Waiting for channel (up to 60s)...");
5453
- const ready = await waitForChannelReady(CHANNEL_PORT, 6e4, CHANNEL_HOST);
5454
- if (ready) {
5455
- console.log(` channel ready at ${CHANNEL_HOST}:${CHANNEL_PORT}`);
5456
- try {
5457
- console.log(" warming up inference...");
5458
- await submitToChannel("grade-bash", "Proposed command: echo hello\nUser intent: warmup\nRecent user messages: []\nRecent actions: []\nOrg rules: []\n", { timeoutMs: 3e4 });
5459
- console.log(" inference warm\n");
5460
- } catch {
5461
- console.log(" warmup skipped (non-fatal)\n");
5462
- }
5488
+ const t1 = ensureRunning();
5489
+ const t2 = ensureRunning({ channel: CHANNEL_SECONDARY });
5490
+ console.log(`Channel 1 (org rules): pueue id=${t1.id} status=${t1.status}`);
5491
+ console.log(`Channel 2 (CWE scan): pueue id=${t2.id} status=${t2.status}`);
5492
+ console.log("Waiting for both channels (up to 60s)...");
5493
+ const [ready1, ready2] = await Promise.all([
5494
+ waitForChannelReady(CHANNEL_PORT, 6e4, CHANNEL_HOST),
5495
+ waitForChannelReady(CHANNEL_2_PORT, 6e4, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession)
5496
+ ]);
5497
+ if (ready1) {
5498
+ console.log(` channel 1 ready at ${CHANNEL_HOST}:${CHANNEL_PORT}`);
5463
5499
  } else {
5464
- console.warn(` \u26A0 Channel did not come up within 60s.`);
5465
- try {
5466
- const { spawnSync: sp } = await import("child_process");
5467
- const tmuxCheck = sp("tmux", ["has-session", "-t", "synkro-local-cc"], { encoding: "utf-8" });
5468
- console.warn(` tmux session: ${tmuxCheck.status === 0 ? "running" : "not running"}`);
5469
- const pueueTask = findTask();
5470
- console.warn(` pueue task: ${pueueTask ? `id=${pueueTask.id} status=${pueueTask.status}` : "not found"}`);
5471
- const bunCheck = sp("bun", ["--version"], { encoding: "utf-8" });
5472
- console.warn(` bun: ${bunCheck.status === 0 ? bunCheck.stdout.trim() : "not found"}`);
5473
- const claudeCheck = sp("claude", ["--version"], { encoding: "utf-8" });
5474
- console.warn(` claude: ${claudeCheck.status === 0 ? claudeCheck.stdout.trim().split("\n")[0] : "not found"}`);
5475
- if (pueueTask) {
5476
- const logs = tailLogs(15);
5477
- if (logs && logs !== "(no output)") {
5478
- console.warn(` pueue logs (last 15 lines):`);
5479
- for (const line of logs.split("\n").slice(0, 15)) {
5480
- console.warn(` ${line}`);
5481
- }
5482
- }
5483
- }
5484
- const logPath = join11(homedir10(), ".synkro", "cc_sessions", "run-claude.log");
5485
- if (existsSync11(logPath)) {
5486
- const logContent = readFileSync10(logPath, "utf-8").trim().split("\n").slice(-10);
5487
- console.warn(` run-claude.log:`);
5488
- for (const line of logContent) console.warn(` ${line}`);
5489
- }
5490
- } catch {
5491
- }
5492
- console.warn(` Run \`synkro local-cc status\` and \`synkro local-cc logs --tmux\` to debug.
5493
- `);
5500
+ console.warn(` \u26A0 Channel 1 did not come up within 60s.`);
5501
+ printChannelDiagnostics();
5494
5502
  }
5495
- const t2 = ensureRunning({ channel: CHANNEL_SECONDARY });
5496
- console.log(`Local-CC CWE channel: id=${t2.id} status=${t2.status}`);
5497
- console.log("Waiting for CWE channel (up to 60s)...");
5498
- const ready2 = await waitForChannelReady(CHANNEL_2_PORT, 6e4, CHANNEL_HOST, CHANNEL_SECONDARY.tmuxSession);
5499
5503
  if (ready2) {
5500
- console.log(` CWE channel ready at ${CHANNEL_HOST}:${CHANNEL_2_PORT}`);
5501
- try {
5502
- console.log(" warming up CWE inference...");
5503
- await submitToChannel("grade-cwe", 'File: /tmp/warmup.ts\nContent (first 4000 chars):\nconsole.log("hello");\n\nCWE rules to check against:\n[]\n', { timeoutMs: 3e4, port: CHANNEL_2_PORT });
5504
- console.log(" CWE inference warm\n");
5505
- } catch {
5506
- console.log(" CWE warmup skipped (non-fatal)\n");
5507
- }
5504
+ console.log(` channel 2 ready at ${CHANNEL_HOST}:${CHANNEL_2_PORT}`);
5508
5505
  } else {
5509
- console.warn(` \u26A0 CWE channel did not come up within 60s.`);
5510
- console.warn(` Run \`synkro local-cc status\` to debug.
5511
- `);
5506
+ console.warn(` \u26A0 Channel 2 did not come up within 60s.`);
5507
+ console.warn(` Run \`synkro local-cc status\` to debug.`);
5508
+ }
5509
+ if (ready1 || ready2) {
5510
+ console.log("Warming up inference...");
5511
+ const warmups = [];
5512
+ if (ready1) {
5513
+ warmups.push(
5514
+ submitToChannel("grade-bash", "Proposed command: echo hello\nUser intent: warmup\nRecent user messages: []\nRecent actions: []\nOrg rules: []\n", { timeoutMs: 3e4 }).then(() => {
5515
+ console.log(" channel 1 warm");
5516
+ }).catch(() => {
5517
+ console.log(" channel 1 warmup skipped (non-fatal)");
5518
+ })
5519
+ );
5520
+ }
5521
+ if (ready2) {
5522
+ warmups.push(
5523
+ submitToChannel("grade-cwe", 'File: /tmp/warmup.ts\nContent (first 4000 chars):\nconsole.log("hello");\n\nCWE rules to check against:\n[]\n', { timeoutMs: 3e4, port: CHANNEL_2_PORT }).then(() => {
5524
+ console.log(" channel 2 warm");
5525
+ }).catch(() => {
5526
+ console.log(" channel 2 warmup skipped (non-fatal)");
5527
+ })
5528
+ );
5529
+ }
5530
+ await Promise.all(warmups);
5531
+ console.log();
5512
5532
  }
5513
5533
  } catch (err) {
5514
5534
  console.warn(` \u26A0 Local-CC setup failed: ${err.message}
@@ -7004,7 +7024,7 @@ var localCc_exports = {};
7004
7024
  __export(localCc_exports, {
7005
7025
  localCcCommand: () => localCcCommand
7006
7026
  });
7007
- import { spawnSync as spawnSync3 } from "child_process";
7027
+ import { spawnSync as spawnSync4 } from "child_process";
7008
7028
  import { homedir as homedir14 } from "os";
7009
7029
  import { join as join16 } from "path";
7010
7030
  import { existsSync as existsSync16, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
@@ -7148,7 +7168,7 @@ async function cmdStatus() {
7148
7168
  }
7149
7169
  const ch1Up = await isChannelAvailable();
7150
7170
  console.log(`Channel 1 ${CHANNEL_HOST}:${CHANNEL_PORT}: ${ch1Up ? "reachable" : "unreachable"}`);
7151
- const tmux1 = spawnSync3("tmux", ["has-session", "-t", TMUX_SESSION_NAME], { encoding: "utf-8" });
7171
+ const tmux1 = spawnSync4("tmux", ["has-session", "-t", TMUX_SESSION_NAME], { encoding: "utf-8" });
7152
7172
  console.log(`tmux '${TMUX_SESSION_NAME}': ${tmux1.status === 0 ? "live" : "absent"}`);
7153
7173
  const t2 = findTask(CHANNEL_SECONDARY);
7154
7174
  if (!t2) {
@@ -7158,7 +7178,7 @@ async function cmdStatus() {
7158
7178
  }
7159
7179
  const ch2Up = await isChannelAvailable(CHANNEL_2_PORT);
7160
7180
  console.log(`Channel 2 ${CHANNEL_HOST}:${CHANNEL_2_PORT}: ${ch2Up ? "reachable" : "unreachable"}`);
7161
- const tmux2 = spawnSync3("tmux", ["has-session", "-t", TMUX_SESSION_NAME_2], { encoding: "utf-8" });
7181
+ const tmux2 = spawnSync4("tmux", ["has-session", "-t", TMUX_SESSION_NAME_2], { encoding: "utf-8" });
7162
7182
  console.log(`tmux '${TMUX_SESSION_NAME_2}': ${tmux2.status === 0 ? "live" : "absent"}`);
7163
7183
  }
7164
7184
  async function cmdEnable() {
@@ -7350,7 +7370,7 @@ function cmdLogs(rest) {
7350
7370
  function cmdAttach(rest) {
7351
7371
  assertTmuxInstalled();
7352
7372
  const readonly = rest.some((a) => a === "--readonly" || a === "-r");
7353
- const has = spawnSync3("tmux", ["has-session", "-t", TMUX_SESSION_NAME], { encoding: "utf-8" });
7373
+ const has = spawnSync4("tmux", ["has-session", "-t", TMUX_SESSION_NAME], { encoding: "utf-8" });
7354
7374
  if (has.status !== 0) {
7355
7375
  console.error(`No tmux session '${TMUX_SESSION_NAME}' running. Start it with: synkro local-cc start`);
7356
7376
  process.exit(1);
@@ -7363,7 +7383,7 @@ function cmdAttach(rest) {
7363
7383
  console.log("Detach with Ctrl-B then D. (Do not press Ctrl-C \u2014 that would interrupt claude.)");
7364
7384
  console.log();
7365
7385
  const args2 = readonly ? ["attach-session", "-r", "-t", TMUX_SESSION_NAME] : ["attach-session", "-t", TMUX_SESSION_NAME];
7366
- const r = spawnSync3("tmux", args2, { stdio: "inherit" });
7386
+ const r = spawnSync4("tmux", args2, { stdio: "inherit" });
7367
7387
  process.exit(r.status ?? 0);
7368
7388
  }
7369
7389
  async function cmdTest() {