@synkro-sh/cli 1.4.32 → 1.4.33

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
@@ -148,6 +148,17 @@ function installCCHooks(settingsPath, config) {
148
148
  ],
149
149
  [SYNKRO_MARKER]: true
150
150
  });
151
+ settings.hooks.PreToolUse.push({
152
+ matcher: "ExitPlanMode",
153
+ hooks: [
154
+ {
155
+ type: "command",
156
+ command: config.planJudgeScriptPath,
157
+ timeout: 45
158
+ }
159
+ ],
160
+ [SYNKRO_MARKER]: true
161
+ });
151
162
  settings.hooks.PostToolUse.push({
152
163
  matcher: "Edit|Write|MultiEdit|NotebookEdit",
153
164
  hooks: [
@@ -432,7 +443,7 @@ var init_mcpConfig = __esm({
432
443
  });
433
444
 
434
445
  // cli/installer/hookScripts.ts
435
- var SYNKRO_COMMON_SCRIPT, CC_BASH_JUDGE_SCRIPT, CC_EDIT_PRECHECK_SCRIPT, CC_EDIT_CAPTURE_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;
446
+ 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;
436
447
  var init_hookScripts = __esm({
437
448
  "cli/installer/hookScripts.ts"() {
438
449
  "use strict";
@@ -581,6 +592,49 @@ synkro_capture_local() {
581
592
  ) &
582
593
  }
583
594
 
595
+ # Fire full-content telemetry for local verdicts (used when capture_depth is full or evidence_on_violation).
596
+ synkro_capture_local_full() {
597
+ local hook_type="$1" verdict="$2" severity="$3" category="$4" tool_name="$5" repo="$6" session_id="$7"
598
+ local command="$8" reasoning="$9" rules_checked="\${10:-[]}" violated_rules="\${11:-[]}" recent_user_messages="\${12:-[]}"
599
+ (
600
+ BODY=$(jq -n \\
601
+ --arg eid "$(uuidgen 2>/dev/null || echo "evt_$(date +%s)_$$")" \\
602
+ --arg ht "$hook_type" --arg v "$verdict" --arg s "$severity" --arg c "$category" \\
603
+ --arg tn "$tool_name" --arg r "$repo" --arg sid "$session_id" \\
604
+ --arg cmd "$command" --arg rsn "$reasoning" --arg cd "$SYNKRO_CAPTURE_DEPTH" \\
605
+ --argjson rc "$rules_checked" --argjson vr "$violated_rules" --argjson rum "$recent_user_messages" \\
606
+ '{capture_type:"local_verdict",event_id:$eid,hook_type:$ht,verdict:$v,severity:$s,category:$c,
607
+ model:"claude-sonnet-4-6",tool_name:$tn,capture_depth:$cd,
608
+ command:(if ($cmd|length) > 0 then $cmd else null end),
609
+ reasoning:(if ($rsn|length) > 0 then $rsn else null end),
610
+ rules_checked:$rc, violated_rules:$vr, recent_user_messages:$rum}
611
+ + (if $r != "" then {repo:$r} else {} end)
612
+ + (if $sid != "" then {session_id:$sid} else {} end)')
613
+ curl -sS -X POST "\${GATEWAY_URL}/api/v1/hook/capture" \\
614
+ -H "Content-Type: application/json" -H "Authorization: Bearer $JWT" \\
615
+ -d "$BODY" --max-time 3 >/dev/null 2>&1
616
+ ) &
617
+ }
618
+
619
+ # Dispatch local verdict capture based on capture_depth privacy setting.
620
+ # For full: always send full content. For evidence_on_violation: full only on violations. For local_only: anonymized only.
621
+ synkro_dispatch_capture() {
622
+ local hook_type="$1" verdict="$2" severity="$3" category="$4" tool_name="$5" repo="$6" session_id="$7"
623
+ local command="$8" reasoning="$9" rules_checked="\${10:-[]}" violated_rules="\${11:-[]}" recent_user_messages="\${12:-[]}"
624
+ local send_full=false
625
+ case "\${SYNKRO_CAPTURE_DEPTH:-local_only}" in
626
+ full) send_full=true ;;
627
+ evidence_on_violation)
628
+ case "$verdict" in block|warning|deny) send_full=true ;; esac ;;
629
+ esac
630
+ if [ "$send_full" = "true" ]; then
631
+ synkro_capture_local_full "$hook_type" "$verdict" "$severity" "$category" "$tool_name" "$repo" "$session_id" \\
632
+ "$command" "$reasoning" "$rules_checked" "$violated_rules" "$recent_user_messages"
633
+ else
634
+ synkro_capture_local "$hook_type" "$verdict" "$severity" "$category" "$tool_name" "$repo" "$session_id"
635
+ fi
636
+ }
637
+
584
638
  # Look up a rule's mode from cached rules. Returns "blocking" or "audit".
585
639
  synkro_rule_mode() {
586
640
  local rid="$1"
@@ -700,18 +754,24 @@ if [ "$ROUTE" = "local" ]; then
700
754
  fi
701
755
  synkro_parse_local_verdict "$CC_RESP"
702
756
 
757
+ # Build violated rules JSON for full-content capture
758
+ VIOLATED_JSON="[]"
759
+ [ -n "$LOCAL_RULE_ID" ] && VIOLATED_JSON=$(jq -n --arg r "$LOCAL_RULE_ID" '[$r]')
760
+
703
761
  if [ "$LOCAL_OK" = "false" ]; then
704
762
  RULE_MODE="\${LOCAL_RULE_MODE:-$(synkro_rule_mode "\${LOCAL_RULE_ID}")}"
705
763
  if [ "$RULE_MODE" = "audit" ]; then
706
764
  REASON="[synkro:local] bashGuard \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
707
765
  jq -n --arg m "$REASON" '{systemMessage: $m}'
708
- synkro_capture_local "bash" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
766
+ synkro_dispatch_capture "bash" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
767
+ "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "\${RECENT_USER_MESSAGES:-[]}"
709
768
  else
710
769
  CMD_HASH=$(printf '%s' "$COMMAND" | shasum -a 256 | cut -c1-16)
711
770
  if [ -n "$SESSION_ID" ] && synkro_consent_has_active "$SESSION_ID" "$CMD_HASH"; then
712
771
  REASON="[synkro:local] bashGuard \u2192 pass (consented retry): \${LOCAL_REASON:-retrying previously consented command}"
713
772
  jq -n --arg m "$REASON" '{systemMessage: $m}'
714
- synkro_capture_local "bash" "pass" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
773
+ synkro_dispatch_capture "bash" "pass" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
774
+ "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "\${RECENT_USER_MESSAGES:-[]}"
715
775
  else
716
776
  if [ -n "$SESSION_ID" ] && synkro_consent_has_consumed "$SESSION_ID" "$CMD_HASH"; then
717
777
  synkro_consent_clear_consumed "$SESSION_ID" "$CMD_HASH"
@@ -720,12 +780,14 @@ if [ "$ROUTE" = "local" ]; then
720
780
  REASON="[synkro:local] bashGuard \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
721
781
  jq -n --arg dec "$DEC" --arg reason "$REASON" --arg ctx "$REASON" \\
722
782
  '{systemMessage:$reason,hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$dec,permissionDecisionReason:$reason,additionalContext:$ctx}}'
723
- synkro_capture_local "bash" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
783
+ synkro_dispatch_capture "bash" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
784
+ "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "\${RECENT_USER_MESSAGES:-[]}"
724
785
  fi
725
786
  fi
726
787
  else
727
788
  jq -n --arg m "[synkro:local] bashGuard \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
728
- synkro_capture_local "bash" "pass" "audit" "\${LOCAL_CAT:-trivial_utility}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
789
+ synkro_dispatch_capture "bash" "pass" "audit" "\${LOCAL_CAT:-trivial_utility}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
790
+ "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "\${RECENT_USER_MESSAGES:-[]}"
729
791
  fi
730
792
  exit 0
731
793
  fi
@@ -893,22 +955,30 @@ if [ "$ROUTE" = "local" ]; then
893
955
  fi
894
956
  synkro_parse_local_verdict "$CC_RESP"
895
957
 
958
+ # Build edit content description and violated rules for full-content capture
959
+ EDIT_CONTENT="file=$FILE_PATH content=$(printf '%s' "$PROPOSED" | head -c 2000)"
960
+ VIOLATED_JSON="[]"
961
+ [ -n "$LOCAL_RULE_ID" ] && VIOLATED_JSON=$(jq -n --arg r "$LOCAL_RULE_ID" '[$r]')
962
+
896
963
  if [ "$LOCAL_OK" = "false" ]; then
897
964
  RULE_MODE="\${LOCAL_RULE_MODE:-$(synkro_rule_mode "\${LOCAL_RULE_ID}")}"
898
965
  if [ "$RULE_MODE" = "audit" ]; then
899
966
  REASON="[synkro:local] editGuard $FILE_SHORT \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
900
967
  jq -n --arg m "$REASON" '{systemMessage: $m}'
901
- synkro_capture_local "edit" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
968
+ synkro_dispatch_capture "edit" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
969
+ "$EDIT_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "[]"
902
970
  else
903
971
  if [ "$IS_HEADLESS" = "1" ]; then DEC="deny"; else DEC="ask"; fi
904
972
  REASON="[synkro:local] editGuard $FILE_SHORT \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
905
973
  jq -n --arg dec "$DEC" --arg reason "$REASON" --arg ctx "$REASON" \\
906
974
  '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$dec,permissionDecisionReason:$reason,additionalContext:$ctx}}'
907
- synkro_capture_local "edit" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
975
+ synkro_dispatch_capture "edit" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
976
+ "$EDIT_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "[]"
908
977
  fi
909
978
  else
910
979
  jq -n --arg m "[synkro:local] editGuard $FILE_SHORT \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
911
- synkro_capture_local "edit" "pass" "audit" "\${LOCAL_CAT:-trivial_edit}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
980
+ synkro_dispatch_capture "edit" "pass" "audit" "\${LOCAL_CAT:-trivial_edit}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
981
+ "$EDIT_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "[]"
912
982
  fi
913
983
  exit 0
914
984
  fi
@@ -1045,20 +1115,27 @@ if [ "$ROUTE" = "local" ]; then
1045
1115
  fi
1046
1116
  synkro_parse_local_verdict "$CC_RESP"
1047
1117
 
1118
+ SCAN_CONTENT="file=$FILE_PATH"
1119
+ SCAN_VIOLATED="[]"
1120
+ [ -n "$LOCAL_RULE_ID" ] && SCAN_VIOLATED=$(jq -n --arg r "$LOCAL_RULE_ID" '[$r]')
1121
+
1048
1122
  if [ "$LOCAL_OK" = "false" ]; then
1049
1123
  RULE_MODE="\${LOCAL_RULE_MODE:-$(synkro_rule_mode "\${LOCAL_RULE_ID}")}"
1050
1124
  if [ "$RULE_MODE" = "audit" ]; then
1051
1125
  REASON="[synkro:local] editScan $BASENAME \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
1052
1126
  jq -n --arg m "$REASON" '{systemMessage: $m}'
1053
- synkro_capture_local "edit_scan" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
1127
+ synkro_dispatch_capture "edit_scan" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
1128
+ "$SCAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$SCAN_VIOLATED" "[]"
1054
1129
  else
1055
1130
  REASON="[synkro:local] editScan $BASENAME \u2192 block: \${LOCAL_REASON:-policy violation}"
1056
1131
  jq -n --arg m "$REASON" '{systemMessage: $m, additionalContext: $m}'
1057
- synkro_capture_local "edit_scan" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
1132
+ synkro_dispatch_capture "edit_scan" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
1133
+ "$SCAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$SCAN_VIOLATED" "[]"
1058
1134
  fi
1059
1135
  else
1060
1136
  jq -n --arg m "[synkro:local] editScan $BASENAME \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
1061
- synkro_capture_local "edit_scan" "pass" "audit" "\${LOCAL_CAT:-trivial_edit}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID"
1137
+ synkro_dispatch_capture "edit_scan" "pass" "audit" "\${LOCAL_CAT:-trivial_edit}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
1138
+ "$SCAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "[]"
1062
1139
  fi
1063
1140
  exit 0
1064
1141
  fi
@@ -1104,6 +1181,103 @@ else
1104
1181
  echo '{}'
1105
1182
  fi
1106
1183
  exit 0
1184
+ `;
1185
+ CC_PLAN_JUDGE_SCRIPT = `#!/bin/bash
1186
+ SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
1187
+ . "$SCRIPT_DIR/_synkro-common.sh"
1188
+
1189
+ JWT=$(synkro_load_jwt)
1190
+ if [ -z "$JWT" ]; then echo '{}'; exit 0; fi
1191
+ synkro_ensure_fresh_jwt
1192
+
1193
+ PAYLOAD=$(cat)
1194
+ if [ -z "$PAYLOAD" ]; then echo '{}'; exit 0; fi
1195
+
1196
+ TOOL_NAME=$(echo "$PAYLOAD" | jq -r '.tool_name // empty' 2>/dev/null)
1197
+ if [ "$TOOL_NAME" != "ExitPlanMode" ]; then echo '{}'; exit 0; fi
1198
+
1199
+ PLAN=$(echo "$PAYLOAD" | jq -r '.tool_input.plan // empty' 2>/dev/null)
1200
+ if [ -z "$PLAN" ] || [ \${#PLAN} -lt 20 ]; then echo '{}'; exit 0; fi
1201
+
1202
+ SESSION_ID=$(echo "$PAYLOAD" | jq -r '.session_id // empty' 2>/dev/null)
1203
+ CWD=$(echo "$PAYLOAD" | jq -r '.cwd // empty' 2>/dev/null)
1204
+ GIT_REPO=$(synkro_detect_repo "\${CWD:-.}")
1205
+
1206
+ PLAN_SHORT=$(printf '%s' "$PLAN" | head -c 80)
1207
+ synkro_log "planReview checking: $PLAN_SHORT..."
1208
+
1209
+ synkro_load_config
1210
+ ROUTE=$(synkro_route)
1211
+
1212
+ if [ "$ROUTE" = "local" ]; then
1213
+ GRADER_FILE=$(mktemp -t synkro-plan.XXXXXX)
1214
+ trap "rm -f \\"$GRADER_FILE\\"" EXIT
1215
+ printf 'Plan:\\n%s\\nOrg rules: %s\\n' "$(printf '%s' "$PLAN" | head -c 8000)" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
1216
+
1217
+ CC_RESP=$(synkro_local_grade plan < "$GRADER_FILE" 2>&1)
1218
+ if [ $? -ne 0 ]; then
1219
+ echo '{}'; exit 0
1220
+ fi
1221
+ synkro_parse_local_verdict "$CC_RESP"
1222
+
1223
+ PLAN_CONTENT=$(printf '%s' "$PLAN" | head -c 2000)
1224
+ PLAN_VIOLATED="[]"
1225
+ [ -n "$LOCAL_RULE_ID" ] && PLAN_VIOLATED=$(jq -n --arg r "$LOCAL_RULE_ID" '[$r]')
1226
+
1227
+ if [ "$LOCAL_OK" = "false" ]; then
1228
+ VCOUNT=$(printf '%s' "$CC_RESP" | grep -c '<violation>' 2>/dev/null || echo "0")
1229
+ [ "$VCOUNT" = "0" ] && VCOUNT="1"
1230
+ MSG="[synkro:local] planReview \u2192 \${VCOUNT} rule(s) relevant\${LOCAL_RULE_ID:+ (first: $LOCAL_RULE_ID)}: \${LOCAL_REASON:-check org rules during implementation}"
1231
+ jq -n --arg m "$MSG" '{systemMessage: $m}'
1232
+ synkro_dispatch_capture "plan_review" "advisory" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "ExitPlanMode" "$GIT_REPO" "$SESSION_ID" \\
1233
+ "$PLAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$PLAN_VIOLATED" "[]"
1234
+ else
1235
+ jq -n --arg m "[synkro:local] planReview \u2192 clean: \${LOCAL_REASON:-no relevant org rules for this plan}" '{systemMessage: $m}'
1236
+ synkro_dispatch_capture "plan_review" "clean" "audit" "\${LOCAL_CAT:-general}" "ExitPlanMode" "$GIT_REPO" "$SESSION_ID" \\
1237
+ "$PLAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "[]"
1238
+ fi
1239
+ exit 0
1240
+ fi
1241
+
1242
+ # \u2500\u2500\u2500 Cloud grading \u2500\u2500\u2500
1243
+ BODY=$(jq -n \\
1244
+ --arg hook_event "PreToolUse" \\
1245
+ --arg tool_name "ExitPlanMode" \\
1246
+ --arg plan "$(printf '%s' "$PLAN" | head -c 16000)" \\
1247
+ --arg session_id "$SESSION_ID" \\
1248
+ --arg cwd "$CWD" \\
1249
+ --arg repo "$GIT_REPO" \\
1250
+ '{
1251
+ hook_event: $hook_event,
1252
+ tool_name: $tool_name,
1253
+ tool_input: {plan: $plan},
1254
+ session_id: (if ($session_id | length) > 0 then $session_id else null end),
1255
+ cwd: (if ($cwd | length) > 0 then $cwd else null end),
1256
+ repo: (if ($repo | length) > 0 then $repo else null end)
1257
+ }')
1258
+
1259
+ RESP=$(synkro_post_with_retry "\${GATEWAY_URL}/api/v1/hook/judge" "$BODY" 12)
1260
+
1261
+ if [ -z "$RESP" ]; then
1262
+ synkro_log "planReview \u2192 error (timeout)"
1263
+ echo '{}'
1264
+ exit 0
1265
+ fi
1266
+
1267
+ if ! echo "$RESP" | jq -e '.hook_response' >/dev/null 2>&1; then
1268
+ echo '{}'
1269
+ exit 0
1270
+ fi
1271
+
1272
+ # Force advisory: convert any blocking decision to systemMessage
1273
+ HR=$(echo "$RESP" | jq -c '.hook_response')
1274
+ if echo "$HR" | jq -e '.hookSpecificOutput.permissionDecision' >/dev/null 2>&1; then
1275
+ REASON=$(echo "$HR" | jq -r '.hookSpecificOutput.permissionDecisionReason // "check org rules"' 2>/dev/null)
1276
+ jq -n --arg m "[synkro:cloud] planReview \u2192 advisory: $REASON" '{systemMessage: $m}'
1277
+ else
1278
+ echo "$HR"
1279
+ fi
1280
+ exit 0
1107
1281
  `;
1108
1282
  CC_STOP_SUMMARY_SCRIPT = `#!/bin/bash
1109
1283
  SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
@@ -1200,20 +1374,18 @@ RESP=$(curl -sS -G "\${GATEWAY_URL}/api/v1/hook/config" \\
1200
1374
  --data-urlencode "repo=\${GIT_REPO:-}" \\
1201
1375
  -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null || echo "")
1202
1376
 
1203
- PLAN_NUDGE="Before implementing any multi-step plan, call the synkro-guardrails analyze_plan tool with your implementation plan to check for relevant org coding rules."
1204
-
1205
1377
  OPEN=0
1206
1378
  if [ -n "$RESP" ]; then
1207
1379
  OPEN=$(echo "$RESP" | jq -r '.session_context.open_findings // 0' 2>/dev/null)
1208
1380
  fi
1209
1381
 
1210
1382
  if [ "$OPEN" = "0" ] || [ -z "$OPEN" ]; then
1211
- jq -n --arg m "$ROUTE_LINE"$'\\n'"[synkro] $PLAN_NUDGE" '{systemMessage: $m}'
1383
+ jq -n --arg m "$ROUTE_LINE" '{systemMessage: $m}'
1212
1384
  else
1213
1385
  if [ "$OPEN" = "1" ]; then
1214
- SYS_MSG="$ROUTE_LINE"$'\\n'"[synkro] session start \u2192 1 open finding in this repo from a prior session. $PLAN_NUDGE"
1386
+ SYS_MSG="$ROUTE_LINE"$'\\n'"[synkro] session start \u2192 1 open finding in this repo from a prior session."
1215
1387
  else
1216
- SYS_MSG="$ROUTE_LINE"$'\\n'"[synkro] session start \u2192 \${OPEN} open findings in this repo from prior sessions. $PLAN_NUDGE"
1388
+ SYS_MSG="$ROUTE_LINE"$'\\n'"[synkro] session start \u2192 \${OPEN} open findings in this repo from prior sessions."
1217
1389
  fi
1218
1390
  jq -n --arg m "$SYS_MSG" '{systemMessage: $m}'
1219
1391
  fi
@@ -3349,7 +3521,7 @@ async function fetchPrimers() {
3349
3521
  }
3350
3522
  async function getPrimer(role) {
3351
3523
  const prompts = await fetchPrimers();
3352
- const primer = role === "grade-edit" ? prompts.grader_primer_edit : prompts.grader_primer_bash;
3524
+ const primer = role === "grade-edit" ? prompts.grader_primer_edit : role === "grade-plan" ? prompts.grader_primer_plan : prompts.grader_primer_bash;
3353
3525
  if (!primer) {
3354
3526
  throw new Error(`No primer for role "${role}" returned from API.`);
3355
3527
  }
@@ -3658,6 +3830,7 @@ function writeHookScripts() {
3658
3830
  const bashFollowupScriptPath = join11(HOOKS_DIR, "cc-bash-followup.sh");
3659
3831
  const editCaptureScriptPath = join11(HOOKS_DIR, "cc-edit-capture.sh");
3660
3832
  const editPrecheckScriptPath = join11(HOOKS_DIR, "cc-edit-precheck.sh");
3833
+ const planJudgeScriptPath = join11(HOOKS_DIR, "cc-plan-judge.sh");
3661
3834
  const stopSummaryScriptPath = join11(HOOKS_DIR, "cc-stop-summary.sh");
3662
3835
  const sessionStartScriptPath = join11(HOOKS_DIR, "cc-session-start.sh");
3663
3836
  const transcriptSyncScriptPath = join11(HOOKS_DIR, "cc-transcript-sync.sh");
@@ -3670,6 +3843,7 @@ function writeHookScripts() {
3670
3843
  writeFileSync7(bashFollowupScriptPath, CC_BASH_FOLLOWUP_SCRIPT, "utf-8");
3671
3844
  writeFileSync7(editCaptureScriptPath, CC_EDIT_CAPTURE_SCRIPT, "utf-8");
3672
3845
  writeFileSync7(editPrecheckScriptPath, CC_EDIT_PRECHECK_SCRIPT, "utf-8");
3846
+ writeFileSync7(planJudgeScriptPath, CC_PLAN_JUDGE_SCRIPT, "utf-8");
3673
3847
  writeFileSync7(stopSummaryScriptPath, CC_STOP_SUMMARY_SCRIPT, "utf-8");
3674
3848
  writeFileSync7(sessionStartScriptPath, CC_SESSION_START_SCRIPT, "utf-8");
3675
3849
  writeFileSync7(transcriptSyncScriptPath, CC_TRANSCRIPT_SYNC_SCRIPT, "utf-8");
@@ -3682,6 +3856,7 @@ function writeHookScripts() {
3682
3856
  chmodSync2(bashFollowupScriptPath, 493);
3683
3857
  chmodSync2(editCaptureScriptPath, 493);
3684
3858
  chmodSync2(editPrecheckScriptPath, 493);
3859
+ chmodSync2(planJudgeScriptPath, 493);
3685
3860
  chmodSync2(stopSummaryScriptPath, 493);
3686
3861
  chmodSync2(sessionStartScriptPath, 493);
3687
3862
  chmodSync2(transcriptSyncScriptPath, 493);
@@ -3695,6 +3870,7 @@ function writeHookScripts() {
3695
3870
  bashFollowupScript: bashFollowupScriptPath,
3696
3871
  editCaptureScript: editCaptureScriptPath,
3697
3872
  editPrecheckScript: editPrecheckScriptPath,
3873
+ planJudgeScript: planJudgeScriptPath,
3698
3874
  stopSummaryScript: stopSummaryScriptPath,
3699
3875
  sessionStartScript: sessionStartScriptPath,
3700
3876
  transcriptSyncScript: transcriptSyncScriptPath,
@@ -3733,7 +3909,7 @@ function writeConfigEnv(opts) {
3733
3909
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3734
3910
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3735
3911
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
3736
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.32")}`
3912
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.33")}`
3737
3913
  ];
3738
3914
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
3739
3915
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -3862,6 +4038,7 @@ function isAlreadyInstalled() {
3862
4038
  join11(HOOKS_DIR, "cc-bash-followup.sh"),
3863
4039
  join11(HOOKS_DIR, "cc-edit-precheck.sh"),
3864
4040
  join11(HOOKS_DIR, "cc-edit-capture.sh"),
4041
+ join11(HOOKS_DIR, "cc-plan-judge.sh"),
3865
4042
  join11(HOOKS_DIR, "cc-stop-summary.sh"),
3866
4043
  join11(HOOKS_DIR, "cc-session-start.sh")
3867
4044
  ];
@@ -4000,6 +4177,7 @@ async function installCommand(opts = {}) {
4000
4177
  console.log(` ${scripts.bashFollowupScript}`);
4001
4178
  console.log(` ${scripts.editCaptureScript}`);
4002
4179
  console.log(` ${scripts.editPrecheckScript}`);
4180
+ console.log(` ${scripts.planJudgeScript}`);
4003
4181
  console.log(` ${scripts.stopSummaryScript}`);
4004
4182
  console.log(` ${scripts.sessionStartScript}`);
4005
4183
  console.log(` ${scripts.transcriptSyncScript}
@@ -4034,6 +4212,7 @@ async function installCommand(opts = {}) {
4034
4212
  bashFollowupScriptPath: scripts.bashFollowupScript,
4035
4213
  editCaptureScriptPath: scripts.editCaptureScript,
4036
4214
  editPrecheckScriptPath: scripts.editPrecheckScript,
4215
+ planJudgeScriptPath: scripts.planJudgeScript,
4037
4216
  stopSummaryScriptPath: scripts.stopSummaryScript,
4038
4217
  sessionStartScriptPath: scripts.sessionStartScript,
4039
4218
  transcriptSyncScriptPath: scripts.transcriptSyncScript,
@@ -6079,8 +6258,9 @@ async function gradeCommand(args2) {
6079
6258
  let role;
6080
6259
  if (mode === "edit") role = "grade-edit";
6081
6260
  else if (mode === "bash") role = "grade-bash";
6261
+ else if (mode === "plan") role = "grade-plan";
6082
6262
  else {
6083
- console.error("Usage: synkro grade <edit|bash>");
6263
+ console.error("Usage: synkro grade <edit|bash|plan>");
6084
6264
  process.exit(2);
6085
6265
  }
6086
6266
  const payload = await readStdin();