@snapback/cli 1.0.0

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.
@@ -0,0 +1,906 @@
1
+ import { __name } from './chunk-WCQVDF3K.js';
2
+ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
3
+ import { homedir, platform } from 'os';
4
+ import { resolve, dirname, join } from 'path';
5
+
6
+ var __defProp = Object.defineProperty;
7
+ var __name2 = /* @__PURE__ */ __name((target, value) => __defProp(target, "name", {
8
+ value,
9
+ configurable: true
10
+ }), "__name");
11
+ var CLIENT_CONFIGS = {
12
+ claude: /* @__PURE__ */ __name2((home) => {
13
+ switch (platform()) {
14
+ case "darwin":
15
+ return [
16
+ join(home, "Library/Application Support/Claude/claude_desktop_config.json")
17
+ ];
18
+ case "win32":
19
+ return [
20
+ join(process.env.APPDATA || "", "Claude/claude_desktop_config.json")
21
+ ];
22
+ default:
23
+ return [
24
+ join(home, ".config/Claude/claude_desktop_config.json")
25
+ ];
26
+ }
27
+ }, "claude"),
28
+ // Project-level first (user preference), then global fallback
29
+ cursor: /* @__PURE__ */ __name2((_home, cwd) => [
30
+ ...cwd ? [
31
+ join(cwd, ".cursor/mcp.json")
32
+ ] : [],
33
+ join(_home, ".cursor/mcp.json")
34
+ ], "cursor"),
35
+ windsurf: /* @__PURE__ */ __name2((home) => [
36
+ join(home, ".codeium/windsurf/mcp_config.json")
37
+ ], "windsurf"),
38
+ continue: /* @__PURE__ */ __name2((home) => [
39
+ join(home, ".continue/config.json")
40
+ ], "continue"),
41
+ // New clients
42
+ vscode: /* @__PURE__ */ __name2((_home, cwd) => [
43
+ ...cwd ? [
44
+ join(cwd, ".vscode/mcp.json")
45
+ ] : []
46
+ ], "vscode"),
47
+ zed: /* @__PURE__ */ __name2((home) => [
48
+ join(home, ".config/zed/settings.json")
49
+ ], "zed"),
50
+ cline: /* @__PURE__ */ __name2((home) => [
51
+ join(home, ".cline/mcp.json")
52
+ ], "cline"),
53
+ gemini: /* @__PURE__ */ __name2((home) => [
54
+ join(home, ".gemini/settings.json")
55
+ ], "gemini"),
56
+ aider: /* @__PURE__ */ __name2((home) => [
57
+ join(home, ".aider/mcp.yaml")
58
+ ], "aider"),
59
+ "roo-code": /* @__PURE__ */ __name2((home) => [
60
+ join(home, ".roo-code/mcp.json")
61
+ ], "roo-code"),
62
+ // Qoder (VS Code fork) - supports both project-level and global configs
63
+ qoder: /* @__PURE__ */ __name2((home) => {
64
+ switch (platform()) {
65
+ case "darwin":
66
+ return [
67
+ join(home, "Library/Application Support/Qoder/SharedClientCache/extension/local/mcp.json")
68
+ ];
69
+ case "win32":
70
+ return [
71
+ join(process.env.APPDATA || "", "Qoder/mcp.json")
72
+ ];
73
+ default:
74
+ return [
75
+ join(home, ".config/Qoder/mcp.json")
76
+ ];
77
+ }
78
+ }, "qoder")
79
+ };
80
+ var CLIENT_DISPLAY_NAMES = {
81
+ claude: "Claude Desktop",
82
+ cursor: "Cursor",
83
+ windsurf: "Windsurf",
84
+ continue: "Continue",
85
+ vscode: "VS Code",
86
+ zed: "Zed",
87
+ cline: "Cline",
88
+ gemini: "Gemini/Antigravity",
89
+ aider: "Aider",
90
+ "roo-code": "Roo Code",
91
+ qoder: "Qoder"
92
+ };
93
+ function detectAIClients(options = {}) {
94
+ const home = homedir();
95
+ const cwd = options.cwd || process.cwd();
96
+ const clients = [];
97
+ const seenPaths = /* @__PURE__ */ new Set();
98
+ for (const [name, getPaths] of Object.entries(CLIENT_CONFIGS)) {
99
+ const paths = getPaths(home, cwd);
100
+ for (const configPath of paths) {
101
+ if (seenPaths.has(configPath)) {
102
+ continue;
103
+ }
104
+ seenPaths.add(configPath);
105
+ const exists = existsSync(configPath);
106
+ let hasSnapback = false;
107
+ if (exists) {
108
+ try {
109
+ const content = readFileSync(configPath, "utf-8");
110
+ if (configPath.endsWith(".yaml") || configPath.endsWith(".yml")) {
111
+ hasSnapback = content.includes("snapback");
112
+ } else {
113
+ const parsed = JSON.parse(content);
114
+ hasSnapback = checkForSnapback(parsed, name);
115
+ }
116
+ } catch {
117
+ }
118
+ }
119
+ clients.push({
120
+ name,
121
+ displayName: CLIENT_DISPLAY_NAMES[name] || name,
122
+ configPath,
123
+ exists,
124
+ hasSnapback,
125
+ format: name
126
+ });
127
+ }
128
+ }
129
+ const detected = clients.filter((c) => c.exists);
130
+ const needsSetup = detected.filter((c) => !c.hasSnapback);
131
+ return {
132
+ clients,
133
+ detected,
134
+ needsSetup
135
+ };
136
+ }
137
+ __name(detectAIClients, "detectAIClients");
138
+ __name2(detectAIClients, "detectAIClients");
139
+ function getClient(clientName) {
140
+ const result = detectAIClients();
141
+ return result.clients.find((c) => c.name === clientName && c.exists);
142
+ }
143
+ __name(getClient, "getClient");
144
+ __name2(getClient, "getClient");
145
+ function getConfiguredClients() {
146
+ const result = detectAIClients();
147
+ return result.detected.filter((c) => c.hasSnapback);
148
+ }
149
+ __name(getConfiguredClients, "getConfiguredClients");
150
+ __name2(getConfiguredClients, "getConfiguredClients");
151
+ function checkForSnapback(config, format) {
152
+ if (!config || typeof config !== "object") {
153
+ return false;
154
+ }
155
+ const configObj = config;
156
+ switch (format) {
157
+ case "claude":
158
+ case "cursor":
159
+ case "windsurf":
160
+ case "vscode":
161
+ case "cline":
162
+ case "roo-code":
163
+ case "qoder":
164
+ if ("mcpServers" in configObj && typeof configObj.mcpServers === "object" && configObj.mcpServers !== null) {
165
+ const servers = configObj.mcpServers;
166
+ return "snapback" in servers;
167
+ }
168
+ return false;
169
+ case "gemini":
170
+ case "zed":
171
+ if ("mcpServers" in configObj && typeof configObj.mcpServers === "object" && configObj.mcpServers !== null) {
172
+ const servers = configObj.mcpServers;
173
+ return "snapback" in servers;
174
+ }
175
+ return false;
176
+ case "continue":
177
+ if ("experimental" in configObj && typeof configObj.experimental === "object" && configObj.experimental !== null) {
178
+ const experimental = configObj.experimental;
179
+ if ("modelContextProtocolServers" in experimental && Array.isArray(experimental.modelContextProtocolServers)) {
180
+ return experimental.modelContextProtocolServers.some((server) => typeof server === "object" && server !== null && server.name === "snapback");
181
+ }
182
+ }
183
+ return false;
184
+ case "aider":
185
+ return false;
186
+ default:
187
+ return false;
188
+ }
189
+ }
190
+ __name(checkForSnapback, "checkForSnapback");
191
+ __name2(checkForSnapback, "checkForSnapback");
192
+ function getClientConfigPath(clientName) {
193
+ const getPaths = CLIENT_CONFIGS[clientName];
194
+ if (!getPaths) {
195
+ return void 0;
196
+ }
197
+ const paths = getPaths(homedir());
198
+ return paths[0];
199
+ }
200
+ __name(getClientConfigPath, "getClientConfigPath");
201
+ __name2(getClientConfigPath, "getClientConfigPath");
202
+ function readClientConfig(client) {
203
+ try {
204
+ const content = readFileSync(client.configPath, "utf-8");
205
+ return JSON.parse(content);
206
+ } catch {
207
+ return void 0;
208
+ }
209
+ }
210
+ __name(readClientConfig, "readClientConfig");
211
+ __name2(readClientConfig, "readClientConfig");
212
+ function validateClientConfig(client) {
213
+ const issues = [];
214
+ if (!existsSync(client.configPath)) {
215
+ issues.push({
216
+ severity: "error",
217
+ code: "CONFIG_NOT_FOUND",
218
+ message: `Config file not found: ${client.configPath}`,
219
+ fix: `Run: snap tools configure --${client.name}`
220
+ });
221
+ return {
222
+ valid: false,
223
+ issues
224
+ };
225
+ }
226
+ let configContent;
227
+ let parsedConfig;
228
+ try {
229
+ configContent = readFileSync(client.configPath, "utf-8");
230
+ } catch (error) {
231
+ issues.push({
232
+ severity: "error",
233
+ code: "CONFIG_READ_ERROR",
234
+ message: `Cannot read config file: ${error instanceof Error ? error.message : "Unknown error"}`,
235
+ fix: "Check file permissions"
236
+ });
237
+ return {
238
+ valid: false,
239
+ issues
240
+ };
241
+ }
242
+ try {
243
+ parsedConfig = JSON.parse(configContent);
244
+ } catch (error) {
245
+ issues.push({
246
+ severity: "error",
247
+ code: "CONFIG_PARSE_ERROR",
248
+ message: `Invalid JSON in config file: ${error instanceof Error ? error.message : "Unknown error"}`,
249
+ fix: `Edit ${client.configPath} to fix JSON syntax, or run: snap tools configure --${client.name} --force`
250
+ });
251
+ return {
252
+ valid: false,
253
+ issues
254
+ };
255
+ }
256
+ if (!client.hasSnapback) {
257
+ issues.push({
258
+ severity: "warning",
259
+ code: "SNAPBACK_NOT_CONFIGURED",
260
+ message: "SnapBack MCP server not found in config",
261
+ fix: `Run: snap tools configure --${client.name}`
262
+ });
263
+ return {
264
+ valid: false,
265
+ issues
266
+ };
267
+ }
268
+ const snapbackConfig = extractSnapbackConfig(parsedConfig, client.format);
269
+ if (!snapbackConfig) {
270
+ issues.push({
271
+ severity: "error",
272
+ code: "SNAPBACK_CONFIG_INVALID",
273
+ message: "SnapBack config found but cannot be parsed"
274
+ });
275
+ return {
276
+ valid: false,
277
+ issues
278
+ };
279
+ }
280
+ validateSnapbackConfig(snapbackConfig, issues);
281
+ if (snapbackConfig.command && snapbackConfig.args) {
282
+ const workspaceIdx = snapbackConfig.args.indexOf("--workspace");
283
+ if (workspaceIdx !== -1 && workspaceIdx + 1 < snapbackConfig.args.length) {
284
+ const workspacePath = snapbackConfig.args[workspaceIdx + 1];
285
+ const wsValidation = validateWorkspacePath(workspacePath);
286
+ if (!wsValidation.exists) {
287
+ issues.push({
288
+ severity: "error",
289
+ code: "WORKSPACE_NOT_FOUND",
290
+ message: `Workspace path does not exist: ${workspacePath}`,
291
+ fix: "Update workspace path or run: snap tools configure --force"
292
+ });
293
+ } else if (!wsValidation.hasMarkers) {
294
+ issues.push({
295
+ severity: "warning",
296
+ code: "WORKSPACE_NO_MARKERS",
297
+ message: `Workspace path has no markers (.git, package.json, .snapback): ${workspacePath}`,
298
+ fix: "Run: snap init"
299
+ });
300
+ }
301
+ }
302
+ }
303
+ return {
304
+ valid: issues.filter((i) => i.severity === "error").length === 0,
305
+ issues,
306
+ config: snapbackConfig
307
+ };
308
+ }
309
+ __name(validateClientConfig, "validateClientConfig");
310
+ __name2(validateClientConfig, "validateClientConfig");
311
+ function validateWorkspacePath(workspacePath) {
312
+ const absPath = resolve(workspacePath);
313
+ if (!existsSync(absPath)) {
314
+ return {
315
+ exists: false,
316
+ hasMarkers: false,
317
+ path: absPath
318
+ };
319
+ }
320
+ const hasGit = existsSync(resolve(absPath, ".git"));
321
+ const hasPackageJson = existsSync(resolve(absPath, "package.json"));
322
+ const hasSnapback = existsSync(resolve(absPath, ".snapback"));
323
+ return {
324
+ exists: true,
325
+ hasMarkers: hasGit || hasPackageJson || hasSnapback,
326
+ path: absPath
327
+ };
328
+ }
329
+ __name(validateWorkspacePath, "validateWorkspacePath");
330
+ __name2(validateWorkspacePath, "validateWorkspacePath");
331
+ function extractSnapbackConfig(parsed, format) {
332
+ if (!parsed || typeof parsed !== "object") {
333
+ return null;
334
+ }
335
+ const config = parsed;
336
+ switch (format) {
337
+ case "claude":
338
+ case "cursor":
339
+ case "windsurf":
340
+ case "vscode":
341
+ case "cline":
342
+ case "roo-code":
343
+ case "qoder":
344
+ case "gemini":
345
+ case "zed":
346
+ if ("mcpServers" in config && typeof config.mcpServers === "object" && config.mcpServers !== null) {
347
+ const servers = config.mcpServers;
348
+ if ("snapback" in servers) {
349
+ return servers.snapback;
350
+ }
351
+ }
352
+ return null;
353
+ case "continue":
354
+ if ("experimental" in config && typeof config.experimental === "object" && config.experimental !== null) {
355
+ const experimental = config.experimental;
356
+ if ("modelContextProtocolServers" in experimental && Array.isArray(experimental.modelContextProtocolServers)) {
357
+ const server = experimental.modelContextProtocolServers.find((s) => typeof s === "object" && s !== null && s.name === "snapback");
358
+ return server ? server : null;
359
+ }
360
+ }
361
+ return null;
362
+ default:
363
+ return null;
364
+ }
365
+ }
366
+ __name(extractSnapbackConfig, "extractSnapbackConfig");
367
+ __name2(extractSnapbackConfig, "extractSnapbackConfig");
368
+ function validateSnapbackConfig(config, issues) {
369
+ if (!config.command && !config.url) {
370
+ issues.push({
371
+ severity: "error",
372
+ code: "MISSING_COMMAND_OR_URL",
373
+ message: "Config must have either 'command' (stdio) or 'url' (HTTP)",
374
+ fix: "Run: snap tools configure --force"
375
+ });
376
+ return;
377
+ }
378
+ if (config.command) {
379
+ if (!config.args || !Array.isArray(config.args)) {
380
+ issues.push({
381
+ severity: "error",
382
+ code: "MISSING_ARGS",
383
+ message: "Command-based config must have 'args' array",
384
+ fix: "Run: snap tools configure --force"
385
+ });
386
+ } else {
387
+ if (!config.args.includes("mcp")) {
388
+ issues.push({
389
+ severity: "error",
390
+ code: "MISSING_MCP_ARG",
391
+ message: "Args must include 'mcp' command",
392
+ fix: "Run: snap tools configure --force"
393
+ });
394
+ }
395
+ if (!config.args.includes("--stdio")) {
396
+ issues.push({
397
+ severity: "error",
398
+ code: "MISSING_STDIO_ARG",
399
+ message: "Args must include '--stdio' flag",
400
+ fix: "Run: snap tools configure --force"
401
+ });
402
+ }
403
+ if (!config.args.includes("--workspace")) {
404
+ issues.push({
405
+ severity: "warning",
406
+ code: "MISSING_WORKSPACE_ARG",
407
+ message: "Args should include '--workspace' path for reliability",
408
+ fix: "Run: snap tools configure --force"
409
+ });
410
+ }
411
+ }
412
+ }
413
+ if (config.url) {
414
+ try {
415
+ new URL(config.url);
416
+ } catch {
417
+ issues.push({
418
+ severity: "error",
419
+ code: "INVALID_URL",
420
+ message: `Invalid server URL: ${config.url}`,
421
+ fix: "Run: snap tools configure --force"
422
+ });
423
+ }
424
+ }
425
+ if (config.env) {
426
+ if (!config.env.SNAPBACK_API_KEY && !config.env.SNAPBACK_WORKSPACE_ID) {
427
+ issues.push({
428
+ severity: "info",
429
+ code: "NO_AUTH",
430
+ message: "No API key or workspace ID found (free tier will be used)"
431
+ });
432
+ }
433
+ }
434
+ }
435
+ __name(validateSnapbackConfig, "validateSnapbackConfig");
436
+ __name2(validateSnapbackConfig, "validateSnapbackConfig");
437
+ function getSnapbackMCPConfig(options = {}) {
438
+ const { apiKey, workspaceId, serverUrl, useBinary = false, customCommand, additionalEnv, workspaceRoot, useLocalDev = false, localCliPath } = options;
439
+ const env = {
440
+ ...additionalEnv
441
+ };
442
+ if (workspaceId) {
443
+ env.SNAPBACK_WORKSPACE_ID = workspaceId;
444
+ }
445
+ if (apiKey) {
446
+ env.SNAPBACK_API_KEY = apiKey;
447
+ }
448
+ if (customCommand) {
449
+ return {
450
+ command: customCommand,
451
+ args: [],
452
+ ...Object.keys(env).length > 0 && {
453
+ env
454
+ }
455
+ };
456
+ }
457
+ if (serverUrl || !useLocalDev && !useBinary) {
458
+ const url = serverUrl || "https://snapback-mcp.fly.dev";
459
+ return {
460
+ url,
461
+ ...Object.keys(env).length > 0 && {
462
+ env
463
+ }
464
+ };
465
+ }
466
+ if (useLocalDev && localCliPath) {
467
+ const tier = apiKey ? "pro" : "free";
468
+ const args = [
469
+ localCliPath,
470
+ "mcp",
471
+ "--stdio",
472
+ "--tier",
473
+ tier
474
+ ];
475
+ if (workspaceRoot) {
476
+ args.push("--workspace", workspaceRoot);
477
+ }
478
+ return {
479
+ command: "node",
480
+ args,
481
+ ...Object.keys(env).length > 0 && {
482
+ env
483
+ }
484
+ };
485
+ }
486
+ if (useBinary) {
487
+ const tier = apiKey ? "pro" : "free";
488
+ const args = [
489
+ "mcp",
490
+ "--stdio",
491
+ "--tier",
492
+ tier
493
+ ];
494
+ if (workspaceRoot) {
495
+ args.push("--workspace", workspaceRoot);
496
+ }
497
+ return {
498
+ command: "snapback",
499
+ args,
500
+ ...Object.keys(env).length > 0 && {
501
+ env
502
+ }
503
+ };
504
+ }
505
+ return {
506
+ url: "https://snapback-mcp.fly.dev",
507
+ ...Object.keys(env).length > 0 && {
508
+ env
509
+ }
510
+ };
511
+ }
512
+ __name(getSnapbackMCPConfig, "getSnapbackMCPConfig");
513
+ __name2(getSnapbackMCPConfig, "getSnapbackMCPConfig");
514
+ function writeClientConfig(client, mcpConfig) {
515
+ try {
516
+ const configDir = dirname(client.configPath);
517
+ mkdirSync(configDir, {
518
+ recursive: true
519
+ });
520
+ let existingConfig = {
521
+ mcpServers: {}
522
+ };
523
+ let hasExistingConfig = false;
524
+ if (existsSync(client.configPath)) {
525
+ try {
526
+ const content = readFileSync(client.configPath, "utf-8");
527
+ existingConfig = JSON.parse(content);
528
+ hasExistingConfig = Object.keys(existingConfig).length > 0;
529
+ } catch {
530
+ }
531
+ }
532
+ let backup;
533
+ if (hasExistingConfig) {
534
+ backup = `${client.configPath}.backup.${Date.now()}`;
535
+ writeFileSync(backup, JSON.stringify(existingConfig, null, 2));
536
+ }
537
+ const newConfig = mergeConfig(existingConfig, mcpConfig, client.format);
538
+ writeFileSync(client.configPath, JSON.stringify(newConfig, null, 2));
539
+ return {
540
+ success: true,
541
+ backup
542
+ };
543
+ } catch (error) {
544
+ return {
545
+ success: false,
546
+ error: error instanceof Error ? error.message : "Unknown error"
547
+ };
548
+ }
549
+ }
550
+ __name(writeClientConfig, "writeClientConfig");
551
+ __name2(writeClientConfig, "writeClientConfig");
552
+ function removeSnapbackConfig(client) {
553
+ try {
554
+ if (!existsSync(client.configPath)) {
555
+ return {
556
+ success: true
557
+ };
558
+ }
559
+ const content = readFileSync(client.configPath, "utf-8");
560
+ const config = JSON.parse(content);
561
+ switch (client.format) {
562
+ case "claude":
563
+ case "cursor":
564
+ case "windsurf":
565
+ if (config.mcpServers?.snapback) {
566
+ delete config.mcpServers.snapback;
567
+ }
568
+ break;
569
+ case "continue": {
570
+ const experimental = config.experimental;
571
+ if (experimental?.modelContextProtocolServers) {
572
+ const servers = experimental.modelContextProtocolServers;
573
+ experimental.modelContextProtocolServers = servers.filter((s) => s.name !== "snapback");
574
+ }
575
+ break;
576
+ }
577
+ }
578
+ writeFileSync(client.configPath, JSON.stringify(config, null, 2));
579
+ return {
580
+ success: true
581
+ };
582
+ } catch (error) {
583
+ return {
584
+ success: false,
585
+ error: error instanceof Error ? error.message : "Unknown error"
586
+ };
587
+ }
588
+ }
589
+ __name(removeSnapbackConfig, "removeSnapbackConfig");
590
+ __name2(removeSnapbackConfig, "removeSnapbackConfig");
591
+ function mergeConfig(existing, snapbackConfig, format) {
592
+ switch (format) {
593
+ case "claude":
594
+ case "cursor":
595
+ case "windsurf":
596
+ case "vscode":
597
+ case "cline":
598
+ case "roo-code":
599
+ case "gemini":
600
+ case "zed":
601
+ case "qoder":
602
+ return {
603
+ ...existing,
604
+ mcpServers: {
605
+ ...existing.mcpServers || {},
606
+ snapback: snapbackConfig
607
+ }
608
+ };
609
+ case "continue": {
610
+ const continueConfig = existing;
611
+ const experimental = continueConfig.experimental || {};
612
+ const servers = experimental.modelContextProtocolServers || [];
613
+ const filteredServers = servers.filter((s) => s.name !== "snapback");
614
+ filteredServers.push({
615
+ name: "snapback",
616
+ ...snapbackConfig
617
+ });
618
+ return {
619
+ ...continueConfig,
620
+ experimental: {
621
+ ...experimental,
622
+ modelContextProtocolServers: filteredServers
623
+ }
624
+ };
625
+ }
626
+ case "aider":
627
+ return existing;
628
+ default:
629
+ return {
630
+ ...existing,
631
+ mcpServers: {
632
+ ...existing.mcpServers || {},
633
+ snapback: snapbackConfig
634
+ }
635
+ };
636
+ }
637
+ }
638
+ __name(mergeConfig, "mergeConfig");
639
+ __name2(mergeConfig, "mergeConfig");
640
+ function validateConfig(client) {
641
+ try {
642
+ const content = readFileSync(client.configPath, "utf-8");
643
+ const config = JSON.parse(content);
644
+ switch (client.format) {
645
+ case "claude":
646
+ case "cursor":
647
+ case "windsurf":
648
+ case "vscode":
649
+ case "cline":
650
+ case "roo-code":
651
+ case "gemini":
652
+ case "zed":
653
+ case "qoder":
654
+ return Boolean(config.mcpServers?.snapback?.command || config.mcpServers?.snapback?.url);
655
+ case "continue": {
656
+ const expCfg = config.experimental;
657
+ const srvs = expCfg?.modelContextProtocolServers;
658
+ return Boolean(srvs?.some((s) => s.name === "snapback" && (s.command || s.url)));
659
+ }
660
+ case "aider":
661
+ return false;
662
+ default:
663
+ return false;
664
+ }
665
+ } catch {
666
+ return false;
667
+ }
668
+ }
669
+ __name(validateConfig, "validateConfig");
670
+ __name2(validateConfig, "validateConfig");
671
+ function repairClientConfig(client, options = {}) {
672
+ const fixed = [];
673
+ const remaining = [];
674
+ const validation = validateClientConfig(client);
675
+ if (options.force) {
676
+ return performFullReconfiguration(client, options);
677
+ }
678
+ if (validation.valid && validation.issues.length === 0) {
679
+ return {
680
+ success: true,
681
+ fixed: [],
682
+ remaining: []
683
+ };
684
+ }
685
+ for (const issue of validation.issues) {
686
+ const fixResult = attemptFix(client, issue, validation, options);
687
+ if (fixResult.success) {
688
+ fixed.push(issue.message);
689
+ } else {
690
+ remaining.push(issue.message);
691
+ }
692
+ }
693
+ const hasCriticalErrors = remaining.some((msg) => validation.issues.find((i) => i.message === msg && i.severity === "error"));
694
+ if (hasCriticalErrors) {
695
+ return performFullReconfiguration(client, options);
696
+ }
697
+ return {
698
+ success: remaining.length === 0,
699
+ fixed,
700
+ remaining
701
+ };
702
+ }
703
+ __name(repairClientConfig, "repairClientConfig");
704
+ __name2(repairClientConfig, "repairClientConfig");
705
+ function injectWorkspacePath(client, workspaceRoot) {
706
+ const fixed = [];
707
+ const remaining = [];
708
+ const detectedWorkspace = workspaceRoot || detectWorkspaceRoot(process.cwd());
709
+ if (!detectedWorkspace) {
710
+ return {
711
+ success: false,
712
+ fixed,
713
+ remaining: [
714
+ "Could not auto-detect workspace root"
715
+ ],
716
+ error: "No workspace markers found (.git, package.json, .snapback)"
717
+ };
718
+ }
719
+ if (!existsSync(detectedWorkspace)) {
720
+ return {
721
+ success: false,
722
+ fixed,
723
+ remaining: [
724
+ `Workspace path does not exist: ${detectedWorkspace}`
725
+ ],
726
+ error: "Invalid workspace path"
727
+ };
728
+ }
729
+ const validation = validateClientConfig(client);
730
+ if (!validation.config) {
731
+ return {
732
+ success: false,
733
+ fixed,
734
+ remaining: [
735
+ "No existing SnapBack config found"
736
+ ],
737
+ error: "Must run initial configuration first"
738
+ };
739
+ }
740
+ if (!validation.config.command) {
741
+ return {
742
+ success: true,
743
+ fixed: [
744
+ "Config uses HTTP transport - no workspace path needed"
745
+ ],
746
+ remaining: []
747
+ };
748
+ }
749
+ const hasWorkspace = validation.config.args?.includes("--workspace");
750
+ if (hasWorkspace) {
751
+ fixed.push("Workspace path already configured");
752
+ return {
753
+ success: true,
754
+ fixed,
755
+ remaining
756
+ };
757
+ }
758
+ const result = performFullReconfiguration(client, {
759
+ workspaceRoot: detectedWorkspace
760
+ });
761
+ if (result.success) {
762
+ fixed.push(`Injected workspace path: ${detectedWorkspace}`);
763
+ }
764
+ return {
765
+ success: result.success,
766
+ fixed,
767
+ remaining
768
+ };
769
+ }
770
+ __name(injectWorkspacePath, "injectWorkspacePath");
771
+ __name2(injectWorkspacePath, "injectWorkspacePath");
772
+ function attemptFix(client, issue, _validation, options) {
773
+ switch (issue.code) {
774
+ case "CONFIG_NOT_FOUND":
775
+ case "CONFIG_PARSE_ERROR":
776
+ case "SNAPBACK_NOT_CONFIGURED":
777
+ case "MISSING_COMMAND_OR_URL":
778
+ case "MISSING_ARGS":
779
+ case "MISSING_MCP_ARG":
780
+ case "MISSING_STDIO_ARG":
781
+ case "INVALID_URL":
782
+ return performFullReconfiguration(client, options);
783
+ case "MISSING_WORKSPACE_ARG": {
784
+ const workspace = options.workspaceRoot || detectWorkspaceRoot(process.cwd());
785
+ if (workspace) {
786
+ return performFullReconfiguration(client, {
787
+ ...options,
788
+ workspaceRoot: workspace
789
+ });
790
+ }
791
+ return {
792
+ success: false
793
+ };
794
+ }
795
+ case "WORKSPACE_NOT_FOUND": {
796
+ const detected = detectWorkspaceRoot(process.cwd());
797
+ if (detected) {
798
+ return performFullReconfiguration(client, {
799
+ ...options,
800
+ workspaceRoot: detected
801
+ });
802
+ }
803
+ return {
804
+ success: false
805
+ };
806
+ }
807
+ case "WORKSPACE_NO_MARKERS":
808
+ return {
809
+ success: true
810
+ };
811
+ case "NO_AUTH":
812
+ return {
813
+ success: true
814
+ };
815
+ default:
816
+ return {
817
+ success: false
818
+ };
819
+ }
820
+ }
821
+ __name(attemptFix, "attemptFix");
822
+ __name2(attemptFix, "attemptFix");
823
+ function performFullReconfiguration(client, options) {
824
+ try {
825
+ const workspaceRoot = options.workspaceRoot || detectWorkspaceRoot(process.cwd());
826
+ const mcpConfig = getSnapbackMCPConfig({
827
+ apiKey: options.apiKey,
828
+ workspaceId: options.workspaceId,
829
+ workspaceRoot: workspaceRoot || void 0,
830
+ useLocalDev: true,
831
+ localCliPath: findCliPath()
832
+ });
833
+ const writeResult = writeClientConfig(client, mcpConfig);
834
+ if (writeResult.success) {
835
+ return {
836
+ success: true,
837
+ fixed: [
838
+ "Full reconfiguration completed"
839
+ ],
840
+ remaining: []
841
+ };
842
+ }
843
+ return {
844
+ success: false,
845
+ fixed: [],
846
+ remaining: [
847
+ "Write failed"
848
+ ],
849
+ error: writeResult.error
850
+ };
851
+ } catch (error) {
852
+ return {
853
+ success: false,
854
+ fixed: [],
855
+ remaining: [
856
+ "Reconfiguration failed"
857
+ ],
858
+ error: error instanceof Error ? error.message : "Unknown error"
859
+ };
860
+ }
861
+ }
862
+ __name(performFullReconfiguration, "performFullReconfiguration");
863
+ __name2(performFullReconfiguration, "performFullReconfiguration");
864
+ function detectWorkspaceRoot(startPath) {
865
+ let currentPath = resolve(startPath);
866
+ const maxIterations = 50;
867
+ let iterations = 0;
868
+ while (iterations < maxIterations) {
869
+ iterations++;
870
+ const hasGit = existsSync(resolve(currentPath, ".git"));
871
+ const hasPackageJson = existsSync(resolve(currentPath, "package.json"));
872
+ const hasSnapback = existsSync(resolve(currentPath, ".snapback"));
873
+ if (hasGit || hasPackageJson || hasSnapback) {
874
+ return currentPath;
875
+ }
876
+ const parent = resolve(currentPath, "..");
877
+ if (parent === currentPath) {
878
+ break;
879
+ }
880
+ currentPath = parent;
881
+ }
882
+ return null;
883
+ }
884
+ __name(detectWorkspaceRoot, "detectWorkspaceRoot");
885
+ __name2(detectWorkspaceRoot, "detectWorkspaceRoot");
886
+ function findCliPath() {
887
+ const cwd = process.cwd();
888
+ const candidates = [
889
+ resolve(cwd, "apps/cli/dist/index.js"),
890
+ resolve(cwd, "dist/index.js"),
891
+ resolve(cwd, "../cli/dist/index.js"),
892
+ resolve(cwd, "../../apps/cli/dist/index.js")
893
+ ];
894
+ for (const path of candidates) {
895
+ if (existsSync(path)) {
896
+ return path;
897
+ }
898
+ }
899
+ return void 0;
900
+ }
901
+ __name(findCliPath, "findCliPath");
902
+ __name2(findCliPath, "findCliPath");
903
+
904
+ export { detectAIClients, getClient, getClientConfigPath, getConfiguredClients, getSnapbackMCPConfig, injectWorkspacePath, readClientConfig, removeSnapbackConfig, repairClientConfig, validateClientConfig, validateConfig, validateWorkspacePath, writeClientConfig };
905
+ //# sourceMappingURL=chunk-RU7BOXR3.js.map
906
+ //# sourceMappingURL=chunk-RU7BOXR3.js.map