@synkro-sh/cli 1.6.45 → 1.6.46

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
@@ -1280,6 +1280,39 @@ export function effectiveGraderPool(synkroFile: SynkroFileConfig, hookAgentKind:
1280
1280
  return 'cursor';
1281
1281
  }
1282
1282
 
1283
+ // \u2500\u2500\u2500 .synkro presence (per-repo onboarding) \u2500\u2500\u2500
1284
+ // A repo is "onboarded" to Synkro when it has a .synkro file at its git root.
1285
+ // The hooks are installed globally, so they fire in every repo \u2014 including ones
1286
+ // the user never set up. Without a .synkro there, grading is SKIPPED gracefully
1287
+ // rather than attempting a grade that surfaces a confusing "grader unavailable"
1288
+ // error: the tool call passes through and we show a one-time hint pointing at
1289
+ // "synkro install".
1290
+ export function synkroFilePresent(cwd?: string): boolean {
1291
+ try {
1292
+ const root = cwd ? findGitRoot(cwd) : '';
1293
+ if (!root) return false;
1294
+ return existsSync(root + '/.synkro');
1295
+ } catch { return false; }
1296
+ }
1297
+
1298
+ const NO_SYNKRO_HINT_DIR = join(HOME, '.synkro', '.no-synkro-hint');
1299
+
1300
+ // Returns the onboarding hint the FIRST time a session touches a repo with no
1301
+ // .synkro file, then null on subsequent calls so it doesn't repeat on every
1302
+ // tool call. Caller passes null straight through to outputEmpty().
1303
+ export function noSynkroSkipMessage(sessionId: string): string | null {
1304
+ try {
1305
+ mkdirSync(NO_SYNKRO_HINT_DIR, { recursive: true });
1306
+ const key = (sessionId || 'nosession').replace(/[^a-zA-Z0-9_-]/g, '_');
1307
+ const marker = join(NO_SYNKRO_HINT_DIR, key);
1308
+ if (existsSync(marker)) return null;
1309
+ writeFileSync(marker, '', { flag: 'w' });
1310
+ } catch {
1311
+ // best-effort dedup \u2014 if the marker can't be written, still show the hint
1312
+ }
1313
+ return '[synkro] No .synkro config in this repo \u2014 grading skipped. Run \`synkro install\` here to enable Synkro.';
1314
+ }
1315
+
1283
1316
  // \u2500\u2500\u2500 Channel Health \u2500\u2500\u2500
1284
1317
 
