@synkro-sh/cli 1.4.82 → 1.4.84

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
@@ -1991,7 +1991,7 @@ async function main() {
1991
1991
  const tagStr = tag(rt, config);
1992
1992
 
1993
1993
  if (config.silent) {
1994
- outputJson({ systemMessage: tagStr + ' editGuard \\u2192 skipped (silent mode)' });
1994
+ outputJson({ systemMessage: tagStr + ' editGuard \u2192 skipped (silent mode)' });
1995
1995
  return;
1996
1996
  }
1997
1997
 
@@ -2013,7 +2013,7 @@ async function main() {
2013
2013
  try {
2014
2014
  gradeResp = await localGrade('edit', graderPrompt);
2015
2015
  } catch {
2016
- outputJson({ systemMessage: tagStr + ' editGuard ' + fileShort + ' \\u2192 local grader unavailable, skipped' });
2016
+ outputJson({ systemMessage: tagStr + ' editGuard ' + fileShort + ' \u2192 local grader unavailable, skipped' });
2017
2017
  return;
2018
2018
  }
2019
2019
 
@@ -2034,7 +2034,7 @@ async function main() {
2034
2034
  ccModel: transcript.ccModel,
2035
2035
  });
2036
2036
  outputJson({
2037
- systemMessage: tagStr + ' editGuard ' + fileShort + ' \\u2192 blocked: ' + guardReason,
2037
+ systemMessage: tagStr + ' editGuard ' + fileShort + ' \u2192 blocked: ' + guardReason,
2038
2038
  hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: denyReason, additionalContext: denyReason },
2039
2039
  });
2040
2040
  return;
@@ -2047,7 +2047,7 @@ async function main() {
2047
2047
  rulesChecked: config.rules, violatedRules,
2048
2048
  ccModel: transcript.ccModel,
2049
2049
  });
