@synkro-sh/cli 1.4.37 → 1.4.38

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
@@ -737,6 +746,20 @@ IS_HEADLESS="\${SYNKRO_HEADLESS:-0}"
737
746
  case "$PERMISSION_MODE" in acceptEdits|bypassPermissions|plan|auto) IS_HEADLESS="1" ;; esac
738
747
 
739
748
  synkro_load_config
749
+ TAG=$(synkro_tag)
750
+
751
+ if [ "$SYNKRO_SILENT" = "true" ]; then
752
+ jq -n --arg m "$TAG bashGuard \u2192 skipped (silent mode)" '{systemMessage: $m}'
753
+ exit 0
754
+ fi
755
+
756
+ # Pre-grade consent check: skip grading if user already approved this command in this session
757
+ CMD_HASH=$(printf '%s' "$COMMAND" | shasum -a 256 | cut -c1-16)
758
+ if [ -n "$SESSION_ID" ] && [ -n "$CMD_HASH" ] && synkro_consent_has_active "$SESSION_ID" "$CMD_HASH"; then
759
+ jq -n --arg m "$TAG bashGuard \u2192 pass (previously approved)" '{systemMessage: $m}'
760
+ exit 0
761
+ fi
762
+
740
763
  ROUTE=$(synkro_route)
741
764
 
742
765
  if [ "$ROUTE" = "local" ]; then
@@ -747,7 +770,7 @@ if [ "$ROUTE" = "local" ]; then
747
770
 
748
771
  CC_RESP=$(synkro_local_grade bash < "$GRADER_FILE" 2>&1)
749
772
  if [ $? -ne 0 ]; then
750
- jq -n --arg m "[synkro:local] bashGuard \u2192 pass: local grader unavailable (run synkro local-cc start)" '{systemMessage: $m}'
773
+ jq -n --arg m "$TAG bashGuard \u2192 pass: local grader unavailable (run synkro local-cc start)" '{systemMessage: $m}'
751
774
  exit 0
752
775
  fi
753
776
  synkro_parse_local_verdict "$CC_RESP"
@@ -759,31 +782,20 @@ if [ "$ROUTE" = "local" ]; then
759
782
  if [ "$LOCAL_OK" = "false" ]; then
760
783
  RULE_MODE="\${LOCAL_RULE_MODE:-$(synkro_rule_mode "\${LOCAL_RULE_ID}")}"
761
784
  if [ "$RULE_MODE" = "audit" ]; then
762
- REASON="[synkro:local] bashGuard \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
785
+ REASON="$TAG bashGuard \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
763
786
  jq -n --arg m "$REASON" '{systemMessage: $m}'
764
787
  synkro_dispatch_capture "bash" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
765
788
  "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "\${RECENT_USER_MESSAGES:-[]}"
766
789
  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
790
+ if [ "$IS_HEADLESS" = "1" ]; then DEC="deny"; else DEC="ask"; fi
791
+ REASON="$TAG bashGuard \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
792
+ jq -n --arg dec "$DEC" --arg reason "$REASON" --arg ctx "$REASON" \\
793
+ '{systemMessage:$reason,hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$dec,permissionDecisionReason:$reason,additionalContext:$ctx}}'
794
+ synkro_dispatch_capture "bash" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
795
+ "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "\${RECENT_USER_MESSAGES:-[]}"
784
796
  fi
785
797
  else
786
- jq -n --arg m "[synkro:local] bashGuard \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
798
+ jq -n --arg m "$TAG bashGuard \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
787
799
  synkro_dispatch_capture "bash" "pass" "audit" "\${LOCAL_CAT:-trivial_utility}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
788
800
  "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "\${RECENT_USER_MESSAGES:-[]}"
789
801
  fi
@@ -938,6 +950,13 @@ if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
938
950
  fi
939
951
 
940
952
  synkro_load_config
953
+ TAG=$(synkro_tag)
954
+
955
+ if [ "$SYNKRO_SILENT" = "true" ]; then
956
+ jq -n --arg m "$TAG editGuard \u2192 skipped (silent mode)" '{systemMessage: $m}'
957
+ exit 0
958
+ fi
959
+
941
960
  ROUTE=$(synkro_route)
942
961
 
