@synkro-sh/cli 1.4.9 → 1.4.11

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
@@ -132,7 +132,7 @@ function installCCHooks(settingsPath, config) {
132
132
  {
133
133
  type: "command",
134
134
  command: config.bashJudgeScriptPath,
135
- timeout: 15
135
+ timeout: 30
136
136
  }
137
137
  ],
138
138
  [SYNKRO_MARKER]: true
@@ -709,8 +709,17 @@ ensure_fresh_jwt() {
709
709
 
710
710
  ensure_fresh_jwt
711
711
 
712
- # Resolve tier + capture_depth live from /cli/me every call. Default to local_only (fail-safe).
713
- ME_RESP=$(curl -sS "\${GATEWAY_URL}/api/v1/cli/me" -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null || echo "")
712
+ # Parallel fetch: /cli/me + rules in background, wait for both.
713
+ _ME_TMP=$(mktemp -t synkro-me.XXXXXX)
714
+ _RULES_TMP=$(mktemp -t synkro-rules.XXXXXX)
715
+ trap "rm -f \\"$_ME_TMP\\" \\"$_RULES_TMP\\"" EXIT
716
+ curl -sS "\${GATEWAY_URL}/api/v1/cli/me" -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null > "$_ME_TMP" &
717
+ _ME_PID=$!
718
+ curl -sS "\${GATEWAY_URL}/api/v1/cli/pr-rules" -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null > "$_RULES_TMP" &
719
+ _RULES_PID=$!
720
+ wait $_ME_PID 2>/dev/null; wait $_RULES_PID 2>/dev/null
721
+
722
+ ME_RESP=$(cat "$_ME_TMP" 2>/dev/null || echo "")
714
723
  SYNKRO_INFERENCE_TIER=$(echo "$ME_RESP" | jq -r '.tier // empty' 2>/dev/null)
715
724
  SYNKRO_CAPTURE_DEPTH=$(echo "$ME_RESP" | jq -r '.capture_depth // empty' 2>/dev/null)
716
725
  SYNKRO_INFERENCE_TIER="\${SYNKRO_INFERENCE_TIER:-fast}"
@@ -721,32 +730,14 @@ if command -v claude >/dev/null 2>&1; then
721
730
  USE_LOCAL=true
722
731
  fi
723
732
 
724
- # Prefer local grading whenever it's available \u2014 the local-cc channel beats
725
- # the cloud fast tier on latency, and it's available regardless of the
726
- # server-assigned tier. Fall through to the cloud curl only when neither
727
- # the channel nor a usable \`claude\` CLI is reachable.
728
733
  if synkro_channel_up || { [ "$SYNKRO_INFERENCE_TIER" = "free" ] && command -v claude >/dev/null 2>&1; }; then
729
- # \u2500\u2500\u2500 LOCAL PATH: channel-grader first, then \`claude --print\` cold fallback. \u2500\u2500\u2500
730
-
731
734
  RULES_CACHE="$HOME/.synkro/.rules-cache-bash"
732
- ORG_RULES="[]"
733
- if [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ]; then
734
- ORG_RULES=$(curl -sS "\${GATEWAY_URL}/api/v1/cli/pr-rules" \\
735
- -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null \\
736
- | jq -c '[.rules[]? | select(.hook_stage == "pre" or .hook_stage == "both" or .hook_stage == null) | {rule_id, text, severity, category}]' 2>/dev/null || echo "[]")
737
- if [ -n "$ORG_RULES" ] && [ "$ORG_RULES" != "null" ] && [ "$ORG_RULES" != "[]" ]; then
738
- printf '%s' "$ORG_RULES" > "$RULES_CACHE" 2>/dev/null || true
739
- elif [ -f "$RULES_CACHE" ]; then
740
- ORG_RULES=$(cat "$RULES_CACHE" 2>/dev/null || echo "[]")
741
- fi
742
- else
743
- ORG_RULES=$(printf '%s' "$COMMAND" | head -c 4000 \\
744
- | jq -Rs '{content: .}' \\
745
- | curl -sS "\${GATEWAY_URL}/api/v1/cli/pr-rules?top_k=15" \\
746
- -X POST -H "Content-Type: application/json" \\
747
- -H "Authorization: Bearer $JWT" \\
748
- -d @- --max-time 2 2>/dev/null \\
749
- | jq -c '[.rules[]? | select(.hook_stage == "pre" or .hook_stage == "both" or .hook_stage == null) | {rule_id, text, severity, category}]' 2>/dev/null || echo "[]")
735
+ RULES_JQ='[.rules[]? | select(.hook_stage == "pre" or .hook_stage == "both" or .hook_stage == null) | {rule_id, text, severity, category}]'
736
+ ORG_RULES=$(jq -c "$RULES_JQ" "$_RULES_TMP" 2>/dev/null || echo "[]")
737
+ if [ -n "$ORG_RULES" ] && [ "$ORG_RULES" != "null" ] && [ "$ORG_RULES" != "[]" ]; then
738
+ printf '%s' "$ORG_RULES" > "$RULES_CACHE" 2>/dev/null || true
739
+ elif [ -f "$RULES_CACHE" ]; then
740
+ ORG_RULES=$(cat "$RULES_CACHE" 2>/dev/null || echo "[]")
750
741
  fi
751
742
  if [ -z "$ORG_RULES" ] || [ "$ORG_RULES" = "null" ]; then ORG_RULES="[]"; fi
752
743
 
@@ -831,7 +822,9 @@ if [ "$VERDICT" != "__LOCAL_XML_PARSED__" ]; then
831
822
  ALTERNATIVE=$(echo "$VERDICT" | jq -r '.alternative // ""' 2>/dev/null)
832
823
  CATEGORY=$(echo "$VERDICT" | jq -r '.category // "destructive_command"' 2>/dev/null)
833
824
  RISK_LEVEL=$(echo "$VERDICT" | jq -r '.risk_level // empty' 2>/dev/null)
825
+ VIOLATED_RULES=$(echo "$VERDICT" | jq -c '.violated_rules // []' 2>/dev/null)
834
826
  fi
827
+ VIOLATED_RULES="\${VIOLATED_RULES:-[]}"
835
828
  # Defaults if any var is empty (defensive \u2014 XML primer should always emit them).
836
829
  SEVERITY="\${SEVERITY:-audit}"
837
830
  VERDICT_KIND="\${VERDICT_KIND:-warn}"
@@ -951,6 +944,7 @@ if [ "$USE_LOCAL" = "true" ] && [ -n "$VERDICT_KIND" ]; then
951
944
  --arg reasoning "$REASONING" \\
952
945
  --arg alternative "$ALTERNATIVE" \\
953
946
  --argjson rules_checked "\${ORG_RULES:-[]}" \\
947
+ --argjson violated_rules "\${VIOLATED_RULES:-[]}" \\
954
948
  --argjson recent_user_messages "\${RECENT_USER_MESSAGES:-[]}" \\
955
949
  '{
956
950
  event_id: $event_id,
@@ -963,7 +957,8 @@ if [ "$USE_LOCAL" = "true" ] && [ -n "$VERDICT_KIND" ]; then
963
957
  model: $model,
964
958
  tool_name: $tool_name,
965
959
  capture_depth: $capture_depth,
966
- rules_checked: $rules_checked
960
+ rules_checked: $rules_checked,
961
+ violated_rules: $violated_rules
967
962
  } + (if $repo != "" then {repo: $repo} else {} end)