2050
- outputJson({ systemMessage: tagStr + ' editGuard ' + fileShort + ' \\u2192 warning: ' + guardReason, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro local edit judge (audit). ' + guardReason } });
2050
+ outputJson({ systemMessage: tagStr + ' editGuard ' + fileShort + ' \u2192 warning: ' + guardReason, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro local edit judge (audit). ' + guardReason } });
2051
2051
  return;
2052
2052
  }
2053
2053
 
@@ -2058,7 +2058,7 @@ async function main() {
2058
2058
  rulesChecked: config.rules, violatedRules: [],
2059
2059
  ccModel: transcript.ccModel,
2060
2060
  });
2061
- const passLine = tagStr + ' editGuard ' + fileShort + ' \\u2192 pass: ' + (verdict.reason || 'no policy violations detected');
2061
+ const passLine = tagStr + ' editGuard ' + fileShort + ' \u2192 pass: ' + (verdict.reason || 'no policy violations detected');
2062
2062
  outputJson({ systemMessage: passLine, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro local edit judge. ' + (verdict.reason || 'no policy violations detected') } });
2063
2063
  return;
2064
2064
  }
@@ -2093,13 +2093,13 @@ async function main() {
2093
2093
  const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 8000);
2094
2094
 
2095
2095
  if (!resp) {
2096
- log('editGuard ' + fileShort + ' \\u2192 error (timeout)');
2096
+ log('editGuard ' + fileShort + ' \u2192 error (timeout)');
2097
2097
  outputEmpty();
2098
2098
  return;
2099
2099
  }
2100
2100
 
2101
2101
  if (!resp.hook_response || typeof resp.hook_response !== 'object') {
2102
- log('editGuard ' + fileShort + ' \\u2192 pass (no hook_response)');
2102
+ log('editGuard ' + fileShort + ' \u2192 pass (no hook_response)');
2103
2103
  outputEmpty();
2104
2104
  return;
2105
2105
  }
@@ -2108,7 +2108,7 @@ async function main() {
2108
2108
  const decision = hookResp?.hookSpecificOutput?.permissionDecision;
2109
2109
 
2110
2110
  if (decision === 'deny' || decision === 'ask') {
2111
- log('editGuard ' + fileShort + ' \\u2192 BLOCKED');
2111
+ log('editGuard ' + fileShort + ' \u2192 BLOCKED');
2112
2112
  // Strip permissionDecision \u2014 we use systemMessage only
2113
2113
  const cleaned = { ...hookResp };
2114
2114
  if (cleaned.hookSpecificOutput) {
@@ -2119,7 +2119,7 @@ async function main() {
2119
2119
  outputJson(cleaned);
2120
2120
  } else {
2121
2121
  const reason = hookResp.reason || '';
2122
- log('editGuard ' + fileShort + ' \\u2192 pass' + (reason ? ': ' + reason : ''));
2122
+ log('editGuard ' + fileShort + ' \u2192 pass' + (reason ? ': ' + reason : ''));
2123
2123
  outputJson(hookResp);
2124
2124
  }
2125
2125
  } catch (err) {
@@ -2594,7 +2594,7 @@ async function main() {
2594
2594
  const rt = await route(config);
2595
2595
 
2596
2596
  if (config.silent) {
2597
- outputJson({ systemMessage: '[synkro:' + rt + ':cveScan] ' + fileShort + ' \\u2192 skipped (silent mode)' });
2597
+ outputJson({ systemMessage: '[synkro:' + rt + ':cveScan] ' + fileShort + ' \u2192 skipped (silent mode)' });
2598
2598
  return;
2599
2599
  }
2600
2600
 
@@ -2603,7 +2603,7 @@ async function main() {
2603
2603
  // Reconstruct proposed content
2604
2604
  const proposed = reconstructContent(toolName, toolInput, filePath, cwd);
2605
2605
  if (!proposed) {
2606
- outputJson({ systemMessage: cveTag + ' ' + fileShort + ' \\u2192 skip (no content)' });
2606
+ outputJson({ systemMessage: cveTag + ' ' + fileShort + ' \u2192 skip (no content)' });
2607
2607
  return;
2608
2608
  }
2609
2609
 
@@ -2632,7 +2632,7 @@ async function main() {
2632
2632
  });
2633
2633
  cveResp = await resp.json();
2634
2634
  } catch {
2635
- outputJson({ systemMessage: cveTag + ' ' + fileShort + ' \\u2192 error (timeout)' });
2635
+ outputJson({ systemMessage: cveTag + ' ' + fileShort + ' \u2192 error (timeout)' });
2636
2636
  return;
2637
2637
  }
2638
2638
 
@@ -2669,7 +2669,7 @@ async function main() {
2669
2669
  const top3 = findings.slice(0, 3).map(formatFinding).join('; ');
2670
2670
  const count = findings.length;
2671
2671
  const label = count === 1 ? 'advisory' : 'advisories';
2672
- const cveMsg = cveTag + ' ' + fileShort + ' \\u2192 ' + count + ' ' + label;
2672
+ const cveMsg = cveTag + ' ' + fileShort + ' \u2192 ' + count + ' ' + label;
2673
2673
  const ctx = 'CVE: ' + top3 + '\\nFix all issues before retrying. Do NOT ask the user to make the edit manually \u2014 upgrade the vulnerable dependencies yourself.';
2674
2674
 
2675
2675
  const cveIds = findings.slice(0, 10).map((f: any) =>
@@ -2689,7 +2689,7 @@ async function main() {
2689
2689
  return;
2690
2690
  }
2691
2691
 
2692
- outputJson({ systemMessage: cveTag + ' ' + fileShort + ' \\u2192 clean' });
2692
+ outputJson({ systemMessage: cveTag + ' ' + fileShort + ' \u2192 clean' });
2693
2693
  } catch (err) {
2694
2694
  process.stderr.write('[synkro] cveGuard error: ' + String(err) + '\\n');
2695
2695
  outputEmpty();
@@ -2698,7 +2698,7 @@ async function main() {
2698
2698
 
2699
2699
  main();
2700
2700
  `;
2701
- BASH_JUDGE_TS = `#!/usr/bin/env bun
2701
+ BASH_JUDGE_TS = String.raw`#!/usr/bin/env bun
2702
2702
  import process from 'node:process';
2703
2703
  import {
2704
2704
  loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
@@ -2751,17 +2751,17 @@ async function main() {
2751
2751
  if (!jwt) { outputEmpty(); return; }
2752
2752
  jwt = await ensureFreshJwt(jwt);
2753
2753
 
2754
- // \u2500\u2500\u2500 Install protection: server-side pkg-scan (CVE + typosquat + tarball + reputation) \u2500\u2500\u2500
2754
+ // ─── Install protection: server-side pkg-scan (CVE + typosquat + tarball + reputation) ───
2755
2755
  let installScanMsg = '';
2756
2756
  if (toolName === 'Bash') {
2757
2757
  const pkgInstallMatch = command.match(
2758
- /^(?:.*&&s*|.*;s*)?(?:npms+(?:install|i|add)|pnpms+(?:add|install|i)|yarns+add|buns+(?:add|install|i)|(?:uvs+)?pip3?s+install|gos+get|cargos+add|gems+install|composers+require)s+([^|;&><]+)/
2758
+ /^(?:.*&&\s*|.*;\s*)?(?:npm\s+(?:install|i|add)|pnpm\s+(?:add|install|i)|yarn\s+add|bun\s+(?:add|install|i)|(?:uv\s+)?pip3?\s+install|go\s+get|cargo\s+add|gem\s+install|composer\s+require)\s+([^|;&><]+)/
2759
2759
  );
2760
- const isPip = /(?:uvs+)?pip3?s+install/.test(command);
2760
+ const isPip = /(?:uv\s+)?pip3?\s+install/.test(command);
2761
2761
  if (pkgInstallMatch) {
2762
2762
  const rawArgs = pkgInstallMatch[1];
2763
2763
  const packages: Array<{ name: string; version: string; ecosystem: string }> = [];
2764
- const tokens = rawArgs.split(/s+/);
2764
+ const tokens = rawArgs.split(/\s+/);
2765
2765
  let skipNext = false;
2766
2766
  for (const token of tokens) {
2767
2767
  if (skipNext) { skipNext = false; continue; }
@@ -2803,11 +2803,9 @@ async function main() {
2803
2803
  const blockSignals = pkgResults
2804
2804
  .flatMap((p: any) => (p.signals || []).filter((s: any) => s.severity === 'critical' || s.severity === 'high'))
2805
2805
  .slice(0, 5);
2806
- const scanMsg = '[synkro:installScan] ' + cmdShort + ' \u2192 blocked';
2807
- const details = blockSignals.map((s: any) => s.detail).join('
2808
- ');
2809
- const ctx = details + '
2810
- Do NOT install packages with security risks. Use a patched version or a different package.';
2806
+ const scanMsg = '[synkro:installScan] ' + cmdShort + ' blocked';
2807
+ const details = blockSignals.map((s: any) => s.detail).join('\n');
2808
+ const ctx = details + '\nDo NOT install packages with security risks. Use a patched version or a different package.';
2811
2809
 
2812
2810
  const config = await loadConfig(jwt);
2813
2811
  for (const p of pkgResults) {
@@ -2848,7 +2846,7 @@ Do NOT install packages with security risks. Use a patched version or a differen
2848
2846
  installScanMsg = '[synkro:installScan] ' + summary;
2849
2847
  } else {
2850
2848
  const scannedPkgs = packages.map(p => p.name + '@' + p.version).join(', ');
2851
- installScanMsg = '[synkro:installScan] ' + scannedPkgs + ' \u2192 clean';
2849
+ installScanMsg = '[synkro:installScan] ' + scannedPkgs + ' clean';
2852
2850
  }
2853
2851
  } catch (e) {
2854
2852
  log('bashGuard pkg-scan failed: ' + String(e));
@@ -2864,7 +2862,7 @@ Do NOT install packages with security risks. Use a patched version or a differen
2864
2862
  const tagStr = tag(rt, config);
2865
2863
 
2866
2864
  if (config.silent) {
2867
- const msg = (installScanMsg ? installScanMsg + '\\n' : '') + tagStr + ' bashGuard \\u2192 skipped (silent mode)';
2865
+ const msg = (installScanMsg ? installScanMsg + '\\n' : '') + tagStr + ' bashGuard skipped (silent mode)';
2868
2866
  outputJson({ systemMessage: msg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: msg } });
2869
2867
  return;
2870
2868
  }
@@ -2883,7 +2881,7 @@ Do NOT install packages with security risks. Use a patched version or a differen
2883
2881
  try {
2884
2882
  gradeResp = await localGrade('bash', graderPrompt);
2885
2883
  } catch {
2886
- outputJson({ systemMessage: tagStr + ' bashGuard \\u2192 local grader unavailable, skipped' });
2884
+ outputJson({ systemMessage: tagStr + ' bashGuard local grader unavailable, skipped' });
2887
2885
  return;
2888
2886
  }
2889
2887
 
@@ -2895,7 +2893,7 @@ Do NOT install packages with security risks. Use a patched version or a differen
2895
2893
  const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');
2896
2894
 
2897
2895
  if (mode === 'audit') {
2898
- const reason = tagStr + ' bashGuard \\u2192 warning' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation');
2896
+ const reason = tagStr + ' bashGuard warning' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation');
2899
2897
  const combined = (installScanMsg ? installScanMsg + '\\n' : '') + reason;
2900
2898
  outputJson({ systemMessage: combined, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: combined } });
2901
2899
  dispatchCapture(jwt, 'bash', 'warning', verdict.severity || 'medium', verdict.category || 'security',
@@ -2904,7 +2902,7 @@ Do NOT install packages with security risks. Use a patched version or a differen
2904
2902
  recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,
2905
2903
  });
2906
2904
  } else {
2907
- const reason = tagStr + ' bashGuard \\u2192 blocked' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation') + '. Ask the user for explicit consent before retrying.';
2905
+ const reason = tagStr + ' bashGuard blocked' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation') + '. Ask the user for explicit consent before retrying.';
2908
2906
  const combined = (installScanMsg ? installScanMsg + '\\n' : '') + reason;
2909
2907
  outputJson({
2910
2908
  systemMessage: combined,
@@ -2917,7 +2915,7 @@ Do NOT install packages with security risks. Use a patched version or a differen
2917
2915
  });
2918
2916
  }
2919
2917
  } else {
2920
- const reason = tagStr + ' bashGuard \\u2192 pass: ' + (verdict.reason || 'no policy violations detected');
2918
+ const reason = tagStr + ' bashGuard pass: ' + (verdict.reason || 'no policy violations detected');
2921
2919
  const combined = (installScanMsg ? installScanMsg + '\\n' : '') + reason;
2922
2920
  outputJson({ systemMessage: combined, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: combined } });
2923
2921
  dispatchCapture(jwt, 'bash', 'pass', 'audit', verdict.category || 'trivial_utility',
@@ -2930,7 +2928,7 @@ Do NOT install packages with security risks. Use a patched version or a differen
2930
2928
  return;
2931
2929
  }
2932
2930
 
2933
- // \u2500\u2500\u2500 Cloud grading \u2500\u2500\u2500
2931
+ // ─── Cloud grading ───
2934
2932
  const isHeadless = ['acceptEdits', 'bypassPermissions', 'plan', 'auto'].includes(permissionMode)
2935
2933
  || process.env.SYNKRO_HEADLESS === '1';
2936
2934
 
@@ -2957,7 +2955,7 @@ Do NOT install packages with security risks. Use a patched version or a differen
2957
2955
  const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 8000);
2958
2956
 
2959
2957
  if (!resp) {
2960
- log('bashGuard ' + cmdShort + ' \\u2192 error (timeout)');
2958
+ log('bashGuard ' + cmdShort + ' error (timeout)');
2961
2959
  if (installScanMsg) {
2962
2960
  outputJson({ systemMessage: installScanMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: installScanMsg } });
2963
2961
  } else { outputEmpty(); }
@@ -2965,7 +2963,7 @@ Do NOT install packages with security risks. Use a patched version or a differen
2965
2963
  }
2966
2964
 
2967
2965
  if (!resp.hook_response || typeof resp.hook_response !== 'object') {
2968
- log('bashGuard ' + cmdShort + ' \\u2192 pass (no hook_response)');
2966
+ log('bashGuard ' + cmdShort + ' pass (no hook_response)');
2969
2967
  if (installScanMsg) {
2970
2968
  outputJson({ systemMessage: installScanMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: installScanMsg } });
2971
2969
  } else { outputEmpty(); }
@@ -3041,7 +3039,7 @@ async function main() {
3041
3039
  const tagStr = tag(rt, config);
3042
3040
 
3043
3041
  if (config.silent) {
3044
- const msg = tagStr + ' agentGuard \\u2192 skipped (silent mode)';
3042
+ const msg = tagStr + ' agentGuard \u2192 skipped (silent mode)';
3045
3043
  outputJson({ systemMessage: msg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: msg } });
3046
3044
  return;
3047
3045
  }
@@ -3064,7 +3062,7 @@ async function main() {
3064
3062
  try {
3065
3063
  gradeResp = await localGrade('bash', graderPrompt);
3066
3064
  } catch {
3067
- outputJson({ systemMessage: tagStr + ' agentGuard \\u2192 local grader unavailable, skipped' });
3065
+ outputJson({ systemMessage: tagStr + ' agentGuard \u2192 local grader unavailable, skipped' });
3068
3066
  return;
3069
3067
  }
3070
3068
 
@@ -3077,7 +3075,7 @@ async function main() {
3077
3075
  const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');
3078
3076
 
3079
3077
  if (mode === 'audit') {
3080
- const reason = tagStr + ' agentGuard \\u2192 warning' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation');
3078
+ const reason = tagStr + ' agentGuard \u2192 warning' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation');
3081
3079
  outputJson({ systemMessage: reason, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: reason } });
3082
3080
  dispatchCapture(jwt, 'agent', 'warning', verdict.severity || 'medium', verdict.category || 'security',
3083
3081
  toolName, gitRepo, sessionId, config.captureDepth, {
@@ -3085,7 +3083,7 @@ async function main() {
3085
3083
  recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,
3086
3084
  });
3087
3085
  } else {
3088
- const reason = tagStr + ' agentGuard \\u2192 blocked' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation') + '. Ask the user for explicit consent before retrying.';
3086
+ const reason = tagStr + ' agentGuard \u2192 blocked' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation') + '. Ask the user for explicit consent before retrying.';
3089
3087
  outputJson({
3090
3088
  systemMessage: reason,
3091
3089
  hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason, additionalContext: reason },
@@ -3097,7 +3095,7 @@ async function main() {
3097
3095
  });
3098
3096
  }
3099
3097
  } else {
3100
- const reason = tagStr + ' agentGuard \\u2192 pass: ' + (verdict.reason || 'no policy violations detected');
3098
+ const reason = tagStr + ' agentGuard \u2192 pass: ' + (verdict.reason || 'no policy violations detected');
3101
3099
  outputJson({ systemMessage: reason, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: reason } });
3102
3100
  dispatchCapture(jwt, 'agent', 'pass', 'audit', verdict.category || 'subagent_spawn',
3103
3101
  toolName, gitRepo, sessionId, config.captureDepth, {
@@ -3136,13 +3134,13 @@ async function main() {
3136
3134
  const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 8000);
3137
3135
 
3138
3136
  if (!resp) {
3139
- log('agentGuard ' + promptShort + ' \\u2192 error (timeout)');
3137
+ log('agentGuard ' + promptShort + ' \u2192 error (timeout)');
3140
3138
  outputEmpty();
3141
3139
  return;
3142
3140
  }
3143
3141
 
3144
3142
  if (!resp.hook_response || typeof resp.hook_response !== 'object') {
3145
- log('agentGuard ' + promptShort + ' \\u2192 pass (no hook_response)');
3143
+ log('agentGuard ' + promptShort + ' \u2192 pass (no hook_response)');
3146
3144
  outputEmpty();
3147
3145
  return;
3148
3146
  }
@@ -3237,7 +3235,7 @@ async function main() {
3237
3235
  const tagStr = tag(rt, config);
3238
3236
 
3239
3237
  if (config.silent) {
3240
- outputJson({ systemMessage: tagStr + ' planReview \\u2192 skipped (silent mode)' });
3238
+ outputJson({ systemMessage: tagStr + ' planReview \u2192 skipped (silent mode)' });
3241
3239
  return;
3242
3240
  }
3243
3241
 
@@ -3254,7 +3252,7 @@ async function main() {
3254
3252
  try {
3255
3253
  gradeResp = await localGrade('plan', graderPrompt);
3256
3254
  } catch {
3257
- outputJson({ systemMessage: tagStr + ' planReview \\u2192 local grader unavailable, skipped' });
3255
+ outputJson({ systemMessage: tagStr + ' planReview \u2192 local grader unavailable, skipped' });
3258
3256
  return;
3259
3257
  }
3260
3258
 
@@ -3265,7 +3263,7 @@ async function main() {
3265
3263
  if (!verdict.ok) {
3266
3264
  const reviewMsg = (verdict.ruleId ? '(first: ' + verdict.ruleId + ') ' : '') + (verdict.reason || 'check org rules during implementation');
3267
3265
  appendReviewToPlan(planFile, '\\u26a0\\ufe0f Advisory \\u2014 ' + reviewMsg);
3268
- const advLine = tagStr + ' planReview \\u2192 ' + reviewMsg;
3266
+ const advLine = tagStr + ' planReview \u2192 ' + reviewMsg;
3269
3267
  outputJson({ systemMessage: advLine, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro local plan judge (advisory). ' + reviewMsg } });
3270
3268
  dispatchCapture(jwt, 'plan_review', 'advisory', verdict.severity || 'medium', verdict.category || 'general',
3271
3269
  'ExitPlanMode', gitRepo, sessionId, config.captureDepth, {
@@ -3275,7 +3273,7 @@ async function main() {
3275
3273
  } else {
3276
3274
  const reviewMsg = verdict.reason || 'no relevant org rules for this plan';
3277
3275
  appendReviewToPlan(planFile, '\\u2705 Clean \\u2014 ' + reviewMsg);
3278
- const cleanLine = tagStr + ' planReview \\u2192 clean: ' + reviewMsg;
3276
+ const cleanLine = tagStr + ' planReview \u2192 clean: ' + reviewMsg;
3279
3277
  outputJson({ systemMessage: cleanLine, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro local plan judge. ' + reviewMsg } });
3280
3278
  dispatchCapture(jwt, 'plan_review', 'clean', 'audit', verdict.category || 'general',
3281
3279
  'ExitPlanMode', gitRepo, sessionId, config.captureDepth, {
@@ -3299,7 +3297,7 @@ async function main() {
3299
3297
  const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 12000);
3300
3298
 
3301
3299
  if (!resp) {
3302
- log('planReview \\u2192 error (timeout)');
3300
+ log('planReview \u2192 error (timeout)');
3303
3301
  outputEmpty();
3304
3302
  return;
3305
3303
  }
@@ -3311,7 +3309,7 @@ async function main() {
3311
3309
  if (decision) {
3312
3310
  const reason = hookResp?.hookSpecificOutput?.permissionDecisionReason || 'check org rules';
3313
3311
  appendReviewToPlan(planFile, '\\u26a0\\ufe0f Advisory \\u2014 ' + reason);
3314
- outputJson({ systemMessage: tagStr + ' planReview \\u2192 advisory: ' + reason });
3312
+ outputJson({ systemMessage: tagStr + ' planReview \u2192 advisory: ' + reason });
3315
3313
  } else {
3316
3314
  const cloudMsg = hookResp.systemMessage || '';
3317
3315
  if (cloudMsg) appendReviewToPlan(planFile, '\\u2705 ' + cloudMsg);
@@ -3401,9 +3399,9 @@ async function main() {
3401
3399
  const tagStr = tag('local', config);
3402
3400
 
3403
3401
  if (!findings) {
3404
- outputJson({ systemMessage: tagStr + ' stop \\u2192 0 issues across ' + edits + ' edit(s), session complete' });
3402
+ outputJson({ systemMessage: tagStr + ' stop \u2192 0 issues across ' + edits + ' edit(s), session complete' });
3405
3403
  } else {
3406
- outputJson({ systemMessage: tagStr + ' stop \\u2192 ' + findings + ' finding(s): ' + autoFixed + ' auto-fixed, ' + open + ' open' });
3404
+ outputJson({ systemMessage: tagStr + ' stop \u2192 ' + findings + ' finding(s): ' + autoFixed + ' auto-fixed, ' + open + ' open' });
3407
3405
  }
3408
3406
  } catch (err) {
3409
3407
  process.stderr.write('[synkro] stopSummary error: ' + String(err) + '\\n');
@@ -3466,9 +3464,9 @@ async function main() {
3466
3464
  if (!openFindings) {
3467
3465
  outputJson({ systemMessage: routeLine });
3468
3466
  } else if (openFindings === 1) {
3469
- outputJson({ systemMessage: routeLine + '\\n' + tagStr + ' session start \\u2192 1 open finding in this repo from a prior session.' });
3467
+ outputJson({ systemMessage: routeLine + '\\n' + tagStr + ' session start \u2192 1 open finding in this repo from a prior session.' });
3470
3468
  } else {
3471
- outputJson({ systemMessage: routeLine + '\\n' + tagStr + ' session start \\u2192 ' + openFindings + ' open findings in this repo from prior sessions.' });
3469
+ outputJson({ systemMessage: routeLine + '\\n' + tagStr + ' session start \u2192 ' + openFindings + ' open findings in this repo from prior sessions.' });
3472
3470
  }
3473
3471
  } catch (err) {
3474
3472
  process.stderr.write('[synkro] sessionStart error: ' + String(err) + '\\n');
@@ -6398,7 +6396,7 @@ function writeConfigEnv(opts) {
6398
6396
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
6399
6397
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
6400
6398
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
6401
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.82")}`
6399
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.84")}`
6402
6400
  ];
6403
6401
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
6404
6402
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);