@synkro-sh/cli 1.7.92 → 1.7.93

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
@@ -147,7 +147,7 @@ function getIdentity() {
147
147
  if (cached2) return cached2;
148
148
  let cliVersion = "0.0.0";
149
149
  try {
150
- cliVersion = "1.7.92";
150
+ cliVersion = "1.7.93";
151
151
  } catch {
152
152
  }
153
153
  const creds = loadCredentialsIdentity();
@@ -1735,18 +1735,6 @@ function installCCHooks(settingsPath, config) {
1735
1735
  ],
1736
1736
  [SYNKRO_MARKER]: true
1737
1737
  });
1738
- if (config.promptRouteScriptPath) {
1739
- settings.hooks.UserPromptSubmit.push({
1740
- hooks: [
1741
- {
1742
- type: "command",
1743
- command: config.promptRouteScriptPath,
1744
- timeout: 5
1745
- }
1746
- ],
1747
- [SYNKRO_MARKER]: true
1748
- });
1749
- }
1750
1738
  settings.hooks.Stop = settings.hooks.Stop ?? [];
1751
1739
  removeSynkroEntries(settings.hooks, "Stop");
1752
1740
  settings.hooks.Stop.push({
@@ -2112,7 +2100,6 @@ function installCodexHooks(hooksJsonPath, config) {
2112
2100
  if (config.taskActivateIntentScriptPath) push(h, "PermissionRequest", [cmd(config.taskActivateIntentScriptPath, 5)], M_ACTIVATE);
2113
2101
  push(h, "PostToolUse", [cmd(config.bashFollowupScriptPath, 10)], M_BASH);
2114
2102
  push(h, "UserPromptSubmit", [cmd(config.userPromptSubmitScriptPath, 5)]);
2115
- if (config.promptRouteScriptPath) push(h, "UserPromptSubmit", [cmd(config.promptRouteScriptPath, 5)]);
2116
2103
  push(h, "SessionStart", [cmd(config.sessionStartScriptPath, 5)]);
2117
2104
  push(h, "SubagentStart", [cmd(config.subagentStartScriptPath, 5)]);
2118
2105
  if (!config.skipTranscriptSync) push(h, "SubagentStop", [cmd(config.subagentStopScriptPath, 5)]);
@@ -2141,13 +2128,14 @@ function uninstallCodexHooks(hooksJsonPath) {
2141
2128
  writeHooksFileAtomic2(hooksJsonPath, file);
2142
2129
  return true;
2143
2130
  }
2144
- var SYNKRO_MARKER3, M_BASH, CODEX_EDIT_MATCHER, M_EDIT, M_AGENT, M_MCP, M_ACTIVATE, ALL_EVENTS2;
2131
+ var SYNKRO_MARKER3, CODEX_BASH_MATCHER, M_BASH, CODEX_EDIT_MATCHER, M_EDIT, M_AGENT, M_MCP, M_ACTIVATE, ALL_EVENTS2;
2145
2132
  var init_codexHookConfig = __esm({
2146
2133
  "cli/installer/codexHookConfig.ts"() {
2147
2134
  "use strict";
2148
2135
  init_platform();
2149
2136
  SYNKRO_MARKER3 = "__synkro_managed__";
2150
- M_BASH = "Bash";
2137
+ CODEX_BASH_MATCHER = "^(?:Bash|exec_command|functions[._]exec_command)$";
2138
+ M_BASH = CODEX_BASH_MATCHER;
2151
2139
  CODEX_EDIT_MATCHER = "^(?:apply_patch|ApplyPatch|Edit|Write|functions[._]apply_patch)$";
2152
2140
  M_EDIT = CODEX_EDIT_MATCHER;
2153
2141
  M_AGENT = "Agent";
@@ -2157,6 +2145,126 @@ var init_codexHookConfig = __esm({
2157
2145
  }
2158
2146
  });
2159
2147
 
2148
+ // cli/installer/codexHookTrust.ts
2149
+ import { spawn as spawn2 } from "child_process";
2150
+ function parseCodexHookTrustOutput(stdout) {
2151
+ for (const line of stdout.split("\n")) {
2152
+ let message;
2153
+ try {
2154
+ message = JSON.parse(line);
2155
+ } catch {
2156
+ continue;
2157
+ }
2158
+ const data = message?.result?.data;
2159
+ if (!Array.isArray(data)) continue;
2160
+ const hooks = data.flatMap((entry) => Array.isArray(entry?.hooks) ? entry.hooks : []).filter((hook) => typeof hook.command === "string" && isSynkroHookCommand(hook.command));
2161
+ if (!hooks.length) return null;
2162
+ return {
2163
+ total: hooks.length,
2164
+ trusted: hooks.filter((hook) => hook.trustStatus === "trusted" || hook.trustStatus === "managed").length,
2165
+ needsReview: hooks.filter((hook) => hook.trustStatus === "modified" || hook.trustStatus === "untrusted").length,
2166
+ disabled: hooks.filter((hook) => hook.enabled === false).length
2167
+ };
2168
+ }
2169
+ return null;
2170
+ }
2171
+ function inspectCodexHookTrust(codexBinary = "codex", cwd = process.cwd()) {
2172
+ return new Promise((resolve6) => {
2173
+ let settled = false;
2174
+ let stdout = "";
2175
+ let pending = "";
2176
+ let child;
2177
+ const finish = (summary) => {
2178
+ if (settled) return;
2179
+ settled = true;
2180
+ clearTimeout(timer);
2181
+ try {
2182
+ child.stdin.end();
2183
+ } catch {
2184
+ }
2185
+ try {
2186
+ child.kill();
2187
+ } catch {
2188
+ }
2189
+ resolve6(summary);
2190
+ };
2191
+ const timer = setTimeout(() => finish(null), 1e4);
2192
+ try {
2193
+ child = spawn2(codexBinary, ["app-server", "--stdio"], {
2194
+ cwd,
2195
+ stdio: ["pipe", "pipe", "ignore"],
2196
+ windowsHide: true
2197
+ });
2198
+ child.once("error", () => finish(null));
2199
+ child.once("exit", () => finish(parseCodexHookTrustOutput(stdout)));
2200
+ child.stdout.setEncoding("utf8");
2201
+ child.stdout.on("data", (chunk) => {
2202
+ stdout += chunk;
2203
+ pending += chunk;
2204
+ const lines = pending.split("\n");
2205
+ pending = lines.pop() || "";
2206
+ for (const line of lines) {
2207
+ let message;
2208
+ try {
2209
+ message = JSON.parse(line);
2210
+ } catch {
2211
+ continue;
2212
+ }
2213
+ if (message?.id === 1 && message?.result) {
2214
+ child.stdin.write(JSON.stringify({
2215
+ id: 2,
2216
+ method: "hooks/list",
2217
+ params: { cwds: [cwd] }
2218
+ }) + "\n");
2219
+ } else if (message?.id === 2) {
2220
+ finish(parseCodexHookTrustOutput(line));
2221
+ }
2222
+ }
2223
+ });
2224
+ child.stdin.write(JSON.stringify({
2225
+ id: 1,
2226
+ method: "initialize",
2227
+ params: {
2228
+ clientInfo: { name: "synkro-cli", version: "1" },
2229
+ capabilities: { experimentalApi: true }
2230
+ }
2231
+ }) + "\n");
2232
+ } catch {
2233
+ finish(null);
2234
+ }
2235
+ });
2236
+ }
2237
+ function codexHookTrustLines(summary) {
2238
+ if (!summary) {
2239
+ return [
2240
+ " \u26A0 Codex hook trust could not be verified.",
2241
+ " Restart Codex, run /hooks, and review the Synkro hooks before relying on enforcement."
2242
+ ];
2243
+ }
2244
+ if (summary.needsReview === 0 && summary.disabled === 0 && summary.trusted === summary.total) {
2245
+ return [` \u2713 Codex hook trust confirmed (${summary.trusted}/${summary.total} active)`];
2246
+ }
2247
+ const issues = [
2248
+ summary.needsReview > 0 ? `${summary.needsReview} need review` : "",
2249
+ summary.disabled > 0 ? `${summary.disabled} disabled` : ""
2250
+ ].filter(Boolean).join(", ");
2251
+ return [
2252
+ ` \u26A0 Codex security enforcement is not active (${issues}).`,
2253
+ " Restart Codex, run /hooks, and trust only the Synkro hooks before relying on enforcement."
2254
+ ];
2255
+ }
2256
+ async function reportCodexHookTrust(codexBinary, cwd) {
2257
+ const summary = await inspectCodexHookTrust(codexBinary || "codex", cwd);
2258
+ for (const line of codexHookTrustLines(summary)) console.log(line);
2259
+ return summary;
2260
+ }
2261
+ var init_codexHookTrust = __esm({
2262
+ "cli/installer/codexHookTrust.ts"() {
2263
+ "use strict";
2264
+ init_platform();
2265
+ }
2266
+ });
2267
+
2160
2268
  // cli/installer/mcpConfig.ts
2161
2269
  import { existsSync as existsSync13, readFileSync as readFileSync12, writeFileSync as writeFileSync9, renameSync as renameSync6, mkdirSync as mkdirSync7 } from "fs";
2162
2270
  import { homedir as homedir12 } from "os";
@@ -2982,6 +3090,26 @@ export function normalizeCodexApplyPatch(payload: any): void {
2982
3090
  } catch { /* leave payload untouched on any parse error */ }
2983
3091
  }
2984
3092
 
3093
+ /**
3094
+ * Normalize Codex app/local-function command aliases into the Bash hook shape
3095
+ * consumed by scanRouter. Codex CLI already emits Bash + command; bridges may
3096
+ * retain exec_command (optionally namespaced) + cmd.
3097
+ */
3098
+ export function normalizeCodexExecCommand(payload: any): void {
3099
+ const toolName = String(payload?.tool_name || '');
3100
+ if (!/^(?:exec_command|functions[._]exec_command)$/i.test(toolName)) return;
3101
+ const toolInput = payload?.tool_input && typeof payload.tool_input === 'object'
3102
+ ? payload.tool_input
3103
+ : {};
3104
+ payload.tool_name = 'Bash';
3105
+ payload.tool_input = {
3106
+ ...toolInput,
3107
+ command: typeof toolInput.command === 'string'
3108
+ ? toolInput.command
3109
+ : String(toolInput.cmd || ''),
3110
+ };
3111
+ }
3112
+
2985
3113
  type RecoveredCodexEdit = { payload: Record<string, any>; baseContent?: string };
2986
3114
 
2987
3115
  function completedCodexToolRecord(entry: any): any {
@@ -3295,9 +3423,12 @@ export async function runStub(surface: string, opts: StubOpts = {}): Promise<voi
3295
3423
  const input = await readStdin();
3296
3424
  if (!input.trim()) { out(failOpen(harness)); return; }
3297
3425
  const payload = JSON.parse(input);
3298
- // Codex: rewrite apply_patch tool_input into the CC edit shape before any
3299
- // surface reads it (edit/cwe/cve). No-op for CC/Cursor and non-edit tools.
3300
- if (harness === 'codex') normalizeCodexApplyPatch(payload);
3426
+ // Codex: rewrite bridged tool aliases into the shared CC shapes before any
3427
+ // surface reads them. No-op for CC/Cursor and already-canonical tools.
3428
+ if (harness === 'codex') {
3429
+ normalizeCodexApplyPatch(payload);
3430
+ normalizeCodexExecCommand(payload);
3431
+ }
3301
3432
  if (harness === 'codex' && opts.subagent) {
3302
3433
  const parentSessionId = String(payload?.session_id || '');
3303
3434
  const childSessionId = String(payload?.agent_id || '');
@@ -3763,165 +3894,8 @@ function emitStubTelemetry(
3763
3894
  STUB_USER_PROMPT_SUBMIT_TS = stubHook("prompt-submit", "{ telemetry: true }");
3764
3895
  STUB_BASH_FOLLOWUP_TS = stubHook("bash-followup", "{ telemetry: true }");
3765
3896
  STUB_PROMPT_ROUTE_TS = `#!/usr/bin/env bun
3766
- import { readFileSync, unlinkSync, mkdirSync, writeFileSync, readdirSync, existsSync } from 'node:fs';
3767
- import { homedir } from 'node:os';
3768
- import { join } from 'node:path';
3769
- import { spawnSync, spawn } from 'node:child_process';
3770
-
3771
- const HOME = homedir();
3772
- const MODEL_RE = /^[A-Za-z0-9._:-]{1,64}$/;
3773
-
3774
- function cliBin(): string {
3775
- try {
3776
- const txt = readFileSync(join(HOME, '.synkro', 'config.env'), 'utf-8');
3777
- for (const line of txt.split('\\n')) {
3778
- const t = line.trim();
3779
- if (t.startsWith('SYNKRO_CLI_BIN=')) {
3780
- let v = t.slice('SYNKRO_CLI_BIN='.length).trim();
3781
- if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
3782
- return v;
3783
- }
3784
- }
3785
- } catch {}
3786
- return process.env.SYNKRO_CLI_BIN || '';
3787
- }
3788
-
3789
- function mcpJwt(): string {
3790
- try { return readFileSync(join(HOME, '.synkro', '.mcp-jwt'), 'utf-8').trim(); } catch { return ''; }
3791
- }
3792
-
3793
- // The caller's own tmux session, from the inherited $TMUX \u2014 so routing targets
3794
- // THIS session, not whichever shim last wrote the shared active-session file.
3795
- function currentTmuxSession(): string {
3796
- if (!process.env.TMUX) return '';
3797
- try {
3798
- const pane = process.env.TMUX_PANE || '';
3799
- const args = pane
3800
- ? ['display-message', '-p', '-t', pane, '#{session_name}']
3801
- : ['display-message', '-p', '#{session_name}'];
3802
- const r = spawnSync('tmux', args, { encoding: 'utf-8' });
3803
- if (r.status === 0) return (r.stdout || '').trim();
3804
- } catch {}
3805
- return '';
3806
- }
3807
-
3808
- const chunks: Buffer[] = [];
3809
- for await (const chunk of process.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
3810
-
3811
- // Verdict: allow ({}) unless we decide to BLOCK this prompt for routing. We must
3812
- // emit exactly one JSON line at the end (not up-front) so the block path can win.
3813
- let blockForRouting = false;
3814
- try {
3815
- const payload = JSON.parse(Buffer.concat(chunks).toString('utf-8') || '{}');
3816
- const sid = String(payload.session_id || payload.conversation_id || '');
3817
- if (sid) {
3818
- const safeSid = sid.replace(/[^A-Za-z0-9_-]/g, '_');
3819
- const tmuxSession = currentTmuxSession();
3820
- const prompt = String(payload.prompt || '');
3821
-
3822
- // Session registry: record THIS live session (heartbeat \u2014 ts refreshed every
3823
- // prompt) so a router or a human can enumerate live sessions via
3824
- // 'synkro sessions' and target exactly one by session_id. Wrapped + best-effort
3825
- // so it never delays or breaks the prompt.
3826
- try {
3827
- const sessDir = join(HOME, '.synkro', 'pty', 'sessions');
3828
- mkdirSync(sessDir, { recursive: true });
3829
- const rec = {
3830
- session_id: sid,
3831
- tmux_session: tmuxSession,
3832
- cwd: String(payload.cwd || ''),
3833
- last_prompt: prompt.slice(0, 200),
3834
- transcript_path: String(payload.transcript_path || ''),
3835
- ts: Date.now(),
3836
- };
3837
- writeFileSync(join(sessDir, safeSid + '.json'), JSON.stringify(rec));
3838
- // TTL-prune stale records (24h) \u2014 cheap, no per-record process spawn. Live
3839
- // sessions refresh their ts every prompt, so only ended ones age out.
3840
- const TTL = 24 * 60 * 60 * 1000;
3841
- const now = Date.now();
3842
- for (const f of readdirSync(sessDir)) {
3843
- if (!f.endsWith('.json')) continue;
3844
- try {
3845
- const r = JSON.parse(readFileSync(join(sessDir, f), 'utf-8'));
3846
- if (!r || typeof r.ts !== 'number' || now - r.ts > TTL) unlinkSync(join(sessDir, f));
3847
- } catch { try { unlinkSync(join(sessDir, f)); } catch {} }
3848
- }
3849
- } catch {}
3850
-
3851
- // \u2500\u2500 Routing: block + re-inject \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3852
- // Loop-guard: a re-injected prompt carries route-guard-<sid>; clear it and
3853
- // pass straight through (never route the re-injected prompt \u2014 that would loop).
3854
- const guardFile = join(HOME, '.synkro', 'pty', 'route-guard-' + safeSid);
3855
- let isReinjected = false;
3856
- try {
3857
- if (existsSync(guardFile)) { isReinjected = true; try { unlinkSync(guardFile); } catch {} }
3858
- } catch {}
3859
-
3860
- // Only route real task prompts. Skip empty input, slash-commands (/\u2026), bash
3861
- // lines (!\u2026), and common conversational turns \u2014 those are never something to
3862
- // pick a coding model for, and blocking them would add a needless pause. The
3863
- // haiku 'keep' verdict is the backstop for anything this quick list misses.
3864
- const t = prompt.trim();
3865
- const norm = t.toLowerCase().replace(/[.!?,;:\\s]+$/, '').trim();
3866
- const CONVERSATIONAL = new Set([
3867
- 'ok', 'okay', 'k', 'kk', 'yes', 'yep', 'yeah', 'ya', 'y', 'no', 'nope', 'n',
3868
- 'sure', 'thanks', 'thank you', 'thx', 'ty', 'cool', 'nice', 'great', 'awesome',
3869
- 'perfect', 'got it', 'gotcha', 'right', 'makes sense', 'sounds good', 'lgtm',
3870
- 'continue', 'keep going', 'go on', 'go ahead', 'proceed', 'next', 'more',
3871
- 'again', 'retry', 'stop', 'wait', 'hmm', 'hm', 'oh', 'i see', 'done', 'good',
3872
- ]);
3873
- const routable = t.length > 0 && !t.startsWith('/') && !t.startsWith('!') && !CONVERSATIONAL.has(norm);
3874
-
3875
- // Routing is opt-in via a marker file \u2014 either global ('routing-on', every
3876
- // session) or per-session ('routing-on-<sid>', just this one). Per-session lets
3877
- // us enable routing for one session (e.g. a test) without touching the others.
3878
- let routingEnabled = false;
3879
- try {
3880
- routingEnabled = existsSync(join(HOME, '.synkro', 'pty', 'routing-on'))
3881
- || existsSync(join(HOME, '.synkro', 'pty', 'routing-on-' + safeSid));
3882
- } catch {}
3883
-
3884
- if (!isReinjected && routable && routingEnabled) {
3885
- // ESCALATION-ONLY: classify in-place (fast local head, 1s cap). Block +
3886
- // escalate to Opus ONLY for a confident opus verdict; never downgrade. Any
3887
- // timeout / non-opus / error \u2192 run on the current model (no block). This
3888
- // makes the classifier's weakness (under-firing opus) harmless: worst case
3889
- // is a MISSED escalation = status quo, never a broken output on a weak model.
3890
- let escalate = false;
3891
- try {
3892
- const resp = await fetch('http://127.0.0.1:' + (process.env.SYNKRO_GRADER_HOST_PORT || '18929') + '/submit', {
3893
- method: 'POST',
3894
- headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + mcpJwt() },
3895
- body: JSON.stringify({ role: 'route-classify', payload: t.slice(0, 1000), content: 'x', hedge: false }),
3896
- signal: AbortSignal.timeout(1000),
3897
- });
3898
- if (resp.ok) {
3899
- const data = await resp.json();
3900
- if (/<model>\\s*claude-opus-4-8\\s*<\\/model>/i.test(String(data.result || ''))) escalate = true;
3901
- }
3902
- } catch {}
3903
- if (escalate) {
3904
- try {
3905
- const bin = cliBin();
3906
- if (bin) {
3907
- // Orchestrator applies the pre-decided escalation (skip re-classify).
3908
- const child = spawn('node', [bin, 'route-and-resubmit', sid, t.slice(0, 1000), tmuxSession || '', 'claude-opus-4-8'], { detached: true, stdio: 'ignore' });
3909
- child.unref();
3910
- blockForRouting = true; // hold this prompt; the orchestrator re-submits it on Opus
3911
- }
3912
- } catch {}
3913
- }
3914
- }
3915
- }
3916
- } catch {}
3917
-
3918
- // Emit the single verdict line. Block (with a visible reason) when routing is
3919
- // taking over the prompt; otherwise allow. Fail-open: any throw above \u2192 allow.
3920
- if (blockForRouting) {
3921
- process.stdout.write(JSON.stringify({ decision: 'block', reason: 'Synkro is routing this to the best model and will resubmit it automatically\u2026' }) + '\\n');
3922
- } else {
3923
- process.stdout.write('{}\\n');
3924
- }
3897
+ for await (const _chunk of process.stdin) {}
3898
+ process.stdout.write('{}\\n');
3925
3899
  `;
3926
3900
  STUB_TASK_ACTIVATE_INTENT_TS = `#!/usr/bin/env bun
3927
3901
  import { readFileSync } from 'node:fs';
@@ -4560,7 +4534,7 @@ __export(claudeDesktopTap_exports, {
4560
4534
  claudeDesktopInstalled: () => claudeDesktopInstalled,
4561
4535
  runClaudeDesktopTap: () => runClaudeDesktopTap
4562
4536
  });
4563
- import { spawn as spawn2 } from "child_process";
4537
+ import { spawn as spawn3 } from "child_process";
4564
4538
  import { writeFileSync as writeFileSync12, mkdtempSync, mkdirSync as mkdirSync10, readFileSync as readFileSync15, existsSync as existsSync17 } from "fs";
4565
4539
  import { join as join13 } from "path";
4566
4540
  import { homedir as homedir15 } from "os";
@@ -4709,7 +4683,7 @@ async function runClaudeDesktopTap(opts = {}) {
4709
4683
  const runnerPath = join13(sessionDir, "run.sh");
4710
4684
  writeFileSync12(runnerPath, buildRunner(sessionDir), { mode: 493 });
4711
4685
  await new Promise((resolve6) => {
4712
- const child = spawn2("bash", [runnerPath], {
4686
+ const child = spawn3("bash", [runnerPath], {
4713
4687
  stdio: "inherit",
4714
4688
  env: { ...process.env, SYNKRO_CAPTURE_URL: CAPTURE_URL, SYNKRO_SCAN_URL: SCAN_URL, SYNKRO_SCAN_TURN_URL: SCAN_TURN_URL, SYNKRO_DLP_POLICY_URL: DLP_POLICY_URL, SYNKRO_TURN_VERDICTS_URL: TURN_VERDICTS_URL, SYNKRO_TURN_VERDICT_URL: TURN_VERDICT_URL, SYNKRO_MCP_EVENT_URL: MCP_EVENT_URL, SYNKRO_TAP_TOKEN: token, SYNKRO_TAP_TOKEN_FILE: JWT_PATH, SYNKRO_CD_BACKFILL: opts.backfill ? "1" : "" }
4715
4689
  });
@@ -6093,6 +6067,7 @@ __export(dockerInstall_exports, {
6093
6067
  dockerUpdate: () => dockerUpdate,
6094
6068
  ensurePgliteProxyCredentials: () => ensurePgliteProxyCredentials,
6095
6069
  imageTag: () => imageTag,
6070
+ isContainerActiveState: () => isContainerActiveState,
6096
6071
  normalizeProvider: () => normalizeProvider,
6097
6072
  poolLabel: () => poolLabel,
6098
6073
  readContainerConfig: () => readContainerConfig,
@@ -6126,6 +6101,9 @@ function resolveContainerName(raw = process.env.SYNKRO_CONTAINER_NAME) {
6126
6101
  }
6127
6102
  return value;
6128
6103
  }
6104
+ function isContainerActiveState(status) {
6105
+ return status === "running" || status === "restarting" || status === "paused";
6106
+ }
6129
6107
  function createPgliteScramVerifier(password, salt = randomBytes2(16)) {
6130
6108
  const iterations = 4096;
6131
6109
  const saltedPassword = pbkdf2Sync(password, salt, iterations, 32, "sha256");
@@ -6134,27 +6112,29 @@ function createPgliteScramVerifier(password, salt = randomBytes2(16)) {
6134
6112
  const serverKey = createHmac("sha256", saltedPassword).update("Server Key").digest();
6135
6113
  return `SCRAM-SHA-256$${iterations}:${salt.toString("base64")}$${storedKey.toString("base64")}:${serverKey.toString("base64")}`;
6136
6114
  }
6137
- function ensurePgliteProxyCredentials() {
6138
- const hasPassword = existsSync19(PGLITE_PASSWORD_PATH) && readFileSync17(PGLITE_PASSWORD_PATH, "utf-8").trim().length > 0;
6139
- const hasUserlist = existsSync19(PGLITE_USERLIST_PATH) && readFileSync17(PGLITE_USERLIST_PATH, "utf-8").trim().length > 0;
6115
+ function ensurePgliteProxyCredentials(paths = {}) {
6116
+ const passwordPath = paths.passwordPath ?? PGLITE_PASSWORD_PATH;
6117
+ const userlistPath = paths.userlistPath ?? PGLITE_USERLIST_PATH;
6118
+ const hasPassword = existsSync19(passwordPath) && readFileSync17(passwordPath, "utf-8").trim().length > 0;
6119
+ const hasUserlist = existsSync19(userlistPath) && readFileSync17(userlistPath, "utf-8").trim().length > 0;
6140
6120
  if (hasPassword && hasUserlist) {
6141
- chmodSync3(PGLITE_PASSWORD_PATH, 384);
6142
- chmodSync3(PGLITE_USERLIST_PATH, 384);
6121
+ chmodSync3(passwordPath, 384);
6122
+ chmodSync3(userlistPath, 384);
6143
6123
  return;
6144
6124
  }
6145
6125
  const password = randomBytes2(24).toString("base64url");
6146
6126
  const verifier = createPgliteScramVerifier(password);
6147
6127
  const suffix = `${process.pid}.${Date.now()}.tmp`;
6148
- const passwordTmp = `${PGLITE_PASSWORD_PATH}.${suffix}`;
6149
- const userlistTmp = `${PGLITE_USERLIST_PATH}.${suffix}`;
6128
+ const passwordTmp = `${passwordPath}.${suffix}`;
6129
+ const userlistTmp = `${userlistPath}.${suffix}`;
6150
6130
  writeFileSync14(passwordTmp, `${password}
6151
6131
  `, { mode: 384 });
6152
6132
  writeFileSync14(userlistTmp, `"synkro" "${verifier}"
6153
6133
  `, { mode: 384 });
6154
- renameSync7(passwordTmp, PGLITE_PASSWORD_PATH);
6155
- renameSync7(userlistTmp, PGLITE_USERLIST_PATH);
6156
- chmodSync3(PGLITE_PASSWORD_PATH, 384);
6157
- chmodSync3(PGLITE_USERLIST_PATH, 384);
6134
+ renameSync7(passwordTmp, passwordPath);
6135
+ renameSync7(userlistTmp, userlistPath);
6136
+ chmodSync3(passwordPath, 384);
6137
+ chmodSync3(userlistPath, 384);
6158
6138
  }
6159
6139
  function resolveConductorProvider(pool, counts) {
6160
6140
  if (pool !== "auto") return pool;
@@ -6615,7 +6595,7 @@ function dockerStatus() {
6615
6595
  timeout: 5e3
6616
6596
  });
6617
6597
  const status = (r.stdout || "").trim();
6618
- if (status !== "running") return { running: false };
6598
+ if (!isContainerActiveState(status)) return { running: false };
6619
6599
  return {
6620
6600
  running: true,
6621
6601
  image: imageTag(),
@@ -6785,7 +6765,7 @@ var init_dockerInstall = __esm({
6785
6765
  HOST_PGLITE_PORT = parseInt(process.env.SYNKRO_HOST_PGLITE_PORT || "15433", 10);
6786
6766
  CONTAINER_NAME = resolveContainerName();
6787
6767
  defaultImageVersion = () => {
6788
- if (true) return "1.7.92";
6768
+ if (true) return "1.7.93";
6789
6769
  try {
6790
6770
  const pkg = JSON.parse(readFileSync17(new URL("../../package.json", import.meta.url), "utf8"));
6791
6771
  if (pkg.version) return pkg.version;
@@ -7022,6 +7002,7 @@ __export(ptyShim_exports, {
7022
7002
  injectModel: () => injectModel,
7023
7003
  installPtyShim: () => installPtyShim,
7024
7004
  listSessions: () => listSessions,
7005
+ restoreLegacyClaudeShim: () => restoreLegacyClaudeShim,
7025
7006
  uninstallPtyShim: () => uninstallPtyShim
7026
7007
  });
7027
7008
  import {
@@ -7038,48 +7019,11 @@ import {
7038
7019
  } from "fs";
7039
7020
  import { homedir as homedir20 } from "os";
7040
7021
  import { join as join18 } from "path";
7041
- import { spawnSync as spawnSync5, spawn as spawn3 } from "child_process";
7022
+ import { spawnSync as spawnSync5, spawn as spawn4 } from "child_process";
7042
7023
  function rcFiles() {
7043
7024
  const h = homedir20();
7044
7025
  return [join18(h, ".zshrc"), join18(h, ".bashrc"), join18(h, ".bash_profile")];
7045
7026
  }
7046
- function resolveRealClaude() {
7047
- const r = spawnSync5("bash", ["-lc", `PATH="$(printf '%s' "$PATH" | sed "s#$HOME/.synkro/bin:##g")" command -v claude`], { encoding: "utf-8" });
7048
- const p = (r.stdout || "").trim();
7049
- if (p) {
7050
- try {
7051
- return realpathSync(p);
7052
- } catch {
7053
- return p;
7054
- }
7055
- }
7056
- for (const c of [join18(homedir20(), ".local", "bin", "claude"), "/usr/local/bin/claude", "/opt/homebrew/bin/claude"]) {
7057
- if (existsSync21(c)) {
7058
- try {
7059
- return realpathSync(c);
7060
- } catch {
7061
- return c;
7062
- }
7063
- }
7064
- }
7065
- return "claude";
7066
- }
7067
- function findClaudeLink() {
7068
- const r = spawnSync5("bash", ["-lc", `PATH="$(printf '%s' "$PATH" | sed "s#$HOME/.synkro/bin:##g")" command -v claude`], { encoding: "utf-8" });
7069
- let linkPath = (r.stdout || "").trim();
7070
- if (!linkPath) {
7071
- const c = join18(homedir20(), ".local", "bin", "claude");
7072
- if (existsSync21(c)) linkPath = c;
7073
- else return null;
7074
- }
7075
- if (linkPath === SHIM_PATH) return null;
7076
- let realTarget = linkPath;
7077
- try {
7078
- realTarget = realpathSync(linkPath);
7079
- } catch {
7080
- }
7081
- return { linkPath, realTarget };
7082
- }
7083
7027
  function isOurShim(path) {
7084
7028
  try {
7085
7029
  return readFileSync20(path, "utf-8").slice(0, 300).includes("Synkro pty shim");
@@ -7087,44 +7031,39 @@ function isOurShim(path) {
7087
7031
  return false;
7088
7032
  }
7089
7033
  }
7090
- function shadowClaude() {
7091
- const found = findClaudeLink();
7092
- if (!found) {
7093
- console.log(" \xB7 no claude binary to shadow \u2014 PATH shim only");
7094
- return;
7095
- }
7096
- const { linkPath, realTarget } = found;
7097
- if (isOurShim(linkPath)) {
7098
- console.log(` \xB7 ${linkPath.replace(homedir20(), "~")} already shadowed`);
7099
- return;
7100
- }
7101
- let wasSymlink = false;
7034
+ function latestVersionedClaude(versionsDir) {
7035
+ let versions = [];
7102
7036
  try {
7103
- wasSymlink = lstatSync(linkPath).isSymbolicLink();
7037
+ versions = readdirSync3(versionsDir).map((entry) => join18(versionsDir, entry)).filter((entry) => existsSync21(entry)).sort((a, b) => a.localeCompare(b, void 0, { numeric: true, sensitivity: "base" }));
7104
7038
  } catch {
7105
7039
  }
7106
- const state = { linkPath, realTarget, wasSymlink };
7107
- try {
7108
- writeFileSync16(SHADOW_STATE_FILE, JSON.stringify(state), "utf-8");
7109
- } catch {
7110
- }
7111
- try {
7112
- rmSync2(linkPath, { force: true });
7113
- writeFileSync16(linkPath, SHIM_SOURCE.replace("__BAKED_CLAUDE__", realTarget), "utf-8");
7114
- chmodSync4(linkPath, 493);
7115
- console.log(` \u2713 shadowed ${linkPath.replace(homedir20(), "~")} \u2192 shim (real: ${realTarget.replace(homedir20(), "~")})`);
7116
- } catch (e) {
7117
- console.warn(` \u26A0 could not shadow ${linkPath}: ${e.message}`);
7040
+ return versions.at(-1) || "";
7041
+ }
7042
+ function restoreLegacyClaudeShim(linkPath, versionsDir) {
7043
+ if (!isOurShim(linkPath)) return false;
7044
+ const realTarget = latestVersionedClaude(versionsDir);
7045
+ if (!realTarget) {
7046
+ throw new Error(`no Claude version found under ${versionsDir}`);
7118
7047
  }
7048
+ rmSync2(linkPath, { force: true });
7049
+ symlinkSync(realTarget, linkPath);
7050
+ return true;
7119
7051
  }
7120
7052
  function unshadowClaude() {
7121
7053
  let state;
7122
7054
  try {
7123
7055
  state = JSON.parse(readFileSync20(SHADOW_STATE_FILE, "utf-8"));
7124
7056
  } catch {
7057
+ }
7058
+ if (!state?.linkPath) {
7059
+ const legacyLink = join18(homedir20(), ".local", "bin", "claude");
7060
+ const versionsDir = join18(homedir20(), ".local", "share", "claude", "versions");
7061
+ if (restoreLegacyClaudeShim(legacyLink, versionsDir)) {
7062
+ const target = realpathSync(legacyLink);
7063
+ console.log(`\u2713 restored ${legacyLink.replace(homedir20(), "~")} \u2192 ${target.replace(homedir20(), "~")}`);
7064
+ }
7125
7065
  return;
7126
7066
  }
7127
- if (!state?.linkPath) return;
7128
7067
  try {
7129
7068
  if (existsSync21(state.linkPath) && !isOurShim(state.linkPath)) return;
7130
7069
  rmSync2(state.linkPath, { force: true });
@@ -7134,25 +7073,6 @@ function unshadowClaude() {
7134
7073
  console.warn(` \u26A0 could not restore claude: ${e.message} \u2014 run: ln -sf ${state.realTarget} ${state.linkPath}`);
7135
7074
  }
7136
7075
  }
7137
- function addPathBlock() {
7138
- const touched = [];
7139
- for (const rc of rcFiles()) {
7140
- if (!existsSync21(rc) && rc.endsWith(".bash_profile")) continue;
7141
- let body = "";
7142
- try {
7143
- body = readFileSync20(rc, "utf-8");
7144
- } catch {
7145
- }
7146
- const cleaned = stripBlock(body);
7147
- const next = cleaned.replace(/\n*$/, "") + (cleaned ? "\n\n" : "") + RC_BLOCK + "\n";
7148
- try {
7149
- writeFileSync16(rc, next, "utf-8");
7150
- touched.push(rc);
7151
- } catch {
7152
- }
7153
- }
7154
- return touched;
7155
- }
7156
7076
  function stripBlock(body) {
7157
7077
  if (!body.includes(RC_BEGIN)) return body;
7158
7078
  const re = new RegExp(`\\n?${escapeRe(RC_BEGIN)}[\\s\\S]*?${escapeRe(RC_END)}\\n?`, "g");
@@ -7162,28 +7082,14 @@ function escapeRe(s) {
7162
7082
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7163
7083
  }
7164
7084
  function installPtyShim() {
7165
- mkdirSync14(SHIM_BIN_DIR, { recursive: true });
7166
- mkdirSync14(PTY_STATE_DIR, { recursive: true });
7167
- const real = resolveRealClaude();
7168
- writeFileSync16(SHIM_PATH, SHIM_SOURCE.replace("__BAKED_CLAUDE__", real), "utf-8");
7169
- chmodSync4(SHIM_PATH, 493);
7170
- const touched = addPathBlock();
7171
- console.log(` \u2713 pty routing shim installed (real claude: ${real})`);
7172
- shadowClaude();
7173
- if (touched.length) console.log(` added ~/.synkro/bin to PATH in: ${touched.map((t) => t.replace(homedir20(), "~")).join(", ")}`);
7085
+ console.warn("Synkro host model routing has been removed; cleaning up any legacy pty shim.");
7086
+ uninstallPtyShim();
7174
7087
  }
7175
7088
  function uninstallPtyShim() {
7176
7089
  try {
7177
7090
  unshadowClaude();
7178
7091
  } catch {
7179
7092
  }
7180
- try {
7181
- const ls = spawnSync5("tmux", ["ls", "-F", "#{session_name}"], { encoding: "utf-8" });
7182
- for (const s of (ls.stdout || "").split("\n")) {
7183
- if (s.startsWith(SHIM_SESSION_PREFIX)) spawnSync5("tmux", ["kill-session", "-t", `=${s}`], { encoding: "utf-8" });
7184
- }
7185
- } catch {
7186
- }
7187
7093
  const cleaned = [];
7188
7094
  for (const rc of rcFiles()) {
7189
7095
  if (!existsSync21(rc)) continue;
@@ -7284,7 +7190,7 @@ function injectModel(model, sessionOverride) {
7284
7190
  } catch {
7285
7191
  }
7286
7192
  const poll = `old=$(cat '${pidFile}' 2>/dev/null); if [ -n "$old" ]; then kill "$old" 2>/dev/null; fi; rm -f '${pidFile}'; set -o noclobber; echo $$ > '${pidFile}' 2>/dev/null || exit 1; set +o noclobber; trap 'rm -f "${pidFile}"' EXIT; for i in $(seq 1 40); do if tmux capture-pane -t ${session} -p -S -25 2>/dev/null | grep -qiE 'switch model|yes, switch'; then tmux send-keys -t ${session} -l '1'; sleep 0.1; tmux send-keys -t ${session} Enter; break; fi; sleep 0.1; done`;
7287
- const child = spawn3("bash", ["-c", poll], { detached: true, stdio: "ignore" });
7193
+ const child = spawn4("bash", ["-c", poll], { detached: true, stdio: "ignore" });
7288
7194
  child.unref();
7289
7195
  return true;
7290
7196
  }
@@ -7570,7 +7476,7 @@ __export(install_exports, {
7570
7476
  import { existsSync as existsSync22, mkdirSync as mkdirSync15, writeFileSync as writeFileSync17, chmodSync as chmodSync5, readFileSync as readFileSync21, readdirSync as readdirSync4, unlinkSync as unlinkSync7, statSync as statSync2 } from "fs";
7571
7477
  import { homedir as homedir21 } from "os";
7572
7478
  import { join as join19, isAbsolute, resolve as resolve4 } from "path";
7573
- import { execSync as execSync4, spawn as spawn4 } from "child_process";
7479
+ import { execSync as execSync4, spawn as spawn5 } from "child_process";
7574
7480
  import { createInterface as createInterface2 } from "readline";
7575
7481
  import { createHash as createHash4 } from "crypto";
7576
7482
  function resolvePersistedHookMode() {
@@ -7698,7 +7604,6 @@ async function promptTranscriptSources(wantCC, wantCursor, wantCodex) {
7698
7604
  function ensureSynkroDir() {
7699
7605
  mkdirSync15(SYNKRO_DIR11, { recursive: true });
7700
7606
  mkdirSync15(HOOKS_DIR, { recursive: true });
7701
- mkdirSync15(BIN_DIR, { recursive: true });
7702
7607
  mkdirSync15(OFFSETS_DIR, { recursive: true });
7703
7608
  mkdirSync15(join19(SYNKRO_DIR11, "sessions"), { recursive: true });
7704
7609
  }
@@ -7722,7 +7627,7 @@ function writeHookScripts() {
7722
7627
  const subagentStartScriptPath = join19(HOOKS_DIR, "codex-subagent-start.ts");
7723
7628
  const subagentStopScriptPath = join19(HOOKS_DIR, "codex-subagent-stop.ts");
7724
7629
  const userPromptSubmitScriptPath = join19(HOOKS_DIR, "cc-user-prompt-submit.ts");
7725
- const promptRouteScriptPath = join19(HOOKS_DIR, "cc-prompt-route.ts");
7630
+ const legacyPromptRouteScriptPath = join19(HOOKS_DIR, "cc-prompt-route.ts");
7726
7631
  const commonScriptPath = join19(HOOKS_DIR, "_synkro-common.ts");
7727
7632
  const commonBashScriptPath = join19(HOOKS_DIR, "_synkro-common.sh");
7728
7633
  const installScanScriptPath = join19(HOOKS_DIR, "cc-install-scan.ts");
@@ -7753,7 +7658,7 @@ function writeHookScripts() {
7753
7658
  [subagentStartScriptPath, STUB_SUBAGENT_START_TS],
7754
7659
  [subagentStopScriptPath, STUB_SUBAGENT_STOP_TS],
7755
7660
  [userPromptSubmitScriptPath, STUB_USER_PROMPT_SUBMIT_TS],
7756
- [promptRouteScriptPath, STUB_PROMPT_ROUTE_TS],
7661
+ [legacyPromptRouteScriptPath, STUB_PROMPT_ROUTE_TS],
7757
7662
  [installScanScriptPath, STUB_INSTALL_SCAN_TS],
7758
7663
  [taskActivateIntentScriptPath, STUB_TASK_ACTIVATE_INTENT_TS],
7759
7664
  [mcpGateScriptPath, STUB_MCP_GATE_TS],
@@ -7792,7 +7697,6 @@ function writeHookScripts() {
7792
7697
  subagentStartScript: subagentStartScriptPath,
7793
7698
  subagentStopScript: subagentStopScriptPath,
7794
7699
  userPromptSubmitScript: userPromptSubmitScriptPath,
7795
- promptRouteScript: promptRouteScriptPath,
7796
7700
  installScanScript: installScanScriptPath,
7797
7701
  cursorBashJudgeScript: cursorBashJudgePath,
7798
7702
  cursorEditCaptureScript: cursorEditCapturePath,
@@ -7830,7 +7734,7 @@ function writeConfigEnv(opts) {
7830
7734
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
7831
7735
  `SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
7832
7736
  `SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
7833
- `SYNKRO_VERSION=${shellQuoteSingle2("1.7.92")}`
7737
+ `SYNKRO_VERSION=${shellQuoteSingle2("1.7.93")}`
7834
7738
  ];
7835
7739
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
7836
7740
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
@@ -8572,7 +8476,7 @@ async function installCommand(opts = {}) {
8572
8476
  await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
8573
8477
  emit("install", {
8574
8478
  phase: "started",
8575
- cli_version_to: "1.7.92",
8479
+ cli_version_to: "1.7.93",
8576
8480
  agents_detected: agents.map((a) => a.kind),
8577
8481
  with_github: false,
8578
8482
  with_local_cc: false,
@@ -8613,7 +8517,6 @@ async function installCommand(opts = {}) {
8613
8517
  sessionStartScriptPath: scripts.sessionStartScript,
8614
8518
  transcriptSyncScriptPath: scripts.transcriptSyncScript,
8615
8519
  userPromptSubmitScriptPath: scripts.userPromptSubmitScript,
8616
- promptRouteScriptPath: scripts.promptRouteScript,
8617
8520
  installScanScriptPath: scripts.installScanScript,
8618
8521
  taskActivateIntentScriptPath: scripts.taskActivateIntentScript,
8619
8522
  mcpGateScriptPath: scripts.mcpGateScript,
@@ -8661,25 +8564,22 @@ async function installCommand(opts = {}) {
8661
8564
  subagentStartScriptPath: scripts.subagentStartScript,
8662
8565
  subagentStopScriptPath: scripts.subagentStopScript,
8663
8566
  userPromptSubmitScriptPath: scripts.userPromptSubmitScript,
8664
- promptRouteScriptPath: scripts.promptRouteScript,
8665
8567
  installScanScriptPath: scripts.installScanScript,
8666
8568
  taskActivateIntentScriptPath: scripts.taskActivateIntentScript,
8667
8569
  mcpGateScriptPath: scripts.mcpGateScript,
8668
8570
  skipTranscriptSync: !transcriptCodex
8669
8571
  });
8670
8572
  console.log(`Configured ${agent.name} hooks at ${agent.settingsPath}`);
8671
- console.log(" One-time setup: restart Codex, run /hooks, then review and trust the Synkro hooks.");
8573
+ await reportCodexHookTrust(agent.binaryPath, process.cwd());
8672
8574
  }
8673
8575
  }
8674
8576
  console.log();
8675
- if (hasClaudeCode) {
8676
- try {
8677
- installPtyShim();
8678
- } catch (e) {
8679
- console.warn(` \u26A0 pty shim install skipped: ${e.message}`);
8680
- }
8681
- console.log();
8577
+ try {
8578
+ uninstallPtyShim();
8579
+ } catch (e) {
8580
+ console.warn(` \u26A0 legacy pty shim cleanup skipped: ${e.message}`);
8682
8581
  }
8582
+ console.log();
8683
8583
  let userId;
8684
8584
  let orgId;
8685
8585
  let email;
@@ -9093,7 +8993,7 @@ async function installCommand(opts = {}) {
9093
8993
  }
9094
8994
  }
9095
8995
  try {
9096
- const child = spawn4(process.execPath, [process.argv[1], "reachability-scan", "--quiet"], {
8996
+ const child = spawn5(process.execPath, [process.argv[1], "reachability-scan", "--quiet"], {
9097
8997
  detached: true,
9098
8998
  stdio: "ignore",
9099
8999
  cwd: process.cwd()
@@ -9274,7 +9174,6 @@ function reconcileHarness() {
9274
9174
  sessionStartScriptPath: scripts.sessionStartScript,
9275
9175
  transcriptSyncScriptPath: scripts.transcriptSyncScript,
9276
9176
  userPromptSubmitScriptPath: scripts.userPromptSubmitScript,
9277
- promptRouteScriptPath: scripts.promptRouteScript,
9278
9177
  installScanScriptPath: scripts.installScanScript,
9279
9178
  taskActivateIntentScriptPath: scripts.taskActivateIntentScript,
9280
9179
  mcpGateScriptPath: scripts.mcpGateScript,
@@ -9344,14 +9243,13 @@ function reconcileHarness() {
9344
9243
  subagentStartScriptPath: scripts.subagentStartScript,
9345
9244
  subagentStopScriptPath: scripts.subagentStopScript,
9346
9245
  userPromptSubmitScriptPath: scripts.userPromptSubmitScript,
9347
- promptRouteScriptPath: scripts.promptRouteScript,
9348
9246
  installScanScriptPath: scripts.installScanScript,
9349
9247
  taskActivateIntentScriptPath: scripts.taskActivateIntentScript,
9350
9248
  mcpGateScriptPath: scripts.mcpGateScript,
9351
9249
  skipTranscriptSync: !persistedTranscriptConsent("CODEX")
9352
9250
  });
9353
9251
  console.log(" \u2713 Codex hooks registered");
9354
- console.log(" One-time setup: restart Codex, run /hooks, then review and trust the Synkro hooks.");
9252
+ console.log(" Restart Codex and review /hooks whenever Codex marks a Synkro hook modified.");
9355
9253
  try {
9356
9254
  installCodexMcpConfig({ gatewayUrl: "", bearerToken: "", local: true });
9357
9255
  console.log(" \u2713 Codex MCP registered");
@@ -10119,7 +10017,7 @@ async function syncCodexTranscriptsBulk(gatewayUrl, token, repo) {
10119
10017
  }
10120
10018
  return { sessions: totalSessions, messages: totalMessages };
10121
10019
  }
10122
- var SYNKRO_DIR11, HOOKS_DIR, BIN_DIR, CONFIG_PATH4, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, CLOUD_JWT_PATH, SKILLS_DISCOVERED_PATH;
10020
+ var SYNKRO_DIR11, HOOKS_DIR, CONFIG_PATH4, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, CLOUD_JWT_PATH, SKILLS_DISCOVERED_PATH;
10123
10021
  var init_install = __esm({
10124
10022
  "cli/commands/install.ts"() {
10125
10023
  "use strict";
@@ -10127,6 +10025,7 @@ var init_install = __esm({
10127
10025
  init_ccHookConfig();
10128
10026
  init_cursorHookConfig();
10129
10027
  init_codexHookConfig();
10028
+ init_codexHookTrust();
10130
10029
  init_mcpConfig();
10131
10030
  init_synkroCommand();
10132
10031
  init_skillParser();
@@ -10146,7 +10045,6 @@ var init_install = __esm({
10146
10045
  init_codexTranscriptMessages();
10147
10046
  SYNKRO_DIR11 = join19(homedir21(), ".synkro");
10148
10047
  HOOKS_DIR = join19(SYNKRO_DIR11, "hooks");
10149
- BIN_DIR = join19(SYNKRO_DIR11, "bin");
10150
10048
  CONFIG_PATH4 = join19(SYNKRO_DIR11, "config.env");
10151
10049
  MCP_STDIO_PROXY_SRC = `#!/usr/bin/env bun
10152
10050
  import { readFileSync } from 'node:fs';
@@ -11202,7 +11100,7 @@ var scanPr_exports = {};
11202
11100
  __export(scanPr_exports, {
11203
11101
  scanPrCommand: () => scanPrCommand
11204
11102
  });
11205
- import { execSync as execSync5, spawn as spawn5 } from "child_process";
11103
+ import { execSync as execSync5, spawn as spawn6 } from "child_process";
11206
11104
  import { readFileSync as readFileSync24, existsSync as existsSync26 } from "fs";
11207
11105
  import { join as join23 } from "path";
11208
11106
  function parseMatchSpec(condition) {
@@ -11413,7 +11311,7 @@ ${hunks}`;
11413
11311
  const fullPrompt = promptHeader + userMessage;
11414
11312
  return new Promise((resolve6) => {
11415
11313
  const t0 = Date.now();
11416
- const proc = spawn5(
11314
+ const proc = spawn6(
11417
11315
  "claude",
11418
11316
  ["--print", "--model", "claude-sonnet-4-6", "--output-format", "json", "--no-session-persistence"],
11419
11317
  {
@@ -11512,7 +11410,7 @@ ${JSON.stringify(findings, null, 2)}
11512
11410
  function spawnOpusConsolidator(findings, claudeToken) {
11513
11411
  return new Promise((resolve6) => {
11514
11412
  const prompt = buildConsolidationPrompt(findings);
11515
- const proc = spawn5(
11413
+ const proc = spawn6(
11516
11414
  "claude",
11517
11415
  ["--print", "--model", "claude-opus-4-7", "--output-format", "json", "--no-session-persistence"],
11518
11416
  {
@@ -11925,296 +11823,20 @@ var init_scanPr = __esm({
11925
11823
  }
11926
11824
  });
11927
11825
 
11928
- // cli/local-cc/routeDecide.ts
11929
- var routeDecide_exports = {};
11930
- __export(routeDecide_exports, {
11931
- routeDecide: () => routeDecide
11932
- });
11933
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync19 } from "fs";
11826
+ // cli/local-cc/pueue.ts
11827
+ import { execFileSync as execFileSync4, spawnSync as spawnSync9, spawn as spawn7 } from "child_process";
11934
11828
  import { homedir as homedir25 } from "os";
11935
11829
  import { join as join24 } from "path";
11936
- function safeSid(sid) {
11937
- return sid.replace(/[^A-Za-z0-9_-]/g, "_");
11938
- }
11939
- function loadMcpJwt() {
11940
- try {
11941
- return readFileSync25(join24(SYNKRO_DIR13, ".mcp-jwt"), "utf-8").trim();
11942
- } catch {
11943
- return "";
11944
- }
11945
- }
11946
- async function routeDecide(sessionId) {
11947
- if (!sessionId) return;
11948
- const sid = safeSid(sessionId);
11949
- let prompt = "";
11950
- try {
11951
- const rec = JSON.parse(readFileSync25(join24(SESSIONS_DIR2, sid + ".json"), "utf-8"));
11952
- prompt = String(rec.last_prompt || "").trim();
11953
- } catch {
11954
- return;
11955
- }
11956
- if (!prompt) return;
11957
- let model = "";
11958
- try {
11959
- const resp = await fetch(`http://127.0.0.1:${GRADER_HOST_PORT}/submit`, {
11960
- method: "POST",
11961
- headers: { "Content-Type": "application/json", Authorization: "Bearer " + loadMcpJwt() },
11962
- body: JSON.stringify({ role: "route-classify", payload: prompt, content: prompt, hedge: false }),
11963
- // First call may spin up the route lane (a haiku worker boot), so allow headroom.
11964
- signal: AbortSignal.timeout(12e4)
11965
- });
11966
- if (!resp.ok) return;
11967
- const data = await resp.json();
11968
- const m = (data.result || "").match(/<model>\s*([A-Za-z0-9._:-]+)\s*<\/model>/i);
11969
- model = m ? m[1].trim() : "";
11970
- } catch {
11971
- return;
11972
- }
11973
- if (!model || !VALID_MODELS.has(model)) return;
11974
- const lastFile = join24(PTY_DIR, "route-last-" + sid);
11975
- let last = "";
11976
- try {
11977
- last = readFileSync25(lastFile, "utf-8").trim();
11978
- } catch {
11979
- }
11980
- if (model === last) return;
11981
- try {
11982
- writeFileSync19(lastFile, model);
11983
- } catch {
11984
- }
11985
- try {
11986
- writeFileSync19(join24(PTY_DIR, "route-" + sid), model);
11987
- } catch {
11988
- }
11989
- }
11990
- var SYNKRO_DIR13, PTY_DIR, SESSIONS_DIR2, VALID_MODELS, GRADER_HOST_PORT;
11991
- var init_routeDecide = __esm({
11992
- "cli/local-cc/routeDecide.ts"() {
11993
- "use strict";
11994
- SYNKRO_DIR13 = join24(homedir25(), ".synkro");
11995
- PTY_DIR = join24(SYNKRO_DIR13, "pty");
11996
- SESSIONS_DIR2 = join24(PTY_DIR, "sessions");
11997
- VALID_MODELS = /* @__PURE__ */ new Set(["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-4-8"]);
11998
- GRADER_HOST_PORT = process.env.SYNKRO_GRADER_HOST_PORT || "18929";
11999
- }
12000
- });
12001
-
12002
- // cli/local-cc/routeOrchestrate.ts
12003
- var routeOrchestrate_exports = {};
12004
- __export(routeOrchestrate_exports, {
12005
- routeAndResubmit: () => routeAndResubmit
12006
- });
12007
- import { readFileSync as readFileSync26, writeFileSync as writeFileSync20 } from "fs";
12008
- import { homedir as homedir26 } from "os";
12009
- import { join as join25 } from "path";
12010
- import { spawnSync as spawnSync9 } from "child_process";
12011
- function safeSid2(sid) {
12012
- return sid.replace(/[^A-Za-z0-9_-]/g, "_");
12013
- }
12014
- function safeSession(s) {
12015
- return /^[A-Za-z0-9._:-]{1,64}$/.test(s);
12016
- }
12017
- function loadMcpJwt2() {
12018
- try {
12019
- return readFileSync26(join25(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
12020
- } catch {
12021
- return "";
12022
- }
12023
- }
12024
- async function classifyTask(prompt) {
12025
- try {
12026
- const resp = await fetch(`http://127.0.0.1:${GRADER_HOST_PORT2}/submit`, {
12027
- method: "POST",
12028
- headers: { "Content-Type": "application/json", Authorization: "Bearer " + loadMcpJwt2() },
12029
- body: JSON.stringify({ role: "route-classify", payload: prompt, content: prompt, hedge: false }),
12030
- signal: AbortSignal.timeout(6e4)
12031
- });
12032
- if (!resp.ok) return null;
12033
- const data = await resp.json();
12034
- const m = (data.result || "").match(/<model>\s*([A-Za-z0-9._:-]+)\s*<\/model>/i);
12035
- if (!m) return null;
12036
- const v = m[1].trim();
12037
- if (v.toLowerCase() === "keep") return "keep";
12038
- return VALID_MODELS2.has(v) ? v : null;
12039
- } catch {
12040
- return null;
12041
- }
12042
- }
12043
- function lastRoutedModel(sid) {
12044
- try {
12045
- const v = readFileSync26(join25(PTY_DIR2, "route-last-" + sid), "utf-8").trim();
12046
- return VALID_MODELS2.has(v) ? v : "";
12047
- } catch {
12048
- return "";
12049
- }
12050
- }
12051
- function resolveSession(sid, tmuxSession) {
12052
- const candidates = [];
12053
- if (tmuxSession) candidates.push(tmuxSession);
12054
- try {
12055
- const rec = JSON.parse(readFileSync26(join25(SESSIONS_DIR3, safeSid2(sid) + ".json"), "utf-8"));
12056
- if (rec.tmux_session) candidates.push(rec.tmux_session);
12057
- } catch {
12058
- }
12059
- try {
12060
- candidates.push(readFileSync26(ACTIVE_SESSION_FILE2, "utf-8").trim());
12061
- } catch {
12062
- }
12063
- for (const c of candidates) if (c && safeSession(c)) return c;
12064
- return "";
12065
- }
12066
- async function routeAndResubmit(sessionId, task, tmuxSession, forceModel) {
12067
- const trimmed = (task || "").trim();
12068
- if (!sessionId || !trimmed) return;
12069
- const sid = safeSid2(sessionId);
12070
- const session = resolveSession(sid, tmuxSession);
12071
- if (!session) return;
12072
- if (spawnSync9("tmux", ["has-session", "-t", `=${session}`], { encoding: "utf-8" }).status !== 0) return;
12073
- const current = lastRoutedModel(sid);
12074
- const picked = forceModel && VALID_MODELS2.has(forceModel) ? forceModel : await classifyTask(trimmed);
12075
- const doSwap = !!picked && picked !== "keep" && picked !== current;
12076
- const sk = (...a) => spawnSync9("tmux", ["send-keys", "-t", session, ...a], { encoding: "utf-8" });
12077
- const capture = () => spawnSync9("tmux", ["capture-pane", "-t", session, "-p", "-S", "-25"], { encoding: "utf-8" }).stdout || "";
12078
- await wait(400);
12079
- if (doSwap) {
12080
- sk("C-u");
12081
- await wait(80);
12082
- sk("-l", `/model ${picked}`);
12083
- sk("Enter");
12084
- for (let i = 0; i < 50; i++) {
12085
- await wait(100);
12086
- if (/switch model|yes, switch/i.test(capture())) {
12087
- sk("-l", "1");
12088
- await wait(120);
12089
- sk("Enter");
12090
- break;
12091
- }
12092
- }
12093
- await wait(500);
12094
- try {
12095
- writeFileSync20(join25(PTY_DIR2, "route-last-" + sid), picked);
12096
- } catch {
12097
- }
12098
- }
12099
- try {
12100
- writeFileSync20(join25(PTY_DIR2, "route-guard-" + sid), "1");
12101
- } catch {
12102
- }
12103
- sk("C-u");
12104
- await wait(80);
12105
- spawnSync9("tmux", ["set-buffer", "-b", "synkro-route", trimmed], { encoding: "utf-8" });
12106
- spawnSync9("tmux", ["paste-buffer", "-t", session, "-b", "synkro-route", "-d"], { encoding: "utf-8" });
12107
- await wait(120);
12108
- sk("Enter");
12109
- }
12110
- var SYNKRO_DIR14, PTY_DIR2, SESSIONS_DIR3, ACTIVE_SESSION_FILE2, VALID_MODELS2, GRADER_HOST_PORT2, wait;
12111
- var init_routeOrchestrate = __esm({
12112
- "cli/local-cc/routeOrchestrate.ts"() {
12113
- "use strict";
12114
- SYNKRO_DIR14 = join25(homedir26(), ".synkro");
12115
- PTY_DIR2 = join25(SYNKRO_DIR14, "pty");
12116
- SESSIONS_DIR3 = join25(PTY_DIR2, "sessions");
12117
- ACTIVE_SESSION_FILE2 = join25(PTY_DIR2, "active");
12118
- VALID_MODELS2 = /* @__PURE__ */ new Set(["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-4-8"]);
12119
- GRADER_HOST_PORT2 = process.env.SYNKRO_GRADER_HOST_PORT || "18929";
12120
- wait = (ms) => new Promise((r) => setTimeout(r, ms));
12121
- }
12122
- });
12123
-
12124
- // cli/local-cc/routingToggle.ts
12125
- var routingToggle_exports = {};
12126
- __export(routingToggle_exports, {
12127
- routingCommand: () => routingCommand
12128
- });
12129
- import { writeFileSync as writeFileSync21, unlinkSync as unlinkSync9, existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
12130
- import { homedir as homedir27 } from "os";
12131
- import { join as join26 } from "path";
12132
- function safeSid3(sid) {
12133
- return sid.replace(/[^A-Za-z0-9_-]/g, "_");
12134
- }
12135
- function markerFor(session) {
12136
- return session ? join26(PTY_DIR3, "routing-on-" + safeSid3(session)) : join26(PTY_DIR3, "routing-on");
12137
- }
12138
- function routingCommand(args2) {
12139
- const sub = (args2[0] || "status").trim();
12140
- let session;
12141
- const si = args2.indexOf("--session");
12142
- if (si >= 0 && args2[si + 1]) session = args2[si + 1].trim();
12143
- if (sub === "on") {
12144
- try {
12145
- writeFileSync21(markerFor(session), "1");
12146
- } catch (e) {
12147
- console.error("routing on failed:", String(e));
12148
- return;
12149
- }
12150
- console.log(session ? `Routing ON for session ${session}.` : "Routing ON (all sessions).");
12151
- console.log(" New task prompts will be classified and run on the best model automatically.");
12152
- return;
12153
- }
12154
- if (sub === "off") {
12155
- let removed = 0;
12156
- if (session) {
12157
- if (existsSync27(markerFor(session))) {
12158
- try {
12159
- unlinkSync9(markerFor(session));
12160
- removed++;
12161
- } catch {
12162
- }
12163
- }
12164
- } else {
12165
- for (const f of safeList()) {
12166
- if (f === "routing-on" || f.startsWith("routing-on-")) {
12167
- try {
12168
- unlinkSync9(join26(PTY_DIR3, f));
12169
- removed++;
12170
- } catch {
12171
- }
12172
- }
12173
- }
12174
- }
12175
- console.log(`Routing OFF${session ? ` for session ${session}` : ""} (${removed} marker${removed === 1 ? "" : "s"} cleared).`);
12176
- return;
12177
- }
12178
- const markers = safeList().filter((f) => f === "routing-on" || f.startsWith("routing-on-"));
12179
- if (markers.length === 0) {
12180
- console.log("Routing is OFF (no markers).");
12181
- return;
12182
- }
12183
- console.log("Routing is ON:");
12184
- for (const m of markers) {
12185
- if (m === "routing-on") console.log(" \u2022 global (all sessions)");
12186
- else console.log(" \u2022 session " + m.slice("routing-on-".length));
12187
- }
12188
- }
12189
- function safeList() {
12190
- try {
12191
- return readdirSync6(PTY_DIR3);
12192
- } catch {
12193
- return [];
12194
- }
12195
- }
12196
- var PTY_DIR3;
12197
- var init_routingToggle = __esm({
12198
- "cli/local-cc/routingToggle.ts"() {
12199
- "use strict";
12200
- PTY_DIR3 = join26(homedir27(), ".synkro", "pty");
12201
- }
12202
- });
12203
-
12204
- // cli/local-cc/pueue.ts
12205
- import { execFileSync as execFileSync4, spawnSync as spawnSync10, spawn as spawn6 } from "child_process";
12206
- import { homedir as homedir28 } from "os";
12207
- import { join as join27 } from "path";
12208
11830
  import { connect as connect2 } from "net";
12209
11831
  function pueueAvailable() {
12210
- const r = spawnSync10("pueue", ["--version"], { encoding: "utf-8" });
11832
+ const r = spawnSync9("pueue", ["--version"], { encoding: "utf-8" });
12211
11833
  if (r.status !== 0) {
12212
11834
  throw new PueueError("pueue CLI not found on PATH. Install pueue (https://github.com/Nukesor/pueue) and start `pueued`.");
12213
11835
  }
12214
11836
  }
12215
11837
  function statusJson() {
12216
11838
  pueueAvailable();
12217
- const r = spawnSync10("pueue", ["status", "--json"], { encoding: "utf-8" });
11839
+ const r = spawnSync9("pueue", ["status", "--json"], { encoding: "utf-8" });
12218
11840
  if (r.status !== 0) {
12219
11841
  throw new PueueError(`pueue status failed: ${r.stderr || r.stdout || "unknown error"} \u2014 is pueued running?`);
12220
11842
  }
@@ -12259,18 +11881,18 @@ function startTask(opts = {}) {
12259
11881
  let existing = findTask(ch);
12260
11882
  while (existing) {
12261
11883
  if (existing.status === "Running" || existing.status === "Queued") {
12262
- spawnSync10("tmux", ["kill-session", "-t", `=${ch.tmuxSession}`], { encoding: "utf-8" });
12263
- spawnSync10("pueue", ["kill", String(existing.id)], { encoding: "utf-8" });
11884
+ spawnSync9("tmux", ["kill-session", "-t", `=${ch.tmuxSession}`], { encoding: "utf-8" });
11885
+ spawnSync9("pueue", ["kill", String(existing.id)], { encoding: "utf-8" });
12264
11886
  for (let i = 0; i < 10; i++) {
12265
11887
  const check = findTask(ch);
12266
11888
  if (!check || check.id !== existing.id || check.status !== "Running" && check.status !== "Queued") break;
12267
- spawnSync10("sleep", ["0.5"], { encoding: "utf-8" });
11889
+ spawnSync9("sleep", ["0.5"], { encoding: "utf-8" });
12268
11890
  }
12269
11891
  }
12270
- spawnSync10("pueue", ["remove", String(existing.id)], { encoding: "utf-8" });
11892
+ spawnSync9("pueue", ["remove", String(existing.id)], { encoding: "utf-8" });
12271
11893
  existing = findTask(ch);
12272
11894
  }
12273
- const runScript = join27(cwd, "run-claude.sh");
11895
+ const runScript = join24(cwd, "run-claude.sh");
12274
11896
  const args2 = [
12275
11897
  "add",
12276
11898
  "--label",
@@ -12281,7 +11903,7 @@ function startTask(opts = {}) {
12281
11903
  "bash",
12282
11904
  runScript
12283
11905
  ];
12284
- const r = spawnSync10("pueue", args2, { encoding: "utf-8" });
11906
+ const r = spawnSync9("pueue", args2, { encoding: "utf-8" });
12285
11907
  if (r.status !== 0) {
12286
11908
  throw new PueueError(`pueue add failed: ${r.stderr || r.stdout}`);
12287
11909
  }
@@ -12292,25 +11914,25 @@ function startTask(opts = {}) {
12292
11914
  return created;
12293
11915
  }
12294
11916
  function stopTask(channel = CHANNEL_PRIMARY) {
12295
- spawnSync10("tmux", ["kill-session", "-t", `=${channel.tmuxSession}`], { encoding: "utf-8" });
11917
+ spawnSync9("tmux", ["kill-session", "-t", `=${channel.tmuxSession}`], { encoding: "utf-8" });
12296
11918
  let t = findTask(channel);
12297
11919
  while (t) {
12298
11920
  if (t.status === "Running" || t.status === "Queued") {
12299
- spawnSync10("pueue", ["kill", String(t.id)], { encoding: "utf-8" });
11921
+ spawnSync9("pueue", ["kill", String(t.id)], { encoding: "utf-8" });
12300
11922
  for (let i = 0; i < 10; i++) {
12301
11923
  const check = findTask(channel);
12302
11924
  if (!check || check.id !== t.id || check.status !== "Running" && check.status !== "Queued") break;
12303
- spawnSync10("sleep", ["0.5"], { encoding: "utf-8" });
11925
+ spawnSync9("sleep", ["0.5"], { encoding: "utf-8" });
12304
11926
  }
12305
11927
  }
12306
- spawnSync10("pueue", ["remove", String(t.id)], { encoding: "utf-8" });
11928
+ spawnSync9("pueue", ["remove", String(t.id)], { encoding: "utf-8" });
12307
11929
  t = findTask(channel);
12308
11930
  }
12309
11931
  }
12310
11932
  function tailLogs(lines = 80, channel = CHANNEL_PRIMARY) {
12311
11933
  const t = findTask(channel);
12312
11934
  if (!t) return `(no ${channel.taskLabel} task)`;
12313
- const r = spawnSync10("pueue", ["log", "--lines", String(lines), String(t.id)], { encoding: "utf-8" });
11935
+ const r = spawnSync9("pueue", ["log", "--lines", String(lines), String(t.id)], { encoding: "utf-8" });
12314
11936
  return r.stdout || r.stderr || "(no output)";
12315
11937
  }
12316
11938
  function ensureRunning(opts = {}) {
@@ -12335,8 +11957,8 @@ function probePort(host, port, timeoutMs = 500) {
12335
11957
  });
12336
11958
  }
12337
11959
  function tmuxDismissPrompts(tmuxSession = TMUX_SESSION) {
12338
- spawnSync10("tmux", ["send-keys", "-t", tmuxSession, "1"], { encoding: "utf-8" });
12339
- spawnSync10("tmux", ["send-keys", "-t", tmuxSession, "Enter"], { encoding: "utf-8" });
11960
+ spawnSync9("tmux", ["send-keys", "-t", tmuxSession, "1"], { encoding: "utf-8" });
11961
+ spawnSync9("tmux", ["send-keys", "-t", tmuxSession, "Enter"], { encoding: "utf-8" });
12340
11962
  }
12341
11963
  async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tmuxSession = TMUX_SESSION) {
12342
11964
  const deadline = Date.now() + timeoutMs;
@@ -12348,46 +11970,46 @@ async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tm
12348
11970
  return probePort(host, port);
12349
11971
  }
12350
11972
  function brewInstall(pkg) {
12351
- const brew = spawnSync10("brew", ["--version"], { encoding: "utf-8" });
11973
+ const brew = spawnSync9("brew", ["--version"], { encoding: "utf-8" });
12352
11974
  if (brew.status !== 0) return false;
12353
11975
  console.log(` Installing ${pkg} via brew...`);
12354
- const r = spawnSync10("brew", ["install", pkg], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
11976
+ const r = spawnSync9("brew", ["install", pkg], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
12355
11977
  return r.status === 0;
12356
11978
  }
12357
11979
  function assertPueueInstalled() {
12358
- let r = spawnSync10("pueue", ["--version"], { encoding: "utf-8" });
11980
+ let r = spawnSync9("pueue", ["--version"], { encoding: "utf-8" });
12359
11981
  if (r.status !== 0) {
12360
11982
  if (process.platform === "darwin" && brewInstall("pueue")) {
12361
- r = spawnSync10("pueue", ["--version"], { encoding: "utf-8" });
11983
+ r = spawnSync9("pueue", ["--version"], { encoding: "utf-8" });
12362
11984
  if (r.status !== 0) throw new PueueError("pueue install succeeded but binary not found on PATH.");
12363
11985
  } else {
12364
11986
  throw new PueueError("pueue not found. Install it: brew install pueue (macOS) or https://github.com/Nukesor/pueue");
12365
11987
  }
12366
11988
  }
12367
- const status = spawnSync10("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
11989
+ const status = spawnSync9("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
12368
11990
  if (status.status !== 0) {
12369
11991
  console.log(" Starting pueued daemon...");
12370
- const child = spawn6("pueued", ["-d"], { stdio: "ignore", detached: true });
11992
+ const child = spawn7("pueued", ["-d"], { stdio: "ignore", detached: true });
12371
11993
  child.unref();
12372
- spawnSync10("sleep", ["1"]);
12373
- const retry = spawnSync10("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
11994
+ spawnSync9("sleep", ["1"]);
11995
+ const retry = spawnSync9("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
12374
11996
  if (retry.status !== 0) {
12375
11997
  throw new PueueError("pueue daemon not reachable after starting pueued. Check `pueued` manually.");
12376
11998
  }
12377
11999
  }
12378
- spawnSync10("pueue", ["parallel", "2"], { encoding: "utf-8" });
12000
+ spawnSync9("pueue", ["parallel", "2"], { encoding: "utf-8" });
12379
12001
  }
12380
12002
  function assertClaudeInstalled() {
12381
- const r = spawnSync10("claude", ["--version"], { encoding: "utf-8" });
12003
+ const r = spawnSync9("claude", ["--version"], { encoding: "utf-8" });
12382
12004
  if (r.status !== 0) {
12383
12005
  throw new PueueError("claude CLI not found on PATH. Install Claude Code first: https://docs.claude.com/claude-code");
12384
12006
  }
12385
12007
  }
12386
12008
  function assertTmuxInstalled() {
12387
- let r = spawnSync10("tmux", ["-V"], { encoding: "utf-8" });
12009
+ let r = spawnSync9("tmux", ["-V"], { encoding: "utf-8" });
12388
12010
  if (r.status !== 0) {
12389
12011
  if (process.platform === "darwin" && brewInstall("tmux")) {
12390
- r = spawnSync10("tmux", ["-V"], { encoding: "utf-8" });
12012
+ r = spawnSync9("tmux", ["-V"], { encoding: "utf-8" });
12391
12013
  if (r.status !== 0) throw new PueueError("tmux install succeeded but binary not found on PATH.");
12392
12014
  } else {
12393
12015
  throw new PueueError("tmux not found. Install it: brew install tmux (macOS) or apt install tmux (Linux)");
@@ -12400,12 +12022,12 @@ var init_pueue = __esm({
12400
12022
  "use strict";
12401
12023
  TASK_LABEL = "synkro-local-cc";
12402
12024
  TMUX_SESSION = "synkro-local-cc";
12403
- SESSION_DIR2 = join27(homedir28(), ".synkro", "cc_sessions");
12025
+ SESSION_DIR2 = join24(homedir25(), ".synkro", "cc_sessions");
12404
12026
  TASK_LABEL_2 = "synkro-local-cc-2";
12405
12027
  TMUX_SESSION_2 = "synkro-local-cc-2";
12406
- SESSION_DIR_22 = join27(homedir28(), ".synkro", "cc_sessions_2");
12407
- SESSION_DIR_32 = join27(homedir28(), ".synkro", "cc_sessions_3");
12408
- SESSION_DIR_42 = join27(homedir28(), ".synkro", "cc_sessions_4");
12028
+ SESSION_DIR_22 = join24(homedir25(), ".synkro", "cc_sessions_2");
12029
+ SESSION_DIR_32 = join24(homedir25(), ".synkro", "cc_sessions_3");
12030
+ SESSION_DIR_42 = join24(homedir25(), ".synkro", "cc_sessions_4");
12409
12031
  PueueError = class extends Error {
12410
12032
  constructor(message, cause) {
12411
12033
  super(message);
@@ -12420,13 +12042,13 @@ var init_pueue = __esm({
12420
12042
  });
12421
12043
 
12422
12044
  // cli/local-cc/settings.ts
12423
- import { existsSync as existsSync28, readFileSync as readFileSync27 } from "fs";
12424
- import { homedir as homedir29 } from "os";
12425
- import { join as join28 } from "path";
12045
+ import { existsSync as existsSync27, readFileSync as readFileSync25 } from "fs";
12046
+ import { homedir as homedir26 } from "os";
12047
+ import { join as join25 } from "path";
12426
12048
  function isLocalCCEnabled() {
12427
- if (!existsSync28(CONFIG_PATH5)) return false;
12049
+ if (!existsSync27(CONFIG_PATH5)) return false;
12428
12050
  try {
12429
- const content = readFileSync27(CONFIG_PATH5, "utf-8");
12051
+ const content = readFileSync25(CONFIG_PATH5, "utf-8");
12430
12052
  const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);
12431
12053
  return match?.[1] === "yes";
12432
12054
  } catch {
@@ -12437,7 +12059,7 @@ var CONFIG_PATH5;
12437
12059
  var init_settings = __esm({
12438
12060
  "cli/local-cc/settings.ts"() {
12439
12061
  "use strict";
12440
- CONFIG_PATH5 = join28(homedir29(), ".synkro", "config.env");
12062
+ CONFIG_PATH5 = join25(homedir26(), ".synkro", "config.env");
12441
12063
  }
12442
12064
  });
12443
12065
 
@@ -12446,11 +12068,11 @@ var localCc_exports = {};
12446
12068
  __export(localCc_exports, {
12447
12069
  localCcCommand: () => localCcCommand
12448
12070
  });
12449
- import { spawnSync as spawnSync11 } from "child_process";
12450
- import { homedir as homedir30 } from "os";
12451
- import { join as join29 } from "path";
12071
+ import { spawnSync as spawnSync10 } from "child_process";
12072
+ import { homedir as homedir27 } from "os";
12073
+ import { join as join26 } from "path";
12452
12074
  import { readFileSync as fsReadFileSync, existsSync as fsExistsSync } from "fs";
12453
- import { existsSync as existsSync29, readFileSync as readFileSync28, writeFileSync as writeFileSync22 } from "fs";
12075
+ import { existsSync as existsSync28, readFileSync as readFileSync26, writeFileSync as writeFileSync19 } from "fs";
12454
12076
  function deploymentMode() {
12455
12077
  const env = (process.env.SYNKRO_DEPLOYMENT_MODE || "").toLowerCase();
12456
12078
  if (env === "docker") return "docker";
@@ -12556,15 +12178,15 @@ TROUBLESHOOTING
12556
12178
  `);
12557
12179
  }
12558
12180
  function readGatewayUrl() {
12559
- if (existsSync29(CONFIG_PATH6)) {
12560
- const m = readFileSync28(CONFIG_PATH6, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
12181
+ if (existsSync28(CONFIG_PATH6)) {
12182
+ const m = readFileSync26(CONFIG_PATH6, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
12561
12183
  if (m) return m[1];
12562
12184
  }
12563
12185
  return "https://api.synkro.sh";
12564
12186
  }
12565
12187
  function updateLocalInferenceFlag(enabled) {
12566
- if (!existsSync29(CONFIG_PATH6)) return;
12567
- let content = readFileSync28(CONFIG_PATH6, "utf-8");
12188
+ if (!existsSync28(CONFIG_PATH6)) return;
12189
+ let content = readFileSync26(CONFIG_PATH6, "utf-8");
12568
12190
  const flag = enabled ? "yes" : "no";
12569
12191
  if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
12570
12192
  content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);
@@ -12573,7 +12195,7 @@ function updateLocalInferenceFlag(enabled) {
12573
12195
  SYNKRO_LOCAL_INFERENCE='${flag}'
12574
12196
  `;
12575
12197
  }
12576
- writeFileSync22(CONFIG_PATH6, content, "utf-8");
12198
+ writeFileSync19(CONFIG_PATH6, content, "utf-8");
12577
12199
  }
12578
12200
  async function setServerGradingProvider(provider) {
12579
12201
  await ensureValidToken();
@@ -12627,7 +12249,7 @@ async function cmdStatus() {
12627
12249
  }
12628
12250
  const ch1Up = await isChannelAvailable();
12629
12251
  console.log(`Channel 1 ${CHANNEL_HOST}:${CHANNEL_PORT}: ${ch1Up ? "reachable" : "unreachable"}`);
12630
- const tmux1 = spawnSync11("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
12252
+ const tmux1 = spawnSync10("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
12631
12253
  console.log(`tmux '${TMUX_SESSION_NAME}': ${tmux1.status === 0 ? "live" : "absent"}`);
12632
12254
  const t2 = findTask(CHANNEL_SECONDARY);
12633
12255
  if (!t2) {
@@ -12637,7 +12259,7 @@ async function cmdStatus() {
12637
12259
  }
12638
12260
  const ch2Up = await isChannelAvailable(CHANNEL_2_PORT);
12639
12261
  console.log(`Channel 2 ${CHANNEL_HOST}:${CHANNEL_2_PORT}: ${ch2Up ? "reachable" : "unreachable"}`);
12640
- const tmux2 = spawnSync11("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME_2}`], { encoding: "utf-8" });
12262
+ const tmux2 = spawnSync10("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME_2}`], { encoding: "utf-8" });
12641
12263
  console.log(`tmux '${TMUX_SESSION_NAME_2}': ${tmux2.status === 0 ? "live" : "absent"}`);
12642
12264
  }
12643
12265
  async function cmdEnable() {
@@ -12845,7 +12467,7 @@ function cmdLogs(rest) {
12845
12467
  }
12846
12468
  return "200";
12847
12469
  })();
12848
- spawnSync11("docker", ["logs", "--tail", tailArg, ...followFlag, "synkro-server"], { stdio: "inherit" });
12470
+ spawnSync10("docker", ["logs", "--tail", tailArg, ...followFlag, "synkro-server"], { stdio: "inherit" });
12849
12471
  return;
12850
12472
  }
12851
12473
  for (const arg of rest) {
@@ -12893,7 +12515,7 @@ function cmdLogs(rest) {
12893
12515
  function cmdAttach(rest) {
12894
12516
  assertTmuxInstalled();
12895
12517
  const readonly = rest.some((a) => a === "--readonly" || a === "-r");
12896
- const has = spawnSync11("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
12518
+ const has = spawnSync10("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
12897
12519
  if (has.status !== 0) {
12898
12520
  console.error(`No tmux session '${TMUX_SESSION_NAME}' running. Start it with: synkro local-cc start`);
12899
12521
  process.exit(1);
@@ -12906,7 +12528,7 @@ function cmdAttach(rest) {
12906
12528
  console.log("Detach with Ctrl-B then D. (Do not press Ctrl-C \u2014 that would interrupt claude.)");
12907
12529
  console.log();
12908
12530
  const args2 = readonly ? ["attach-session", "-r", "-t", TMUX_SESSION_NAME] : ["attach-session", "-t", TMUX_SESSION_NAME];
12909
- const r = spawnSync11("tmux", args2, { stdio: "inherit" });
12531
+ const r = spawnSync10("tmux", args2, { stdio: "inherit" });
12910
12532
  process.exit(r.status ?? 0);
12911
12533
  }
12912
12534
  async function cmdTest() {
@@ -13007,8 +12629,8 @@ var init_localCc = __esm({
13007
12629
  init_install();
13008
12630
  init_client2();
13009
12631
  init_stub();
13010
- SYNKRO_CONFIG_PATH = join29(homedir30(), ".synkro", "config.env");
13011
- CONFIG_PATH6 = join29(homedir30(), ".synkro", "config.env");
12632
+ SYNKRO_CONFIG_PATH = join26(homedir27(), ".synkro", "config.env");
12633
+ CONFIG_PATH6 = join26(homedir27(), ".synkro", "config.env");
13012
12634
  }
13013
12635
  });
13014
12636
 
@@ -13017,14 +12639,14 @@ var import_exports = {};
13017
12639
  __export(import_exports, {
13018
12640
  importCommand: () => importCommand
13019
12641
  });
13020
- import { existsSync as existsSync30, readFileSync as readFileSync29, readdirSync as readdirSync7 } from "fs";
13021
- import { homedir as homedir31 } from "os";
13022
- import { join as join30 } from "path";
12642
+ import { existsSync as existsSync29, readFileSync as readFileSync27, readdirSync as readdirSync6 } from "fs";
12643
+ import { homedir as homedir28 } from "os";
12644
+ import { join as join27 } from "path";
13023
12645
  import { execSync as execSync6 } from "child_process";
13024
12646
  import { createInterface as createInterface4 } from "readline";
13025
12647
  function readMcpJwt() {
13026
12648
  try {
13027
- return readFileSync29(join30(homedir31(), ".synkro", ".mcp-jwt"), "utf-8").trim();
12649
+ return readFileSync27(join27(homedir28(), ".synkro", ".mcp-jwt"), "utf-8").trim();
13028
12650
  } catch {
13029
12651
  return "";
13030
12652
  }
@@ -13032,7 +12654,7 @@ function readMcpJwt() {
13032
12654
  function readConfigEnv2() {
13033
12655
  const out = {};
13034
12656
  try {
13035
- for (const line of readFileSync29(CONFIG_PATH7, "utf-8").split("\n")) {
12657
+ for (const line of readFileSync27(CONFIG_PATH7, "utf-8").split("\n")) {
13036
12658
  const t = line.trim();
13037
12659
  if (!t || t.startsWith("#")) continue;
13038
12660
  const eq = t.indexOf("=");
@@ -13044,8 +12666,8 @@ function readConfigEnv2() {
13044
12666
  }
13045
12667
  function projectsFolder() {
13046
12668
  const sanitized = process.cwd().replace(/\//g, "-");
13047
- const dir = join30(homedir31(), ".claude", "projects", sanitized);
13048
- return existsSync30(dir) ? dir : null;
12669
+ const dir = join27(homedir28(), ".claude", "projects", sanitized);
12670
+ return existsSync29(dir) ? dir : null;
13049
12671
  }
13050
12672
  function repoName() {
13051
12673
  try {
@@ -13084,7 +12706,7 @@ function extractToolResultText(content, e) {
13084
12706
  return t;
13085
12707
  }
13086
12708
  function parseSession(filePath, sessionId) {
13087
- const lines = readFileSync29(filePath, "utf-8").split("\n").filter(Boolean);
12709
+ const lines = readFileSync27(filePath, "utf-8").split("\n").filter(Boolean);
13088
12710
  const messages = [];
13089
12711
  const actions = [];
13090
12712
  let step = 0;
@@ -13151,7 +12773,7 @@ async function importCommand() {
13151
12773
  console.log("No Claude Code transcripts found for this repo (~/.claude/projects).");
13152
12774
  return;
13153
12775
  }
13154
- const files = readdirSync7(dir).filter((f) => f.endsWith(".jsonl"));
12776
+ const files = readdirSync6(dir).filter((f) => f.endsWith(".jsonl"));
13155
12777
  if (!files.length) {
13156
12778
  console.log("No sessions to import.");
13157
12779
  return;
@@ -13164,7 +12786,7 @@ async function importCommand() {
13164
12786
  return;
13165
12787
  }
13166
12788
  }
13167
- const sessions = files.map((f) => parseSession(join30(dir, f), f.replace(".jsonl", ""))).filter((s) => s.messages.length > 0);
12789
+ const sessions = files.map((f) => parseSession(join27(dir, f), f.replace(".jsonl", ""))).filter((s) => s.messages.length > 0);
13168
12790
  const totalMsgs = sessions.reduce((n, s) => n + s.messages.length, 0);
13169
12791
  let ok = 0, fail = 0;
13170
12792
  if (isCloud) {
@@ -13239,7 +12861,7 @@ var init_import = __esm({
13239
12861
  "cli/commands/import.ts"() {
13240
12862
  "use strict";
13241
12863
  init_stub();
13242
- CONFIG_PATH7 = join30(homedir31(), ".synkro", "config.env");
12864
+ CONFIG_PATH7 = join27(homedir28(), ".synkro", "config.env");
13243
12865
  }
13244
12866
  });
13245
12867
 
@@ -13281,10 +12903,10 @@ var init_packVerify = __esm({
13281
12903
  });
13282
12904
 
13283
12905
  // cli/installer/lockfile.ts
13284
- import { existsSync as existsSync31, readFileSync as readFileSync30, writeFileSync as writeFileSync23 } from "fs";
13285
- import { join as join31 } from "path";
12906
+ import { existsSync as existsSync30, readFileSync as readFileSync28, writeFileSync as writeFileSync20 } from "fs";
12907
+ import { join as join28 } from "path";
13286
12908
  function lockPath(repoRoot2) {
13287
- return join31(repoRoot2, LOCK_FILE);
12909
+ return join28(repoRoot2, LOCK_FILE);
13288
12910
  }
13289
12911
  function writeLockfile(repoRoot2, entries) {
13290
12912
  const sorted = [...entries].sort((a, b) => a.ref.localeCompare(b.ref));
@@ -13302,7 +12924,7 @@ function writeLockfile(repoRoot2, entries) {
13302
12924
  ""
13303
12925
  ])
13304
12926
  ].join("\n");
13305
- writeFileSync23(lockPath(repoRoot2), body, "utf-8");
12927
+ writeFileSync20(lockPath(repoRoot2), body, "utf-8");
13306
12928
  }
13307
12929
  var LOCK_FILE;
13308
12930
  var init_lockfile = __esm({
@@ -13317,9 +12939,9 @@ var sync_exports = {};
13317
12939
  __export(sync_exports, {
13318
12940
  syncCommand: () => syncCommand
13319
12941
  });
13320
- import { existsSync as existsSync32, mkdirSync as mkdirSync18, readdirSync as readdirSync8, rmSync as rmSync4, writeFileSync as writeFileSync24 } from "fs";
13321
- import { homedir as homedir32 } from "os";
13322
- import { join as join32 } from "path";
12942
+ import { existsSync as existsSync31, mkdirSync as mkdirSync18, readdirSync as readdirSync7, rmSync as rmSync4, writeFileSync as writeFileSync21 } from "fs";
12943
+ import { homedir as homedir29 } from "os";
12944
+ import { join as join29 } from "path";
13323
12945
  function cacheKey(ref, version) {
13324
12946
  return ref.replace(/\//g, "__").replace(/[^\w.@-]/g, "_") + "@" + version + ".json";
13325
12947
  }
@@ -13350,7 +12972,7 @@ async function syncCommand(_args = []) {
13350
12972
  }
13351
12973
  const gateway = (process.env.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/\/$/, "");
13352
12974
  const cloud = process.env.SYNKRO_DEPLOY_LOCATION === "cloud";
13353
- const cacheDir = join32(homedir32(), ".synkro", "cache", "packs");
12975
+ const cacheDir = join29(homedir29(), ".synkro", "cache", "packs");
13354
12976
  if (!cloud) mkdirSync18(cacheDir, { recursive: true });
13355
12977
  console.log(`Syncing ${refs.length} standard(s) from the registry\u2026`);
13356
12978
  const lock = [];
@@ -13379,7 +13001,7 @@ async function syncCommand(_args = []) {
13379
13001
  if (!cloud) {
13380
13002
  const fname = cacheKey(ref, data.version);
13381
13003
  keptCacheFiles.add(fname);
13382
- writeFileSync24(join32(cacheDir, fname), JSON.stringify({
13004
+ writeFileSync21(join29(cacheDir, fname), JSON.stringify({
13383
13005
  ref,
13384
13006
  version: data.version,
13385
13007
  digest: data.digest,
@@ -13391,11 +13013,11 @@ async function syncCommand(_args = []) {
13391
13013
  const ruleCount = Array.isArray(pack.rules) ? pack.rules.length : 0;
13392
13014
  console.log(` \u2713 ${ref}:${data.version} \u2014 verified (${ruleCount} rule${ruleCount === 1 ? "" : "s"})`);
13393
13015
  }
13394
- if (!cloud && existsSync32(cacheDir)) {
13395
- for (const f of readdirSync8(cacheDir)) {
13016
+ if (!cloud && existsSync31(cacheDir)) {
13017
+ for (const f of readdirSync7(cacheDir)) {
13396
13018
  if (f.endsWith(".json") && !keptCacheFiles.has(f)) {
13397
13019
  try {
13398
- rmSync4(join32(cacheDir, f));
13020
+ rmSync4(join29(cacheDir, f));
13399
13021
  } catch {
13400
13022
  }
13401
13023
  }
@@ -13424,13 +13046,13 @@ var whoami_exports = {};
13424
13046
  __export(whoami_exports, {
13425
13047
  whoamiCommand: () => whoamiCommand
13426
13048
  });
13427
- import { readFileSync as readFileSync31, existsSync as existsSync33 } from "fs";
13428
- import { join as join33 } from "path";
13429
- import { homedir as homedir33 } from "os";
13049
+ import { readFileSync as readFileSync29, existsSync as existsSync32 } from "fs";
13050
+ import { join as join30 } from "path";
13051
+ import { homedir as homedir30 } from "os";
13430
13052
  function readConfigEnv3() {
13431
- if (!existsSync33(CONFIG_PATH8)) return {};
13053
+ if (!existsSync32(CONFIG_PATH8)) return {};
13432
13054
  const out = {};
13433
- for (const line of readFileSync31(CONFIG_PATH8, "utf-8").split("\n")) {
13055
+ for (const line of readFileSync29(CONFIG_PATH8, "utf-8").split("\n")) {
13434
13056
  const t = line.trim();
13435
13057
  if (!t || t.startsWith("#")) continue;
13436
13058
  const eq = t.indexOf("=");
@@ -13440,8 +13062,8 @@ function readConfigEnv3() {
13440
13062
  }
13441
13063
  function jwtStatus() {
13442
13064
  try {
13443
- if (!existsSync33(JWT_PATH2)) return { status: "none" };
13444
- const jwt2 = readFileSync31(JWT_PATH2, "utf-8").trim();
13065
+ if (!existsSync32(JWT_PATH2)) return { status: "none" };
13066
+ const jwt2 = readFileSync29(JWT_PATH2, "utf-8").trim();
13445
13067
  if (!jwt2) return { status: "none" };
13446
13068
  const payload = jwt2.split(".")[1];
13447
13069
  if (!payload) return { status: "valid" };
@@ -13499,13 +13121,13 @@ async function whoamiCommand(args2 = []) {
13499
13121
  console.log("synkro identity");
13500
13122
  for (const [k, v] of rows) console.log(` ${k.padEnd(width)} ${v}`);
13501
13123
  }
13502
- var SYNKRO_DIR15, CONFIG_PATH8, JWT_PATH2, GRADING_LABEL;
13124
+ var SYNKRO_DIR13, CONFIG_PATH8, JWT_PATH2, GRADING_LABEL;
13503
13125
  var init_whoami = __esm({
13504
13126
  "cli/commands/whoami.ts"() {
13505
13127
  "use strict";
13506
- SYNKRO_DIR15 = join33(homedir33(), ".synkro");
13507
- CONFIG_PATH8 = join33(SYNKRO_DIR15, "config.env");
13508
- JWT_PATH2 = join33(SYNKRO_DIR15, ".mcp-jwt");
13128
+ SYNKRO_DIR13 = join30(homedir30(), ".synkro");
13129
+ CONFIG_PATH8 = join30(SYNKRO_DIR13, "config.env");
13130
+ JWT_PATH2 = join30(SYNKRO_DIR13, ".mcp-jwt");
13509
13131
  GRADING_LABEL = {
13510
13132
  local: "on-device worker pool",
13511
13133
  cloud: "Synkro Cloud worker pool",
@@ -13550,12 +13172,12 @@ __export(linear_exports, {
13550
13172
  formatLinks: () => formatLinks,
13551
13173
  linearCommand: () => linearCommand
13552
13174
  });
13553
- import { readFileSync as readFileSync32 } from "fs";
13554
- import { homedir as homedir34 } from "os";
13555
- import { join as join34 } from "path";
13175
+ import { readFileSync as readFileSync30 } from "fs";
13176
+ import { homedir as homedir31 } from "os";
13177
+ import { join as join31 } from "path";
13556
13178
  function mcpJwt() {
13557
13179
  try {
13558
- return readFileSync32(join34(SYNKRO_DIR16, ".mcp-jwt"), "utf-8").trim();
13180
+ return readFileSync30(join31(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
13559
13181
  } catch {
13560
13182
  return "";
13561
13183
  }
@@ -13590,11 +13212,11 @@ async function linearCommand(_args = []) {
13590
13212
  }
13591
13213
  console.log(formatLinks(links));
13592
13214
  }
13593
- var SYNKRO_DIR16, PORT2, BASE;
13215
+ var SYNKRO_DIR14, PORT2, BASE;
13594
13216
  var init_linear = __esm({
13595
13217
  "cli/commands/linear.ts"() {
13596
13218
  "use strict";
13597
- SYNKRO_DIR16 = join34(homedir34(), ".synkro");
13219
+ SYNKRO_DIR14 = join31(homedir31(), ".synkro");
13598
13220
  PORT2 = process.env.SYNKRO_MCP_PORT || "18931";
13599
13221
  BASE = `http://127.0.0.1:${PORT2}`;
13600
13222
  }
@@ -13602,7 +13224,7 @@ var init_linear = __esm({
13602
13224
 
13603
13225
  // cli/scanning/cveReachability.ts
13604
13226
  import { parse } from "@babel/parser";
13605
- import { readFileSync as readFileSync33 } from "fs";
13227
+ import { readFileSync as readFileSync31 } from "fs";
13606
13228
  function walk(node, visit) {
13607
13229
  if (!node || typeof node.type !== "string") return;
13608
13230
  visit(node);
@@ -13743,10 +13365,10 @@ var init_cveReachability = __esm({
13743
13365
  });
13744
13366
 
13745
13367
  // cli/reachability/reachabilityScan.ts
13746
- import { spawnSync as spawnSync12, execFileSync as execFileSync5 } from "child_process";
13747
- import { readFileSync as readFileSync34, writeFileSync as writeFileSync25, existsSync as existsSync34, readdirSync as readdirSync9 } from "fs";
13748
- import { join as join35 } from "path";
13749
- import { homedir as homedir35 } from "os";
13368
+ import { spawnSync as spawnSync11, execFileSync as execFileSync5 } from "child_process";
13369
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync22, existsSync as existsSync33, readdirSync as readdirSync8 } from "fs";
13370
+ import { join as join32 } from "path";
13371
+ import { homedir as homedir32 } from "os";
13750
13372
  import { createRequire } from "module";
13751
13373
  function walkSourceFiles(repoRoot2, maxFiles = 4e3, maxBytes = 5e5) {
13752
13374
  const SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "coverage", ".next", ".turbo", "out", ".cache", ".synkro", ".claude", "vendor", "__tests__", "test-results"]);
@@ -13757,13 +13379,13 @@ function walkSourceFiles(repoRoot2, maxFiles = 4e3, maxBytes = 5e5) {
13757
13379
  const dir = stack.pop();
13758
13380
  let ents;
13759
13381
  try {
13760
- ents = readdirSync9(dir, { withFileTypes: true });
13382
+ ents = readdirSync8(dir, { withFileTypes: true });
13761
13383
  } catch {
13762
13384
  continue;
13763
13385
  }
13764
13386
  for (const e of ents) {
13765
13387
  if (files.length >= maxFiles) break;
13766
- const full = join35(dir, e.name);
13388
+ const full = join32(dir, e.name);
13767
13389
  if (e.isDirectory()) {
13768
13390
  if (!SKIP.has(e.name) && !e.name.startsWith(".")) stack.push(full);
13769
13391
  continue;
@@ -13771,7 +13393,7 @@ function walkSourceFiles(repoRoot2, maxFiles = 4e3, maxBytes = 5e5) {
13771
13393
  if (!EXT.test(e.name) || e.name.endsWith(".d.ts")) continue;
13772
13394
  const rel = full.startsWith(repoRoot2 + "/") ? full.slice(repoRoot2.length + 1) : full;
13773
13395
  try {
13774
- const content = readFileSync34(full, "utf8");
13396
+ const content = readFileSync32(full, "utf8");
13775
13397
  if (content.length <= maxBytes) files.push({ path: rel, content });
13776
13398
  } catch {
13777
13399
  }
@@ -13790,12 +13412,12 @@ function cleanVersion(spec) {
13790
13412
  function gatherManifestVersions(repoRoot2) {
13791
13413
  const out = {};
13792
13414
  const dirs = [repoRoot2];
13793
- const pkgsDir = join35(repoRoot2, "packages");
13794
- if (existsSync34(pkgsDir)) {
13415
+ const pkgsDir = join32(repoRoot2, "packages");
13416
+ if (existsSync33(pkgsDir)) {
13795
13417
  try {
13796
- for (const d of readdirSync9(pkgsDir)) {
13797
- const pd = join35(pkgsDir, d);
13798
- if (existsSync34(join35(pd, "package.json"))) dirs.push(pd);
13418
+ for (const d of readdirSync8(pkgsDir)) {
13419
+ const pd = join32(pkgsDir, d);
13420
+ if (existsSync33(join32(pd, "package.json"))) dirs.push(pd);
13799
13421
  }
13800
13422
  } catch {
13801
13423
  }
@@ -13804,7 +13426,7 @@ function gatherManifestVersions(repoRoot2) {
13804
13426
  for (const dir of dirs) {
13805
13427
  let pkg;
13806
13428
  try {
13807
- pkg = JSON.parse(readFileSync34(join35(dir, "package.json"), "utf8"));
13429
+ pkg = JSON.parse(readFileSync32(join32(dir, "package.json"), "utf8"));
13808
13430
  } catch {
13809
13431
  continue;
13810
13432
  }
@@ -13824,28 +13446,28 @@ function findJelly(repoRoot2) {
13824
13446
  try {
13825
13447
  const pkgJson = require2.resolve("@cs-au-dk/jelly/package.json");
13826
13448
  const dir = pkgJson.slice(0, pkgJson.length - "package.json".length);
13827
- const pkg = JSON.parse(readFileSync34(pkgJson, "utf8"));
13449
+ const pkg = JSON.parse(readFileSync32(pkgJson, "utf8"));
13828
13450
  const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin && (pkg.bin.jelly || pkg.bin[Object.keys(pkg.bin)[0]]);
13829
13451
  if (bin) {
13830
- const p = join35(dir, bin);
13831
- if (existsSync34(p)) return p;
13452
+ const p = join32(dir, bin);
13453
+ if (existsSync33(p)) return p;
13832
13454
  }
13833
13455
  } catch {
13834
13456
  }
13835
13457
  for (const base of [repoRoot2, process.cwd()]) {
13836
- const b = join35(base, "node_modules", ".bin", "jelly");
13837
- if (existsSync34(b)) return b;
13458
+ const b = join32(base, "node_modules", ".bin", "jelly");
13459
+ if (existsSync33(b)) return b;
13838
13460
  }
13839
13461
  return null;
13840
13462
  }
13841
13463
  function findEntries(repoRoot2) {
13842
13464
  const dirs = [repoRoot2];
13843
- const pkgsDir = join35(repoRoot2, "packages");
13844
- if (existsSync34(pkgsDir)) {
13465
+ const pkgsDir = join32(repoRoot2, "packages");
13466
+ if (existsSync33(pkgsDir)) {
13845
13467
  try {
13846
- for (const d of readdirSync9(pkgsDir)) {
13847
- const pd = join35(pkgsDir, d);
13848
- if (existsSync34(join35(pd, "package.json"))) dirs.push(pd);
13468
+ for (const d of readdirSync8(pkgsDir)) {
13469
+ const pd = join32(pkgsDir, d);
13470
+ if (existsSync33(join32(pd, "package.json"))) dirs.push(pd);
13849
13471
  }
13850
13472
  } catch {
13851
13473
  }
@@ -13853,12 +13475,12 @@ function findEntries(repoRoot2) {
13853
13475
  const entries = [];
13854
13476
  for (const dir of dirs) {
13855
13477
  try {
13856
- const pkg = JSON.parse(readFileSync34(join35(dir, "package.json"), "utf8"));
13478
+ const pkg = JSON.parse(readFileSync32(join32(dir, "package.json"), "utf8"));
13857
13479
  const cands = [pkg.source, pkg.module, pkg.main, "src/index.ts", "src/index.js", "src/main.ts", "src/server.ts", "index.ts", "index.js"];
13858
13480
  for (const c of cands) {
13859
13481
  if (typeof c !== "string") continue;
13860
- const f = join35(dir, c);
13861
- if (existsSync34(f)) {
13482
+ const f = join32(dir, c);
13483
+ if (existsSync33(f)) {
13862
13484
  entries.push(f);
13863
13485
  break;
13864
13486
  }
@@ -13891,9 +13513,9 @@ function parseApiUsage(log) {
13891
13513
  }
13892
13514
  function runReachabilityScan(repoRoot2, opts = {}) {
13893
13515
  const commit = currentCommit(repoRoot2);
13894
- if (!opts.force && commit && existsSync34(REACHABILITY_PATH)) {
13516
+ if (!opts.force && commit && existsSync33(REACHABILITY_PATH)) {
13895
13517
  try {
13896
- const prev = JSON.parse(readFileSync34(REACHABILITY_PATH, "utf8"));
13518
+ const prev = JSON.parse(readFileSync32(REACHABILITY_PATH, "utf8"));
13897
13519
  if (prev.commit === commit) return { ok: true, cached: true, packages: Object.keys(prev.packages || {}).length };
13898
13520
  } catch {
13899
13521
  }
@@ -13944,7 +13566,7 @@ function runReachabilityScan(repoRoot2, opts = {}) {
13944
13566
  if (jelly) {
13945
13567
  const entries = findEntries(repoRoot2);
13946
13568
  if (entries.length > 0) {
13947
- const r = spawnSync12(
13569
+ const r = spawnSync11(
13948
13570
  process.execPath,
13949
13571
  [jelly, "-b", repoRoot2, "--api-usage", ...entries],
13950
13572
  { encoding: "utf8", timeout: opts.timeoutMs ?? 18e4, maxBuffer: 2e8 }
@@ -13982,7 +13604,7 @@ function runReachabilityScan(repoRoot2, opts = {}) {
13982
13604
  if (Object.keys(packages).length === 0) return { ok: false, reason: "no package usage found (no jelly output, no AST imports)" };
13983
13605
  const file = { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), commit, tool, packages, versions: gatherManifestVersions(repoRoot2) };
13984
13606
  try {
13985
- writeFileSync25(REACHABILITY_PATH, JSON.stringify(file, null, 2));
13607
+ writeFileSync22(REACHABILITY_PATH, JSON.stringify(file, null, 2));
13986
13608
  } catch (e) {
13987
13609
  return { ok: false, reason: "write failed: " + String(e.message || e) };
13988
13610
  }
@@ -13994,7 +13616,7 @@ var init_reachabilityScan = __esm({
13994
13616
  "use strict";
13995
13617
  init_cveReachability();
13996
13618
  require2 = createRequire(import.meta.url);
13997
- REACHABILITY_PATH = join35(homedir35(), ".synkro", "reachability.json");
13619
+ REACHABILITY_PATH = join32(homedir32(), ".synkro", "reachability.json");
13998
13620
  }
13999
13621
  });
14000
13622
 
@@ -14003,15 +13625,15 @@ var reachabilityScan_exports = {};
14003
13625
  __export(reachabilityScan_exports, {
14004
13626
  reachabilityScanCommand: () => reachabilityScanCommand
14005
13627
  });
14006
- import { readFileSync as readFileSync35, existsSync as existsSync35 } from "fs";
14007
- import { join as join36 } from "path";
14008
- import { homedir as homedir36 } from "os";
13628
+ import { readFileSync as readFileSync33, existsSync as existsSync34 } from "fs";
13629
+ import { join as join33 } from "path";
13630
+ import { homedir as homedir33 } from "os";
14009
13631
  import { execFileSync as execFileSync6 } from "child_process";
14010
13632
  function readConfigEnv4() {
14011
- const p = join36(SYNKRO_DIR17, "config.env");
14012
- if (!existsSync35(p)) return {};
13633
+ const p = join33(SYNKRO_DIR15, "config.env");
13634
+ if (!existsSync34(p)) return {};
14013
13635
  const out = {};
14014
- for (const line of readFileSync35(p, "utf-8").split("\n")) {
13636
+ for (const line of readFileSync33(p, "utf-8").split("\n")) {
14015
13637
  const t = line.trim();
14016
13638
  if (!t || t.startsWith("#")) continue;
14017
13639
  const eq = t.indexOf("=");
@@ -14043,11 +13665,11 @@ async function pushToCloud(cfg, repo) {
14043
13665
  while (gwBase.endsWith("/")) gwBase = gwBase.slice(0, -1);
14044
13666
  let jwt2 = "";
14045
13667
  try {
14046
- jwt2 = readFileSync35(join36(SYNKRO_DIR17, ".mcp-jwt"), "utf-8").trim();
13668
+ jwt2 = readFileSync33(join33(SYNKRO_DIR15, ".mcp-jwt"), "utf-8").trim();
14047
13669
  } catch {
14048
13670
  }
14049
- if (!jwt2 || !existsSync35(REACHABILITY_PATH)) return;
14050
- const body = readFileSync35(REACHABILITY_PATH, "utf-8");
13671
+ if (!jwt2 || !existsSync34(REACHABILITY_PATH)) return;
13672
+ const body = readFileSync33(REACHABILITY_PATH, "utf-8");
14051
13673
  try {
14052
13674
  const resp = await fetch(gwBase + "/api/v1/reachability?repo=" + encodeURIComponent(repo), {
14053
13675
  method: "POST",
@@ -14074,12 +13696,12 @@ async function reachabilityScanCommand(args2 = []) {
14074
13696
  const isCloud = cfg.SYNKRO_DEPLOY_LOCATION === "cloud" || cfg.SYNKRO_STORAGE_MODE === "cloud";
14075
13697
  if (isCloud) await pushToCloud(cfg, cfg.SYNKRO_CONNECTED_REPO || repoSlug(root));
14076
13698
  }
14077
- var SYNKRO_DIR17;
13699
+ var SYNKRO_DIR15;
14078
13700
  var init_reachabilityScan2 = __esm({
14079
13701
  "cli/commands/reachabilityScan.ts"() {
14080
13702
  "use strict";
14081
13703
  init_reachabilityScan();
14082
- SYNKRO_DIR17 = join36(homedir36(), ".synkro");
13704
+ SYNKRO_DIR15 = join33(homedir33(), ".synkro");
14083
13705
  }
14084
13706
  });
14085
13707
 
@@ -14209,13 +13831,13 @@ var config_exports = {};
14209
13831
  __export(config_exports, {
14210
13832
  configCommand: () => configCommand
14211
13833
  });
14212
- import { readFileSync as readFileSync36, writeFileSync as writeFileSync26, existsSync as existsSync36 } from "fs";
14213
- import { join as join37 } from "path";
14214
- import { homedir as homedir37 } from "os";
13834
+ import { readFileSync as readFileSync34, writeFileSync as writeFileSync23, existsSync as existsSync35 } from "fs";
13835
+ import { join as join34 } from "path";
13836
+ import { homedir as homedir34 } from "os";
14215
13837
  function readConfigEnv5() {
14216
- if (!existsSync36(CONFIG_PATH9)) return {};
13838
+ if (!existsSync35(CONFIG_PATH9)) return {};
14217
13839
  const out = {};
14218
- for (const line of readFileSync36(CONFIG_PATH9, "utf-8").split("\n")) {
13840
+ for (const line of readFileSync34(CONFIG_PATH9, "utf-8").split("\n")) {
14219
13841
  const t = line.trim();
14220
13842
  if (!t || t.startsWith("#")) continue;
14221
13843
  const eq = t.indexOf("=");
@@ -14224,11 +13846,11 @@ function readConfigEnv5() {
14224
13846
  return out;
14225
13847
  }
14226
13848
  function updateConfigValue(key, value) {
14227
- if (!existsSync36(CONFIG_PATH9)) {
13849
+ if (!existsSync35(CONFIG_PATH9)) {
14228
13850
  console.error("No config found. Run `synkro install` first.");
14229
13851
  process.exit(1);
14230
13852
  }
14231
- const lines = readFileSync36(CONFIG_PATH9, "utf-8").split("\n");
13853
+ const lines = readFileSync34(CONFIG_PATH9, "utf-8").split("\n");
14232
13854
  const pattern = new RegExp(`^${key}=`);
14233
13855
  let found = false;
14234
13856
  const updated = lines.map((line) => {
@@ -14239,7 +13861,7 @@ function updateConfigValue(key, value) {
14239
13861
  return line;
14240
13862
  });
14241
13863
  if (!found) updated.splice(updated.length - 1, 0, `${key}='${value}'`);
14242
- writeFileSync26(CONFIG_PATH9, updated.join("\n"), "utf-8");
13864
+ writeFileSync23(CONFIG_PATH9, updated.join("\n"), "utf-8");
14243
13865
  }
14244
13866
  function resolveInferenceMode(cfg) {
14245
13867
  if ((cfg.SYNKRO_GRADING_MODE || "local") === "byok") return "byok";
@@ -14391,14 +14013,14 @@ To change:`);
14391
14013
  }
14392
14014
  if (inferenceValue !== "cloud") await reconcileContainer();
14393
14015
  }
14394
- var SYNKRO_DIR18, CONFIG_PATH9;
14016
+ var SYNKRO_DIR16, CONFIG_PATH9;
14395
14017
  var init_config = __esm({
14396
14018
  "cli/commands/config.ts"() {
14397
14019
  "use strict";
14398
14020
  init_stub();
14399
14021
  init_optout();
14400
- SYNKRO_DIR18 = join37(homedir37(), ".synkro");
14401
- CONFIG_PATH9 = join37(SYNKRO_DIR18, "config.env");
14022
+ SYNKRO_DIR16 = join34(homedir34(), ".synkro");
14023
+ CONFIG_PATH9 = join34(SYNKRO_DIR16, "config.env");
14402
14024
  }
14403
14025
  });
14404
14026
 
@@ -14587,14 +14209,14 @@ Usage:
14587
14209
  });
14588
14210
 
14589
14211
  // cli/bootstrap.js
14590
- import { readFileSync as readFileSync37, existsSync as existsSync37 } from "fs";
14212
+ import { readFileSync as readFileSync35, existsSync as existsSync36 } from "fs";
14591
14213
  import { resolve as resolve5 } from "path";
14592
14214
  var envCandidates = [
14593
14215
  resolve5(process.env.HOME ?? "", ".synkro", "config.env")
14594
14216
  ];
14595
14217
  for (const envPath of envCandidates) {
14596
- if (!existsSync37(envPath)) continue;
14597
- const envContent = readFileSync37(envPath, "utf-8");
14218
+ if (!existsSync36(envPath)) continue;
14219
+ const envContent = readFileSync35(envPath, "utf-8");
14598
14220
  for (const line of envContent.split("\n")) {
14599
14221
  const trimmed = line.trim();
14600
14222
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -14611,7 +14233,7 @@ var subArgs = args.slice(1);
14611
14233
  var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
14612
14234
  var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "version", "--version", "-v", "help", "--help", "-h", ""]);
14613
14235
  function printVersion() {
14614
- console.log("1.7.92");
14236
+ console.log("1.7.93");
14615
14237
  }
14616
14238
  function printHelp2() {
14617
14239
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
@@ -14703,79 +14325,17 @@ async function main() {
14703
14325
  await scanPrCommand2();
14704
14326
  break;
14705
14327
  }
14706
- case "route": {
14707
- const { injectModel: injectModel2 } = await Promise.resolve().then(() => (init_ptyShim(), ptyShim_exports));
14708
- const model = (subArgs[0] || "").trim();
14709
- const sessionOverride = (subArgs[1] || "").trim() || void 0;
14710
- if (model) {
14711
- try {
14712
- injectModel2(model, sessionOverride);
14713
- } catch {
14714
- }
14715
- }
14716
- break;
14717
- }
14718
- case "route-decide": {
14719
- const { routeDecide: routeDecide2 } = await Promise.resolve().then(() => (init_routeDecide(), routeDecide_exports));
14720
- const sid = (subArgs[0] || "").trim();
14721
- if (sid) {
14722
- try {
14723
- await routeDecide2(sid);
14724
- } catch {
14725
- }
14726
- }
14727
- break;
14728
- }
14729
- case "route-and-resubmit": {
14730
- const { routeAndResubmit: routeAndResubmit2 } = await Promise.resolve().then(() => (init_routeOrchestrate(), routeOrchestrate_exports));
14731
- const sid = (subArgs[0] || "").trim();
14732
- const task = (subArgs[1] || "").trim();
14733
- const tmuxSession = (subArgs[2] || "").trim() || void 0;
14734
- const forceModel = (subArgs[3] || "").trim() || void 0;
14735
- if (sid && task) {
14736
- try {
14737
- await routeAndResubmit2(sid, task, tmuxSession, forceModel);
14738
- } catch {
14739
- }
14740
- }
14741
- break;
14742
- }
14743
- case "routing": {
14744
- const { routingCommand: routingCommand2 } = await Promise.resolve().then(() => (init_routingToggle(), routingToggle_exports));
14745
- routingCommand2(subArgs);
14746
- break;
14747
- }
14328
+ case "route":
14329
+ case "route-decide":
14330
+ case "route-and-resubmit":
14331
+ case "routing":
14748
14332
  case "sessions": {
14749
- const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_ptyShim(), ptyShim_exports));
14750
- const rows = listSessions2({ liveOnly: !subArgs.includes("--all") });
14751
- if (rows.length === 0) {
14752
- console.log("No active Synkro-wrapped Claude Code sessions.");
14753
- console.log("(Start one with `claude`; it registers on your first prompt.)");
14754
- break;
14755
- }
14756
- const now = Date.now();
14757
- const age = (ts) => {
14758
- const s = Math.max(0, Math.round((now - (ts || 0)) / 1e3));
14759
- if (s < 60) return `${s}s`;
14760
- if (s < 3600) return `${Math.round(s / 60)}m`;
14761
- return `${Math.round(s / 3600)}h`;
14762
- };
14763
- console.log(`${rows.length} session${rows.length === 1 ? "" : "s"}:
14764
- `);
14765
- for (const r of rows) {
14766
- console.log(` ${r.session_id} (${age(r.ts)} ago)`);
14767
- if (r.cwd) console.log(` cwd: ${r.cwd}`);
14768
- if (r.last_prompt) console.log(` prompt: ${r.last_prompt.replace(/\s+/g, " ").slice(0, 80)}`);
14769
- console.log(` route: echo <model> > ~/.synkro/pty/route-${r.session_id}`);
14770
- console.log();
14771
- }
14333
+ console.error("Synkro host model routing has been removed.");
14772
14334
  break;
14773
14335
  }
14774
14336
  case "pty-shim": {
14775
- const { installPtyShim: installPtyShim2, uninstallPtyShim: uninstallPtyShim2 } = await Promise.resolve().then(() => (init_ptyShim(), ptyShim_exports));
14776
- const sub = (subArgs[0] || "install").trim();
14777
- if (sub === "uninstall" || sub === "remove") uninstallPtyShim2();
14778
- else installPtyShim2();
14337
+ const { uninstallPtyShim: uninstallPtyShim2 } = await Promise.resolve().then(() => (init_ptyShim(), ptyShim_exports));
14338
+ uninstallPtyShim2();
14779
14339
  break;
14780
14340
  }
14781
14341
  case "local-cc": {