943
962
  if [ "$ROUTE" = "local" ]; then
@@ -948,7 +967,7 @@ if [ "$ROUTE" = "local" ]; then
948
967
 
949
968
  CC_RESP=$(synkro_local_grade edit < "$GRADER_FILE" 2>&1)
950
969
  if [ $? -ne 0 ]; then
951
- jq -n --arg m "[synkro:local] editGuard \u2192 pass: local grader unavailable (run synkro local-cc start)" '{systemMessage: $m}'
970
+ jq -n --arg m "$TAG editGuard \u2192 pass: local grader unavailable (run synkro local-cc start)" '{systemMessage: $m}'
952
971
  exit 0
953
972
  fi
954
973
  synkro_parse_local_verdict "$CC_RESP"
@@ -961,20 +980,20 @@ if [ "$ROUTE" = "local" ]; then
961
980
  if [ "$LOCAL_OK" = "false" ]; then
962
981
  RULE_MODE="\${LOCAL_RULE_MODE:-$(synkro_rule_mode "\${LOCAL_RULE_ID}")}"
963
982
  if [ "$RULE_MODE" = "audit" ]; then
964
- REASON="[synkro:local] editGuard $FILE_SHORT \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
983
+ REASON="$TAG editGuard $FILE_SHORT \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
965
984
  jq -n --arg m "$REASON" '{systemMessage: $m}'
966
985
  synkro_dispatch_capture "edit" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
967
986
  "$EDIT_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "[]"
968
987
  else
969
988
  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}"
989
+ REASON="$TAG editGuard $FILE_SHORT \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
971
990
  jq -n --arg dec "$DEC" --arg reason "$REASON" --arg ctx "$REASON" \\
972
991
  '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$dec,permissionDecisionReason:$reason,additionalContext:$ctx}}'
973
992
  synkro_dispatch_capture "edit" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
974
993
  "$EDIT_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "[]"
975
994
  fi
976
995
  else
977
- jq -n --arg m "[synkro:local] editGuard $FILE_SHORT \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
996
+ jq -n --arg m "$TAG editGuard $FILE_SHORT \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
978
997
  synkro_dispatch_capture "edit" "pass" "audit" "\${LOCAL_CAT:-trivial_edit}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
979
998
  "$EDIT_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "[]"
980
999
  fi
@@ -1099,6 +1118,12 @@ while [ "$_PKG_DIR" != "/" ]; do
1099
1118
  done
1100
1119
 
1101
1120
  synkro_load_config
1121
+ TAG=$(synkro_tag)
1122
+
1123
+ if [ "$SYNKRO_SILENT" = "true" ]; then
1124
+ echo '{}'; exit 0
1125
+ fi
1126
+
1102
1127
  ROUTE=$(synkro_route)
1103
1128
 
1104
1129
  if [ "$ROUTE" = "local" ]; then
@@ -1120,18 +1145,18 @@ if [ "$ROUTE" = "local" ]; then
1120
1145
  if [ "$LOCAL_OK" = "false" ]; then
1121
1146
  RULE_MODE="\${LOCAL_RULE_MODE:-$(synkro_rule_mode "\${LOCAL_RULE_ID}")}"
1122
1147
  if [ "$RULE_MODE" = "audit" ]; then
1123
- REASON="[synkro:local] editScan $BASENAME \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
1148
+ REASON="$TAG editScan $BASENAME \u2192 warning\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
1124
1149
  jq -n --arg m "$REASON" '{systemMessage: $m}'
1125
1150
  synkro_dispatch_capture "edit_scan" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
1126
1151
  "$SCAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$SCAN_VIOLATED" "[]"
1127
1152
  else
1128
- REASON="[synkro:local] editScan $BASENAME \u2192 block: \${LOCAL_REASON:-policy violation}"
1153
+ REASON="$TAG editScan $BASENAME \u2192 block: \${LOCAL_REASON:-policy violation}"
1129
1154
  jq -n --arg m "$REASON" '{systemMessage: $m, additionalContext: $m}'
1130
1155
  synkro_dispatch_capture "edit_scan" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
1131
1156
  "$SCAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$SCAN_VIOLATED" "[]"
1132
1157
  fi
1133
1158
  else