968
963
  + (if $session_id != "" then {session_id: $session_id} else {} end)
969
964
  + (if $tool_use_id != "" then {tool_use_id: $tool_use_id} else {} end)
@@ -1217,34 +1212,30 @@ ensure_fresh_jwt() {
1217
1212
  ensure_fresh_jwt
1218
1213
 
1219
1214
 
1220
- # Resolve tier + capture_depth live from /cli/me every call. Default to local_only (fail-safe).
1221
- ME_RESP=$(curl -sS "\${GATEWAY_URL}/api/v1/cli/me" -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null || echo "")
1215
+ # Parallel fetch: /cli/me + rules in background, wait for both.
1216
+ _ME_TMP=$(mktemp -t synkro-me.XXXXXX)
1217
+ _RULES_TMP=$(mktemp -t synkro-rules.XXXXXX)
1218
+ trap "rm -f \\"$_ME_TMP\\" \\"$_RULES_TMP\\"" EXIT
1219
+ curl -sS "\${GATEWAY_URL}/api/v1/cli/me" -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null > "$_ME_TMP" &
1220
+ _ME_PID=$!
1221
+ curl -sS "\${GATEWAY_URL}/api/v1/cli/pr-rules" -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null > "$_RULES_TMP" &
1222
+ _RULES_PID=$!
1223
+ wait $_ME_PID 2>/dev/null; wait $_RULES_PID 2>/dev/null
1224
+
1225
+ ME_RESP=$(cat "$_ME_TMP" 2>/dev/null || echo "")
1222
1226
  SYNKRO_INFERENCE_TIER=$(echo "$ME_RESP" | jq -r '.tier // empty' 2>/dev/null)
1223
1227
  SYNKRO_CAPTURE_DEPTH=$(echo "$ME_RESP" | jq -r '.capture_depth // empty' 2>/dev/null)
1224
1228
  SYNKRO_INFERENCE_TIER="\${SYNKRO_INFERENCE_TIER:-fast}"
1225
1229
  SYNKRO_CAPTURE_DEPTH="\${SYNKRO_CAPTURE_DEPTH:-local_only}"
1226
1230
 
1227
1231
  if synkro_channel_up || { [ "$SYNKRO_INFERENCE_TIER" = "free" ] && command -v claude >/dev/null 2>&1; }; then
1228
- # \u2500\u2500\u2500 LOCAL PATH: channel-grader first, then \`claude --print\` cold fallback. \u2500\u2500\u2500
1229
1232
  RULES_CACHE="$HOME/.synkro/.rules-cache-edit"
1230
- ORG_RULES="[]"
1231
- if [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ]; then
1232
- ORG_RULES=$(curl -sS "\${GATEWAY_URL}/api/v1/cli/pr-rules" \\
1233
- -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null \\
1234
- | jq -c '[.rules[]? | select(.hook_stage == "pre" or .hook_stage == "both" or .hook_stage == null) | {rule_id, text, severity, category, mode}]' 2>/dev/null || echo "[]")
1235
- if [ -n "$ORG_RULES" ] && [ "$ORG_RULES" != "null" ] && [ "$ORG_RULES" != "[]" ]; then
1236
- printf '%s' "$ORG_RULES" > "$RULES_CACHE" 2>/dev/null || true
1237
- elif [ -f "$RULES_CACHE" ]; then
1238
- ORG_RULES=$(cat "$RULES_CACHE" 2>/dev/null || echo "[]")
1239
- fi
1240
- else
1241
- ORG_RULES=$(printf '%s' "$PROPOSED" | head -c 8000 \\
1242
- | jq -Rs '{content: .}' \\
1243
- | curl -sS "\${GATEWAY_URL}/api/v1/cli/pr-rules?top_k=20" \\
1244
- -X POST -H "Content-Type: application/json" \\
1245
- -H "Authorization: Bearer $JWT" \\
1246
- -d @- --max-time 2 2>/dev/null \\
1247
- | jq -c '[.rules[]? | select(.hook_stage == "pre" or .hook_stage == "both" or .hook_stage == null) | {rule_id, text, severity, category, mode}]' 2>/dev/null || echo "[]")
1233
+ RULES_JQ='[.rules[]? | select(.hook_stage == "pre" or .hook_stage == "both" or .hook_stage == null) | {rule_id, text, severity, category, mode}]'
1234
+ ORG_RULES=$(jq -c "$RULES_JQ" "$_RULES_TMP" 2>/dev/null || echo "[]")
1235
+ if [ -n "$ORG_RULES" ] && [ "$ORG_RULES" != "null" ] && [ "$ORG_RULES" != "[]" ]; then
1236
+ printf '%s' "$ORG_RULES" > "$RULES_CACHE" 2>/dev/null || true
1237
+ elif [ -f "$RULES_CACHE" ]; then
1238
+ ORG_RULES=$(cat "$RULES_CACHE" 2>/dev/null || echo "[]")
1248
1239
  fi
1249
1240
  if [ -z "$ORG_RULES" ] || [ "$ORG_RULES" = "null" ]; then ORG_RULES="[]"; fi
1250
1241
 
@@ -1661,30 +1652,25 @@ if [ -n "$SESSION_ID" ] && [ -n "$TOOL_USE_ID" ]; then
1661
1652
  ) &
