@synkro-sh/cli 1.3.17 → 1.3.19

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
@@ -102,7 +102,7 @@ function removeSynkroEntries(events, eventName) {
102
102
  if (!Array.isArray(arr)) return;
103
103
  events[eventName] = arr.filter((entry) => !isSynkroEntry(entry));
104
104
  }
105
- function installCCHooks(settingsPath, config2) {
105
+ function installCCHooks(settingsPath, config) {
106
106
  const settings = readSettings(settingsPath);
107
107
  settings.hooks = settings.hooks ?? {};
108
108
  removeSynkroEntries(settings.hooks, "PreToolUse");
@@ -119,7 +119,7 @@ function installCCHooks(settingsPath, config2) {
119
119
  hooks: [
120
120
  {
121
121
  type: "command",
122
- command: config2.bashJudgeScriptPath,
122
+ command: config.bashJudgeScriptPath,
123
123
  timeout: 15
124
124
  }
125
125
  ],
@@ -130,7 +130,7 @@ function installCCHooks(settingsPath, config2) {
130
130
  hooks: [
131
131
  {
132
132
  type: "command",
133
- command: config2.editPrecheckScriptPath,
133
+ command: config.editPrecheckScriptPath,
134
134
  timeout: 15
135
135
  }
136
136
  ],
@@ -141,7 +141,7 @@ function installCCHooks(settingsPath, config2) {
141
141
  hooks: [
142
142
  {
143
143
  type: "command",
144
- command: config2.editCaptureScriptPath,
144
+ command: config.editCaptureScriptPath,
145
145
  timeout: 20
146
146
  }
147
147
  ],
@@ -152,7 +152,7 @@ function installCCHooks(settingsPath, config2) {
152
152
  hooks: [
153
153
  {
154
154
  type: "command",
155
- command: config2.bashFollowupScriptPath
155
+ command: config.bashFollowupScriptPath
156
156
  }
157
157
  ],
158
158
  [SYNKRO_MARKER]: true
@@ -161,7 +161,7 @@ function installCCHooks(settingsPath, config2) {
161
161
  hooks: [
162
162
  {
163
163
  type: "command",
164
- command: config2.stopSummaryScriptPath
164
+ command: config.stopSummaryScriptPath
165
165
  }
166
166
  ],
167
167
  [SYNKRO_MARKER]: true
@@ -170,19 +170,19 @@ function installCCHooks(settingsPath, config2) {
170
170
  hooks: [
171
171
  {
172
172
  type: "command",
173
- command: config2.sessionStartScriptPath
173
+ command: config.sessionStartScriptPath
174
174
  }
175
175
  ],
176
176
  [SYNKRO_MARKER]: true
177
177
  });
178
- if (!config2.skipTranscriptSync) {
178
+ if (!config.skipTranscriptSync) {
179
179
  settings.hooks.Stop = settings.hooks.Stop ?? [];
180
180
  removeSynkroEntries(settings.hooks, "Stop");
181
181
  settings.hooks.Stop.push({
182
182
  hooks: [
183
183
  {
184
184
  type: "command",
185
- command: config2.transcriptSyncScriptPath,
185
+ command: config.transcriptSyncScriptPath,
186
186
  timeout: 3
187
187
  }
188
188
  ],
@@ -252,50 +252,50 @@ function readClaudeJson() {
252
252
  throw new Error(`Failed to parse ${CC_CONFIG_PATH}: ${err.message}`);
253
253
  }
254
254
  }
255
- function writeClaudeJsonAtomic(config2) {
255
+ function writeClaudeJsonAtomic(config) {
256
256
  mkdirSync2(dirname2(CC_CONFIG_PATH), { recursive: true });
257
257
  const tmpPath = `${CC_CONFIG_PATH}.synkro.tmp`;
258
- writeFileSync2(tmpPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
258
+ writeFileSync2(tmpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
259
259
  renameSync2(tmpPath, CC_CONFIG_PATH);
260
260
  }
261
261
  function installMcpConfig(opts) {
262
- const config2 = readClaudeJson();
263
- config2.mcpServers = config2.mcpServers ?? {};
264
- for (const [name, entry] of Object.entries(config2.mcpServers)) {
265
- if (entry?.[SYNKRO_MARKER2] === true) delete config2.mcpServers[name];
262
+ const config = readClaudeJson();
263
+ config.mcpServers = config.mcpServers ?? {};
264
+ for (const [name, entry] of Object.entries(config.mcpServers)) {
265
+ if (entry?.[SYNKRO_MARKER2] === true) delete config.mcpServers[name];
266
266
  }
267
267
  const url = `${opts.gatewayUrl.replace(/\/$/, "")}/api/v1/mcp/guardrails`;
268
- config2.mcpServers[SYNKRO_SERVER_NAME] = {
268
+ config.mcpServers[SYNKRO_SERVER_NAME] = {
269
269
  type: "http",
270
270
  url,
271
271
  headers: { Authorization: `Bearer ${opts.bearerToken}` },
272
272
  [SYNKRO_MARKER2]: true
273
273
  };
274
- writeClaudeJsonAtomic(config2);
274
+ writeClaudeJsonAtomic(config);
275
275
  return { path: CC_CONFIG_PATH, url };
276
276
  }
277
277
  function uninstallMcpConfig() {
278
278
  if (!existsSync3(CC_CONFIG_PATH)) return false;
279
- const config2 = readClaudeJson();
280
- if (!config2.mcpServers || Object.keys(config2.mcpServers).length === 0) return false;
279
+ const config = readClaudeJson();
280
+ if (!config.mcpServers || Object.keys(config.mcpServers).length === 0) return false;
281
281
  let removed = false;
282
- for (const [name, entry] of Object.entries(config2.mcpServers)) {
282
+ for (const [name, entry] of Object.entries(config.mcpServers)) {
283
283
  if (entry?.[SYNKRO_MARKER2] === true) {
284
- delete config2.mcpServers[name];
284
+ delete config.mcpServers[name];
285
285
  removed = true;
286
286
  }
287
287
  }
288
288
  if (!removed) return false;
289
- if (Object.keys(config2.mcpServers).length === 0) delete config2.mcpServers;
290
- writeClaudeJsonAtomic(config2);
289
+ if (Object.keys(config.mcpServers).length === 0) delete config.mcpServers;
290
+ writeClaudeJsonAtomic(config);
291
291
  return true;
292
292
  }
293
293
  function inspectMcpConfig() {
294
294
  if (!existsSync3(CC_CONFIG_PATH)) {
295
295
  return { installed: false, configPath: CC_CONFIG_PATH };
296
296
  }
297
- const config2 = readClaudeJson();
298
- const entry = config2.mcpServers?.[SYNKRO_SERVER_NAME];
297
+ const config = readClaudeJson();
298
+ const entry = config.mcpServers?.[SYNKRO_SERVER_NAME];
299
299
  if (!entry || entry[SYNKRO_MARKER2] !== true) {
300
300
  return { installed: false, configPath: CC_CONFIG_PATH };
301
301
  }
@@ -2440,10 +2440,12 @@ var init_auth = __esm({
2440
2440
  });
2441
2441
 
2442
2442
  // cli/api/projects.ts
2443
- import { config } from "dotenv";
2443
+ function setApiBaseUrl(url) {
2444
+ API_URL = url;
2445
+ }
2444
2446
  async function callApi(method, endpoint, body) {
2445
2447
  if (!API_URL) {
2446
- throw new Error("SYNKRO_CRUD_URL (or SYNKRO_API_URL) is not set. Add it to your .env file.");
2448
+ throw new Error("API URL not configured. Run `synkro install` first.");
2447
2449
  }
2448
2450
  const url = `${API_URL}${endpoint}`;
2449
2451
  const accessToken = getAccessToken();
@@ -2480,8 +2482,7 @@ var init_projects = __esm({
2480
2482
  "cli/api/projects.ts"() {
2481
2483
  "use strict";
2482
2484
  init_auth();
2483
- config({ quiet: true });
2484
- API_URL = process.env.SYNKRO_CRUD_URL || process.env.SYNKRO_API_URL;
2485
+ API_URL = "https://api.synkro.sh/api";
2485
2486
  }
2486
2487
  });
2487
2488
 
@@ -2935,8 +2936,8 @@ async function setupGithubCommand(opts = {}) {
2935
2936
  console.error("Not authenticated. Run `synkro-cli login` first.");
2936
2937
  process.exit(1);
2937
2938
  }
2938
- const config2 = readConfig();
2939
- const gatewayUrl = (config2.SYNKRO_GATEWAY_URL || process.env.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/\/$/, "");
2939
+ const config = readConfig();
2940
+ const gatewayUrl = (config.SYNKRO_GATEWAY_URL || process.env.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/\/$/, "");
2940
2941
  const jwt2 = getAccessToken();
2941
2942
  if (!jwt2) {
2942
2943
  console.error("Could not load access token from ~/.synkro/credentials.json. Run `synkro-cli login`.");
@@ -3223,6 +3224,7 @@ function writeConfigEnv(opts) {
3223
3224
  const safeOrgId = sanitizeConfigValue(opts.orgId);
3224
3225
  const safeEmail = sanitizeConfigValue(opts.email);
3225
3226
  const safeTier = sanitizeConfigValue(opts.tier ?? "pro", 32);
3227
+ const safeInference = sanitizeConfigValue(opts.inference ?? "fast", 16);
3226
3228
  const lines = [
3227
3229
  "# Synkro CLI config (managed by synkro install)",
3228
3230
  "# JWT auth \u2014 the hook scripts read SYNKRO_CREDENTIALS_PATH at runtime",
@@ -3230,7 +3232,8 @@ function writeConfigEnv(opts) {
3230
3232
  `SYNKRO_GATEWAY_URL=${shellQuoteSingle(safeGateway)}`,
3231
3233
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3232
3234
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3233
- `SYNKRO_VERSION=${shellQuoteSingle("1.3.17")}`
3235
+ `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
3236
+ `SYNKRO_VERSION=${shellQuoteSingle("1.3.19")}`
3234
3237
  ];
3235
3238
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
3236
3239
  if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);
@@ -3242,6 +3245,42 @@ function writeConfigEnv(opts) {
3242
3245
  writeFileSync5(CONFIG_PATH2, lines.join("\n"), "utf-8");
3243
3246
  chmodSync(CONFIG_PATH2, 384);
3244
3247
  }
3248
+ function collectLocalMetadata() {
3249
+ const meta = { platform: process.platform };
3250
+ try {
3251
+ meta.display_name = execSync4("git config user.name", { encoding: "utf-8", timeout: 3e3 }).trim();
3252
+ } catch {
3253
+ }
3254
+ try {
3255
+ const remote = execSync4("git remote get-url origin", { encoding: "utf-8", timeout: 3e3 }).trim();
3256
+ const m = remote.match(/(?:github\.com)[:/](.+?)(?:\.git)?$/);
3257
+ if (m) meta.active_repo = m[1];
3258
+ } catch {
3259
+ }
3260
+ return meta;
3261
+ }
3262
+ async function fetchUserProfile(gatewayUrl, token) {
3263
+ try {
3264
+ const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {
3265
+ headers: { "Authorization": `Bearer ${token}` }
3266
+ });
3267
+ if (!resp.ok) return { tier: "pro", inference: "fast" };
3268
+ const data = await resp.json();
3269
+ const meta = collectLocalMetadata();
3270
+ fetch(`${gatewayUrl}/api/v1/cli/me`, {
3271
+ method: "PATCH",
3272
+ headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
3273
+ body: JSON.stringify(meta)
3274
+ }).catch(() => {
3275
+ });
3276
+ return {
3277
+ tier: data.plan_tier ?? "pro",
3278
+ inference: data.fast_inference ? "fast" : "standard"
3279
+ };
3280
+ } catch {
3281
+ return { tier: "pro", inference: "fast" };
3282
+ }
3283
+ }
3245
3284
  function assertGatewayAllowed(gatewayUrl) {
3246
3285
  let parsed;
3247
3286
  try {
@@ -3299,6 +3338,24 @@ async function installCommand(opts = {}) {
3299
3338
  process.exit(1);
3300
3339
  }
3301
3340
  if (!opts.force && isAuthenticated() && isAlreadyInstalled()) {
3341
+ setApiBaseUrl(`${gatewayUrl}/api`);
3342
+ await ensureValidToken();
3343
+ const currentRepo = detectGitRepo2();
3344
+ if (currentRepo) {
3345
+ try {
3346
+ const projects = await listProjects();
3347
+ const alreadyLinked = projects.some(
3348
+ (p) => p.repos?.some((r) => r.full_name === currentRepo)
3349
+ );
3350
+ if (!alreadyLinked) {
3351
+ console.log(`Synkro is installed. This repo (${currentRepo}) is not linked yet.
3352
+ `);
3353
+ await promptRepoConnection();
3354
+ return;
3355
+ }
3356
+ } catch {
3357
+ }
3358
+ }
3302
3359
  console.log("\u2713 Synkro is already installed and configured.");
3303
3360
  console.log(" Run `synkro-cli update` to refresh hook scripts and judge prompts.");
3304
3361
  console.log(" Run `synkro-cli install --force` to reinstall from scratch.");
@@ -3336,6 +3393,7 @@ async function installCommand(opts = {}) {
3336
3393
  console.error("No access token available after auth.");
3337
3394
  process.exit(1);
3338
3395
  }
3396
+ setApiBaseUrl(`${gatewayUrl}/api`);
3339
3397
  await promptRepoConnection({ linkRepo: opts.linkRepo });
3340
3398
  const agents = detectAgents();
3341
3399
  if (agents.length === 0) {
@@ -3439,8 +3497,10 @@ async function installCommand(opts = {}) {
3439
3497
  email = info.email;
3440
3498
  } catch {
3441
3499
  }
3442
- writeConfigEnv({ gatewayUrl, userId, orgId, email, transcriptConsent });
3443
- console.log(`Wrote config to ${CONFIG_PATH2}
3500
+ const profile = await fetchUserProfile(gatewayUrl, token);
3501
+ writeConfigEnv({ gatewayUrl, userId, orgId, email, tier: profile.tier, inference: profile.inference, transcriptConsent });
3502
+ console.log(`Wrote config to ${CONFIG_PATH2}`);
3503
+ console.log(` inference: ${profile.inference} (server-side grading)
3444
3504
  `);
3445
3505
  if (transcriptConsent) {
3446
3506
  try {
@@ -3683,6 +3743,7 @@ var init_install = __esm({
3683
3743
  init_graderDaemon();
3684
3744
  init_stub();
3685
3745
  init_repoConnect();
3746
+ init_projects();
3686
3747
  SYNKRO_DIR2 = join6(homedir5(), ".synkro");
3687
3748
  HOOKS_DIR = join6(SYNKRO_DIR2, "hooks");
3688
3749
  BIN_DIR = join6(SYNKRO_DIR2, "bin");
@@ -3785,7 +3846,7 @@ function readConfigEnv() {
3785
3846
  }
3786
3847
  return out;
3787
3848
  }
3788
- function statusCommand() {
3849
+ async function statusCommand() {
3789
3850
  console.log("Synkro CLI status\n");
3790
3851
  if (isAuthenticated()) {
3791
3852
  const info = getUserInfo();
@@ -3796,21 +3857,32 @@ function statusCommand() {
3796
3857
  console.log("Authentication: \u2717 not logged in (run: synkro-cli login)");
3797
3858
  }
3798
3859
  console.log();
3799
- const config2 = readConfigEnv();
3860
+ const config = readConfigEnv();
3861
+ const gatewayUrl = (config.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/^['"]|['"]$/g, "");
3862
+ let serverTier = config.SYNKRO_TIER || "(unset)";
3863
+ let serverInference = config.SYNKRO_INFERENCE || "fast";
3864
+ const token = getAccessToken();
3865
+ if (token) {
3866
+ try {
3867
+ const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {
3868
+ headers: { "Authorization": `Bearer ${token}` },
3869
+ signal: AbortSignal.timeout(5e3)
3870
+ });
3871
+ if (resp.ok) {
3872
+ const data = await resp.json();
3873
+ serverInference = data.fast_inference ? "fast" : "standard";
3874
+ serverTier = data.plan_tier ?? data.tier ?? serverTier;
3875
+ }
3876
+ } catch {
3877
+ }
3878
+ }
3800
3879
  console.log("Config:");
3801
- console.log(` gateway: ${config2.SYNKRO_GATEWAY_URL ?? "(unset)"}`);
3802
- console.log(` credentials: ${config2.SYNKRO_CREDENTIALS_PATH ?? "(unset)"}`);
3803
- console.log(` tier: ${config2.SYNKRO_TIER ?? "(unset)"}`);
3804
- const info2 = getUserInfo();
3805
- const userId = info2?.id ?? config2.SYNKRO_USER_ID ?? "default";
3806
- const tierCacheFile = join7(SYNKRO_DIR3, `.tier-cache-${userId}`);
3807
- let inferenceTier = config2.SYNKRO_INFERENCE_TIER || null;
3808
- if (!inferenceTier && existsSync8(tierCacheFile)) {
3809
- inferenceTier = readFileSync6(tierCacheFile, "utf-8").trim() || null;
3810
- }
3811
- const tierLabel = inferenceTier === "fast" ? "'fast' (server-side grading)" : inferenceTier === "free" ? "'free' (local daemon grading)" : "(unknown \u2014 fires on next hook)";
3812
- console.log(` inference: ${tierLabel}`);
3813
- console.log(` version: ${config2.SYNKRO_VERSION ?? "(unset)"}`);
3880
+ console.log(` gateway: ${gatewayUrl}`);
3881
+ console.log(` credentials: ${config.SYNKRO_CREDENTIALS_PATH ?? "(unset)"}`);
3882
+ console.log(` tier: ${serverTier}`);
3883
+ const inferenceLabel = serverInference === "fast" ? "'fast' (server-side grading)" : "'standard' (local grading)";
3884
+ console.log(` inference: ${inferenceLabel}`);
3885
+ console.log(` version: ${config.SYNKRO_VERSION ?? "(unset)"}`);
3814
3886
  console.log();
3815
3887
  const agents = detectAgents();
3816
3888
  console.log("Detected agents:");
@@ -3950,6 +4022,102 @@ var init_unlink = __esm({
3950
4022
  }
3951
4023
  });
3952
4024
 
4025
+ // cli/commands/config.ts
4026
+ var config_exports = {};
4027
+ __export(config_exports, {
4028
+ configCommand: () => configCommand
4029
+ });
4030
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
4031
+ import { join as join8 } from "path";
4032
+ import { homedir as homedir7 } from "os";
4033
+ function readConfigEnv2() {
4034
+ if (!existsSync9(CONFIG_PATH4)) return {};
4035
+ const out = {};
4036
+ for (const line of readFileSync7(CONFIG_PATH4, "utf-8").split("\n")) {
4037
+ const t = line.trim();
4038
+ if (!t || t.startsWith("#")) continue;
4039
+ const eq = t.indexOf("=");
4040
+ if (eq > 0) out[t.slice(0, eq).trim()] = t.slice(eq + 1).trim().replace(/^['"]|['"]$/g, "");
4041
+ }
4042
+ return out;
4043
+ }
4044
+ function updateConfigValue(key, value) {
4045
+ if (!existsSync9(CONFIG_PATH4)) {
4046
+ console.error("No config found. Run `synkro install` first.");
4047
+ process.exit(1);
4048
+ }
4049
+ const lines = readFileSync7(CONFIG_PATH4, "utf-8").split("\n");
4050
+ const pattern = new RegExp(`^${key}=`);
4051
+ let found = false;
4052
+ const updated = lines.map((line) => {
4053
+ if (pattern.test(line.trim())) {
4054
+ found = true;
4055
+ return `${key}='${value}'`;
4056
+ }
4057
+ return line;
4058
+ });
4059
+ if (!found) updated.splice(updated.length - 1, 0, `${key}='${value}'`);
4060
+ writeFileSync6(CONFIG_PATH4, updated.join("\n"), "utf-8");
4061
+ }
4062
+ async function configCommand(args2) {
4063
+ if (args2.length === 0) {
4064
+ const config2 = readConfigEnv2();
4065
+ console.log("Synkro config:\n");
4066
+ console.log(` inference: ${config2.SYNKRO_INFERENCE || "fast"}`);
4067
+ console.log(` tier: ${config2.SYNKRO_TIER || "pro"}`);
4068
+ console.log(` gateway: ${config2.SYNKRO_GATEWAY_URL || "https://api.synkro.sh"}`);
4069
+ console.log(` version: ${config2.SYNKRO_VERSION || "?"}`);
4070
+ console.log(`
4071
+ To change: synkro config --inference fast|standard`);
4072
+ return;
4073
+ }
4074
+ let inferenceValue;
4075
+ for (const a of args2) {
4076
+ if (a.startsWith("--inference=")) inferenceValue = a.slice("--inference=".length);
4077
+ else if (a === "--inference" && args2.indexOf(a) + 1 < args2.length) inferenceValue = args2[args2.indexOf(a) + 1];
4078
+ }
4079
+ if (!inferenceValue || !["fast", "standard"].includes(inferenceValue)) {
4080
+ console.error("Usage: synkro config --inference fast|standard");
4081
+ process.exit(1);
4082
+ }
4083
+ if (!isAuthenticated()) {
4084
+ console.error("Not authenticated. Run `synkro login` first.");
4085
+ process.exit(1);
4086
+ }
4087
+ const token = getAccessToken();
4088
+ const config = readConfigEnv2();
4089
+ const gatewayUrl = (config.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/\/$/, "");
4090
+ try {
4091
+ const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {
4092
+ method: "PATCH",
4093
+ headers: {
4094
+ "Authorization": `Bearer ${token}`,
4095
+ "Content-Type": "application/json"
4096
+ },
4097
+ body: JSON.stringify({ fast_inference: inferenceValue === "fast" })
4098
+ });
4099
+ if (!resp.ok) {
4100
+ const errText = await resp.text().catch(() => "");
4101
+ console.error(`Failed to update: ${resp.status} ${errText.slice(0, 200)}`);
4102
+ process.exit(1);
4103
+ }
4104
+ } catch (err) {
4105
+ console.error(`Failed to reach server: ${err.message}`);
4106
+ process.exit(1);
4107
+ }
4108
+ updateConfigValue("SYNKRO_INFERENCE", inferenceValue);
4109
+ console.log(`\u2713 Inference set to '${inferenceValue}'.`);
4110
+ }
4111
+ var SYNKRO_DIR4, CONFIG_PATH4;
4112
+ var init_config = __esm({
4113
+ "cli/commands/config.ts"() {
4114
+ "use strict";
4115
+ init_stub();
4116
+ SYNKRO_DIR4 = join8(homedir7(), ".synkro");
4117
+ CONFIG_PATH4 = join8(SYNKRO_DIR4, "config.env");
4118
+ }
4119
+ });
4120
+
3953
4121
  // cli/commands/scanPr.ts
3954
4122
  var scanPr_exports = {};
3955
4123
  __export(scanPr_exports, {
@@ -4542,9 +4710,9 @@ var disconnect_exports = {};
4542
4710
  __export(disconnect_exports, {
4543
4711
  disconnectCommand: () => disconnectCommand
4544
4712
  });
4545
- import { existsSync as existsSync9, rmSync } from "fs";
4546
- import { homedir as homedir7 } from "os";
4547
- import { join as join8 } from "path";
4713
+ import { existsSync as existsSync10, rmSync } from "fs";
4714
+ import { homedir as homedir8 } from "os";
4715
+ import { join as join9 } from "path";
4548
4716
  function disconnectCommand(args2 = []) {
4549
4717
  const purge = args2.includes("--purge");
4550
4718
  console.log("Synkro disconnect starting...\n");
@@ -4562,25 +4730,25 @@ function disconnectCommand(args2 = []) {
4562
4730
  console.log(`${mcpRemoved ? "\u2713" : "\xB7"} MCP guardrails server: ${mcpRemoved ? "removed entry from ~/.claude.json" : "no Synkro MCP entry found"}`);
4563
4731
  }
4564
4732
  if (purge) {
4565
- if (existsSync9(SYNKRO_DIR4)) {
4566
- rmSync(SYNKRO_DIR4, { recursive: true, force: true });
4567
- console.log(`\u2713 Removed ${SYNKRO_DIR4}`);
4733
+ if (existsSync10(SYNKRO_DIR5)) {
4734
+ rmSync(SYNKRO_DIR5, { recursive: true, force: true });
4735
+ console.log(`\u2713 Removed ${SYNKRO_DIR5}`);
4568
4736
  } else {
4569
- console.log(`\xB7 ${SYNKRO_DIR4} already gone, nothing to remove`);
4737
+ console.log(`\xB7 ${SYNKRO_DIR5} already gone, nothing to remove`);
4570
4738
  }
4571
- } else if (existsSync9(SYNKRO_DIR4)) {
4572
- console.log(`Config preserved at ${SYNKRO_DIR4}. Run with --purge to remove.`);
4739
+ } else if (existsSync10(SYNKRO_DIR5)) {
4740
+ console.log(`Config preserved at ${SYNKRO_DIR5}. Run with --purge to remove.`);
4573
4741
  }
4574
4742
  console.log("\nSynkro disconnected.");
4575
4743
  }
4576
- var SYNKRO_DIR4;
4744
+ var SYNKRO_DIR5;
4577
4745
  var init_disconnect = __esm({
4578
4746
  "cli/commands/disconnect.ts"() {
4579
4747
  "use strict";
4580
4748
  init_agentDetect();
4581
4749
  init_ccHookConfig();
4582
4750
  init_mcpConfig();
4583
- SYNKRO_DIR4 = join8(homedir7(), ".synkro");
4751
+ SYNKRO_DIR5 = join9(homedir8(), ".synkro");
4584
4752
  }
4585
4753
  });
4586
4754
 
@@ -4622,15 +4790,15 @@ var init_reinstall = __esm({
4622
4790
  });
4623
4791
 
4624
4792
  // cli/bootstrap.js
4625
- import { readFileSync as readFileSync7, existsSync as existsSync10 } from "fs";
4793
+ import { readFileSync as readFileSync8, existsSync as existsSync11 } from "fs";
4626
4794
  import { resolve } from "path";
4627
4795
  var envCandidates = [
4628
4796
  resolve(process.cwd(), ".env"),
4629
4797
  resolve(process.env.HOME ?? "", ".synkro", "config.env")
4630
4798
  ];
4631
4799
  for (const envPath of envCandidates) {
4632
- if (!existsSync10(envPath)) continue;
4633
- const envContent = readFileSync7(envPath, "utf-8");
4800
+ if (!existsSync11(envPath)) continue;
4801
+ const envContent = readFileSync8(envPath, "utf-8");
4634
4802
  for (const line of envContent.split("\n")) {
4635
4803
  const trimmed = line.trim();
4636
4804
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -4658,6 +4826,7 @@ Commands:
4658
4826
  status Show current setup state
4659
4827
  link Link repos to a Synkro project (local git or GitHub OAuth)
4660
4828
  unlink Remove repo links from Synkro projects
4829
+ config View or change settings (e.g. --inference fast|standard)
4661
4830
  setup-github Configure GitHub PR scanning (push secrets + workflow file)
4662
4831
  update Refresh hook configs and judge prompts
4663
4832
  disconnect [--purge] Remove Synkro hooks from agents (--purge also removes ~/.synkro)
@@ -4692,7 +4861,7 @@ async function main() {
4692
4861
  }
4693
4862
  case "status": {
4694
4863
  const { statusCommand: statusCommand2 } = await Promise.resolve().then(() => (init_status(), status_exports));
4695
- statusCommand2();
4864
+ await statusCommand2();
4696
4865
  break;
4697
4866
  }
4698
4867
  case "link": {
@@ -4705,6 +4874,11 @@ async function main() {
4705
4874
  await unlinkCommand2();
4706
4875
  break;
4707
4876
  }
4877
+ case "config": {
4878
+ const { configCommand: configCommand2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4879
+ await configCommand2(subArgs);
4880
+ break;
4881
+ }
4708
4882
  case "setup-github": {
4709
4883
  const { setupGithubCommand: setupGithubCommand2 } = await Promise.resolve().then(() => (init_setupGithub(), setupGithub_exports));
4710
4884
  const ghOpts = {};