1134
- jq -n --arg m "[synkro:local] editScan $BASENAME \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
1159
+ jq -n --arg m "$TAG editScan $BASENAME \u2192 pass: \${LOCAL_REASON:-no policy violations detected}" '{systemMessage: $m}'
1135
1160
  synkro_dispatch_capture "edit_scan" "pass" "audit" "\${LOCAL_CAT:-trivial_edit}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
1136
1161
  "$SCAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "[]"
1137
1162
  fi
@@ -1205,6 +1230,13 @@ PLAN_SHORT=$(printf '%s' "$PLAN" | head -c 80)
1205
1230
  synkro_log "planReview checking: $PLAN_SHORT..."
1206
1231
 
1207
1232
  synkro_load_config
1233
+ TAG=$(synkro_tag)
1234
+
1235
+ if [ "$SYNKRO_SILENT" = "true" ]; then
1236
+ jq -n --arg m "$TAG planReview \u2192 skipped (silent mode)" '{systemMessage: $m}'
1237
+ exit 0
1238
+ fi
1239
+
1208
1240
  ROUTE=$(synkro_route)
1209
1241
 
1210
1242
  if [ "$ROUTE" = "local" ]; then
@@ -1225,12 +1257,12 @@ if [ "$ROUTE" = "local" ]; then
1225
1257
  if [ "$LOCAL_OK" = "false" ]; then
1226
1258
  VCOUNT=$(printf '%s' "$CC_RESP" | grep -c '<violation>' 2>/dev/null || echo "0")
1227
1259
  [ "$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}"
1260
+ MSG="$TAG planReview \u2192 \${VCOUNT} rule(s) relevant\${LOCAL_RULE_ID:+ (first: $LOCAL_RULE_ID)}: \${LOCAL_REASON:-check org rules during implementation}"
1229
1261
  jq -n --arg m "$MSG" '{systemMessage: $m}'
1230
1262
  synkro_dispatch_capture "plan_review" "advisory" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "ExitPlanMode" "$GIT_REPO" "$SESSION_ID" \\
1231
1263
  "$PLAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$PLAN_VIOLATED" "[]"
1232
1264
  else
1233
- jq -n --arg m "[synkro:local] planReview \u2192 clean: \${LOCAL_REASON:-no relevant org rules for this plan}" '{systemMessage: $m}'
1265
+ jq -n --arg m "$TAG planReview \u2192 clean: \${LOCAL_REASON:-no relevant org rules for this plan}" '{systemMessage: $m}'
1234
1266
  synkro_dispatch_capture "plan_review" "clean" "audit" "\${LOCAL_CAT:-general}" "ExitPlanMode" "$GIT_REPO" "$SESSION_ID" \\
1235
1267
  "$PLAN_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "[]" "[]"
1236
1268
  fi
@@ -1271,7 +1303,7 @@ fi
1271
1303
  HR=$(echo "$RESP" | jq -c '.hook_response')
1272
1304
  if echo "$HR" | jq -e '.hookSpecificOutput.permissionDecision' >/dev/null 2>&1; then
1273
1305
  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}'
1306
+ jq -n --arg m "$TAG planReview \u2192 advisory: $REASON" '{systemMessage: $m}'
1275
1307
  else
1276
1308
  echo "$HR"
1277
1309
  fi
@@ -1339,10 +1371,12 @@ OPEN=$(echo "$RESP" | jq -r '.open // 0' 2>/dev/null)
1339
1371
 
1340
1372
  if [ "\${EDITS:-0}" = "0" ] || [ -z "$EDITS" ]; then echo '{}'; exit 0; fi
1341
1373
 
1374
+ synkro_load_config
1375
+ TAG=$(synkro_tag)
1342
1376
  if [ "\${FINDINGS:-0}" = "0" ] || [ -z "$FINDINGS" ]; then
1343
- SYS_MSG="[synkro] stop \u2192 0 issues across \${EDITS} edit(s), session complete"
1377
+ SYS_MSG="$TAG stop \u2192 0 issues across \${EDITS} edit(s), session complete"
1344
1378
  else
1345
- SYS_MSG="[synkro] stop \u2192 \${FINDINGS} finding(s): \${AUTO_FIXED} auto-fixed, \${OPEN} open"
1379
+ SYS_MSG="$TAG stop \u2192 \${FINDINGS} finding(s): \${AUTO_FIXED} auto-fixed, \${OPEN} open"
1346
1380
  fi