1662
1653
  fi
1663
1654
 
1664
- # Resolve tier + capture_depth live from /cli/me every call. Default to local_only (fail-safe).
1665
- ME_RESP=$(curl -sS "\${GATEWAY_URL}/api/v1/cli/me" -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null || echo "")
1655
+ # Parallel fetch: /cli/me + rules in background, wait for both.
1656
+ _ME_TMP=$(mktemp -t synkro-me.XXXXXX)
1657
+ _RULES_TMP=$(mktemp -t synkro-rules.XXXXXX)
1658
+ trap "rm -f \\"$_ME_TMP\\" \\"$_RULES_TMP\\"" EXIT
1659
+ curl -sS "\${GATEWAY_URL}/api/v1/cli/me" -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null > "$_ME_TMP" &
1660
+ _ME_PID=$!
1661
+ curl -sS "\${GATEWAY_URL}/api/v1/cli/pr-rules" -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null > "$_RULES_TMP" &
1662
+ _RULES_PID=$!
1663
+ wait $_ME_PID 2>/dev/null; wait $_RULES_PID 2>/dev/null
1664
+
1665
+ ME_RESP=$(cat "$_ME_TMP" 2>/dev/null || echo "")
1666
1666
  SYNKRO_INFERENCE_TIER=$(echo "$ME_RESP" | jq -r '.tier // empty' 2>/dev/null)
