@synkro-sh/cli 1.4.37 → 1.4.39

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
@@ -508,16 +508,25 @@ synkro_channel_up() {
508
508
  (exec 3<>/dev/tcp/127.0.0.1/\${SYNKRO_CHANNEL_PORT:-8929}) 2>/dev/null && exec 3<&- 3>&-
509
509
  }
510
510
 
511
- # Fetch hook config. Sets SYNKRO_CAPTURE_DEPTH, SYNKRO_TIER, SYNKRO_RULES.
511
+ # Fetch hook config. Sets SYNKRO_CAPTURE_DEPTH, SYNKRO_TIER, SYNKRO_RULES, SYNKRO_SILENT, SYNKRO_POLICY_NAME.
512
512
  synkro_load_config() {
513
513
  local resp
514
514
  resp=$(curl -sS "\${GATEWAY_URL}/api/v1/hook/config\${1:+?$1}" -H "Authorization: Bearer $JWT" --max-time 4 2>/dev/null || echo "")
515
515
  if [ -z "$resp" ]; then return; fi
516
516
  SYNKRO_CAPTURE_DEPTH=$(echo "$resp" | jq -r '.capture_depth // "local_only"' 2>/dev/null)
517
517
  SYNKRO_TIER=$(echo "$resp" | jq -r '.tier // "standard"' 2>/dev/null)
518
+ SYNKRO_SILENT=$(echo "$resp" | jq -r '.silent_mode // false' 2>/dev/null)
519
+ SYNKRO_POLICY_NAME=$(echo "$resp" | jq -r '.active_policy_name // empty' 2>/dev/null)
518
520
  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 "[]")
519
521
  }
520
522
 
523
+ # Build the tag prefix for system messages: [synkro] or [synkro:PolicyName] or [synkro:silent]
524
+ synkro_tag() {
525
+ 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]"
528
+ }
529
+
521
530
  # Decide routing: "local" (grade on device) or "cloud" (POST to server)