1347
1381
 
1348
1382
  jq -n --arg m "$SYS_MSG" '{systemMessage: $m}'
@@ -1356,11 +1390,30 @@ JWT=$(synkro_load_jwt)
1356
1390
 
1357
1391
  # Route preamble
1358
1392
  SYNKRO_PORT="\${SYNKRO_CHANNEL_PORT:-8929}"
1393
+ PAYLOAD=$(cat)
1394
+ CWD=$(echo "$PAYLOAD" | jq -r '.cwd // empty' 2>/dev/null)
1395
+ SESSION_ID=$(echo "$PAYLOAD" | jq -r '.session_id // empty' 2>/dev/null)
1396
+ GIT_REPO=$(synkro_detect_repo "\${CWD:-.}")
1397
+
1398
+ RESP=""
1399
+ if [ -n "$JWT" ]; then
1400
+ RESP=$(curl -sS -G "\${GATEWAY_URL}/api/v1/hook/config" \\
1401
+ --data-urlencode "session_id=\${SESSION_ID:-}" \\
1402
+ --data-urlencode "repo=\${GIT_REPO:-}" \\
1403
+ -H "Authorization: Bearer $JWT" --max-time 3 2>/dev/null || echo "")
1404
+ if [ -n "$RESP" ]; then
1405
+ SYNKRO_SILENT=$(echo "$RESP" | jq -r '.silent_mode // false' 2>/dev/null)
1406
+ SYNKRO_POLICY_NAME=$(echo "$RESP" | jq -r '.active_policy_name // empty' 2>/dev/null)
1407
+ fi
1408
+ fi
1409
+
1410
+ TAG=$(synkro_tag)
1411
+
1359
1412
  if (exec 3<>/dev/tcp/127.0.0.1/"$SYNKRO_PORT") 2>/dev/null; then
1360
1413
  exec 3<&- 3>&- 2>/dev/null || true
1361
- ROUTE_LINE="[synkro] inference: local-cc (channel reachable on 127.0.0.1:$SYNKRO_PORT)"
1414
+ ROUTE_LINE="$TAG inference: local-cc (channel reachable on 127.0.0.1:$SYNKRO_PORT)"
1362
1415
  else
1363
- ROUTE_LINE="[synkro] inference: cloud (local-cc channel not reachable)"
1416
+ ROUTE_LINE="$TAG inference: cloud (local-cc channel not reachable)"
1364
1417
  fi
1365
1418
 
1366
1419
  if [ -z "$JWT" ]; then
@@ -1368,16 +1421,6 @@ if [ -z "$JWT" ]; then
1368
1421
  exit 0
1369
1422
  fi
1370
1423
 
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
1424
  OPEN=0
1382
1425
  if [ -n "$RESP" ]; then
1383
1426
  OPEN=$(echo "$RESP" | jq -r '.session_context.open_findings // 0' 2>/dev/null)
@@ -1387,9 +1430,9 @@ if [ "$OPEN" = "0" ] || [ -z "$OPEN" ]; then
1387
1430
  jq -n --arg m "$ROUTE_LINE" '{systemMessage: $m}'
1388
1431
  else
1389
1432
  if [ "$OPEN" = "1" ]; then
1390
- SYS_MSG="$ROUTE_LINE"$'\\n'"[synkro] session start \u2192 1 open finding in this repo from a prior session."
1433
+ SYS_MSG="$ROUTE_LINE"$'\\n'"$TAG session start \u2192 1 open finding in this repo from a prior session."
1391
1434
  else
1392
- SYS_MSG="$ROUTE_LINE"$'\\n'"[synkro] session start \u2192 \${OPEN} open findings in this repo from prior sessions."
1435
+ SYS_MSG="$ROUTE_LINE"$'\\n'"$TAG session start \u2192 \${OPEN} open findings in this repo from prior sessions."
1393
1436
  fi
1394
1437
  jq -n --arg m "$SYS_MSG" '{systemMessage: $m}'
1395
1438
  fi