1667
1667
  SYNKRO_CAPTURE_DEPTH=$(echo "$ME_RESP" | jq -r '.capture_depth // empty' 2>/dev/null)
1668
1668
  SYNKRO_INFERENCE_TIER="\${SYNKRO_INFERENCE_TIER:-fast}"
1669
1669
  SYNKRO_CAPTURE_DEPTH="\${SYNKRO_CAPTURE_DEPTH:-local_only}"
1670
1670
 
1671
1671
  if synkro_channel_up || { [ "$SYNKRO_INFERENCE_TIER" = "free" ] && command -v claude >/dev/null 2>&1; }; then
1672
- # \u2500\u2500\u2500 LOCAL PATH: channel-grader first, then \`claude --print\` cold fallback. \u2500\u2500\u2500
1673
-
1674
1672
  RULES_CACHE="$HOME/.synkro/.rules-cache-edit-capture"
1675
- RULES_RESP=""
1676
- if [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ]; then
1677
- RULES_RESP=$(curl -sS "\${GATEWAY_URL}/api/v1/cli/pr-rules" \\
1678
- -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null || echo "")
1679
- else
1680
- RULES_RESP=$(printf '%s' "$FILE_CONTENT" | head -c 8000 \\
1681
- | jq -Rs '{content: .}' \\
1682
- | curl -sS "\${GATEWAY_URL}/api/v1/cli/pr-rules?top_k=20" \\
1683
- -X POST -H "Content-Type: application/json" \\
1684
- -H "Authorization: Bearer $JWT" \\
1685
- -d @- --max-time 2 2>/dev/null || echo "")
1686
- fi
1687
- ORG_RULES=$(echo "$RULES_RESP" | jq -c '[.rules[]? | select(.hook_stage == "post" or .hook_stage == "both" or .hook_stage == null) | {rule_id, text, severity, category}]' 2>/dev/null || echo "[]")
1673
+ ORG_RULES=$(jq -c '[.rules[]? | select(.hook_stage == "post" or .hook_stage == "both" or .hook_stage == null) | {rule_id, text, severity, category}]' "$_RULES_TMP" 2>/dev/null || echo "[]")
1688
1674
  if [ -n "$ORG_RULES" ] && [ "$ORG_RULES" != "null" ] && [ "$ORG_RULES" != "[]" ]; then
1689
1675
  printf '%s' "$ORG_RULES" > "$RULES_CACHE" 2>/dev/null || true
1690
1676
  elif [ -f "$RULES_CACHE" ]; then
@@ -4432,31 +4418,38 @@ var init_pueue = __esm({
4432
4418
  });
4433
4419
 
4434
4420
  // cli/local-cc/prompts.ts
4435
- import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
4421
+ import { readFileSync as readFileSync9 } from "fs";
4436
4422
  import { homedir as homedir9 } from "os";
4437
4423
  import { join as join10 } from "path";