522
531
  synkro_route() {
523
532
  [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ] && echo "local" && return
@@ -645,6 +654,8 @@ synkro_rule_mode() {
645
654
 
646
655
  SYNKRO_CONSENT_FILE="$HOME/.synkro/.local-consent"
647
656
 
657
+ _TAB=$(printf '\\t')
658
+
648
659
  synkro_consent_grant() {
649
660
  local sid="$1" hash="$2"
650
661
  printf '%s\\t%s\\tactive\\n' "$sid" "$hash" >> "$SYNKRO_CONSENT_FILE" 2>/dev/null || true
@@ -652,26 +663,28 @@ synkro_consent_grant() {
652
663
 
653
664
  synkro_consent_has_active() {
654
665
  local sid="$1" hash="$2"
655
- grep -q "^$sid\\\\t$hash\\\\tactive$" "$SYNKRO_CONSENT_FILE" 2>/dev/null
666
+ grep -q "^\${sid}\${_TAB}\${hash}\${_TAB}active$" "$SYNKRO_CONSENT_FILE" 2>/dev/null
656
667
  }
657
668
 
658
669
  synkro_consent_consume() {
659
670
  local sid="$1" hash="$2"
660
671
  [ ! -f "$SYNKRO_CONSENT_FILE" ] && return
661
672
  local tmp="\${SYNKRO_CONSENT_FILE}.tmp"
662
- sed "s/^$sid\\\\t$hash\\\\tactive$/$sid\\t$hash\\tconsumed/" "$SYNKRO_CONSENT_FILE" > "$tmp" 2>/dev/null && mv "$tmp" "$SYNKRO_CONSENT_FILE" 2>/dev/null || true
673
+ local pat="\${sid}\${_TAB}\${hash}\${_TAB}active"
674
+ local rep="\${sid}\${_TAB}\${hash}\${_TAB}consumed"
675
+ awk -v p="$pat" -v r="$rep" '{if($0==p)print r;else print}' "$SYNKRO_CONSENT_FILE" > "$tmp" 2>/dev/null && mv "$tmp" "$SYNKRO_CONSENT_FILE" 2>/dev/null || true
663
676
  }
664
677
 
665
678
  synkro_consent_has_consumed() {
666
679
  local sid="$1" hash="$2"
667
- grep -q "^$sid\\\\t$hash\\\\tconsumed$" "$SYNKRO_CONSENT_FILE" 2>/dev/null
680
+ grep -q "^\${sid}\${_TAB}\${hash}\${_TAB}consumed$" "$SYNKRO_CONSENT_FILE" 2>/dev/null
668
681
  }
669
682
 
670
683
  synkro_consent_clear_consumed() {
671
684
  local sid="$1" hash="$2"
672
685
  [ ! -f "$SYNKRO_CONSENT_FILE" ] && return
673
686
  local tmp="\${SYNKRO_CONSENT_FILE}.tmp"
674
- grep -v "^$sid\\\\t$hash\\\\tconsumed$" "$SYNKRO_CONSENT_FILE" > "$tmp" 2>/dev/null && mv "$tmp" "$SYNKRO_CONSENT_FILE" 2>/dev/null || true
687
+ grep -v "^\${sid}\${_TAB}\${hash}\${_TAB}consumed$" "$SYNKRO_CONSENT_FILE" > "$tmp" 2>/dev/null && mv "$tmp" "$SYNKRO_CONSENT_FILE" 2>/dev/null || true
675
688
  }
676
689
 
677
690
  synkro_post_with_retry() {
@@ -737,17 +750,24 @@ IS_HEADLESS="\${SYNKRO_HEADLESS:-0}"
737
750
  case "$PERMISSION_MODE" in acceptEdits|bypassPermissions|plan|auto) IS_HEADLESS="1" ;; esac
738
751
 
739
752
  synkro_load_config
753
+ TAG=$(synkro_tag)
754
+
755
+ if [ "$SYNKRO_SILENT" = "true" ]; then
756
+ jq -n --arg m "$TAG bashGuard \u2192 skipped (silent mode)" '{systemMessage: $m}'
757
+ exit 0
758
+ fi
759
+
740
760
  ROUTE=$(synkro_route)
741
761
 
742
762
  if [ "$ROUTE" = "local" ]; then
743
763
  # \u2500\u2500\u2500 Local grading (local_only privacy or local-cc channel) \u2500\u2500\u2500
744
764
  GRADER_FILE=$(mktemp -t synkro-bash.XXXXXX)
745
765
  trap "rm -f \\"$GRADER_FILE\\"" EXIT
746
- printf 'Command: %s\\nUser intent: %s\\nOrg rules: %s\\n' "$COMMAND" "\${USER_INTENT:-none stated}" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
766
+ printf 'Working directory: %s\\nRepo: %s\\nCommand: %s\\nUser intent: %s\\nOrg rules: %s\\n' "\${CWD:-.}" "\${GIT_REPO:-unknown}" "$COMMAND" "\${USER_INTENT:-none stated}" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
747
767
 
748
768
  CC_RESP=$(synkro_local_grade bash < "$GRADER_FILE" 2>&1)
749
769
  if [ $? -ne 0 ]; then
750
- jq -n --arg m "[synkro:local] bashGuard \u2192 pass: local grader unavailable (run synkro local-cc start)" '{systemMessage: $m}'
770
+ jq -n --arg m "$TAG bashGuard \u2192 pass: local grader unavailable (run synkro local-cc start)" '{systemMessage: $m}'
751
771
  exit 0
752
772
  fi
753
773
  synkro_parse_local_verdict "$CC_RESP"
@@ -759,31 +779,20 @@ if [ "$ROUTE" = "local" ]; then
759
779
  if [ "$LOCAL_OK" = "false" ]; then
760
780
  RULE_MODE="\${LOCAL_RULE_MODE:-$(synkro_rule_mode "\${LOCAL_RULE_ID}")}"
761
781
  if [ "$RULE_MODE" = "audit" ]; then
762
- REASON="[synkro:local] bashGuard \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
782
+ REASON="$TAG bashGuard \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
763
783
  jq -n --arg m "$REASON" '{systemMessage: $m}'
764
784
  synkro_dispatch_capture "bash" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
765
785
  "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "\${RECENT_USER_MESSAGES:-[]}"
766
786
  else
767
- CMD_HASH=$(printf '%s' "$COMMAND" | shasum -a 256 | cut -c1-16)
768
- if [ -n "$SESSION_ID" ] && synkro_consent_has_active "$SESSION_ID" "$CMD_HASH"; then
769
- REASON="[synkro:local] bashGuard \u2192 pass (consented retry): \${LOCAL_REASON:-retrying previously consented command}"
770
- jq -n --arg m "$REASON" '{systemMessage: $m}'
771
- synkro_dispatch_capture "bash" "pass" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
772
- "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "\${RECENT_USER_MESSAGES:-[]}"
773
- else
774
- if [ -n "$SESSION_ID" ] && synkro_consent_has_consumed "$SESSION_ID" "$CMD_HASH"; then
775
- synkro_consent_clear_consumed "$SESSION_ID" "$CMD_HASH"
776
- fi
777
- if [ "$IS_HEADLESS" = "1" ]; then DEC="deny"; else DEC="ask"; fi
778
- REASON="[synkro:local] bashGuard \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
779
- jq -n --arg dec "$DEC" --arg reason "$REASON" --arg ctx "$REASON" \\
780
- '{systemMessage:$reason,hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$dec,permissionDecisionReason:$reason,additionalContext:$ctx}}'
781
- synkro_dispatch_capture "bash" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
782
- "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "\${RECENT_USER_MESSAGES:-[]}"
783
- fi
787
+ if [ "$IS_HEADLESS" = "1" ]; then DEC="deny"; else DEC="ask"; fi
788
+ REASON="$TAG bashGuard \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
789
+ jq -n --arg dec "$DEC" --arg reason "$REASON" --arg ctx "$REASON" \\
790
+ '{systemMessage:$reason,hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$dec,permissionDecisionReason:$reason,additionalContext:$ctx}}'
791
+ synkro_dispatch_capture "bash" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
792
+ "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "\${RECENT_USER_MESSAGES:-[]}"
784
793
  fi
785
794
  else
786
- jq -n --arg m "[synkro:local] bashGuard \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
795
+ jq -n --arg m "$TAG bashGuard \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
787
796
  synkro_dispatch_capture "bash" "pass" "audit" "\${LOCAL_CAT:-trivial_utility}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
788
797
  "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "\${RECENT_USER_MESSAGES:-[]}"
789
798
  fi
@@ -938,17 +947,24 @@ if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
938
947
  fi
939
948
 
940
949
  synkro_load_config
950
+ TAG=$(synkro_tag)
951
+
952
+ if [ "$SYNKRO_SILENT" = "true" ]; then
953
+ jq -n --arg m "$TAG editGuard \u2192 skipped (silent mode)" '{systemMessage: $m}'
954
+ exit 0
955
+ fi
956
+
941
957
  ROUTE=$(synkro_route)
942
958
 
943
959
  if [ "$ROUTE" = "local" ]; then
944
960
  # \u2500\u2500\u2500 Local grading (local_only privacy or local-cc channel) \u2500\u2500\u2500
945
961
  GRADER_FILE=$(mktemp -t synkro-edit.XXXXXX)
946
962
  trap "rm -f \\"$GRADER_FILE\\"" EXIT
947
- printf 'File: %s\\nProposed content (first 4000 chars):\\n%s\\nUser intent: %s\\nOrg rules: %s\\n' "$FILE_PATH" "$(printf '%s' "$PROPOSED" | head -c 4000)" "\${USER_INTENT:-none stated}" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
963
+ printf 'Working directory: %s\\nRepo: %s\\nFile: %s\\nProposed content (first 4000 chars):\\n%s\\nUser intent: %s\\nOrg rules: %s\\n' "\${CWD:-.}" "\${GIT_REPO:-unknown}" "$FILE_PATH" "$(printf '%s' "$PROPOSED" | head -c 4000)" "\${USER_INTENT:-none stated}" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
948
964
 
949
965
  CC_RESP=$(synkro_local_grade edit < "$GRADER_FILE" 2>&1)
950
966
  if [ $? -ne 0 ]; then
951
- jq -n --arg m "[synkro:local] editGuard \u2192 pass: local grader unavailable (run synkro local-cc start)" '{systemMessage: $m}'
967
+ jq -n --arg m "$TAG editGuard \u2192 pass: local grader unavailable (run synkro local-cc start)" '{systemMessage: $m}'
952
968
  exit 0
953
969
  fi
954
970
  synkro_parse_local_verdict "$CC_RESP"
@@ -961,20 +977,20 @@ if [ "$ROUTE" = "local" ]; then
961
977
  if [ "$LOCAL_OK" = "false" ]; then
962
978
  RULE_MODE="\${LOCAL_RULE_MODE:-$(synkro_rule_mode "\${LOCAL_RULE_ID}")}"
963
979
  if [ "$RULE_MODE" = "audit" ]; then
964
- REASON="[synkro:local] editGuard $FILE_SHORT \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
980
+ REASON="$TAG editGuard $FILE_SHORT \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
965
981
  jq -n --arg m "$REASON" '{systemMessage: $m}'
966
982
  synkro_dispatch_capture "edit" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
967
983
  "$EDIT_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "[]"
968
984
  else
969
985
  if [ "$IS_HEADLESS" = "1" ]; then DEC="deny"; else DEC="ask"; fi
970
- REASON="[synkro:local] editGuard $FILE_SHORT \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
986
+ REASON="$TAG editGuard $FILE_SHORT \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
971
987
  jq -n --arg dec "$DEC" --arg reason "$REASON" --arg ctx "$REASON" \\
972
988
  '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$dec,permissionDecisionReason:$reason,additionalContext:$ctx}}'
973
989
  synkro_dispatch_capture "edit" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
974
990
  "$EDIT_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "[]"
975
991
  fi
976
992
  else
977
- jq -n --arg m "[synkro:local] editGuard $FILE_SHORT \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
993
+ jq -n --arg m "$TAG editGuard $FILE_SHORT \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
978
994
  synkro_dispatch_capture "edit" "pass" "audit" "\${LOCAL_CAT:-trivial_edit}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
979
995
  "$EDIT_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "[]"
980
996
  fi
@@ -1099,13 +1115,19 @@ while [ "$_PKG_DIR" != "/" ]; do
1099
1115
  done
1100
1116
 
1101
1117
  synkro_load_config
1118
+ TAG=$(synkro_tag)
1119
+
1120
+ if [ "$SYNKRO_SILENT" = "true" ]; then
1121
+ echo '{}'; exit 0
1122
+ fi
1123
+
1102
1124
  ROUTE=$(synkro_route)
1103
1125
 
1104
1126
  if [ "$ROUTE" = "local" ]; then
1105
1127
  # \u2500\u2500\u2500 Local edit scan (local_only privacy or local-cc channel) \u2500\u2500\u2500
1106
1128
  GRADER_FILE=$(mktemp -t synkro-escan.XXXXXX)
1107
1129
  trap "rm -f \\"$GRADER_FILE\\"" EXIT
1108
- printf 'File: %s\\nContent (first 4000 chars):\\n%s\\nOrg rules: %s\\n' "$FILE_PATH" "$(printf '%s' "$FILE_CONTENT" | head -c 4000)" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
1130
+ printf 'Working directory: %s\\nRepo: %s\\nFile: %s\\nContent (first 4000 chars):\\n%s\\nOrg rules: %s\\n' "\${CWD:-.}" "\${GIT_REPO:-unknown}" "$FILE_PATH" "$(printf '%s' "$FILE_CONTENT" | head -c 4000)" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
1109
1131
 
1110
1132
  CC_RESP=$(synkro_local_grade edit < "$GRADER_FILE" 2>&1)
1111
1133
  if [ $? -ne 0 ]; then
@@ -1120,18 +1142,18 @@ if [ "$ROUTE" = "local" ]; then
1120
1142
  if [ "$LOCAL_OK" = "false" ]; then
1121
1143
  RULE_MODE="\${LOCAL_RULE_MODE:-$(synkro_rule_mode "\${LOCAL_RULE_ID}")}"
1122
1144
  if [ "$RULE_MODE" = "audit" ]; then
1123
- REASON="[synkro:local] editScan $BASENAME \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
1145
+ REASON="$TAG editScan $BASENAME \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
1124
1146
  jq -n --arg m "$REASON" '{systemMessage: $m}'
1125
1147
  synkro_dispatch_capture "edit_scan" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
1126
1148
  "$SCAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$SCAN_VIOLATED" "[]"
1127
1149
  else
1128
- REASON="[synkro:local] editScan $BASENAME \u2192 block: \${LOCAL_REASON:-policy violation}"
1150
+ REASON="$TAG editScan $BASENAME \u2192 block: \${LOCAL_REASON:-policy violation}"
1129
1151
  jq -n --arg m "$REASON" '{systemMessage: $m, additionalContext: $m}'
1130
1152
  synkro_dispatch_capture "edit_scan" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
1131
1153
  "$SCAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$SCAN_VIOLATED" "[]"
1132
1154
  fi
1133
1155
  else
1134
- jq -n --arg m "[synkro:local] editScan $BASENAME \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
1156
+ jq -n --arg m "$TAG editScan $BASENAME \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
1135
1157
  synkro_dispatch_capture "edit_scan" "pass" "audit" "\${LOCAL_CAT:-trivial_edit}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
1136
1158
  "$SCAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "[]"
1137
1159
  fi
@@ -1205,12 +1227,19 @@ PLAN_SHORT=$(printf '%s' "$PLAN" | head -c 80)
1205
1227
  synkro_log "planReview checking: $PLAN_SHORT..."
1206
1228
 
1207
1229
  synkro_load_config
1230
+ TAG=$(synkro_tag)
1231
+
1232
+ if [ "$SYNKRO_SILENT" = "true" ]; then
1233
+ jq -n --arg m "$TAG planReview \u2192 skipped (silent mode)" '{systemMessage: $m}'
1234
+ exit 0
1235
+ fi
1236
+
1208
1237
  ROUTE=$(synkro_route)
1209
1238
 
1210
1239
  if [ "$ROUTE" = "local" ]; then
1211
1240
  GRADER_FILE=$(mktemp -t synkro-plan.XXXXXX)
1212
1241
  trap "rm -f \\"$GRADER_FILE\\"" EXIT
1213
- printf 'Plan:\\n%s\\nOrg rules: %s\\n' "$(printf '%s' "$PLAN" | head -c 8000)" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
1242
+ printf 'Working directory: %s\\nRepo: %s\\nPlan:\\n%s\\nOrg rules: %s\\n' "\${CWD:-.}" "\${GIT_REPO:-unknown}" "$(printf '%s' "$PLAN" | head -c 8000)" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
1214
1243
 
1215
1244
  CC_RESP=$(synkro_local_grade plan < "$GRADER_FILE" 2>&1)
1216
1245
  if [ $? -ne 0 ]; then
@@ -1225,12 +1254,12 @@ if [ "$ROUTE" = "local" ]; then
1225
1254
  if [ "$LOCAL_OK" = "false" ]; then
1226
1255
  VCOUNT=$(printf '%s' "$CC_RESP" | grep -c '<violation>' 2>/dev/null || echo "0")
1227
1256
  [ "$VCOUNT" = "0" ] && VCOUNT="1"
1228
- MSG="[synkro:local] planReview \u2192 \${VCOUNT} rule(s) relevant\${LOCAL_RULE_ID:+ (first: $LOCAL_RULE_ID)}: \${LOCAL_REASON:-check org rules during implementation}"
1257
+ MSG="$TAG planReview \u2192 \${VCOUNT} rule(s) relevant\${LOCAL_RULE_ID:+ (first: $LOCAL_RULE_ID)}: \${LOCAL_REASON:-check org rules during implementation}"
1229
1258
  jq -n --arg m "$MSG" '{systemMessage: $m}'
1230
1259
  synkro_dispatch_capture "plan_review" "advisory" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "ExitPlanMode" "$GIT_REPO" "$SESSION_ID" \\
1231
1260
  "$PLAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$PLAN_VIOLATED" "[]"
1232
1261
  else
1233
- jq -n --arg m "[synkro:local] planReview \u2192 clean: \${LOCAL_REASON:-no relevant org rules for this plan}" '{systemMessage: $m}'
1262
+ jq -n --arg m "$TAG planReview \u2192 clean: \${LOCAL_REASON:-no relevant org rules for this plan}" '{systemMessage: $m}'
1234
1263
  synkro_dispatch_capture "plan_review" "clean" "audit" "\${LOCAL_CAT:-general}" "ExitPlanMode" "$GIT_REPO" "$SESSION_ID" \\
1235
1264
  "$PLAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "[]"
1236
1265
  fi
@@ -1271,7 +1300,7 @@ fi
1271
1300
  HR=$(echo "$RESP" | jq -c '.hook_response')
1272
1301
  if echo "$HR" | jq -e '.hookSpecificOutput.permissionDecision' >/dev/null 2>&1; then
1273
1302
  REASON=$(echo "$HR" | jq -r '.hookSpecificOutput.permissionDecisionReason // "check org rules"' 2>/dev/null)
1274
- jq -n --arg m "[synkro:cloud] planReview \u2192 advisory: $REASON" '{systemMessage: $m}'
1303
+ jq -n --arg m "$TAG planReview \u2192 advisory: $REASON" '{systemMessage: $m}'
1275
1304
  else
1276
1305
  echo "$HR"
1277
1306
  fi
@@ -1339,10 +1368,12 @@ OPEN=$(echo "$RESP" | jq -r '.open // 0' 2>/dev/null)
1339
1368
 
1340
1369
  if [ "\${EDITS:-0}" = "0" ] || [ -z "$EDITS" ]; then echo '{}'; exit 0; fi
1341
1370
 
1371
+ synkro_load_config
1372
+ TAG=$(synkro_tag)
1342
1373
  if [ "\${FINDINGS:-0}" = "0" ] || [ -z "$FINDINGS" ]; then
1343
- SYS_MSG="[synkro] stop \u2192 0 issues across \${EDITS} edit(s), session complete"
1374
+ SYS_MSG="$TAG stop \u2192 0 issues across \${EDITS} edit(s), session complete"
1344
1375
  else
1345
- SYS_MSG="[synkro] stop \u2192 \${FINDINGS} finding(s): \${AUTO_FIXED} auto-fixed, \${OPEN} open"
1376
+ SYS_MSG="$TAG stop \u2192 \${FINDINGS} finding(s): \${AUTO_FIXED} auto-fixed, \${OPEN} open"
1346
1377
  fi
1347
1378
 
1348
1379
  jq -n --arg m "$SYS_MSG" '{systemMessage: $m}'
@@ -1356,11 +1387,30 @@ JWT=$(synkro_load_jwt)
1356
1387
 
1357
1388
  # Route preamble
1358
1389
  SYNKRO_PORT="\${SYNKRO_CHANNEL_PORT:-8929}"
1390
+ PAYLOAD=$(cat)
1391
+ CWD=$(echo "$PAYLOAD" | jq -r '.cwd // empty' 2>/dev/null)
1392
+ SESSION_ID=$(echo "$PAYLOAD" | jq -r '.session_id // empty' 2>/dev/null)
1393
+ GIT_REPO=$(synkro_detect_repo "\${CWD:-.}")
1394
+
1395
+ RESP=""
1396
+ if [ -n "$JWT" ]; then
1397
+ RESP=$(curl -sS -G "\${GATEWAY_URL}/api/v1/hook/config" \\
1398
+ --data-urlencode "session_id=\${SESSION_ID:-}" \\
1399
+ --data-urlencode "repo=\${GIT_REPO:-}" \\
1400
+ -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null || echo "")
1401
+ if [ -n "$RESP" ]; then
1402
+ SYNKRO_SILENT=$(echo "$RESP" | jq -r '.silent_mode // false' 2>/dev/null)
1403
+ SYNKRO_POLICY_NAME=$(echo "$RESP" | jq -r '.active_policy_name // empty' 2>/dev/null)
1404
+ fi
1405
+ fi
1406
+
1407
+ TAG=$(synkro_tag)
1408
+
1359
1409
  if (exec 3<>/dev/tcp/127.0.0.1/"$SYNKRO_PORT") 2>/dev/null; then
1360
1410
  exec 3<&- 3>&- 2>/dev/null || true
1361
- ROUTE_LINE="[synkro] inference: local-cc (channel reachable on 127.0.0.1:$SYNKRO_PORT)"
1411
+ ROUTE_LINE="$TAG inference: local-cc (channel reachable on 127.0.0.1:$SYNKRO_PORT)"
1362
1412
  else
1363
- ROUTE_LINE="[synkro] inference: cloud (local-cc channel not reachable)"
1413
+ ROUTE_LINE="$TAG inference: cloud (local-cc channel not reachable)"
1364
1414
  fi
1365
1415
 
1366
1416
  if [ -z "$JWT" ]; then
@@ -1368,16 +1418,6 @@ if [ -z "$JWT" ]; then
1368
1418
  exit 0
1369
1419
  fi
1370
1420
 
1371
- PAYLOAD=$(cat)
1372
- CWD=$(echo "$PAYLOAD" | jq -r '.cwd // empty' 2>/dev/null)
1373
- SESSION_ID=$(echo "$PAYLOAD" | jq -r '.session_id // empty' 2>/dev/null)
1374
- GIT_REPO=$(synkro_detect_repo "\${CWD:-.}")
1375
-
1376
- RESP=$(curl -sS -G "\${GATEWAY_URL}/api/v1/hook/config" \\
1377
- --data-urlencode "session_id=\${SESSION_ID:-}" \\
1378
- --data-urlencode "repo=\${GIT_REPO:-}" \\
1379
- -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null || echo "")
1380
-
1381
1421
  OPEN=0
1382
1422
  if [ -n "$RESP" ]; then
1383
1423
  OPEN=$(echo "$RESP" | jq -r '.session_context.open_findings // 0' 2>/dev/null)
@@ -1387,9 +1427,9 @@ if [ "$OPEN" = "0" ] || [ -z "$OPEN" ]; then
1387
1427
  jq -n --arg m "$ROUTE_LINE" '{systemMessage: $m}'
1388
1428
  else
1389
1429
  if [ "$OPEN" = "1" ]; then
1390
- SYS_MSG="$ROUTE_LINE"$'\\n'"[synkro] session start \u2192 1 open finding in this repo from a prior session."
1430
+ SYS_MSG="$ROUTE_LINE"$'\\n'"$TAG session start \u2192 1 open finding in this repo from a prior session."
1391
1431
  else
1392
- SYS_MSG="$ROUTE_LINE"$'\\n'"[synkro] session start \u2192 \${OPEN} open findings in this repo from prior sessions."
1432
+ SYS_MSG="$ROUTE_LINE"$'\\n'"$TAG session start \u2192 \${OPEN} open findings in this repo from prior sessions."
1393
1433
  fi
1394
1434
  jq -n --arg m "$SYS_MSG" '{systemMessage: $m}'
1395
1435
  fi
@@ -1425,6 +1465,7 @@ BODY=$(jq -n --arg sid "$SESSION_ID" --arg tid "$TOOL_USE_ID" \\
1425
1465
  # Local consent tracking: command ran = user consented
1426
1466
  # On success \u2192 consume (next attempt blocks fresh)
1427
1467
  # On failure \u2192 grant active (retry allowed)
1468
+ # Consent tracking: on success \u2192 consume (next run blocks fresh), on error \u2192 grant (retry allowed)
1428
1469
  if [ -n "$CMD_HASH" ] && [ -n "$SESSION_ID" ]; then
1429
1470
  if [ "$IS_ERROR" = "false" ]; then
1430
1471
  synkro_consent_consume "$SESSION_ID" "$CMD_HASH"
@@ -1459,7 +1500,7 @@ if [ -z "$SESSION_ID" ] || [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH
1459
1500
  fi
1460
1501
 
1461
1502
  # Usage telemetry \u2014 last turn only (metadata, ungated by privacy/consent)
1462
- _LAST_ASST=$(tail -20 "$TRANSCRIPT_PATH" 2>/dev/null | grep '"type":"assistant"' | tail -1)
1503
+ _LAST_ASST=$(grep '"type":"assistant"' "$TRANSCRIPT_PATH" 2>/dev/null | tail -1)
1463
1504
  if [ -n "$_LAST_ASST" ]; then
1464
1505
  _CC_MODEL=$(echo "$_LAST_ASST" | jq -r '.message.model // empty' 2>/dev/null)
1465
1506
  _TI=$(echo "$_LAST_ASST" | jq -r '.message.usage.input_tokens // 0' 2>/dev/null)
@@ -1562,6 +1603,9 @@ GIT_REPO=$(synkro_detect_repo "\${CWD:-.}")
1562
1603
  CMD_SHORT=$(printf '%s' "$COMMAND" | head -c 80)
1563
1604
  synkro_log "bashGuard checking: $CMD_SHORT"
1564
1605
 
1606
+ synkro_load_config
1607
+ if [ "$SYNKRO_SILENT" = "true" ]; then echo '{}'; exit 0; fi
1608
+
1565
1609
  BODY=$(jq -n \\
1566
1610
  --arg cmd "$COMMAND" \\
1567
1611
  --arg session_id "$SESSION_ID" \\
@@ -1615,6 +1659,9 @@ if [ -z "$FILE_PATH" ]; then echo '{}'; exit 0; fi
1615
1659
  BASENAME=$(basename "$FILE_PATH" 2>/dev/null || echo "$FILE_PATH")
1616
1660
  synkro_log "editGuard checking: $BASENAME"
1617
1661
 
1662
+ synkro_load_config
1663
+ if [ "$SYNKRO_SILENT" = "true" ]; then echo '{}'; exit 0; fi
1664
+
1618
1665
  BODY=$(jq -n \\
1619
1666
  --arg file_path "$FILE_PATH" \\
1620
1667
  --arg content "$CONTENT" \\
@@ -3940,7 +3987,7 @@ function writeConfigEnv(opts) {
3940
3987
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3941
3988
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3942
3989
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
3943
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.37")}`
3990
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.39")}`
3944
3991
  ];
3945
3992
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
3946
3993
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);