@synkro-sh/cli 1.4.40 → 1.4.42

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
@@ -170,6 +170,17 @@ function installCCHooks(settingsPath, config) {
170
170
  ],
171
171
  [SYNKRO_MARKER]: true
172
172
  });
173
+ settings.hooks.PostToolUse.push({
174
+ matcher: "Edit|Write|MultiEdit|NotebookEdit",
175
+ hooks: [
176
+ {
177
+ type: "command",
178
+ command: config.cveScanScriptPath,
179
+ timeout: 10
180
+ }
181
+ ],
182
+ [SYNKRO_MARKER]: true
183
+ });
173
184
  settings.hooks.PostToolUse.push({
174
185
  matcher: "Bash",
175
186
  hooks: [
@@ -441,7 +452,7 @@ var init_mcpConfig = __esm({
441
452
  });
442
453
 
443
454
  // cli/installer/hookScripts.ts
444
- var SYNKRO_COMMON_SCRIPT, CC_BASH_JUDGE_SCRIPT, CC_EDIT_PRECHECK_SCRIPT, CC_EDIT_CAPTURE_SCRIPT, CC_PLAN_JUDGE_SCRIPT, CC_STOP_SUMMARY_SCRIPT, CC_SESSION_START_SCRIPT, CC_BASH_FOLLOWUP_SCRIPT, CC_TRANSCRIPT_SYNC_SCRIPT, CURSOR_BASH_JUDGE_SCRIPT, CURSOR_EDIT_PRECHECK_SCRIPT, CURSOR_EDIT_CAPTURE_SCRIPT, CURSOR_BASH_FOLLOWUP_SCRIPT;
455
+ var SYNKRO_COMMON_SCRIPT, CC_BASH_JUDGE_SCRIPT, CC_EDIT_PRECHECK_SCRIPT, CC_EDIT_CAPTURE_SCRIPT, CC_PLAN_JUDGE_SCRIPT, CC_STOP_SUMMARY_SCRIPT, CC_SESSION_START_SCRIPT, CC_BASH_FOLLOWUP_SCRIPT, CC_CVE_SCAN_SCRIPT, CC_TRANSCRIPT_SYNC_SCRIPT, CURSOR_BASH_JUDGE_SCRIPT, CURSOR_EDIT_PRECHECK_SCRIPT, CURSOR_EDIT_CAPTURE_SCRIPT, CURSOR_BASH_FOLLOWUP_SCRIPT;
445
456
  var init_hookScripts = __esm({
446
457
  "cli/installer/hookScripts.ts"() {
447
458
  "use strict";
@@ -520,11 +531,13 @@ synkro_load_config() {
520
531
  SYNKRO_RULES=$(echo "$resp" | 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 "[]")
521
532
  }
522
533
 
523
- # Build the tag prefix for system messages: [synkro] or [synkro:PolicyName] or [synkro:silent]
534
+ # Build the tag prefix: [synkro:route:ruleset] or [synkro:silent]
535
+ # Accepts optional $1 = route override; otherwise calls synkro_route().
524
536
  synkro_tag() {
525
537
  if [ "$SYNKRO_SILENT" = "true" ]; then echo "[synkro:silent]"; return; fi
526
- if [ -n "\${SYNKRO_POLICY_NAME:-}" ]; then echo "[synkro:\${SYNKRO_POLICY_NAME}]"; return; fi
527
- echo "[synkro]"
538
+ local route="\${1:-$(synkro_route)}"
539
+ local rs="\${SYNKRO_POLICY_NAME:-all}"
540
+ echo "[synkro:\${route}:\${rs}]"
528
541
  }
529
542
 
530
543
  # Decide routing: "local" (grade on device) or "cloud" (POST to server)
@@ -750,15 +763,14 @@ IS_HEADLESS="\${SYNKRO_HEADLESS:-0}"
750
763
  case "$PERMISSION_MODE" in acceptEdits|bypassPermissions|plan|auto) IS_HEADLESS="1" ;; esac
751
764
 
752
765
  synkro_load_config
753
- TAG=$(synkro_tag)
766
+ ROUTE=$(synkro_route)
767
+ TAG=$(synkro_tag "$ROUTE")
754
768
 
755
769
  if [ "$SYNKRO_SILENT" = "true" ]; then
756
770
  jq -n --arg m "$TAG bashGuard \u2192 skipped (silent mode)" '{systemMessage: $m}'
757
771
  exit 0
758
772
  fi
759
773
 
760
- ROUTE=$(synkro_route)
761
-
762
774
  if [ "$ROUTE" = "local" ]; then
763
775
  # \u2500\u2500\u2500 Local grading (local_only privacy or local-cc channel) \u2500\u2500\u2500
764
776
  GRADER_FILE=$(mktemp -t synkro-bash.XXXXXX)
@@ -946,15 +958,14 @@ if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
946
958
  fi
947
959
 
948
960
  synkro_load_config
949
- TAG=$(synkro_tag)
961
+ ROUTE=$(synkro_route)
962
+ TAG=$(synkro_tag "$ROUTE")
950
963
 
951
964
  if [ "$SYNKRO_SILENT" = "true" ]; then
952
965
  jq -n --arg m "$TAG editGuard \u2192 skipped (silent mode)" '{systemMessage: $m}'
953
966
  exit 0
954
967
  fi
955
968
 
956
- ROUTE=$(synkro_route)
957
-
958
969
  if [ "$ROUTE" = "local" ]; then
959
970
  # \u2500\u2500\u2500 Local grading (local_only privacy or local-cc channel) \u2500\u2500\u2500
960
971
  GRADER_FILE=$(mktemp -t synkro-edit.XXXXXX)
@@ -1113,14 +1124,13 @@ while [ "$_PKG_DIR" != "/" ]; do
1113
1124
  done
1114
1125
 
1115
1126
  synkro_load_config
1116
- TAG=$(synkro_tag)
1127
+ ROUTE=$(synkro_route)
1128
+ TAG=$(synkro_tag "$ROUTE")
1117
1129
 
1118
1130
  if [ "$SYNKRO_SILENT" = "true" ]; then
1119
1131
  echo '{}'; exit 0
1120
1132
  fi
1121
1133
 
1122
- ROUTE=$(synkro_route)
1123
-
1124
1134
  if [ "$ROUTE" = "local" ]; then
1125
1135
  # \u2500\u2500\u2500 Local edit scan (local_only privacy or local-cc channel) \u2500\u2500\u2500
1126
1136
  GRADER_FILE=$(mktemp -t synkro-escan.XXXXXX)
@@ -1214,7 +1224,12 @@ if [ -z "$PAYLOAD" ]; then echo '{}'; exit 0; fi
1214
1224
  TOOL_NAME=$(echo "$PAYLOAD" | jq -r '.tool_name // empty' 2>/dev/null)
1215
1225
  if [ "$TOOL_NAME" != "ExitPlanMode" ]; then echo '{}'; exit 0; fi
1216
1226
 
1217
- PLAN=$(echo "$PAYLOAD" | jq -r '.tool_input.plan // empty' 2>/dev/null)
1227
+ # ExitPlanMode's tool_input contains {allowedPrompts:[...]}, not plan content.
1228
+ # Read from the most recently modified plan file instead.
1229
+ PLANS_DIR="$HOME/.claude/plans"
1230
+ PLAN_FILE=$(ls -t "$PLANS_DIR"/*.md 2>/dev/null | head -1)
1231
+ if [ -z "$PLAN_FILE" ] || [ ! -f "$PLAN_FILE" ]; then echo '{}'; exit 0; fi
1232
+ PLAN=$(cat "$PLAN_FILE" 2>/dev/null)
1218
1233
  if [ -z "$PLAN" ] || [ \${#PLAN} -lt 20 ]; then echo '{}'; exit 0; fi
1219
1234
 
1220
1235
  SESSION_ID=$(echo "$PAYLOAD" | jq -r '.session_id // empty' 2>/dev/null)
@@ -1224,16 +1239,24 @@ GIT_REPO=$(synkro_detect_repo "\${CWD:-.}")
1224
1239
  PLAN_SHORT=$(printf '%s' "$PLAN" | head -c 80)
1225
1240
  synkro_log "planReview checking: $PLAN_SHORT..."
1226
1241
 
1242
+ # Write review verdict into the plan file so it survives ExitPlanMode rejection
1243
+ append_review_to_plan() {
1244
+ local verdict="$1"
1245
+ local tmp="\${PLAN_FILE}.synkro.tmp"
1246
+ sed '/^<!-- synkro-plan-review -->$/,/^<!-- \\/synkro-plan-review -->$/d' "$PLAN_FILE" | sed -e :a -e '/^\\n*$/{$d;N;ba' -e '}' > "$tmp" 2>/dev/null
1247
+ printf '\\n\\n<!-- synkro-plan-review -->\\n\\n---\\n\\n**Synkro Plan Review** \u2014 %s\\n\\n%s\\n\\n<!-- /synkro-plan-review -->\\n' "$(date '+%Y-%m-%d %H:%M')" "$verdict" >> "$tmp"
1248
+ mv "$tmp" "$PLAN_FILE" 2>/dev/null
1249
+ }
1250
+
1227
1251
  synkro_load_config
1228
- TAG=$(synkro_tag)
1252
+ ROUTE=$(synkro_route)
1253
+ TAG=$(synkro_tag "$ROUTE")
1229
1254
 
1230
1255
  if [ "$SYNKRO_SILENT" = "true" ]; then
1231
1256
  jq -n --arg m "$TAG planReview \u2192 skipped (silent mode)" '{systemMessage: $m}'
1232
1257
  exit 0
1233
1258
  fi
1234
1259
 
1235
- ROUTE=$(synkro_route)
1236
-
1237
1260
  if [ "$ROUTE" = "local" ]; then
1238
1261
  GRADER_FILE=$(mktemp -t synkro-plan.XXXXXX)
1239
1262
  trap "rm -f \\"$GRADER_FILE\\"" EXIT
@@ -1252,11 +1275,15 @@ if [ "$ROUTE" = "local" ]; then
1252
1275
  if [ "$LOCAL_OK" = "false" ]; then
1253
1276
  VCOUNT=$(printf '%s' "$CC_RESP" | grep -c '<violation>' 2>/dev/null || echo "0")
1254
1277
  [ "$VCOUNT" = "0" ] && VCOUNT="1"
1255
- MSG="$TAG planReview \u2192 \${VCOUNT} rule(s) relevant\${LOCAL_RULE_ID:+ (first: $LOCAL_RULE_ID)}: \${LOCAL_REASON:-check org rules during implementation}"
1278
+ REVIEW_MSG="\${VCOUNT} rule(s) relevant\${LOCAL_RULE_ID:+ (first: $LOCAL_RULE_ID)}: \${LOCAL_REASON:-check org rules during implementation}"
1279
+ append_review_to_plan "\u26A0\uFE0F Advisory \u2014 $REVIEW_MSG"
1280
+ MSG="$TAG planReview \u2192 $REVIEW_MSG"
1256
1281
  jq -n --arg m "$MSG" '{systemMessage: $m}'
1257
1282
  synkro_dispatch_capture "plan_review" "advisory" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "ExitPlanMode" "$GIT_REPO" "$SESSION_ID" \\
1258
1283
  "$PLAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$PLAN_VIOLATED" "[]"
1259
1284
  else
1285
+ REVIEW_MSG="Clean \u2014 \${LOCAL_REASON:-no relevant org rules for this plan}"
1286
+ append_review_to_plan "\u2705 $REVIEW_MSG"
1260
1287
  jq -n --arg m "$TAG planReview \u2192 clean: \${LOCAL_REASON:-no relevant org rules for this plan}" '{systemMessage: $m}'
1261
1288
  synkro_dispatch_capture "plan_review" "clean" "audit" "\${LOCAL_CAT:-general}" "ExitPlanMode" "$GIT_REPO" "$SESSION_ID" \\
1262
1289
  "$PLAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "[]"
@@ -1298,8 +1325,11 @@ fi
1298
1325
  HR=$(echo "$RESP" | jq -c '.hook_response')
1299
1326
  if echo "$HR" | jq -e '.hookSpecificOutput.permissionDecision' >/dev/null 2>&1; then
1300
1327
  REASON=$(echo "$HR" | jq -r '.hookSpecificOutput.permissionDecisionReason // "check org rules"' 2>/dev/null)
1328
+ append_review_to_plan "\u26A0\uFE0F Advisory \u2014 $REASON"
1301
1329
  jq -n --arg m "$TAG planReview \u2192 advisory: $REASON" '{systemMessage: $m}'
1302
1330
  else
1331
+ CLOUD_MSG=$(echo "$HR" | jq -r '.systemMessage // empty' 2>/dev/null)
1332
+ [ -n "$CLOUD_MSG" ] && append_review_to_plan "\u2705 $CLOUD_MSG"
1303
1333
  echo "$HR"
1304
1334
  fi
1305
1335
  exit 0
@@ -1402,12 +1432,12 @@ if [ -n "$JWT" ]; then
1402
1432
  fi
1403
1433
  fi
1404
1434
 
1405
- TAG=$(synkro_tag)
1406
-
1407
1435
  if (exec 3<>/dev/tcp/127.0.0.1/"$SYNKRO_PORT") 2>/dev/null; then
1408
1436
  exec 3<&- 3>&- 2>/dev/null || true
1437
+ TAG=$(synkro_tag "local")
1409
1438
  ROUTE_LINE="$TAG inference: local-cc (channel reachable on 127.0.0.1:$SYNKRO_PORT)"
1410
1439
  else
1440
+ TAG=$(synkro_tag "cloud")
1411
1441
  ROUTE_LINE="$TAG inference: cloud (local-cc channel not reachable)"
1412
1442
  fi
1413
1443
 
@@ -1480,6 +1510,77 @@ curl -sS -X POST "\${GATEWAY_URL}/api/v1/hook/capture" \\
1480
1510
 
1481
1511
  echo '{}'
1482
1512
  exit 0
1513
+ `;
1514
+ CC_CVE_SCAN_SCRIPT = `#!/bin/bash
1515
+ SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
1516
+ . "$SCRIPT_DIR/_synkro-common.sh"
1517
+
1518
+ JWT=$(synkro_load_jwt)
1519
+ if [ -z "$JWT" ]; then echo '{}'; exit 0; fi
1520
+ synkro_ensure_fresh_jwt
1521
+
1522
+ PAYLOAD=$(cat)
1523
+ if [ -z "$PAYLOAD" ]; then echo '{}'; exit 0; fi
1524
+
1525
+ TOOL_NAME=$(echo "$PAYLOAD" | jq -r '.tool_name // empty' 2>/dev/null)
1526
+ case "$TOOL_NAME" in Edit|Write|MultiEdit|NotebookEdit) ;; *) echo '{}'; exit 0 ;; esac
1527
+
1528
+ TOOL_INPUT=$(echo "$PAYLOAD" | jq -c '.tool_input // {}' 2>/dev/null)
1529
+ CWD=$(echo "$PAYLOAD" | jq -r '.cwd // empty' 2>/dev/null)
1530
+
1531
+ FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // .notebook_path // .path // empty' 2>/dev/null)
1532
+ if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then echo '{}'; exit 0; fi
1533
+
1534
+ FILE_CONTENT=$(head -c 65536 "$FILE_PATH" 2>/dev/null || echo "")
1535
+ if [ -z "$FILE_CONTENT" ]; then echo '{}'; exit 0; fi
1536
+
1537
+ DEPS_JSON="{}"
1538
+ _PKG_DIR=$(dirname "$FILE_PATH")
1539
+ while [ "$_PKG_DIR" != "/" ]; do
1540
+ if [ -f "$_PKG_DIR/package.json" ]; then
1541
+ DEPS_JSON=$(jq -c '(.dependencies // {}) + (.devDependencies // {})' "$_PKG_DIR/package.json" 2>/dev/null || echo "{}")
1542
+ break
1543
+ fi
1544
+ _PKG_DIR=$(dirname "$_PKG_DIR")
1545
+ done
1546
+
1547
+ synkro_load_config
1548
+ ROUTE=$(synkro_route)
1549
+ TAG=$(synkro_tag "$ROUTE")
1550
+
1551
+ if [ "$SYNKRO_SILENT" = "true" ]; then echo '{}'; exit 0; fi
1552
+
1553
+ BASENAME=$(basename "$FILE_PATH")
1554
+
1555
+ BODY=$(jq -n --arg fp "$FILE_PATH" --arg c "$FILE_CONTENT" --argjson deps "$DEPS_JSON" \\
1556
+ '{file_path:$fp, content:$c, dependencies:$deps}')
1557
+
1558
+ RESP=$(curl -sS -X POST "\${GATEWAY_URL}/api/v1/cve-scan" \\
1559
+ -H "Content-Type: application/json" -H "Authorization: Bearer $JWT" \\
1560
+ -d "$BODY" --max-time 6 2>/dev/null || echo "")
1561
+
1562
+ if [ -z "$RESP" ] || ! echo "$RESP" | jq -e 'type == "object"' >/dev/null 2>&1; then
1563
+ echo '{}'; exit 0
1564
+ fi
1565
+
1566
+ CVE_COUNT=$(echo "$RESP" | jq -r '.findings | length' 2>/dev/null || echo "0")
1567
+ if [ "$CVE_COUNT" -gt 0 ] 2>/dev/null; then
1568
+ CVE_CRIT=$(echo "$RESP" | jq '[.findings[] | select((.severity | tonumber? // 0) >= 7.0)] | length' 2>/dev/null || echo "0")
1569
+ CRIT_PKGS=$(echo "$RESP" | jq -r '[.findings[] | select((.severity | tonumber? // 0) >= 7.0) | .package] | unique | .[:3] | join(", ")' 2>/dev/null || echo "")
1570
+ ALL_PKGS=$(echo "$RESP" | jq -r '[.findings[].package] | unique | .[:3] | join(", ")' 2>/dev/null || echo "")
1571
+ ALL_TOTAL=$(echo "$RESP" | jq -r '[.findings[].package] | unique | length' 2>/dev/null || echo "0")
1572
+ [ "$CVE_COUNT" = "1" ] && LABEL="advisory" || LABEL="advisories"
1573
+ if [ "$CVE_CRIT" -gt 0 ]; then
1574
+ [ "$ALL_TOTAL" -gt 3 ] && CRIT_PKGS="\${CRIT_PKGS}, ..."
1575
+ jq -n --arg m "[synkro:\${ROUTE}:cveScan] \${CVE_COUNT} \${LABEL}, \${CVE_CRIT} critical/high (\${CRIT_PKGS})" '{systemMessage: $m}'
1576
+ else
1577
+ [ "$ALL_TOTAL" -gt 3 ] && ALL_PKGS="\${ALL_PKGS}, ..."
1578
+ jq -n --arg m "[synkro:\${ROUTE}:cveScan] \${CVE_COUNT} \${LABEL} (\${ALL_PKGS})" '{systemMessage: $m}'
1579
+ fi
1580
+ else
1581
+ jq -n --arg m "[synkro:\${ROUTE}:cveScan] clean" '{systemMessage: $m}'
1582
+ fi
1583
+ exit 0
1483
1584
  `;
1484
1585
  CC_TRANSCRIPT_SYNC_SCRIPT = `#!/bin/bash
1485
1586
  SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
@@ -1602,7 +1703,9 @@ CMD_SHORT=$(printf '%s' "$COMMAND" | head -c 80)
1602
1703
  synkro_log "bashGuard checking: $CMD_SHORT"
1603
1704
 
1604
1705
  synkro_load_config
1605
- if [ "$SYNKRO_SILENT" = "true" ]; then echo '{}'; exit 0; fi
1706
+ if [ "$SYNKRO_SILENT" = "true" ]; then
1707
+ echo '{}'; exit 0
1708
+ fi
1606
1709
 
1607
1710
  BODY=$(jq -n \\
1608
1711
  --arg cmd "$COMMAND" \\
@@ -1658,7 +1761,9 @@ BASENAME=$(basename "$FILE_PATH" 2>/dev/null || echo "$FILE_PATH")
1658
1761
  synkro_log "editGuard checking: $BASENAME"
1659
1762
 
1660
1763
  synkro_load_config
1661
- if [ "$SYNKRO_SILENT" = "true" ]; then echo '{}'; exit 0; fi
1764
+ if [ "$SYNKRO_SILENT" = "true" ]; then
1765
+ echo '{}'; exit 0
1766
+ fi
1662
1767
 
1663
1768
  BODY=$(jq -n \\
1664
1769
  --arg file_path "$FILE_PATH" \\
@@ -3906,6 +4011,7 @@ function writeHookScripts() {
3906
4011
  const bashFollowupScriptPath = join11(HOOKS_DIR, "cc-bash-followup.sh");
3907
4012
  const editCaptureScriptPath = join11(HOOKS_DIR, "cc-edit-capture.sh");
3908
4013
  const editPrecheckScriptPath = join11(HOOKS_DIR, "cc-edit-precheck.sh");
4014
+ const cveScanScriptPath = join11(HOOKS_DIR, "cc-cve-scan.sh");
3909
4015
  const planJudgeScriptPath = join11(HOOKS_DIR, "cc-plan-judge.sh");
3910
4016
  const stopSummaryScriptPath = join11(HOOKS_DIR, "cc-stop-summary.sh");
3911
4017
  const sessionStartScriptPath = join11(HOOKS_DIR, "cc-session-start.sh");
@@ -3919,6 +4025,7 @@ function writeHookScripts() {
3919
4025
  writeFileSync7(bashFollowupScriptPath, CC_BASH_FOLLOWUP_SCRIPT, "utf-8");
3920
4026
  writeFileSync7(editCaptureScriptPath, CC_EDIT_CAPTURE_SCRIPT, "utf-8");
3921
4027
  writeFileSync7(editPrecheckScriptPath, CC_EDIT_PRECHECK_SCRIPT, "utf-8");
4028
+ writeFileSync7(cveScanScriptPath, CC_CVE_SCAN_SCRIPT, "utf-8");
3922
4029
  writeFileSync7(planJudgeScriptPath, CC_PLAN_JUDGE_SCRIPT, "utf-8");
3923
4030
  writeFileSync7(stopSummaryScriptPath, CC_STOP_SUMMARY_SCRIPT, "utf-8");
3924
4031
  writeFileSync7(sessionStartScriptPath, CC_SESSION_START_SCRIPT, "utf-8");
@@ -3932,6 +4039,7 @@ function writeHookScripts() {
3932
4039
  chmodSync2(bashFollowupScriptPath, 493);
3933
4040
  chmodSync2(editCaptureScriptPath, 493);
3934
4041
  chmodSync2(editPrecheckScriptPath, 493);
4042
+ chmodSync2(cveScanScriptPath, 493);
3935
4043
  chmodSync2(planJudgeScriptPath, 493);
3936
4044
  chmodSync2(stopSummaryScriptPath, 493);
3937
4045
  chmodSync2(sessionStartScriptPath, 493);
@@ -3946,6 +4054,7 @@ function writeHookScripts() {
3946
4054
  bashFollowupScript: bashFollowupScriptPath,
3947
4055
  editCaptureScript: editCaptureScriptPath,
3948
4056
  editPrecheckScript: editPrecheckScriptPath,
4057
+ cveScanScript: cveScanScriptPath,
3949
4058
  planJudgeScript: planJudgeScriptPath,
3950
4059
  stopSummaryScript: stopSummaryScriptPath,
3951
4060
  sessionStartScript: sessionStartScriptPath,
@@ -3985,7 +4094,7 @@ function writeConfigEnv(opts) {
3985
4094
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3986
4095
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3987
4096
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
3988
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.40")}`
4097
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.42")}`
3989
4098
  ];
3990
4099
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
3991
4100
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -4288,6 +4397,7 @@ async function installCommand(opts = {}) {
4288
4397
  bashFollowupScriptPath: scripts.bashFollowupScript,
4289
4398
  editCaptureScriptPath: scripts.editCaptureScript,
4290
4399
  editPrecheckScriptPath: scripts.editPrecheckScript,
4400
+ cveScanScriptPath: scripts.cveScanScript,
4291
4401
  planJudgeScriptPath: scripts.planJudgeScript,
4292
4402
  stopSummaryScriptPath: scripts.stopSummaryScript,
4293
4403
  sessionStartScriptPath: scripts.sessionStartScript,