@synkro-sh/cli 1.4.64 → 1.4.65

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
@@ -160,6 +160,17 @@ function installCCHooks(settingsPath, config) {
160
160
  ],
161
161
  [SYNKRO_MARKER]: true
162
162
  });
163
+ settings.hooks.PreToolUse.push({
164
+ matcher: "Agent",
165
+ hooks: [
166
+ {
167
+ type: "command",
168
+ command: config.agentJudgeScriptPath,
169
+ timeout: 30
170
+ }
171
+ ],
172
+ [SYNKRO_MARKER]: true
173
+ });
163
174
  settings.hooks.PreToolUse.push({
164
175
  matcher: "ExitPlanMode",
165
176
  hooks: [
@@ -828,7 +839,7 @@ exit 0
828
839
  });
829
840
 
830
841
  // cli/installer/hookScriptsTs.ts
831
- var SYNKRO_COMMON_TS, EDIT_PRECHECK_TS, CWE_PRECHECK_TS, CVE_PRECHECK_TS, BASH_JUDGE_TS, PLAN_JUDGE_TS, STOP_SUMMARY_TS, SESSION_START_TS, BASH_FOLLOWUP_TS, TRANSCRIPT_SYNC_TS, USER_PROMPT_SUBMIT_TS;
842
+ var SYNKRO_COMMON_TS, EDIT_PRECHECK_TS, CWE_PRECHECK_TS, CVE_PRECHECK_TS, BASH_JUDGE_TS, AGENT_JUDGE_TS, PLAN_JUDGE_TS, STOP_SUMMARY_TS, SESSION_START_TS, BASH_FOLLOWUP_TS, TRANSCRIPT_SYNC_TS, USER_PROMPT_SUBMIT_TS;
832
843
  var init_hookScriptsTs = __esm({
833
844
  "cli/installer/hookScriptsTs.ts"() {
834
845
  "use strict";
@@ -1134,7 +1145,7 @@ const PRIMER_KEY: Record<GradeRole, string> = {
1134
1145
  let primerCache: { data: Record<string, string>; ts: number } | null = null;
1135
1146
 
1136
1147
  async function fetchPrimer(role: GradeRole, jwt: string): Promise<string> {
1137
- if (primerCache && Date.now() - primerCache.ts < 60_000) {
1148
+ if (primerCache && Date.now() - primerCache.ts < 300_000) {
1138
1149
  const cached = primerCache.data[PRIMER_KEY[role]];
1139
1150
  if (cached) return cached;
1140
1151
  }
@@ -1188,7 +1199,7 @@ export async function localGrade(surface: string, prompt: string): Promise<strin
1188
1199
  export async function localGradeCwe(prompt: string): Promise<string> {
1189
1200
  const jwt = loadJwt();
1190
1201
  if (!jwt) throw new Error('NO_JWT');
1191
- return channelGrade('grade-cwe', prompt, jwt, 8930, 20000);
1202
+ return channelGrade('grade-cwe', prompt, jwt, 8930, 45000);
1192
1203
  }
1193
1204
 
1194
1205
  // \u2500\u2500\u2500 Verdict Parsing \u2500\u2500\u2500
@@ -2266,7 +2277,7 @@ main();
2266
2277
  BASH_JUDGE_TS = `#!/usr/bin/env bun
2267
2278
  import {
2268
2279
  loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
2269
- parseVerdict, dispatchCapture, ruleMode, postWithRetry, readStdin,
2280
+ parseVerdict, dispatchCapture, dispatchFinding, ruleMode, postWithRetry, readStdin,
2270
2281
  extractTranscript, readLastPrompt, log,
2271
2282
  outputJson, outputEmpty, GATEWAY_URL,
2272
2283
  type HookConfig, type Rule,
@@ -2309,6 +2320,7 @@ async function main() {
2309
2320
  jwt = await ensureFreshJwt(jwt);
2310
2321
 
2311
2322
  // \u2500\u2500\u2500 CVE scan for package install commands \u2500\u2500\u2500
2323
+ let cveScanMsg = '';
2312
2324
  if (toolName === 'Bash') {
2313
2325
  const pkgInstallMatch = command.match(
2314
2326
  /(?: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+(.+)/
@@ -2326,7 +2338,7 @@ async function main() {
2326
2338
  for (const token of tokens) {
2327
2339
  if (skipNext) { skipNext = false; continue; }
2328
2340
  if (token.startsWith('-')) {
2329
- if (/^--(python|target|prefix|root|constraint|requirement|index-url|extra-index-url|find-links|build|src|cache-dir)$/.test(token)) skipNext = true;
2341
+ if (/^--(python|target|prefix|root|constraint|requirement|index-url|extra-index-url|find-links|build|src|cache-dir|filter|workspace)$/.test(token)) skipNext = true;
2330
2342
  continue;
2331
2343
  }
2332
2344
  if (isPip) {
@@ -2344,6 +2356,23 @@ async function main() {
2344
2356
  deps[token] = '*';
2345
2357
  }
2346
2358
  }
2359
+ const unversioned = Object.keys(deps).filter(k => deps[k] === '*');
2360
+ if (unversioned.length > 0) {
2361
+ const lookups = unversioned.map(async (pkg) => {
2362
+ try {
2363
+ let ver: string | null = null;
2364
+ if (isPip) {
2365
+ const r = await fetch('https://pypi.org/pypi/' + encodeURIComponent(pkg) + '/json', { signal: AbortSignal.timeout(4000) });
2366
+ if (r.ok) { const d = await r.json() as any; ver = d?.info?.version || null; }
2367
+ } else {
2368
+ const r = await fetch('https://registry.npmjs.org/' + encodeURIComponent(pkg) + '/latest', { signal: AbortSignal.timeout(4000) });
2369
+ if (r.ok) { const d = await r.json() as any; ver = d?.version || null; }
2370
+ }
2371
+ if (ver) deps[pkg] = ver;
2372
+ } catch {}
2373
+ });
2374
+ await Promise.all(lookups);
2375
+ }
2347
2376
  const manifestFile = isPip ? 'requirements.txt'
2348
2377
  : isGo ? 'go.mod'
2349
2378
  : isCargo ? 'Cargo.toml'
@@ -2364,7 +2393,11 @@ async function main() {
2364
2393
  }).then(r => r.json()) as any;
2365
2394
 
2366
2395
  const findings = Array.isArray(cveResp?.findings) ? cveResp.findings : [];
2367
- if (findings.length > 0) {
2396
+ const scannedPkgs = Object.entries(deps).map(([k, v]) => k + '@' + v).join(', ');
2397
+ if (findings.length === 0) {
2398
+ cveScanMsg = '[synkro:cveScan] ' + scannedPkgs + ' \\u2192 clean, no known vulnerabilities';
2399
+ }
2400
+ else if (findings.length > 0) {
2368
2401
  const top3 = findings.slice(0, 3).map((f: any) => {
2369
2402
  const id = f.cve || f.id || '?';
2370
2403
  const pkg = f.package || '?';
@@ -2376,6 +2409,26 @@ async function main() {
2376
2409
  const label = count === 1 ? 'advisory' : 'advisories';
2377
2410
  const cveMsg = '[synkro:cveScan] ' + cmdShort + ' \\u2192 ' + count + ' ' + label;
2378
2411
  const ctx = 'CVE: ' + top3 + '\\nDo NOT install packages with known vulnerabilities. Use a patched version or a different package.';
2412
+
2413
+ const config = await loadConfig(jwt);
2414
+ for (const f of findings) {
2415
+ dispatchFinding(jwt, {
2416
+ session_id: sessionId,
2417
+ file_path: command,
2418
+ finding_type: 'cve',
2419
+ finding_id: f.cve || f.id || f.package,
2420
+ severity: typeof f.severity === 'number' ? (f.severity >= 9 ? 'critical' : f.severity >= 7 ? 'high' : f.severity >= 4 ? 'medium' : 'low') : (f.severity || 'medium'),
2421
+ status: 'open',
2422
+ detail: f.details || f.summary || null,
2423
+ description: f.summary || null,
2424
+ package_name: f.package || null,
2425
+ package_version: f.version || null,
2426
+ fixed_version: f.fixed || null,
2427
+ aliases: f.aliases || [],
2428
+ references: f.references || [],
2429
+ }, config.captureDepth);
2430
+ }
2431
+
2379
2432
  outputJson({
2380
2433
  systemMessage: cveMsg,
2381
2434
  hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: ctx, additionalContext: ctx },
@@ -2397,7 +2450,8 @@ async function main() {
2397
2450
  const tagStr = tag(rt, config);
2398
2451
 
2399
2452
  if (config.silent) {
2400
- outputJson({ systemMessage: tagStr + ' bashGuard \\u2192 skipped (silent mode)' });
2453
+ const msg = (cveScanMsg ? cveScanMsg + '\\n' : '') + tagStr + ' bashGuard \\u2192 skipped (silent mode)';
2454
+ outputJson({ systemMessage: msg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: msg } });
2401
2455
  return;
2402
2456
  }
2403
2457
 
@@ -2428,7 +2482,8 @@ async function main() {
2428
2482
 
2429
2483
  if (mode === 'audit') {
2430
2484
  const reason = tagStr + ' bashGuard \\u2192 warning' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation');
2431
- outputJson({ systemMessage: reason });
2485
+ const combined = (cveScanMsg ? cveScanMsg + '\\n' : '') + reason;
2486
+ outputJson({ systemMessage: combined, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: combined } });
2432
2487
  dispatchCapture(jwt, 'bash', 'warning', verdict.severity || 'medium', verdict.category || 'security',
2433
2488
  toolName, gitRepo, sessionId, config.captureDepth, {
2434
2489
  command, reasoning: guardReason, rulesChecked: config.rules, violatedRules,
@@ -2436,9 +2491,10 @@ async function main() {
2436
2491
  });
2437
2492
  } else {
2438
2493
  const reason = tagStr + ' bashGuard \\u2192 blocked' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation') + '. Ask the user for explicit consent before retrying.';
2494
+ const combined = (cveScanMsg ? cveScanMsg + '\\n' : '') + reason;
2439
2495
  outputJson({
2440
- systemMessage: reason,
2441
- hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason, additionalContext: reason },
2496
+ systemMessage: combined,
2497
+ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason, additionalContext: combined },
2442
2498
  });
2443
2499
  dispatchCapture(jwt, 'bash', 'block', verdict.severity || 'critical', verdict.category || 'security',
2444
2500
  toolName, gitRepo, sessionId, config.captureDepth, {
@@ -2447,7 +2503,9 @@ async function main() {
2447
2503
  });
2448
2504
  }
2449
2505
  } else {
2450
- outputJson({ systemMessage: tagStr + ' bashGuard \\u2192 pass: ' + (verdict.reason || 'no policy violations detected') });
2506
+ const reason = tagStr + ' bashGuard \\u2192 pass: ' + (verdict.reason || 'no policy violations detected');
2507
+ const combined = (cveScanMsg ? cveScanMsg + '\\n' : '') + reason;
2508
+ outputJson({ systemMessage: combined, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: combined } });
2451
2509
  dispatchCapture(jwt, 'bash', 'pass', 'audit', verdict.category || 'trivial_utility',
2452
2510
  toolName, gitRepo, sessionId, config.captureDepth, {
2453
2511
  command, reasoning: verdict.reason || 'no policy violations detected',
@@ -2486,16 +2544,30 @@ async function main() {
2486
2544
 
2487
2545
  if (!resp) {
2488
2546
  log('bashGuard ' + cmdShort + ' \\u2192 error (timeout)');
2489
- outputEmpty();
2547
+ if (cveScanMsg) {
2548
+ outputJson({ systemMessage: cveScanMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: cveScanMsg } });
2549
+ } else { outputEmpty(); }
2490
2550
  return;
2491
2551
  }
2492
2552
 
2493
2553
  if (!resp.hook_response || typeof resp.hook_response !== 'object') {
2494
2554
  log('bashGuard ' + cmdShort + ' \\u2192 pass (no hook_response)');
2495
- outputEmpty();
2555
+ if (cveScanMsg) {
2556
+ outputJson({ systemMessage: cveScanMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: cveScanMsg } });
2557
+ } else { outputEmpty(); }
2496
2558
  return;
2497
2559
  }
2498
2560
 
2561
+ if (cveScanMsg) {
2562
+ const existing = resp.hook_response.systemMessage || '';
2563
+ resp.hook_response.systemMessage = cveScanMsg + (existing ? '\\n' + existing : '');
2564
+ if (resp.hook_response.hookSpecificOutput) {
2565
+ const existingCtx = resp.hook_response.hookSpecificOutput.additionalContext || '';
2566
+ resp.hook_response.hookSpecificOutput.additionalContext = cveScanMsg + (existingCtx ? '\\n' + existingCtx : '');
2567
+ } else {
2568
+ resp.hook_response.hookSpecificOutput = { hookEventName: 'PreToolUse', additionalContext: resp.hook_response.systemMessage };
2569
+ }
2570
+ }
2499
2571
  outputJson(resp.hook_response);
2500
2572
  } catch (err) {
2501
2573
  process.stderr.write('[synkro] bashGuard error: ' + String(err) + '\\n');
@@ -2503,6 +2575,170 @@ async function main() {
2503
2575
  }
2504
2576
  }
2505
2577
 
2578
+ main();
2579
+ `;
2580
+ AGENT_JUDGE_TS = `#!/usr/bin/env bun
2581
+ import {
2582
+ loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
2583
+ parseVerdict, dispatchCapture, ruleMode, postWithRetry, readStdin,
2584
+ extractTranscript, readLastPrompt, log,
2585
+ outputJson, outputEmpty, GATEWAY_URL,
2586
+ type HookConfig, type Rule,
2587
+ } from './_synkro-common.ts';
2588
+
2589
+ async function main() {
2590
+ try {
2591
+ const input = await readStdin();
2592
+ if (!input.trim()) { outputEmpty(); return; }
2593
+
2594
+ const payload = JSON.parse(input);
2595
+ const toolName = payload.tool_name || '';
2596
+ if (toolName !== 'Agent') {
2597
+ outputEmpty();
2598
+ return;
2599
+ }
2600
+
2601
+ const toolInput = payload.tool_input || {};
2602
+ const sessionId = payload.session_id || '';
2603
+ const toolUseId = payload.tool_use_id || '';
2604
+ const cwd = payload.cwd || '';
2605
+ const permissionMode = payload.permission_mode || '';
2606
+ const transcriptPath = payload.transcript_path || '';
2607
+ const gitRepo = detectRepo(cwd || '.');
2608
+
2609
+ const prompt = toolInput.prompt || '';
2610
+ const description = toolInput.description || '';
2611
+ const subagentType = toolInput.subagent_type || 'general-purpose';
2612
+ if (!prompt) { outputEmpty(); return; }
2613
+
2614
+ const promptShort = prompt.slice(0, 80);
2615
+ log('agentGuard checking: ' + description + ' (' + subagentType + ')');
2616
+
2617
+ let jwt = loadJwt();
2618
+ if (!jwt) { outputEmpty(); return; }
2619
+ jwt = await ensureFreshJwt(jwt);
2620
+
2621
+ const transcript = extractTranscript(transcriptPath);
2622
+ const lastPrompt = readLastPrompt();
2623
+
2624
+ const config = await loadConfig(jwt);
2625
+ const rt = await route(config);
2626
+ const tagStr = tag(rt, config);
2627
+
2628
+ if (config.silent) {
2629
+ const msg = tagStr + ' agentGuard \\u2192 skipped (silent mode)';
2630
+ outputJson({ systemMessage: msg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: msg } });
2631
+ return;
2632
+ }
2633
+
2634
+ if (rt === 'local') {
2635
+ const graderPrompt = [
2636
+ 'Working directory: ' + (cwd || '.'),
2637
+ 'Repo: ' + (gitRepo || 'unknown'),
2638
+ 'Tool: Agent (subagent spawn)',
2639
+ 'Subagent type: ' + subagentType,
2640
+ 'Description: ' + description,
2641
+ 'Subagent prompt (first 4000 chars):',
2642
+ prompt.slice(0, 4000),
2643
+ 'User intent (last human message): ' + (transcript.userIntent || 'none stated'),
2644
+ 'Last user prompt: ' + (lastPrompt || 'none'),
2645
+ 'Org rules: ' + JSON.stringify(config.rules),
2646
+ ].join('\\n');
2647
+
2648
+ let gradeResp: string;
2649
+ try {
2650
+ gradeResp = await localGrade('bash', graderPrompt);
2651
+ } catch {
2652
+ outputEmpty();
2653
+ return;
2654
+ }
2655
+
2656
+ const verdict = parseVerdict(gradeResp);
2657
+ const agentContent = 'agent=' + subagentType + ' desc=' + description + ' prompt=' + prompt.slice(0, 2000);
2658
+ const violatedRules = verdict.ruleId ? [verdict.ruleId] : [];
2659
+
2660
+ if (!verdict.ok) {
2661
+ const mode = verdict.ruleMode || ruleMode(verdict.ruleId, config.rules);
2662
+ const guardReason = (verdict.ruleId ? '(' + verdict.ruleId + ') ' : '') + (verdict.reason || 'policy violation');
2663
+
2664
+ if (mode === 'audit') {
2665
+ const reason = tagStr + ' agentGuard \\u2192 warning' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation');
2666
+ outputJson({ systemMessage: reason, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: reason } });
2667
+ dispatchCapture(jwt, 'agent', 'warning', verdict.severity || 'medium', verdict.category || 'security',
2668
+ toolName, gitRepo, sessionId, config.captureDepth, {
2669
+ command: agentContent, reasoning: guardReason, rulesChecked: config.rules, violatedRules,
2670
+ recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,
2671
+ });
2672
+ } else {
2673
+ const reason = tagStr + ' agentGuard \\u2192 blocked' + (verdict.ruleId ? ' (' + verdict.ruleId + ')' : '') + ': ' + (verdict.reason || 'policy violation') + '. Ask the user for explicit consent before retrying.';
2674
+ outputJson({
2675
+ systemMessage: reason,
2676
+ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason, additionalContext: reason },
2677
+ });
2678
+ dispatchCapture(jwt, 'agent', 'block', verdict.severity || 'critical', verdict.category || 'security',
2679
+ toolName, gitRepo, sessionId, config.captureDepth, {
2680
+ command: agentContent, reasoning: guardReason, rulesChecked: config.rules, violatedRules,
2681
+ recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,
2682
+ });
2683
+ }
2684
+ } else {
2685
+ const reason = tagStr + ' agentGuard \\u2192 pass: ' + (verdict.reason || 'no policy violations detected');
2686
+ outputJson({ systemMessage: reason, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: reason } });
2687
+ dispatchCapture(jwt, 'agent', 'pass', 'audit', verdict.category || 'subagent_spawn',
2688
+ toolName, gitRepo, sessionId, config.captureDepth, {
2689
+ command: agentContent, reasoning: verdict.reason || 'no policy violations detected',
2690
+ rulesChecked: config.rules, violatedRules: [],
2691
+ recentUserMessages: transcript.recentUserMessages, ccModel: transcript.ccModel,
2692
+ });
2693
+ }
2694
+ return;
2695
+ }
2696
+
2697
+ // \u2500\u2500\u2500 Cloud grading \u2500\u2500\u2500
2698
+ const isHeadless = ['acceptEdits', 'bypassPermissions', 'plan', 'auto'].includes(permissionMode)
2699
+ || process.env.SYNKRO_HEADLESS === '1';
2700
+
2701
+ const body: Record<string, any> = {
2702
+ hook_event: 'PreToolUse',
2703
+ tool_name: toolName,
2704
+ tool_input: toolInput,
2705
+ user_intent: transcript.userIntent || null,
2706
+ last_user_message: lastPrompt || null,
2707
+ recent_user_messages: transcript.recentUserMessages,
2708
+ recent_messages: transcript.recentMessages,
2709
+ recent_actions: transcript.recentActions,
2710
+ session_id: sessionId || null,
2711
+ tool_use_id: toolUseId || null,
2712
+ cwd: cwd || null,
2713
+ repo: gitRepo || null,
2714
+ permission_mode: permissionMode || null,
2715
+ headless: isHeadless,
2716
+ cc_model: transcript.ccModel || null,
2717
+ cc_usage: transcript.ccUsage || {},
2718
+ session_summary: transcript.sessionSummary || null,
2719
+ };
2720
+
2721
+ const resp = await postWithRetry(GATEWAY_URL + '/api/v1/hook/judge', body, jwt, 8000);
2722
+
2723
+ if (!resp) {
2724
+ log('agentGuard ' + promptShort + ' \\u2192 error (timeout)');
2725
+ outputEmpty();
2726
+ return;
2727
+ }
2728
+
2729
+ if (!resp.hook_response || typeof resp.hook_response !== 'object') {
2730
+ log('agentGuard ' + promptShort + ' \\u2192 pass (no hook_response)');
2731
+ outputEmpty();
2732
+ return;
2733
+ }
2734
+
2735
+ outputJson(resp.hook_response);
2736
+ } catch (err) {
2737
+ process.stderr.write('[synkro] agentGuard error: ' + String(err) + '\\n');
2738
+ outputEmpty();
2739
+ }
2740
+ }
2741
+
2506
2742
  main();
2507
2743
  `;
2508
2744
  PLAN_JUDGE_TS = `#!/usr/bin/env bun
@@ -5292,6 +5528,7 @@ function writeHookScripts() {
5292
5528
  const cwePrecheckScriptPath = join11(HOOKS_DIR, "cc-cwe-precheck.ts");
5293
5529
  const cvePrecheckScriptPath = join11(HOOKS_DIR, "cc-cve-precheck.ts");
5294
5530
  const planJudgeScriptPath = join11(HOOKS_DIR, "cc-plan-judge.ts");
5531
+ const agentJudgeScriptPath = join11(HOOKS_DIR, "cc-agent-judge.ts");
5295
5532
  const stopSummaryScriptPath = join11(HOOKS_DIR, "cc-stop-summary.ts");
5296
5533
  const sessionStartScriptPath = join11(HOOKS_DIR, "cc-session-start.ts");
5297
5534
  const transcriptSyncScriptPath = join11(HOOKS_DIR, "cc-transcript-sync.ts");
@@ -5308,6 +5545,7 @@ function writeHookScripts() {
5308
5545
  writeFileSync7(cwePrecheckScriptPath, CWE_PRECHECK_TS, "utf-8");
5309
5546
  writeFileSync7(cvePrecheckScriptPath, CVE_PRECHECK_TS, "utf-8");
5310
5547
  writeFileSync7(planJudgeScriptPath, PLAN_JUDGE_TS, "utf-8");
5548
+ writeFileSync7(agentJudgeScriptPath, AGENT_JUDGE_TS, "utf-8");
5311
5549
  writeFileSync7(stopSummaryScriptPath, STOP_SUMMARY_TS, "utf-8");
5312
5550
  writeFileSync7(sessionStartScriptPath, SESSION_START_TS, "utf-8");
5313
5551
  writeFileSync7(transcriptSyncScriptPath, TRANSCRIPT_SYNC_TS, "utf-8");
@@ -5324,6 +5562,7 @@ function writeHookScripts() {
5324
5562
  chmodSync2(cwePrecheckScriptPath, 493);
5325
5563
  chmodSync2(cvePrecheckScriptPath, 493);
5326
5564
  chmodSync2(planJudgeScriptPath, 493);
5565
+ chmodSync2(agentJudgeScriptPath, 493);
5327
5566
  chmodSync2(stopSummaryScriptPath, 493);
5328
5567
  chmodSync2(sessionStartScriptPath, 493);
5329
5568
  chmodSync2(transcriptSyncScriptPath, 493);
@@ -5341,6 +5580,7 @@ function writeHookScripts() {
5341
5580
  cwePrecheckScript: cwePrecheckScriptPath,
5342
5581
  cvePrecheckScript: cvePrecheckScriptPath,
5343
5582
  planJudgeScript: planJudgeScriptPath,
5583
+ agentJudgeScript: agentJudgeScriptPath,
5344
5584
  stopSummaryScript: stopSummaryScriptPath,
5345
5585
  sessionStartScript: sessionStartScriptPath,
5346
5586
  transcriptSyncScript: transcriptSyncScriptPath,
@@ -5380,7 +5620,7 @@ function writeConfigEnv(opts) {
5380
5620
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
5381
5621
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
5382
5622
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
5383
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.64")}`
5623
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.65")}`
5384
5624
  ];
5385
5625
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
5386
5626
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -5686,6 +5926,7 @@ async function installCommand(opts = {}) {
5686
5926
  console.log(` ${scripts.bashFollowupScript}`);
5687
5927
  console.log(` ${scripts.editPrecheckScript}`);
5688
5928
  console.log(` ${scripts.planJudgeScript}`);
5929
+ console.log(` ${scripts.agentJudgeScript}`);
5689
5930
  console.log(` ${scripts.stopSummaryScript}`);
5690
5931
  console.log(` ${scripts.sessionStartScript}`);
5691
5932
  console.log(` ${scripts.transcriptSyncScript}
@@ -5722,6 +5963,7 @@ async function installCommand(opts = {}) {
5722
5963
  cwePrecheckScriptPath: scripts.cwePrecheckScript,
5723
5964
  cvePrecheckScriptPath: scripts.cvePrecheckScript,
5724
5965
  planJudgeScriptPath: scripts.planJudgeScript,
5966
+ agentJudgeScriptPath: scripts.agentJudgeScript,
5725
5967
  stopSummaryScriptPath: scripts.stopSummaryScript,
5726
5968
  sessionStartScriptPath: scripts.sessionStartScript,
5727
5969
  transcriptSyncScriptPath: scripts.transcriptSyncScript,