@synkro-sh/cli 1.4.41 → 1.4.43

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";
@@ -557,7 +568,7 @@ synkro_local_grade() {
557
568
  # Sets LOCAL_OK, LOCAL_REASON, LOCAL_RULE_ID, LOCAL_RULE_MODE, LOCAL_SEV, LOCAL_CAT.
558
569
  synkro_parse_local_verdict() {
559
570
  local resp="$1"
560
- LOCAL_OK="true"; LOCAL_REASON=""; LOCAL_RULE_ID=""; LOCAL_RULE_MODE=""; LOCAL_SEV="low"; LOCAL_CAT="general"
571
+ LOCAL_OK="true"; LOCAL_REASON=""; LOCAL_RULE_ID=""; LOCAL_RULE_MODE=""; LOCAL_SEV="low"; LOCAL_CAT="clean"
561
572
  local inner
562
573
  inner=$(printf '%s' "$resp" | tr '\\n' ' ' | sed -nE 's|.*<synkro-verdict>(.*)</synkro-verdict>.*|\\1|p' | tail -1)
563
574
  [ -z "$inner" ] && return
@@ -570,16 +581,16 @@ synkro_parse_local_verdict() {
570
581
  LOCAL_RULE_ID=$(printf '%s' "$inner" | sed -nE 's|.*<rule_id>(.*)</rule_id>.*|\\1|p' | head -1)
571
582
  LOCAL_RULE_MODE=$(printf '%s' "$inner" | sed -nE 's|.*<rule_mode>(.*)</rule_mode>.*|\\1|p' | head -1)
572
583
  LOCAL_SEV=$(printf '%s' "$inner" | sed -nE 's|.*<risk_level>(.*)</risk_level>.*|\\1|p' | head -1)
573
- LOCAL_CAT=$(printf '%s' "$inner" | sed -nE 's|.*<category>(.*)</category>.*|\\1|p' | head -1)
574
584
  if [ -z "$LOCAL_RULE_ID" ]; then
575
585
  local fv
576
586
  fv=$(printf '%s' "$inner" | awk -v RS='</violation>' '/<violation>/{print; exit}')
577
587
  LOCAL_RULE_ID=$(printf '%s' "$fv" | sed -nE 's|.*<rule_id>(.*)</rule_id>.*|\\1|p' | head -1)
578
588
  [ -z "$LOCAL_REASON" ] && LOCAL_REASON=$(printf '%s' "$fv" | sed -nE 's|.*<reason>(.*)</reason>.*|\\1|p' | head -1)
579
589
  [ -z "$LOCAL_SEV" ] && LOCAL_SEV=$(printf '%s' "$fv" | sed -nE 's|.*<severity>(.*)</severity>.*|\\1|p' | head -1)
580
- [ -z "$LOCAL_CAT" ] && LOCAL_CAT=$(printf '%s' "$fv" | sed -nE 's|.*<category>(.*)</category>.*|\\1|p' | head -1)
581
590
  fi
582
- LOCAL_SEV="\${LOCAL_SEV:-high}"; LOCAL_CAT="\${LOCAL_CAT:-policy_violation}"
591
+ LOCAL_SEV="\${LOCAL_SEV:-high}"
592
+ LOCAL_CAT=$(printf '%s' "$inner" | sed -nE 's|.*<category>(.*)</category>.*|\\1|p' | head -1)
593
+ LOCAL_CAT="\${LOCAL_CAT:-uncategorized}"
583
594
  [ -z "$LOCAL_RULE_ID" ] && LOCAL_RULE_ID=$(printf '%s' "$LOCAL_REASON" | grep -oE '[Rr][0-9]{3}' | head -1)
584
595
  fi
585
596
  }
@@ -1213,7 +1224,12 @@ if [ -z "$PAYLOAD" ]; then echo '{}'; exit 0; fi
1213
1224
  TOOL_NAME=$(echo "$PAYLOAD" | jq -r '.tool_name // empty' 2>/dev/null)
1214
1225
  if [ "$TOOL_NAME" != "ExitPlanMode" ]; then echo '{}'; exit 0; fi
1215
1226
 
1216
- 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)
1217
1233
  if [ -z "$PLAN" ] || [ \${#PLAN} -lt 20 ]; then echo '{}'; exit 0; fi
1218
1234
 
1219
1235
  SESSION_ID=$(echo "$PAYLOAD" | jq -r '.session_id // empty' 2>/dev/null)
@@ -1223,6 +1239,15 @@ GIT_REPO=$(synkro_detect_repo "\${CWD:-.}")
1223
1239
  PLAN_SHORT=$(printf '%s' "$PLAN" | head -c 80)
1224
1240
  synkro_log "planReview checking: $PLAN_SHORT..."
1225
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
+
1226
1251
  synkro_load_config
1227
1252
  ROUTE=$(synkro_route)
1228
1253
  TAG=$(synkro_tag "$ROUTE")
@@ -1250,11 +1275,15 @@ if [ "$ROUTE" = "local" ]; then
1250
1275
  if [ "$LOCAL_OK" = "false" ]; then
1251
1276
  VCOUNT=$(printf '%s' "$CC_RESP" | grep -c '<violation>' 2>/dev/null || echo "0")
1252
1277
  [ "$VCOUNT" = "0" ] && VCOUNT="1"
1253
- 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"
1254
1281
  jq -n --arg m "$MSG" '{systemMessage: $m}'
1255
1282
  synkro_dispatch_capture "plan_review" "advisory" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "ExitPlanMode" "$GIT_REPO" "$SESSION_ID" \\
1256
1283
  "$PLAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$PLAN_VIOLATED" "[]"
1257
1284
  else
1285
+ REVIEW_MSG="Clean \u2014 \${LOCAL_REASON:-no relevant org rules for this plan}"
1286
+ append_review_to_plan "\u2705 $REVIEW_MSG"
1258
1287
  jq -n --arg m "$TAG planReview \u2192 clean: \${LOCAL_REASON:-no relevant org rules for this plan}" '{systemMessage: $m}'
1259
1288
  synkro_dispatch_capture "plan_review" "clean" "audit" "\${LOCAL_CAT:-general}" "ExitPlanMode" "$GIT_REPO" "$SESSION_ID" \\
1260
1289
  "$PLAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "[]"
@@ -1296,8 +1325,11 @@ fi
1296
1325
  HR=$(echo "$RESP" | jq -c '.hook_response')
1297
1326
  if echo "$HR" | jq -e '.hookSpecificOutput.permissionDecision' >/dev/null 2>&1; then
1298
1327
  REASON=$(echo "$HR" | jq -r '.hookSpecificOutput.permissionDecisionReason // "check org rules"' 2>/dev/null)
1328
+ append_review_to_plan "\u26A0\uFE0F Advisory \u2014 $REASON"
1299
1329
  jq -n --arg m "$TAG planReview \u2192 advisory: $REASON" '{systemMessage: $m}'
1300
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"
1301
1333
  echo "$HR"
1302
1334
  fi
1303
1335
  exit 0
@@ -1478,6 +1510,77 @@ curl -sS -X POST "\${GATEWAY_URL}/api/v1/hook/capture" \\
1478
1510
 
1479
1511
  echo '{}'
1480
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
1481
1584
  `;
1482
1585
  CC_TRANSCRIPT_SYNC_SCRIPT = `#!/bin/bash
1483
1586
  SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
@@ -3908,6 +4011,7 @@ function writeHookScripts() {
3908
4011
  const bashFollowupScriptPath = join11(HOOKS_DIR, "cc-bash-followup.sh");
3909
4012
  const editCaptureScriptPath = join11(HOOKS_DIR, "cc-edit-capture.sh");
3910
4013
  const editPrecheckScriptPath = join11(HOOKS_DIR, "cc-edit-precheck.sh");
4014
+ const cveScanScriptPath = join11(HOOKS_DIR, "cc-cve-scan.sh");
3911
4015
  const planJudgeScriptPath = join11(HOOKS_DIR, "cc-plan-judge.sh");
3912
4016
  const stopSummaryScriptPath = join11(HOOKS_DIR, "cc-stop-summary.sh");
3913
4017
  const sessionStartScriptPath = join11(HOOKS_DIR, "cc-session-start.sh");
@@ -3921,6 +4025,7 @@ function writeHookScripts() {
3921
4025
  writeFileSync7(bashFollowupScriptPath, CC_BASH_FOLLOWUP_SCRIPT, "utf-8");
3922
4026
  writeFileSync7(editCaptureScriptPath, CC_EDIT_CAPTURE_SCRIPT, "utf-8");
3923
4027
  writeFileSync7(editPrecheckScriptPath, CC_EDIT_PRECHECK_SCRIPT, "utf-8");
4028
+ writeFileSync7(cveScanScriptPath, CC_CVE_SCAN_SCRIPT, "utf-8");
3924
4029
  writeFileSync7(planJudgeScriptPath, CC_PLAN_JUDGE_SCRIPT, "utf-8");
3925
4030
  writeFileSync7(stopSummaryScriptPath, CC_STOP_SUMMARY_SCRIPT, "utf-8");
3926
4031
  writeFileSync7(sessionStartScriptPath, CC_SESSION_START_SCRIPT, "utf-8");
@@ -3934,6 +4039,7 @@ function writeHookScripts() {
3934
4039
  chmodSync2(bashFollowupScriptPath, 493);
3935
4040
  chmodSync2(editCaptureScriptPath, 493);
3936
4041
  chmodSync2(editPrecheckScriptPath, 493);
4042
+ chmodSync2(cveScanScriptPath, 493);
3937
4043
  chmodSync2(planJudgeScriptPath, 493);
3938
4044
  chmodSync2(stopSummaryScriptPath, 493);
3939
4045
  chmodSync2(sessionStartScriptPath, 493);
@@ -3948,6 +4054,7 @@ function writeHookScripts() {
3948
4054
  bashFollowupScript: bashFollowupScriptPath,
3949
4055
  editCaptureScript: editCaptureScriptPath,
3950
4056
  editPrecheckScript: editPrecheckScriptPath,
4057
+ cveScanScript: cveScanScriptPath,
3951
4058
  planJudgeScript: planJudgeScriptPath,
3952
4059
  stopSummaryScript: stopSummaryScriptPath,
3953
4060
  sessionStartScript: sessionStartScriptPath,
@@ -3987,7 +4094,7 @@ function writeConfigEnv(opts) {
3987
4094
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3988
4095
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3989
4096
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
3990
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.41")}`
4097
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.43")}`
3991
4098
  ];
3992
4099
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
3993
4100
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -4290,6 +4397,7 @@ async function installCommand(opts = {}) {
4290
4397
  bashFollowupScriptPath: scripts.bashFollowupScript,
4291
4398
  editCaptureScriptPath: scripts.editCaptureScript,
4292
4399
  editPrecheckScriptPath: scripts.editPrecheckScript,
4400
+ cveScanScriptPath: scripts.cveScanScript,
4293
4401
  planJudgeScriptPath: scripts.planJudgeScript,
4294
4402
  stopSummaryScriptPath: scripts.stopSummaryScript,
4295
4403
  sessionStartScriptPath: scripts.sessionStartScript,