1285
1318
  export async function channelUp(port = 18929): Promise<boolean> {
@@ -3355,7 +3388,7 @@ import {
3355
3388
  outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isEditTool, hookSessionId, GATEWAY_URL,
3356
3389
  logGraderUnavailable, graderUnavailableMessage, filterRules, ruleFilterText, normalizeMode, countEditLineDelta,
3357
3390
  captureLineMetrics, cursorModelFromPayload, resolveTranscriptPath, isCursorInvokingCcHook,
3358
- loadSynkroFile, effectiveGraderPool,
3391
+ loadSynkroFile, effectiveGraderPool, synkroFilePresent, noSynkroSkipMessage,
3359
3392
  type HookConfig, type Rule,
3360
3393
  } from './_synkro-common.ts';
3361
3394
  import { existsSync, readFileSync } from 'node:fs';
@@ -3390,6 +3423,13 @@ async function main() {
3390
3423
 
3391
3424
  if (filePath.includes('/.synkro/hooks/')) { outputEmpty(); return; }
3392
3425
 
3426
+ if (!synkroFilePresent(cwd)) {
3427
+ const _skipMsg = noSynkroSkipMessage(sessionId);
3428
+ if (_skipMsg) outputJson({ systemMessage: _skipMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: _skipMsg } });
3429
+ else outputEmpty();
3430
+ return;
3431
+ }
3432
+
3393
3433
  const fileShort = basename(filePath);
3394
3434
  log('editGuard checking: ' + fileShort);
3395
3435
 
@@ -3613,7 +3653,7 @@ import {
3613
3653
  outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isEditTool, isShellTool, isCursorHookFormat,
3614
3654
  extractShellCodeWrites, hookSessionId, filePathFromToolInput, emitBlockScanFindings, dispatchFinding, dispatchCapture, GATEWAY_URL,
3615
3655
  logGraderUnavailable, graderUnavailableMessage, resolveTranscriptPath, isCursorInvokingCcHook,
3616
- loadSynkroFile, effectiveGraderPool,
3656
+ loadSynkroFile, effectiveGraderPool, synkroFilePresent, noSynkroSkipMessage,
3617
3657
  } from './_synkro-common.ts';
3618
3658
  import { basename, extname, resolve, join, dirname } from 'node:path';
3619
3659
  import { readFileSync, readdirSync, existsSync } from 'node:fs';
@@ -3767,6 +3807,13 @@ async function main() {
3767
3807
 
3768
3808
  if (isCursorInvokingCcHook(agentKind, ccModel)) { outputEmpty(); return; }
3769
3809
 
3810
+ if (!synkroFilePresent(cwd)) {
3811
+ const _skipMsg = noSynkroSkipMessage(sessionId);
3812
+ if (_skipMsg) outputJson({ systemMessage: _skipMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: _skipMsg } });
3813
+ else outputEmpty();
3814
+ return;
3815
+ }
3816
+
3770
3817
  const targets: CweScanTarget[] = [];
3771
3818
 
3772
3819
  if (isCursorHookFormat() && (shellCommand || isShellTool(toolName))) {
@@ -4173,7 +4220,7 @@ import {
4173
4220
  loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag,
4174
4221
  reconstructContent, readStdin, findNearestDeps, filePathFromToolInput, log,
4175
4222
  outputJson, outputEmpty, setupCursorHookSignals, isEditTool, hookSessionId, dispatchFinding, dispatchCapture, dispatchScanResult, extractTranscript, emitBlockScanFindings, resolveTranscriptPath, GATEWAY_URL,
4176
- isCursorHookFormat,
4223
+ isCursorHookFormat, synkroFilePresent, noSynkroSkipMessage,
4177
4224
  } from './_synkro-common.ts';
4178
4225
  import { basename } from 'node:path';
4179
4226
  import { readFileSync } from 'node:fs';
@@ -4219,6 +4266,13 @@ async function main() {
4219
4266
 
4220
4267
  if (filePath.includes('/.synkro/hooks/')) { outputEmpty(); return; }
4221
4268
 
4269
+ if (!synkroFilePresent(cwd)) {
4270
+ const _skipMsg = noSynkroSkipMessage(sessionId);
4271
+ if (_skipMsg) outputJson({ systemMessage: _skipMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: _skipMsg } });
4272
+ else outputEmpty();
4273
+ return;
4274
+ }
4275
+
4222
4276
  const fileShort = basename(filePath);
4223
4277
 
4224
4278
  let jwt = loadJwt();
@@ -4455,7 +4509,7 @@ import {
4455
4509
  loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag,
4456
4510
  readStdin, runInstallScan, emitBlockScanFindings, dispatchCapture, dispatchScanResult, hashCommand,
4457
4511
  outputJson, outputEmpty, setupCursorHookSignals, isShellTool, hookSessionId, log, GATEWAY_URL,
4458
- resolveTranscriptPath, isCursorHookFormat, loadSynkroFile, effectiveGraderPool,
4512
+ resolveTranscriptPath, isCursorHookFormat, loadSynkroFile, effectiveGraderPool, synkroFilePresent, noSynkroSkipMessage,
4459
4513
  } from './_synkro-common.ts';
4460
4514
  import { writeFileSync, mkdirSync } from 'node:fs';
4461
4515
  import { join } from 'node:path';
@@ -4480,6 +4534,14 @@ async function main() {
4480
4534
  // beforeShellExecution supplies command directly; preToolUse uses tool_name + tool_input.
4481
4535
  if (!isShellTool(toolName) && typeof payload.command !== 'string') { outputEmpty(); return; }
4482
4536
 
4537
+ const _cwd = payload.cwd || (Array.isArray(payload.workspace_roots) ? payload.workspace_roots[0] : '') || '';
4538
+ if (!synkroFilePresent(_cwd)) {
4539
+ const _skipMsg = noSynkroSkipMessage(hookSessionId(payload));
4540
+ if (_skipMsg) outputJson({ systemMessage: _skipMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: _skipMsg } });
4541
+ else outputEmpty();
4542
+ return;
4543
+ }
4544
+
4483
4545
  let jwt = loadJwt();
4484
4546
  if (!jwt) { outputEmpty(); return; }
4485
4547
  jwt = await ensureFreshJwt(jwt);
@@ -4576,7 +4638,7 @@ import {
4576
4638
  extractTranscript, readLastPrompt, appendSessionAction, readSessionLog, compressSessionLog, log,
4577
4639
  outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isShellTool, hookSessionId, GATEWAY_URL,
4578
4640
  logGraderUnavailable, graderUnavailableMessage, filterRules, ruleFilterText, normalizeMode, appendLocalTelemetry, isSafeInRepoRead,
4579
- loadSynkroFile, effectiveGraderPool,
4641
+ loadSynkroFile, effectiveGraderPool, synkroFilePresent, noSynkroSkipMessage,
4580
4642
  hashCommand, resolveTranscriptPath, isCursorHookFormat,
4581
4643
  type HookConfig, type Rule,
4582
4644
  } from './_synkro-common.ts';
@@ -4657,6 +4719,13 @@ async function main() {
4657
4719
 
4658
4720
  const gitRepo = detectRepo(cwd, transcriptPath, command, workspaceRoots);
4659
4721
 
4722
+ if (!synkroFilePresent(cwd)) {
4723
+ const _skipMsg = noSynkroSkipMessage(sessionId);
4724
+ if (_skipMsg) outputJson({ systemMessage: _skipMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: _skipMsg } });
4725
+ else outputEmpty();
4726
+ return;
4727
+ }
4728
+
4660
4729
  if (isDuplicate(command, sessionId)) {
4661
4730
  log('bashGuard skip (dedup): ' + command.slice(0, 80));
4662
4731
  outputEmpty();
@@ -4874,7 +4943,7 @@ import {
4874
4943
  extractTranscript, readLastPrompt, appendSessionAction, readSessionLog, compressSessionLog, log,
4875
4944
  outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isAgentTool, hookSessionId, GATEWAY_URL,
4876
4945
  logGraderUnavailable, graderUnavailableMessage, filterRules, normalizeMode, resolveTranscriptPath, isCursorInvokingCcHook,
4877
- loadSynkroFile, effectiveGraderPool,
4946
+ loadSynkroFile, effectiveGraderPool, synkroFilePresent, noSynkroSkipMessage,
4878
4947
  type HookConfig, type Rule,
4879
4948
  } from './_synkro-common.ts';
4880
4949
 
@@ -4905,6 +4974,13 @@ async function main() {
4905
4974
  const permissionMode = payload.permission_mode || '';
4906
4975
  const transcriptPath = resolveTranscriptPath(payload);
4907
4976
 
4977
+ if (!synkroFilePresent(cwd)) {
4978
+ const _skipMsg = noSynkroSkipMessage(sessionId);
4979
+ if (_skipMsg) outputJson({ systemMessage: _skipMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: _skipMsg } });
4980
+ else outputEmpty();
4981
+ return;
4982
+ }
4983
+
4908
4984
  const prompt = toolInput.prompt || '';
4909
4985
  const description = toolInput.description || '';
4910
4986
  const subagentType = toolInput.subagent_type || 'general-purpose';
@@ -5062,7 +5138,7 @@ import {
5062
5138
  parseVerdict, dispatchCapture, appendSessionAction, readSessionLog, compressSessionLog, postWithRetry, readStdin, log,
5063
5139
  outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isPlanTool, hookSessionId, GATEWAY_URL,
5064
5140
  filterRules, graderUnavailableMessage, resolveTranscriptPath, isCursorInvokingCcHook,
5065
- loadSynkroFile, effectiveGraderPool,
5141
+ loadSynkroFile, effectiveGraderPool, synkroFilePresent, noSynkroSkipMessage,
5066
5142
  } from './_synkro-common.ts';
5067
5143
  import { existsSync, readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';
5068
5144
  import { join } from 'node:path';
@@ -5135,6 +5211,13 @@ async function main() {
5135
5211
  const transcriptPath = resolveTranscriptPath(payload);
5136
5212
  const gitRepo = detectRepo(cwd, transcriptPath, plan, workspaceRoots);
5137
5213
 
5214
+ if (!synkroFilePresent(cwd)) {
5215
+ const _skipMsg = noSynkroSkipMessage(sessionId);
5216
+ if (_skipMsg) outputJson({ systemMessage: _skipMsg, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: _skipMsg } });
5217
+ else outputEmpty();
5218
+ return;
5219
+ }
5220
+
5138
5221
  appendSessionAction(sessionId, { ts: new Date().toISOString(), tool: 'ExitPlanMode', summary: 'plan review: ' + plan.slice(0, 80) });
5139
5222
 
5140
5223
  const planShort = plan.slice(0, 80);
@@ -5249,7 +5332,7 @@ main();
5249
5332
  import {
5250
5333
  loadJwt, detectRepo, loadConfig, tag, readStdin, aggregateUsage, cleanupSessionLog,
5251
5334
  outputJson, outputEmpty, shipCloud, setupCursorHookSignals, hookSessionId, GATEWAY_URL,
5252
- resolveTranscriptPath, emitUsageTick, cursorModelFromPayload, log,
5335
+ resolveTranscriptPath, emitUsageTick, cursorModelFromPayload, log, synkroFilePresent,
5253
5336
  } from './_synkro-common.ts';
5254
5337
 
5255
5338
  async function main() {
@@ -5264,6 +5347,7 @@ async function main() {
5264
5347
 
5265
5348
  const workspaceRoots = Array.isArray(payload.workspace_roots) ? payload.workspace_roots as string[] : [];
5266
5349
  const cwd = payload.cwd || workspaceRoots[0] || '';
5350
+ if (!synkroFilePresent(cwd)) { outputEmpty(); return; }
5267
5351
  const transcriptPath = resolveTranscriptPath(payload);
5268
5352
  const gitRepo = detectRepo(cwd, transcriptPath, '', workspaceRoots);
5269
5353
  const modelFallback = cursorModelFromPayload(payload);
@@ -5317,7 +5401,7 @@ main();
5317
5401
  import {
5318
5402
  loadJwt, detectRepo, channelUp, tag, readStdin, writeCachedRepo,
5319
5403
  outputJson, outputEmpty, setupCursorHookSignals, hookSessionId, resolveTranscriptPath, GATEWAY_URL,
5320
- isLocalStorageMode, loadSynkroFile, log, type HookConfig,
5404
+ isLocalStorageMode, loadSynkroFile, log, synkroFilePresent, type HookConfig,
5321
5405
  } from './_synkro-common.ts';
5322
5406
 
5323
5407
  async function main() {
@@ -5329,6 +5413,7 @@ async function main() {
5329
5413
  const payload = JSON.parse(input);
5330
5414
  const workspaceRoots = Array.isArray(payload.workspace_roots) ? payload.workspace_roots as string[] : [];
5331
5415
  const cwd = payload.cwd || workspaceRoots[0] || '';
5416
+ if (!synkroFilePresent(cwd)) { outputEmpty(); return; }
5332
5417
  const transcriptPath = resolveTranscriptPath(payload);
5333
5418
  const sessionId = hookSessionId(payload);
5334
5419
  const gitRepo = detectRepo(cwd, transcriptPath, '', workspaceRoots);
@@ -5408,7 +5493,7 @@ main();
5408
5493
  import {
5409
5494
  loadJwt, loadConfig, readStdin, hashCommand, consentGrant, consentHasActive, consentConsume,
5410
5495
  appendSessionAction,
5411
- outputEmpty, appendLocalTelemetry, shipCloud, setupCursorHookSignals, isShellTool, hookSessionId, GATEWAY_URL,
5496
+ outputEmpty, appendLocalTelemetry, shipCloud, setupCursorHookSignals, isShellTool, hookSessionId, GATEWAY_URL, synkroFilePresent,
5412
5497
  } from './_synkro-common.ts';
5413
5498
 
5414
5499
  async function main() {
@@ -5418,6 +5503,8 @@ async function main() {
5418
5503
  if (!input.trim()) { outputEmpty(); return; }
5419
5504
 
5420
5505
  const payload = JSON.parse(input);
5506
+ const _cwd = payload.cwd || (Array.isArray(payload.workspace_roots) ? payload.workspace_roots[0] : '') || '';
5507
+ if (!synkroFilePresent(_cwd)) { outputEmpty(); return; }
5421
5508
  const toolName = payload.tool_name || '';
5422
5509
  const shellCmd = typeof payload.command === 'string' ? payload.command : (payload.tool_input?.command || '');
5423
5510
  if (!isShellTool(toolName) && !shellCmd) { outputEmpty(); return; }
@@ -5472,7 +5559,7 @@ main();
5472
5559
  import {
5473
5560
  loadJwt, detectRepo, readStdin, aggregateUsage, appendLocalTelemetry,
5474
5561
  outputEmpty, setupCursorHookSignals, hookSessionId, GATEWAY_URL, readSessionLog, shipCloud,
5475
- resolveTranscriptPath, syncConversationTranscript, emitUsageTick, cursorModelFromPayload,
5562
+ resolveTranscriptPath, syncConversationTranscript, emitUsageTick, cursorModelFromPayload, synkroFilePresent,
5476
5563
  } from './_synkro-common.ts';
5477
5564
  import { readFileSync } from 'node:fs';
5478
5565
 
@@ -5486,6 +5573,7 @@ async function main() {
5486
5573
  const sessionId = hookSessionId(payload);
5487
5574
  const workspaceRoots = Array.isArray(payload.workspace_roots) ? payload.workspace_roots as string[] : [];
5488
5575
  const cwd = payload.cwd || workspaceRoots[0] || '';
5576
+ if (!synkroFilePresent(cwd)) { outputEmpty(); return; }
5489
5577
  const transcriptPath = resolveTranscriptPath(payload);
5490
5578
 
5491
5579
  if (!sessionId || !transcriptPath) {
@@ -5531,7 +5619,7 @@ async function main() {
5531
5619
  main();
5532
5620
  `;
5533
5621
  USER_PROMPT_SUBMIT_TS = `#!/usr/bin/env bun
5534
- import { readStdin, appendLocalTelemetry, aggregateUsage, outputEmpty, setupCursorHookSignals, hookSessionId, detectRepo, resolveTranscriptPath, syncConversationTranscript, emitUsageTick, cursorModelFromPayload } from './_synkro-common.ts';
5622
+ import { readStdin, appendLocalTelemetry, aggregateUsage, outputEmpty, setupCursorHookSignals, hookSessionId, detectRepo, resolveTranscriptPath, syncConversationTranscript, emitUsageTick, cursorModelFromPayload, synkroFilePresent } from './_synkro-common.ts';
5535
5623
  import { writeFileSync, mkdirSync } from 'node:fs';
5536
5624
  import { join, dirname } from 'node:path';
5537
5625
  import { homedir } from 'node:os';
@@ -5542,7 +5630,12 @@ async function main() {
5542
5630
  const input = await readStdin();
5543
5631
  if (!input.trim()) { outputEmpty(); return; }
5544
5632
  const payload = JSON.parse(input);
5545
- const msg = payload.message || payload.prompt || payload.content || '';
5633
+ const _cwd = typeof payload.cwd === 'string' ? payload.cwd
5634
+ : (Array.isArray(payload.workspace_roots) && typeof payload.workspace_roots[0] === 'string' ? payload.workspace_roots[0] : '');
5635
+ if (!synkroFilePresent(_cwd)) { outputEmpty(); return; }
5636
+ const msg = typeof payload.message === 'string' ? payload.message
5637
+ : typeof payload.prompt === 'string' ? payload.prompt
5638
+ : typeof payload.content === 'string' ? payload.content : '';
5546
5639
  if (msg) {
5547
5640
  const promptFile = join(homedir(), '.synkro', '.last-prompt');
5548
5641
  mkdirSync(dirname(promptFile), { recursive: true, mode: 0o700 });
@@ -5581,7 +5674,7 @@ import {
5581
5674
  isSafeInRepoRead, resolveTranscriptPath, postWithRetry, readStdin, hashCommand,
5582
5675
  extractTranscript, readLastPrompt, readSessionLog, compressSessionLog,
5583
5676
  appendLocalTelemetry, logGraderUnavailable, graderUnavailableMessage, log, GATEWAY_URL,
5584
- loadSynkroFile, effectiveGraderPool,
5677
+ loadSynkroFile, effectiveGraderPool, synkroFilePresent,
5585
5678
  type Rule,
5586
5679
  } from './_synkro-common.ts';
5587
5680
  import { createHash } from 'node:crypto';
@@ -5699,6 +5792,10 @@ async function main() {
5699
5792
  const model = rawModel ? (rawModel.startsWith('cursor/') ? rawModel : 'cursor/' + rawModel) : 'cursor';
5700
5793
  const repo = detectRepo(cwd, transcriptPath, command, workspaceRoots);
5701
5794
 
5795
+ // No .synkro at the resolved repo root \u2192 Synkro is dormant here; allow
5796
+ // without grading. Keyed off the validated detectRepo() root, not raw cwd.
5797
+ if (!repo || !synkroFilePresent(repo)) finishAllow();
5798
+
5702
5799
  const cmdShort = command.slice(0, 80);
5703
5800
  log('bashGuard checking: ' + cmdShort);
5704
5801
 
@@ -5882,7 +5979,7 @@ import {
5882
5979
  loadJwt, ensureFreshJwt, detectRepo, readStdin, resolveTranscriptPath,
5883
5980
  appendSessionAction, appendLocalTelemetry, shipCloud, log, GATEWAY_URL,
5884
5981
  countEditLineDelta, dispatchCapture, hookSessionId, cursorModelFromPayload,
5885
- isLocalStorageMode,
5982
+ isLocalStorageMode, synkroFilePresent, isPathUnder,
5886
5983
  } from './_synkro-common.ts';
5887
5984
  import { existsSync, readFileSync } from 'node:fs';
5888
5985
  import { basename, dirname, join } from 'node:path';
@@ -5908,6 +6005,10 @@ async function main() {
5908
6005
  const filePath = String(payload.file_path || payload.path || payload.target_file || '');
5909
6006
  if (!filePath) finish();
5910
6007
 
6008
+ // No .synkro at the resolved repo root \u2192 Synkro is dormant here; skip capture.
6009
+ const _root = detectRepo((typeof payload.cwd === 'string' && payload.cwd) || dirname(filePath), '', filePath, []);
6010
+ if (!_root || !isPathUnder(filePath, _root) || !synkroFilePresent(_root)) finish();
6011
+
5911
6012
  const workspaceRoots = Array.isArray(payload.workspace_roots)
5912
6013
  ? (payload.workspace_roots as unknown[]).filter((r): r is string => typeof r === 'string')
5913
6014
  : [];
@@ -6010,7 +6111,7 @@ main().catch((e) => {
6010
6111
  /** Capture Cursor agent thinking/response text before transcript JSONL redacts it. */
6011
6112
  import {
6012
6113
  readStdin, outputEmpty, setupCursorHookSignals, hookSessionId, detectRepo,
6013
- appendThoughtOverlay, pushConversationMessage,
6114
+ appendThoughtOverlay, pushConversationMessage, synkroFilePresent,
6014
6115
  } from './_synkro-common.ts';
6015
6116
 
6016
6117
  async function main() {
@@ -6030,6 +6131,7 @@ async function main() {
6030
6131
  ? payload.workspace_roots.filter((r): r is string => typeof r === 'string')
6031
6132
  : [];
6032
6133
  const cwd = (typeof payload.cwd === 'string' && payload.cwd) || workspaceRoots[0] || '';
6134
+ if (!synkroFilePresent(cwd)) { outputEmpty(); return; }
6033
6135
  const gitRepo = detectRepo(cwd, '', '', workspaceRoots);
6034
6136
 
6035
6137
  if (event === 'afterAgentThought') {
@@ -8301,7 +8403,7 @@ function writeConfigEnv(opts) {
8301
8403
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
8302
8404
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
8303
8405
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
8304
- `SYNKRO_VERSION=${shellQuoteSingle("1.6.45")}`
8406
+ `SYNKRO_VERSION=${shellQuoteSingle("1.6.46")}`
8305
8407
  ];
8306
8408
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
8307
8409
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -11386,7 +11488,7 @@ var args = process.argv.slice(2);
11386
11488
  var cmd = args[0] || "";
11387
11489
  var subArgs = args.slice(1);
11388
11490
  function printVersion() {
11389
- console.log("1.6.45");
11491
+ console.log("1.6.46");
11390
11492
  }
11391
11493
  function printHelp2() {
11392
11494
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents