@synkro-sh/cli 1.3.31 → 1.3.32

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
@@ -1118,51 +1118,68 @@ if [ "$USE_LOCAL" = "true" ]; then
1118
1118
  VERDICT_JSON='{"ok":true,"violations":[]}'
1119
1119
  fi
1120
1120
 
1121
- LOCAL_BODY=$(jq -n \\
1122
- --argjson verdict "$VERDICT_JSON" \\
1123
- --arg file_path "$FILE_PATH" \\
1124
- --arg tool_name "$TOOL_NAME" \\
1125
- --arg content "$PROPOSED" \\
1126
- --arg file_before "$FILE_BEFORE" \\
1127
- --argjson diff "$DIFF_FIELD" \\
1128
- --arg user_intent "$USER_INTENT" \\
1129
- --argjson recent_actions "$RECENT_ACTIONS" \\
1130
- --arg session_id "$SESSION_ID" \\
1131
- --arg tool_use_id "$TOOL_USE_ID" \\
1132
- --arg cwd "$CWD" \\
1133
- --arg permission_mode "$PERMISSION_MODE" \\
1134
- --arg headless_flag "$HEADLESS_FLAG" \\
1135
- --arg repo "$GIT_REPO" \\
1136
- '{
1137
- verdict: $verdict,
1138
- file_path: $file_path,
1139
- tool_name: $tool_name,
1140
- content: $content,
1141
- file_before: (if ($file_before | length) > 0 then $file_before else null end),
1142
- diff: $diff,
1143
- user_intent: (if ($user_intent | length) > 0 then $user_intent else null end),
1144
- recent_actions: $recent_actions,
1145
- session_id: (if ($session_id | length) > 0 then $session_id else null end),
1146
- tool_use_id: (if ($tool_use_id | length) > 0 then $tool_use_id else null end),
1147
- cwd: (if ($cwd | length) > 0 then $cwd else null end),
1148
- permission_mode: (if ($permission_mode | length) > 0 then $permission_mode else null end),
1149
- headless: ($headless_flag == "1"),
1150
- repo: (if ($repo | length) > 0 then $repo else null end)
1151
- }')
1152
-
1153
- RESP=$(curl -sS -X POST "\${GATEWAY_URL}/api/v1/precheck-edit/local-verdict" \\
1154
- -H "Content-Type: application/json" \\
1155
- -H "Authorization: Bearer $JWT" \\
1156
- -d "$LOCAL_BODY" \\
1157
- --max-time 5 2>/dev/null || echo "")
1121
+ if [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ]; then
1122
+ # No file content / diff / intent / actions leave the device. Synthesize
1123
+ # the hook response from the local verdict only.
1124
+ OK=$(echo "$VERDICT_JSON" | jq -r '.ok // true' 2>/dev/null)
1125
+ if [ "$OK" = "false" ]; then
1126
+ FIRST_REASON=$(echo "$VERDICT_JSON" | jq -r '.violations[0].reason // "policy violation"' 2>/dev/null)
1127
+ RULE_ID=$(echo "$VERDICT_JSON" | jq -r '.violations[0].rule_id // "local_violation"' 2>/dev/null)
1128
+ if [ "$IS_HEADLESS" = "1" ]; then DEC="deny"; else DEC="ask"; fi
1129
+ RESP=$(jq -n \\
1130
+ --arg dec "$DEC" \\
1131
+ --arg reason "[synkro] $RULE_ID: $FIRST_REASON" \\
1132
+ '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: $dec, permissionDecisionReason: $reason, additionalContext: $reason }, reason: $reason }')
1133
+ else
1134
+ RESP=$(jq -n '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "allow" }, reason: "" }')
1135
+ fi
1136
+ else
1137
+ LOCAL_BODY=$(jq -n \\
1138
+ --argjson verdict "$VERDICT_JSON" \\
1139
+ --arg file_path "$FILE_PATH" \\
1140
+ --arg tool_name "$TOOL_NAME" \\
1141
+ --arg content "$PROPOSED" \\
1142
+ --arg file_before "$FILE_BEFORE" \\
1143
+ --argjson diff "$DIFF_FIELD" \\
1144
+ --arg user_intent "$USER_INTENT" \\
1145
+ --argjson recent_actions "$RECENT_ACTIONS" \\
1146
+ --arg session_id "$SESSION_ID" \\
1147
+ --arg tool_use_id "$TOOL_USE_ID" \\
1148
+ --arg cwd "$CWD" \\
1149
+ --arg permission_mode "$PERMISSION_MODE" \\
1150
+ --arg headless_flag "$HEADLESS_FLAG" \\
1151
+ --arg repo "$GIT_REPO" \\
1152
+ '{
1153
+ verdict: $verdict,
1154
+ file_path: $file_path,
1155
+ tool_name: $tool_name,
1156
+ content: $content,
1157
+ file_before: (if ($file_before | length) > 0 then $file_before else null end),
1158
+ diff: $diff,
1159
+ user_intent: (if ($user_intent | length) > 0 then $user_intent else null end),
1160
+ recent_actions: $recent_actions,
1161
+ session_id: (if ($session_id | length) > 0 then $session_id else null end),
1162
+ tool_use_id: (if ($tool_use_id | length) > 0 then $tool_use_id else null end),
1163
+ cwd: (if ($cwd | length) > 0 then $cwd else null end),
1164
+ permission_mode: (if ($permission_mode | length) > 0 then $permission_mode else null end),
1165
+ headless: ($headless_flag == "1"),
1166
+ repo: (if ($repo | length) > 0 then $repo else null end)
1167
+ }')
1158
1168
 
1159
- if echo "$RESP" | grep -qE '"detail":"Token has expired|"detail":"Invalid or expired token'; then
1160
- if refresh_jwt; then
1161
- RESP=$(curl -sS -X POST "\${GATEWAY_URL}/api/v1/precheck-edit/local-verdict" \\
1162
- -H "Content-Type: application/json" \\
1163
- -H "Authorization: Bearer $JWT" \\
1164
- -d "$LOCAL_BODY" \\
1165
- --max-time 5 2>/dev/null || echo "")
1169
+ RESP=$(curl -sS -X POST "\${GATEWAY_URL}/api/v1/precheck-edit/local-verdict" \\
1170
+ -H "Content-Type: application/json" \\
1171
+ -H "Authorization: Bearer $JWT" \\
1172
+ -d "$LOCAL_BODY" \\
1173
+ --max-time 5 2>/dev/null || echo "")
1174
+
1175
+ if echo "$RESP" | grep -qE '"detail":"Token has expired|"detail":"Invalid or expired token'; then
1176
+ if refresh_jwt; then
1177
+ RESP=$(curl -sS -X POST "\${GATEWAY_URL}/api/v1/precheck-edit/local-verdict" \\
1178
+ -H "Content-Type: application/json" \\
1179
+ -H "Authorization: Bearer $JWT" \\
1180
+ -d "$LOCAL_BODY" \\
1181
+ --max-time 5 2>/dev/null || echo "")
1182
+ fi
1166
1183
  fi
1167
1184
  fi
1168
1185
  else
@@ -1402,32 +1419,57 @@ if [ -n "$SESSION_ID" ] && [ -n "$TOOL_USE_ID" ]; then
1402
1419
  ) &
1403
1420
  fi
1404
1421
 
1405
- # Resolve tier (cached 60 min) \u2014 same logic as the other hooks.
1422
+ # Resolve tier + capture_depth (cached 60 min).
1406
1423
  TIER_CACHE_FILE="$HOME/.synkro/.tier-cache-\${SYNKRO_USER_ID:-default}"
1424
+ CD_CACHE_FILE="\${TIER_CACHE_FILE}.cd"
1407
1425
  SYNKRO_INFERENCE_TIER=""
1426
+ SYNKRO_CAPTURE_DEPTH=""
1408
1427
  if find "$TIER_CACHE_FILE" -mmin -60 2>/dev/null | grep -q .; then
1409
1428
  SYNKRO_INFERENCE_TIER=$(cat "$TIER_CACHE_FILE" 2>/dev/null || true)
1429
+ SYNKRO_CAPTURE_DEPTH=$(cat "$CD_CACHE_FILE" 2>/dev/null || true)
1410
1430
  fi
1411
1431
  if [ -z "$SYNKRO_INFERENCE_TIER" ]; then
1412
1432
  ME_RESP=$(curl -sS "\${GATEWAY_URL}/api/v1/cli/me" -H "Authorization: Bearer $JWT" --max-time 2 2>/dev/null || echo "")
1413
1433
  if [ -n "$ME_RESP" ]; then
1414
1434
  SYNKRO_INFERENCE_TIER=$(echo "$ME_RESP" | jq -r '.tier // empty' 2>/dev/null || true)
1415
1435
  [ -n "$SYNKRO_INFERENCE_TIER" ] && printf '%s' "$SYNKRO_INFERENCE_TIER" > "$TIER_CACHE_FILE" 2>/dev/null || true
1436
+ SYNKRO_CAPTURE_DEPTH=$(echo "$ME_RESP" | jq -r '.capture_depth // empty' 2>/dev/null || true)
1437
+ [ -n "$SYNKRO_CAPTURE_DEPTH" ] && printf '%s' "$SYNKRO_CAPTURE_DEPTH" > "$CD_CACHE_FILE" 2>/dev/null || true
1416
1438
  fi
1417
1439
  fi
1418
1440
  SYNKRO_INFERENCE_TIER="\${SYNKRO_INFERENCE_TIER:-fast}"
1441
+ SYNKRO_CAPTURE_DEPTH="\${SYNKRO_CAPTURE_DEPTH:-full}"
1442
+
1443
+ USE_LOCAL=false
1444
+ if [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ] && command -v claude >/dev/null 2>&1; then
1445
+ USE_LOCAL=true
1446
+ elif [ "$SYNKRO_INFERENCE_TIER" = "free" ] && command -v claude >/dev/null 2>&1; then
1447
+ USE_LOCAL=true
1448
+ fi
1419
1449
 