@@ -1425,13 +1468,10 @@ BODY=$(jq -n --arg sid "$SESSION_ID" --arg tid "$TOOL_USE_ID" \\
1425
1468
  # Local consent tracking: command ran = user consented
1426
1469
  # On success \u2192 consume (next attempt blocks fresh)
1427
1470
  # On failure \u2192 grant active (retry allowed)
1471
+ # Command ran (user approved it) \u2014 grant persistent consent for this session
1428
1472
  if [ -n "$CMD_HASH" ] && [ -n "$SESSION_ID" ]; then
1429
- if [ "$IS_ERROR" = "false" ]; then
1430
- synkro_consent_consume "$SESSION_ID" "$CMD_HASH"
1431
- else
1432
- if ! synkro_consent_has_active "$SESSION_ID" "$CMD_HASH"; then
1433
- synkro_consent_grant "$SESSION_ID" "$CMD_HASH"
1434
- fi
1473
+ if ! synkro_consent_has_active "$SESSION_ID" "$CMD_HASH"; then
1474
+ synkro_consent_grant "$SESSION_ID" "$CMD_HASH"
1435
1475
  fi
1436
1476
  fi
1437
1477
 
@@ -1459,7 +1499,7 @@ if [ -z "$SESSION_ID" ] || [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH
1459
1499
  fi
1460
1500
 
1461
1501
  # 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)
1502
+ _LAST_ASST=$(grep '"type":"assistant"' "$TRANSCRIPT_PATH" 2>/dev/null | tail -1)
1463
1503
  if [ -n "$_LAST_ASST" ]; then
1464
1504
  _CC_MODEL=$(echo "$_LAST_ASST" | jq -r '.message.model // empty' 2>/dev/null)
1465
1505
  _TI=$(echo "$_LAST_ASST" | jq -r '.message.usage.input_tokens // 0' 2>/dev/null)
@@ -1562,6 +1602,9 @@ GIT_REPO=$(synkro_detect_repo "\${CWD:-.}")
1562
1602
  CMD_SHORT=$(printf '%s' "$COMMAND" | head -c 80)
1563
1603
  synkro_log "bashGuard checking: $CMD_SHORT"
1564
1604
 
1605
+ synkro_load_config
1606
+ if [ "$SYNKRO_SILENT" = "true" ]; then echo '{}'; exit 0; fi
1607
+
1565
1608
  BODY=$(jq -n \\
1566
1609
  --arg cmd "$COMMAND" \\
1567
1610
  --arg session_id "$SESSION_ID" \\
@@ -1615,6 +1658,9 @@ if [ -z "$FILE_PATH" ]; then echo '{}'; exit 0; fi
1615
1658
  BASENAME=$(basename "$FILE_PATH" 2>/dev/null || echo "$FILE_PATH")
1616
1659
  synkro_log "editGuard checking: $BASENAME"
1617
1660
 
1661
+ synkro_load_config
1662
+ if [ "$SYNKRO_SILENT" = "true" ]; then echo '{}'; exit 0; fi
1663
+
1618
1664
  BODY=$(jq -n \\
1619
1665
  --arg file_path "$FILE_PATH" \\
1620
1666
  --arg content "$CONTENT" \\
@@ -1722,12 +1768,8 @@ if [ -n "$CMD" ]; then
1722
1768
  fi
1723
1769
 
1724
1770
  if [ -n "$CMD_HASH" ] && [ -n "$SESSION_ID" ]; then
1725
- if [ "$IS_ERROR" = "false" ]; then
1726
- synkro_consent_consume "$SESSION_ID" "$CMD_HASH"
1727
- else
1728
- if ! synkro_consent_has_active "$SESSION_ID" "$CMD_HASH"; then
1729
- synkro_consent_grant "$SESSION_ID" "$CMD_HASH"
1730
- fi
1771
+ if ! synkro_consent_has_active "$SESSION_ID" "$CMD_HASH"; then
1772
+ synkro_consent_grant "$SESSION_ID" "$CMD_HASH"
1731
1773
  fi
1732
1774
  fi
1733
1775
 
@@ -3940,7 +3982,7 @@ function writeConfigEnv(opts) {
3940
3982
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3941
3983
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3942
3984
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
3943
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.37")}`
3985
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.38")}`
3944
3986
  ];
3945
3987
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
3946
3988
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);