4438
- function loadCachedPrompts() {
4439
- if (_cached) return _cached;
4440
- if (!existsSync11(CACHE_PATH2)) {
4441
- throw new Error("Prompts cache not found. Run `synkro install` or `synkro update` first.");
4442
- }
4424
+ async function fetchPrimers() {
4425
+ let jwt2 = "";
4426
+ let gatewayUrl = "";
4443
4427
  try {
4444
- _cached = JSON.parse(readFileSync9(CACHE_PATH2, "utf-8"));
4445
- return _cached;
4428
+ const creds = JSON.parse(readFileSync9(CREDS_PATH, "utf-8"));
4429
+ jwt2 = creds.access_token || "";
4430
+ gatewayUrl = creds.gateway_url || "https://api.synkro.sh";
4446
4431
  } catch {
4447
- throw new Error("Prompts cache is corrupted. Run `synkro update` to refresh.");
4432
+ throw new Error("No credentials found. Run `synkro install` first.");
4448
4433
  }
4434
+ if (!jwt2) throw new Error("No access token. Run `synkro install` first.");
4435
+ const resp = await fetch(`${gatewayUrl}/api/v1/cli/judge-prompts`, {
4436
+ headers: { Authorization: `Bearer ${jwt2}` },
4437
+ signal: AbortSignal.timeout(5e3)
4438
+ });
4439
+ if (!resp.ok) throw new Error(`Failed to fetch prompts: ${resp.status}`);
4440
+ return resp.json();
4449
4441
  }
4450
- function getPrimer(role) {
4451
- const cache = loadCachedPrompts();
4452
- const primer = role === "grade-edit" ? cache.grader_primer_edit : cache.grader_primer_bash;
4442
+ async function getPrimer(role) {
4443
+ const prompts = await fetchPrimers();
4444
+ const primer = role === "grade-edit" ? prompts.grader_primer_edit : prompts.grader_primer_bash;
4453
4445
  if (!primer) {
4454
- throw new Error(`No cached primer for role "${role}". Run \`synkro update\` to refresh prompts.`);
4446
+ throw new Error(`No primer for role "${role}" returned from API.`);
4455
4447
  }
4456
4448
  return primer;
4457
4449
  }
4458
- function buildChannelContent(role, payload) {
4459
- return `${getPrimer(role)}
4450
+ async function buildChannelContent(role, payload) {
4451
+ const primer = await getPrimer(role);
4452
+ return `${primer}
4460
4453
 
4461
4454
  ${CHANNEL_REPLY_INSTRUCTIONS}
4462
4455
 
@@ -4465,12 +4458,11 @@ PAYLOAD (the input to evaluate):
4465
4458
 
4466
4459
  ${payload}`;
4467
4460
  }
4468
- var CACHE_PATH2, _cached, CHANNEL_REPLY_INSTRUCTIONS;
4461
+ var CREDS_PATH, CHANNEL_REPLY_INSTRUCTIONS;
4469
4462
  var init_prompts = __esm({
4470
4463
  "cli/local-cc/prompts.ts"() {
4471
4464
  "use strict";
4472
- CACHE_PATH2 = join10(homedir9(), ".synkro", "prompts", "judge-prompts.json");
4473
- _cached = null;
4465
+ CREDS_PATH = join10(homedir9(), ".synkro", "credentials.json");
4474
4466
  CHANNEL_REPLY_INSTRUCTIONS = `
4475
4467
  DELIVERY METHOD \u2014 MANDATORY, OVERRIDES ALL OTHER OUTPUT RULES:
4476
4468
  You are running inside a Synkro MCP channel. Do NOT output your verdict as text.
@@ -4482,7 +4474,7 @@ Any text output is silently discarded. Only the reply tool call is captured.`;
4482
4474
  });
4483
4475
 
4484
4476
  // cli/local-cc/turnLog.ts
4485
- import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync8, openSync as openSync2, readFileSync as readFileSync10, readSync, closeSync as closeSync2, statSync, watchFile, unwatchFile } from "fs";
4477
+ import { appendFileSync, existsSync as existsSync11, mkdirSync as mkdirSync8, openSync as openSync2, readFileSync as readFileSync10, readSync, closeSync as closeSync2, statSync, watchFile, unwatchFile } from "fs";
4486
4478
  import { dirname as dirname5, join as join11 } from "path";
4487
4479
  import { homedir as homedir10 } from "os";
4488
4480
  function truncate(s, max = PREVIEW_MAX) {
@@ -4520,7 +4512,7 @@ function appendTurn(args2) {
4520
4512
  }
4521
4513
  }
4522
4514
  function readRecentTurns(n = 20) {
4523
- if (!existsSync12(TURN_LOG_PATH)) return [];
4515
+ if (!existsSync11(TURN_LOG_PATH)) return [];
4524
4516
  try {
4525
4517
  const size = statSync(TURN_LOG_PATH).size;
4526
4518
  if (size === 0) return [];
@@ -4541,7 +4533,7 @@ function readRecentTurns(n = 20) {
4541
4533
  function followTurns(onEntry) {
4542
4534
  try {
4543
4535
  mkdirSync8(dirname5(TURN_LOG_PATH), { recursive: true });
4544
- if (!existsSync12(TURN_LOG_PATH)) {
4536
+ if (!existsSync11(TURN_LOG_PATH)) {
4545
4537
  appendFileSync(TURN_LOG_PATH, "", "utf-8");
4546
4538
  }
4547
4539
  } catch {
@@ -4612,7 +4604,7 @@ var init_turnLog = __esm({
4612
4604
  import { request as httpRequest } from "http";
4613
4605
  import { connect as connect2 } from "net";
4614
4606
  async function submitToChannel(role, payload, opts = {}) {
4615
- const content = buildChannelContent(role, payload);
4607
+ const content = await buildChannelContent(role, payload);
4616
4608
  const body = JSON.stringify({ role, content });
4617
4609
  const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
4618
4610
  const startedAt = Date.now();
@@ -4709,7 +4701,7 @@ __export(install_exports, {
4709
4701
  installCommand: () => installCommand,
4710
4702
  parseArgs: () => parseArgs
4711
4703
  });
4712
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, chmodSync as chmodSync2, readFileSync as readFileSync11, readdirSync } from "fs";
4704
+ import { existsSync as existsSync12, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, chmodSync as chmodSync2, readFileSync as readFileSync11, readdirSync } from "fs";
4713
4705
  import { homedir as homedir11 } from "os";
4714
4706
  import { join as join12 } from "path";
4715
4707
  import { execSync as execSync5 } from "child_process";
@@ -4813,7 +4805,7 @@ function shellQuoteSingle(value) {
4813
4805
  }
4814
4806
  function resolveSynkroBundle() {
4815
4807
  const scriptPath = process.argv[1];
4816
- if (scriptPath && existsSync13(scriptPath)) return scriptPath;
4808
+ if (scriptPath && existsSync12(scriptPath)) return scriptPath;
4817
4809
  return null;
4818
4810
  }
4819
4811
  function writeConfigEnv(opts) {
@@ -4833,7 +4825,7 @@ function writeConfigEnv(opts) {
4833
4825
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
4834
4826
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
4835
4827
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
4836
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.9")}`
4828
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.11")}`
4837
4829
  ];
4838
4830
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
4839
4831
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -4848,7 +4840,7 @@ function writeConfigEnv(opts) {
4848
4840
  chmodSync2(CONFIG_PATH3, 384);
4849
4841
  }
4850
4842
  function updateLocalInferenceFlag(enabled) {
4851
- if (!existsSync13(CONFIG_PATH3)) return;
4843
+ if (!existsSync12(CONFIG_PATH3)) return;
4852
4844
  let content = readFileSync11(CONFIG_PATH3, "utf-8");
4853
4845
  const flag = enabled ? "yes" : "no";
4854
4846
  if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
@@ -4965,10 +4957,10 @@ function isAlreadyInstalled() {
4965
4957
  join12(HOOKS_DIR, "cc-stop-summary.sh"),
4966
4958
  join12(HOOKS_DIR, "cc-session-start.sh")
4967
4959
  ];
4968
- if (!requiredScripts.every((p) => existsSync13(p))) return false;
4969
- if (!existsSync13(CONFIG_PATH3)) return false;
4960
+ if (!requiredScripts.every((p) => existsSync12(p))) return false;
4961
+ if (!existsSync12(CONFIG_PATH3)) return false;
4970
4962
  const settingsPath = join12(homedir11(), ".claude", "settings.json");
4971
- if (!existsSync13(settingsPath)) return false;
4963
+ if (!existsSync12(settingsPath)) return false;
4972
4964
  try {
4973
4965
  const settings = JSON.parse(readFileSync11(settingsPath, "utf-8"));
4974
4966
  const hooks = settings?.hooks;
@@ -5267,7 +5259,7 @@ function getClaudeProjectsFolder() {
5267
5259
  const cwd = process.cwd();
5268
5260
  const sanitized = "-" + cwd.replace(/\//g, "-");
5269
5261
  const projectsDir = join12(homedir11(), ".claude", "projects", sanitized);
5270
- return existsSync13(projectsDir) ? projectsDir : null;
5262
+ return existsSync12(projectsDir) ? projectsDir : null;
5271
5263
  }
5272
5264
  function extractSessionInsights(projectsDir) {
5273
5265
  const insights = [];
@@ -5541,11 +5533,11 @@ var status_exports = {};
5541
5533
  __export(status_exports, {
5542
5534
  statusCommand: () => statusCommand
5543
5535
  });
5544
- import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
5536
+ import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
5545
5537
  import { homedir as homedir12 } from "os";
5546
5538
  import { join as join13 } from "path";
5547
5539
  function readConfigEnv() {
5548
- if (!existsSync14(CONFIG_PATH4)) return {};
5540
+ if (!existsSync13(CONFIG_PATH4)) return {};
5549
5541
  const out = {};
5550
5542
  const raw = readFileSync12(CONFIG_PATH4, "utf-8");
5551
5543
  for (const line of raw.split("\n")) {
@@ -5641,17 +5633,17 @@ async function statusCommand() {
5641
5633
  const cursorBashFollowupScript = join13(SYNKRO_DIR3, "hooks", "cursor-bash-followup.sh");
5642
5634
  const commonScript = join13(SYNKRO_DIR3, "hooks", "_synkro-common.sh");
5643
5635
  console.log("Hook scripts:");
5644
- console.log(` ${existsSync14(bashScript) ? "\u2713" : "\u2717"} ${bashScript}`);
5645
- console.log(` ${existsSync14(bashFollowupScript) ? "\u2713" : "\u2717"} ${bashFollowupScript}`);
5646
- console.log(` ${existsSync14(editPrecheckScript) ? "\u2713" : "\u2717"} ${editPrecheckScript}`);
5647
- console.log(` ${existsSync14(editCaptureScript) ? "\u2713" : "\u2717"} ${editCaptureScript}`);
5648
- console.log(` ${existsSync14(stopSummaryScript) ? "\u2713" : "\u2717"} ${stopSummaryScript}`);
5649
- console.log(` ${existsSync14(sessionStartScript) ? "\u2713" : "\u2717"} ${sessionStartScript}`);
5650
- console.log(` ${existsSync14(commonScript) ? "\u2713" : "\u2717"} ${commonScript}`);
5651
- console.log(` ${existsSync14(cursorBashJudgeScript) ? "\u2713" : "\u2717"} ${cursorBashJudgeScript}`);
5652
- console.log(` ${existsSync14(cursorEditPrecheckScript) ? "\u2713" : "\u2717"} ${cursorEditPrecheckScript}`);
5653
- console.log(` ${existsSync14(cursorEditCaptureScript) ? "\u2713" : "\u2717"} ${cursorEditCaptureScript}`);
5654
- console.log(` ${existsSync14(cursorBashFollowupScript) ? "\u2713" : "\u2717"} ${cursorBashFollowupScript}`);
5636
+ console.log(` ${existsSync13(bashScript) ? "\u2713" : "\u2717"} ${bashScript}`);
5637
+ console.log(` ${existsSync13(bashFollowupScript) ? "\u2713" : "\u2717"} ${bashFollowupScript}`);
5638
+ console.log(` ${existsSync13(editPrecheckScript) ? "\u2713" : "\u2717"} ${editPrecheckScript}`);
5639
+ console.log(` ${existsSync13(editCaptureScript) ? "\u2713" : "\u2717"} ${editCaptureScript}`);
5640
+ console.log(` ${existsSync13(stopSummaryScript) ? "\u2713" : "\u2717"} ${stopSummaryScript}`);
5641
+ console.log(` ${existsSync13(sessionStartScript) ? "\u2713" : "\u2717"} ${sessionStartScript}`);
5642
+ console.log(` ${existsSync13(commonScript) ? "\u2713" : "\u2717"} ${commonScript}`);
5643
+ console.log(` ${existsSync13(cursorBashJudgeScript) ? "\u2713" : "\u2717"} ${cursorBashJudgeScript}`);
5644
+ console.log(` ${existsSync13(cursorEditPrecheckScript) ? "\u2713" : "\u2717"} ${cursorEditPrecheckScript}`);
5645
+ console.log(` ${existsSync13(cursorEditCaptureScript) ? "\u2713" : "\u2717"} ${cursorEditCaptureScript}`);
5646
+ console.log(` ${existsSync13(cursorBashFollowupScript) ? "\u2713" : "\u2717"} ${cursorBashFollowupScript}`);
5655
5647
  console.log();
5656
5648
  const mcp = inspectMcpConfig();
5657
5649
  console.log("Guardrails MCP server (Claude Code):");
@@ -5762,11 +5754,11 @@ var config_exports = {};
5762
5754
  __export(config_exports, {
5763
5755
  configCommand: () => configCommand
5764
5756
  });
5765
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync15 } from "fs";
5757
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync14 } from "fs";
5766
5758
  import { join as join14 } from "path";
5767
5759
  import { homedir as homedir13 } from "os";
5768
5760
  function readConfigEnv2() {
5769
- if (!existsSync15(CONFIG_PATH5)) return {};
5761
+ if (!existsSync14(CONFIG_PATH5)) return {};
5770
5762
  const out = {};
5771
5763
  for (const line of readFileSync13(CONFIG_PATH5, "utf-8").split("\n")) {
5772
5764
  const t = line.trim();
@@ -5777,7 +5769,7 @@ function readConfigEnv2() {
5777
5769
  return out;
5778
5770
  }
5779
5771
  function updateConfigValue(key, value) {
5780
- if (!existsSync15(CONFIG_PATH5)) {
5772
+ if (!existsSync14(CONFIG_PATH5)) {
5781
5773
  console.error("No config found. Run `synkro install` first.");
5782
5774
  process.exit(1);
5783
5775
  }
@@ -5859,7 +5851,7 @@ __export(scanPr_exports, {
5859
5851
  scanPrCommand: () => scanPrCommand
5860
5852
  });
5861
5853
  import { execSync as execSync6, spawn as spawn2 } from "child_process";
5862
- import { readFileSync as readFileSync14, existsSync as existsSync16 } from "fs";
5854
+ import { readFileSync as readFileSync14, existsSync as existsSync15 } from "fs";
5863
5855
  import { join as join15 } from "path";
5864
5856
  function parseMatchSpec(condition) {
5865
5857
  if (!condition.startsWith("match_spec:")) return null;
@@ -6340,7 +6332,7 @@ function shouldFail(findings, threshold) {
6340
6332
  }
6341
6333
  function readRepoDeps() {
6342
6334
  const pkgPath = join15(process.cwd(), "package.json");
6343
- if (!existsSync16(pkgPath)) return {};
6335
+ if (!existsSync15(pkgPath)) return {};
6344
6336
  try {
6345
6337
  const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
6346
6338
  return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
@@ -6604,7 +6596,7 @@ var disconnect_exports = {};
6604
6596
  __export(disconnect_exports, {
6605
6597
  disconnectCommand: () => disconnectCommand
6606
6598
  });
6607
- import { existsSync as existsSync17, rmSync } from "fs";
6599
+ import { existsSync as existsSync16, rmSync } from "fs";
6608
6600
  import { homedir as homedir14 } from "os";
6609
6601
  import { join as join16 } from "path";
6610
6602
  function tearDownLocalCC() {
@@ -6639,13 +6631,13 @@ function disconnectCommand(args2 = []) {
6639
6631
  console.log(`${mcpRemoved ? "\u2713" : "\xB7"} MCP guardrails server: ${mcpRemoved ? "removed entry from ~/.claude.json" : "no Synkro MCP entry found"}`);
6640
6632
  }
6641
6633
  if (purge) {
6642
- if (existsSync17(SYNKRO_DIR5)) {
6634
+ if (existsSync16(SYNKRO_DIR5)) {
6643
6635
  rmSync(SYNKRO_DIR5, { recursive: true, force: true });
6644
6636
  console.log(`\u2713 Removed ${SYNKRO_DIR5}`);
6645
6637
  } else {
6646
6638
  console.log(`\xB7 ${SYNKRO_DIR5} already gone, nothing to remove`);
6647
6639
  }
6648
- } else if (existsSync17(SYNKRO_DIR5)) {
6640
+ } else if (existsSync16(SYNKRO_DIR5)) {
6649
6641
  console.log(`Config preserved at ${SYNKRO_DIR5}. Run with --purge to remove.`);
6650
6642
  }
6651
6643
  console.log("\nSynkro disconnected.");
@@ -6709,7 +6701,7 @@ __export(localCc_exports, {
6709
6701
  import { spawnSync as spawnSync3 } from "child_process";
6710
6702
  import { homedir as homedir15 } from "os";
6711
6703
  import { join as join17 } from "path";
6712
- import { existsSync as existsSync18, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "fs";
6704
+ import { existsSync as existsSync17, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "fs";
6713
6705
  function printHelp() {
6714
6706
  console.log(`synkro local-cc \u2014 manage the local Claude Code inference session
6715
6707
 
@@ -6799,14 +6791,14 @@ TROUBLESHOOTING
6799
6791
  `);
6800
6792
  }
6801
6793
  function readGatewayUrl() {
6802
- if (existsSync18(CONFIG_PATH6)) {
6794
+ if (existsSync17(CONFIG_PATH6)) {
6803
6795
  const m = readFileSync15(CONFIG_PATH6, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
6804
6796
  if (m) return m[1];
6805
6797
  }
6806
6798
  return "https://api.synkro.sh";
6807
6799
  }
6808
6800
  function updateLocalInferenceFlag2(enabled) {
6809
- if (!existsSync18(CONFIG_PATH6)) return;
6801
+ if (!existsSync17(CONFIG_PATH6)) return;
6810
6802
  let content = readFileSync15(CONFIG_PATH6, "utf-8");
6811
6803
  const flag = enabled ? "yes" : "no";
6812
6804
  if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
@@ -7143,14 +7135,14 @@ var init_grade = __esm({
7143
7135
  });
7144
7136
 
7145
7137
  // cli/bootstrap.js
7146
- import { readFileSync as readFileSync16, existsSync as existsSync19 } from "fs";
7138
+ import { readFileSync as readFileSync16, existsSync as existsSync18 } from "fs";
7147
7139
  import { resolve } from "path";
7148
7140
  var envCandidates = [
7149
7141
  resolve(process.cwd(), ".env"),
7150
7142
  resolve(process.env.HOME ?? "", ".synkro", "config.env")
7151
7143
  ];
7152
7144
  for (const envPath of envCandidates) {
7153
- if (!existsSync19(envPath)) continue;
7145
+ if (!existsSync18(envPath)) continue;
7154
7146
  const envContent = readFileSync16(envPath, "utf-8");
7155
7147
  for (const line of envContent.split("\n")) {
7156
7148
  const trimmed = line.trim();