1420
- if [ "$SYNKRO_INFERENCE_TIER" = "free" ] && command -v claude >/dev/null 2>&1; then
1421
- # \u2500\u2500\u2500 FREE TIER: grade via the persistent claude daemon (mode=edit). \u2500\u2500\u2500
1450
+ if [ "$USE_LOCAL" = "true" ]; then
1451
+ # \u2500\u2500\u2500 LOCAL GRADING: grade via the persistent claude daemon (mode=edit). \u2500\u2500\u2500
1422
1452
 
1423
- # Fetch org guardrail rules relevant to this file content.
1424
- ORG_RULES=$(printf '%s' "$FILE_CONTENT" | head -c 8000 \\
1425
- | jq -Rs '{content: .}' \\
1426
- | curl -sS "\${GATEWAY_URL}/api/v1/cli/pr-rules?top_k=20" \\
1427
- -X POST -H "Content-Type: application/json" \\
1428
- -H "Authorization: Bearer $JWT" \\
1429
- -d @- --max-time 2 2>/dev/null \\
1430
- | jq -c '[.rules[]? | {rule_id, text, severity, category}]' 2>/dev/null || echo "[]")
1453
+ # In local_only: GET all rules (no content sent), cache 1h. Otherwise POST with content.
1454
+ RULES_CACHE="$HOME/.synkro/.rules-cache-edit-capture"
1455
+ if [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ]; then
1456
+ if find "$RULES_CACHE" -mmin -60 2>/dev/null | grep -q .; then
1457
+ ORG_RULES=$(cat "$RULES_CACHE" 2>/dev/null || echo "[]")
1458
+ else
1459
+ ORG_RULES=$(curl -sS "\${GATEWAY_URL}/api/v1/cli/pr-rules" \\
1460
+ -H "Authorization: Bearer $JWT" --max-time 2 2>/dev/null \\
1461
+ | jq -c '[.rules[]? | {rule_id, text, severity, category}]' 2>/dev/null || echo "[]")
1462
+ [ -n "$ORG_RULES" ] && [ "$ORG_RULES" != "null" ] && printf '%s' "$ORG_RULES" > "$RULES_CACHE" 2>/dev/null || true
1463
+ fi
1464
+ else
1465
+ ORG_RULES=$(printf '%s' "$FILE_CONTENT" | head -c 8000 \\
1466
+ | jq -Rs '{content: .}' \\
1467
+ | curl -sS "\${GATEWAY_URL}/api/v1/cli/pr-rules?top_k=20" \\
1468
+ -X POST -H "Content-Type: application/json" \\
1469
+ -H "Authorization: Bearer $JWT" \\
1470
+ -d @- --max-time 2 2>/dev/null \\
1471
+ | jq -c '[.rules[]? | {rule_id, text, severity, category}]' 2>/dev/null || echo "[]")
1472
+ fi
1431
1473
  if [ -z "$ORG_RULES" ] || [ "$ORG_RULES" = "null" ]; then ORG_RULES="[]"; fi
1432
1474
 
1433
1475
  GRADER_PROMPT_FILE=$(mktemp -t synkro-edit-capture.XXXXXX)
@@ -1701,6 +1743,12 @@ CREDS_PATH="\${SYNKRO_CREDENTIALS_PATH:-$HOME/.synkro/credentials.json}"
1701
1743
 
1702
1744
  if [ ! -f "$CREDS_PATH" ]; then echo '{}'; exit 0; fi
1703
1745
  if [ "\${SYNKRO_TRANSCRIPT_CONSENT:-yes}" = "no" ]; then echo '{}'; exit 0; fi
1746
+
1747
+ # Hard-skip in local_only privacy mode \u2014 conversation content must never leave the device.
1748
+ CD_CACHE_FILE="$HOME/.synkro/.tier-cache-\${SYNKRO_USER_ID:-default}.cd"
1749
+ SYNKRO_CAPTURE_DEPTH=$(cat "$CD_CACHE_FILE" 2>/dev/null || echo "full")
1750
+ if [ "$SYNKRO_CAPTURE_DEPTH" = "local_only" ]; then echo '{}'; exit 0; fi
1751
+
1704
1752
  JWT=$(jq -r '.access_token // empty' "$CREDS_PATH" 2>/dev/null)
1705
1753
  if [ -z "$JWT" ]; then echo '{}'; exit 0; fi
1706
1754
 
@@ -3554,7 +3602,7 @@ function writeConfigEnv(opts) {
3554
3602
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3555
3603
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3556
3604
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
3557
- `SYNKRO_VERSION=${shellQuoteSingle("1.3.31")}`
3605
+ `SYNKRO_VERSION=${shellQuoteSingle("1.3.32")}`
3558
3606
  ];
3559
3607
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
3560
3608
  if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);