@synkro-sh/cli 1.4.38 → 1.4.40

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
@@ -654,6 +654,8 @@ synkro_rule_mode() {
654
654
 
655
655
  SYNKRO_CONSENT_FILE="$HOME/.synkro/.local-consent"
656
656
 
657
+ _TAB=$(printf '\\t')
658
+
657
659
  synkro_consent_grant() {
658
660
  local sid="$1" hash="$2"
659
661
  printf '%s\\t%s\\tactive\\n' "$sid" "$hash" >> "$SYNKRO_CONSENT_FILE" 2>/dev/null || true
@@ -661,26 +663,28 @@ synkro_consent_grant() {
661
663
 
662
664
  synkro_consent_has_active() {
663
665
  local sid="$1" hash="$2"
664
- 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
665
667
  }
666
668
 
667
669
  synkro_consent_consume() {
668
670
  local sid="$1" hash="$2"
669
671
  [ ! -f "$SYNKRO_CONSENT_FILE" ] && return
670
672
  local tmp="\${SYNKRO_CONSENT_FILE}.tmp"
671
- 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
672
676
  }
673
677
 
674
678
  synkro_consent_has_consumed() {
675
679
  local sid="$1" hash="$2"
676
- 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
677
681
  }
678
682
 
679
683
  synkro_consent_clear_consumed() {
680
684
  local sid="$1" hash="$2"
681
685
  [ ! -f "$SYNKRO_CONSENT_FILE" ] && return
682
686
  local tmp="\${SYNKRO_CONSENT_FILE}.tmp"
683
- 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
684
688
  }
685
689
 
686
690
  synkro_post_with_retry() {
@@ -753,20 +757,13 @@ if [ "$SYNKRO_SILENT" = "true" ]; then
753
757
  exit 0
754
758
  fi
755
759
 
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
-
763
760
  ROUTE=$(synkro_route)
764
761
 
765
762
  if [ "$ROUTE" = "local" ]; then
766
763
  # \u2500\u2500\u2500 Local grading (local_only privacy or local-cc channel) \u2500\u2500\u2500
767
764
  GRADER_FILE=$(mktemp -t synkro-bash.XXXXXX)
768
765
  trap "rm -f \\"$GRADER_FILE\\"" EXIT
769
- 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 (last human message): %s\\nOrg rules: %s\\n' "\${CWD:-.}" "\${GIT_REPO:-unknown}" "$COMMAND" "\${USER_INTENT:-none stated}" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
770
767
 
771
768
  CC_RESP=$(synkro_local_grade bash < "$GRADER_FILE" 2>&1)
772
769
  if [ $? -ne 0 ]; then
@@ -787,10 +784,9 @@ if [ "$ROUTE" = "local" ]; then
787
784
  synkro_dispatch_capture "bash" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
788
785
  "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "\${RECENT_USER_MESSAGES:-[]}"
789
786
  else
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}}'
787
+ REASON="$TAG bashGuard \u2192 blocked\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}. Ask the user for explicit consent before retrying."
788
+ jq -n --arg reason "$REASON" \\
789
+ '{systemMessage:$reason,hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:$reason,additionalContext:$reason}}'
794
790
  synkro_dispatch_capture "bash" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
795
791
  "$COMMAND" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "\${RECENT_USER_MESSAGES:-[]}"
796
792
  fi
@@ -963,7 +959,7 @@ if [ "$ROUTE" = "local" ]; then
963
959
  # \u2500\u2500\u2500 Local grading (local_only privacy or local-cc channel) \u2500\u2500\u2500
964
960
  GRADER_FILE=$(mktemp -t synkro-edit.XXXXXX)
965
961
  trap "rm -f \\"$GRADER_FILE\\"" EXIT
966
- 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"
962
+ printf 'Working directory: %s\\nRepo: %s\\nFile: %s\\nProposed content (first 4000 chars):\\n%s\\nUser intent (last human message): %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"
967
963
 
968
964
  CC_RESP=$(synkro_local_grade edit < "$GRADER_FILE" 2>&1)
969
965
  if [ $? -ne 0 ]; then
@@ -985,10 +981,9 @@ if [ "$ROUTE" = "local" ]; then
985
981
  synkro_dispatch_capture "edit" "warning" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
986
982
  "$EDIT_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "[]"
987
983
  else
988
- if [ "$IS_HEADLESS" = "1" ]; then DEC="deny"; else DEC="ask"; fi
989
- REASON="$TAG editGuard $FILE_SHORT \u2192 block\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}"
990
- jq -n --arg dec "$DEC" --arg reason "$REASON" --arg ctx "$REASON" \\
991
- '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:$dec,permissionDecisionReason:$reason,additionalContext:$ctx}}'
984
+ REASON="$TAG editGuard $FILE_SHORT \u2192 blocked\${LOCAL_RULE_ID:+ ($LOCAL_RULE_ID)}: \${LOCAL_REASON:-policy violation}. Ask the user for explicit consent before retrying."
985
+ jq -n --arg reason "$REASON" \\
986
+ '{systemMessage:$reason,hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:$reason,additionalContext:$reason}}'
992
987
  synkro_dispatch_capture "edit" "block" "\${LOCAL_SEV}" "\${LOCAL_CAT}" "$TOOL_NAME" "$GIT_REPO" "$SESSION_ID" \\
993
988
  "$EDIT_CONTENT" "$LOCAL_REASON" "\${SYNKRO_RULES:-[]}" "$VIOLATED_JSON" "[]"
994
989
  fi
@@ -1130,7 +1125,7 @@ if [ "$ROUTE" = "local" ]; then
1130
1125
  # \u2500\u2500\u2500 Local edit scan (local_only privacy or local-cc channel) \u2500\u2500\u2500
1131
1126
  GRADER_FILE=$(mktemp -t synkro-escan.XXXXXX)
1132
1127
  trap "rm -f \\"$GRADER_FILE\\"" EXIT
1133
- 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"
1128
+ 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"
1134
1129
 
1135
1130
  CC_RESP=$(synkro_local_grade edit < "$GRADER_FILE" 2>&1)
1136
1131
  if [ $? -ne 0 ]; then
@@ -1242,7 +1237,7 @@ ROUTE=$(synkro_route)
1242
1237
  if [ "$ROUTE" = "local" ]; then
1243
1238
  GRADER_FILE=$(mktemp -t synkro-plan.XXXXXX)
1244
1239
  trap "rm -f \\"$GRADER_FILE\\"" EXIT
1245
- printf 'Plan:\\n%s\\nOrg rules: %s\\n' "$(printf '%s' "$PLAN" | head -c 8000)" "\${SYNKRO_RULES:-[]}" > "$GRADER_FILE"
1240
+ 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"
1246
1241
 
1247
1242
  CC_RESP=$(synkro_local_grade plan < "$GRADER_FILE" 2>&1)
1248
1243
  if [ $? -ne 0 ]; then
@@ -1468,10 +1463,14 @@ BODY=$(jq -n --arg sid "$SESSION_ID" --arg tid "$TOOL_USE_ID" \\
1468
1463
  # Local consent tracking: command ran = user consented
1469
1464
  # On success \u2192 consume (next attempt blocks fresh)
1470
1465
  # On failure \u2192 grant active (retry allowed)
1471
- # Command ran (user approved it) \u2014 grant persistent consent for this session
1466
+ # Consent tracking: on success \u2192 consume (next run blocks fresh), on error \u2192 grant (retry allowed)
1472
1467
  if [ -n "$CMD_HASH" ] && [ -n "$SESSION_ID" ]; then
1473
- if ! synkro_consent_has_active "$SESSION_ID" "$CMD_HASH"; then
1474
- synkro_consent_grant "$SESSION_ID" "$CMD_HASH"
1468
+ if [ "$IS_ERROR" = "false" ]; then
1469
+ synkro_consent_consume "$SESSION_ID" "$CMD_HASH"
1470
+ else
1471
+ if ! synkro_consent_has_active "$SESSION_ID" "$CMD_HASH"; then
1472
+ synkro_consent_grant "$SESSION_ID" "$CMD_HASH"
1473
+ fi
1475
1474
  fi
1476
1475
  fi
1477
1476
 
@@ -1768,8 +1767,12 @@ if [ -n "$CMD" ]; then
1768
1767
  fi
1769
1768
 
1770
1769
  if [ -n "$CMD_HASH" ] && [ -n "$SESSION_ID" ]; then
1771
- if ! synkro_consent_has_active "$SESSION_ID" "$CMD_HASH"; then
1772
- synkro_consent_grant "$SESSION_ID" "$CMD_HASH"
1770
+ if [ "$IS_ERROR" = "false" ]; then
1771
+ synkro_consent_consume "$SESSION_ID" "$CMD_HASH"
1772
+ else
1773
+ if ! synkro_consent_has_active "$SESSION_ID" "$CMD_HASH"; then
1774
+ synkro_consent_grant "$SESSION_ID" "$CMD_HASH"
1775
+ fi
1773
1776
  fi
1774
1777
  fi
1775
1778
 
@@ -3982,7 +3985,7 @@ function writeConfigEnv(opts) {
3982
3985
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3983
3986
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3984
3987
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
3985
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.38")}`
3988
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.40")}`
3986
3989
  ];
3987
3990
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
3988
3991
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);