@gmickel/gno 0.3.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.
- package/README.md +256 -0
- package/assets/skill/SKILL.md +112 -0
- package/assets/skill/cli-reference.md +327 -0
- package/assets/skill/examples.md +234 -0
- package/assets/skill/mcp-reference.md +159 -0
- package/package.json +90 -0
- package/src/app/constants.ts +313 -0
- package/src/cli/colors.ts +65 -0
- package/src/cli/commands/ask.ts +545 -0
- package/src/cli/commands/cleanup.ts +105 -0
- package/src/cli/commands/collection/add.ts +120 -0
- package/src/cli/commands/collection/index.ts +10 -0
- package/src/cli/commands/collection/list.ts +108 -0
- package/src/cli/commands/collection/remove.ts +64 -0
- package/src/cli/commands/collection/rename.ts +95 -0
- package/src/cli/commands/context/add.ts +67 -0
- package/src/cli/commands/context/check.ts +153 -0
- package/src/cli/commands/context/index.ts +10 -0
- package/src/cli/commands/context/list.ts +109 -0
- package/src/cli/commands/context/rm.ts +52 -0
- package/src/cli/commands/doctor.ts +393 -0
- package/src/cli/commands/embed.ts +462 -0
- package/src/cli/commands/get.ts +356 -0
- package/src/cli/commands/index-cmd.ts +119 -0
- package/src/cli/commands/index.ts +102 -0
- package/src/cli/commands/init.ts +328 -0
- package/src/cli/commands/ls.ts +217 -0
- package/src/cli/commands/mcp/config.ts +300 -0
- package/src/cli/commands/mcp/index.ts +24 -0
- package/src/cli/commands/mcp/install.ts +203 -0
- package/src/cli/commands/mcp/paths.ts +470 -0
- package/src/cli/commands/mcp/status.ts +222 -0
- package/src/cli/commands/mcp/uninstall.ts +158 -0
- package/src/cli/commands/mcp.ts +20 -0
- package/src/cli/commands/models/clear.ts +103 -0
- package/src/cli/commands/models/index.ts +32 -0
- package/src/cli/commands/models/list.ts +214 -0
- package/src/cli/commands/models/path.ts +51 -0
- package/src/cli/commands/models/pull.ts +199 -0
- package/src/cli/commands/models/use.ts +85 -0
- package/src/cli/commands/multi-get.ts +400 -0
- package/src/cli/commands/query.ts +220 -0
- package/src/cli/commands/ref-parser.ts +108 -0
- package/src/cli/commands/reset.ts +191 -0
- package/src/cli/commands/search.ts +136 -0
- package/src/cli/commands/shared.ts +156 -0
- package/src/cli/commands/skill/index.ts +19 -0
- package/src/cli/commands/skill/install.ts +197 -0
- package/src/cli/commands/skill/paths-cmd.ts +81 -0
- package/src/cli/commands/skill/paths.ts +191 -0
- package/src/cli/commands/skill/show.ts +73 -0
- package/src/cli/commands/skill/uninstall.ts +141 -0
- package/src/cli/commands/status.ts +205 -0
- package/src/cli/commands/update.ts +68 -0
- package/src/cli/commands/vsearch.ts +188 -0
- package/src/cli/context.ts +64 -0
- package/src/cli/errors.ts +64 -0
- package/src/cli/format/search-results.ts +211 -0
- package/src/cli/options.ts +183 -0
- package/src/cli/program.ts +1330 -0
- package/src/cli/run.ts +213 -0
- package/src/cli/ui.ts +92 -0
- package/src/config/defaults.ts +20 -0
- package/src/config/index.ts +55 -0
- package/src/config/loader.ts +161 -0
- package/src/config/paths.ts +87 -0
- package/src/config/saver.ts +153 -0
- package/src/config/types.ts +280 -0
- package/src/converters/adapters/markitdownTs/adapter.ts +140 -0
- package/src/converters/adapters/officeparser/adapter.ts +126 -0
- package/src/converters/canonicalize.ts +89 -0
- package/src/converters/errors.ts +218 -0
- package/src/converters/index.ts +51 -0
- package/src/converters/mime.ts +163 -0
- package/src/converters/native/markdown.ts +115 -0
- package/src/converters/native/plaintext.ts +56 -0
- package/src/converters/path.ts +48 -0
- package/src/converters/pipeline.ts +159 -0
- package/src/converters/registry.ts +74 -0
- package/src/converters/types.ts +123 -0
- package/src/converters/versions.ts +24 -0
- package/src/index.ts +27 -0
- package/src/ingestion/chunker.ts +238 -0
- package/src/ingestion/index.ts +32 -0
- package/src/ingestion/language.ts +276 -0
- package/src/ingestion/sync.ts +671 -0
- package/src/ingestion/types.ts +219 -0
- package/src/ingestion/walker.ts +235 -0
- package/src/llm/cache.ts +467 -0
- package/src/llm/errors.ts +191 -0
- package/src/llm/index.ts +58 -0
- package/src/llm/nodeLlamaCpp/adapter.ts +133 -0
- package/src/llm/nodeLlamaCpp/embedding.ts +165 -0
- package/src/llm/nodeLlamaCpp/generation.ts +88 -0
- package/src/llm/nodeLlamaCpp/lifecycle.ts +317 -0
- package/src/llm/nodeLlamaCpp/rerank.ts +94 -0
- package/src/llm/registry.ts +86 -0
- package/src/llm/types.ts +129 -0
- package/src/mcp/resources/index.ts +151 -0
- package/src/mcp/server.ts +229 -0
- package/src/mcp/tools/get.ts +220 -0
- package/src/mcp/tools/index.ts +160 -0
- package/src/mcp/tools/multi-get.ts +263 -0
- package/src/mcp/tools/query.ts +226 -0
- package/src/mcp/tools/search.ts +119 -0
- package/src/mcp/tools/status.ts +81 -0
- package/src/mcp/tools/vsearch.ts +198 -0
- package/src/pipeline/chunk-lookup.ts +44 -0
- package/src/pipeline/expansion.ts +256 -0
- package/src/pipeline/explain.ts +115 -0
- package/src/pipeline/fusion.ts +185 -0
- package/src/pipeline/hybrid.ts +535 -0
- package/src/pipeline/index.ts +64 -0
- package/src/pipeline/query-language.ts +118 -0
- package/src/pipeline/rerank.ts +223 -0
- package/src/pipeline/search.ts +261 -0
- package/src/pipeline/types.ts +328 -0
- package/src/pipeline/vsearch.ts +348 -0
- package/src/store/index.ts +41 -0
- package/src/store/migrations/001-initial.ts +196 -0
- package/src/store/migrations/index.ts +20 -0
- package/src/store/migrations/runner.ts +187 -0
- package/src/store/sqlite/adapter.ts +1242 -0
- package/src/store/sqlite/index.ts +7 -0
- package/src/store/sqlite/setup.ts +129 -0
- package/src/store/sqlite/types.ts +28 -0
- package/src/store/types.ts +506 -0
- package/src/store/vector/index.ts +13 -0
- package/src/store/vector/sqlite-vec.ts +373 -0
- package/src/store/vector/stats.ts +152 -0
- package/src/store/vector/types.ts +115 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path resolution for MCP server configuration.
|
|
3
|
+
* Supports Claude Desktop, Claude Code, and Codex targets.
|
|
4
|
+
*
|
|
5
|
+
* @module src/cli/commands/mcp/paths
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync } from 'node:fs';
|
|
9
|
+
import { homedir, platform } from 'node:os';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
|
|
12
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
13
|
+
// Types
|
|
14
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export type McpTarget =
|
|
17
|
+
| 'claude-desktop'
|
|
18
|
+
| 'claude-code'
|
|
19
|
+
| 'codex'
|
|
20
|
+
| 'cursor'
|
|
21
|
+
| 'zed'
|
|
22
|
+
| 'windsurf'
|
|
23
|
+
| 'opencode'
|
|
24
|
+
| 'amp'
|
|
25
|
+
| 'lmstudio'
|
|
26
|
+
| 'librechat';
|
|
27
|
+
|
|
28
|
+
export type McpScope = 'user' | 'project';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Config format varies by target.
|
|
32
|
+
* - standard: mcpServers key (Claude Desktop, Cursor, Windsurf, LM Studio)
|
|
33
|
+
* - context_servers: Zed uses context_servers key
|
|
34
|
+
* - mcp: OpenCode uses mcp key with array command
|
|
35
|
+
* - amp_mcp: Amp uses amp.mcpServers key
|
|
36
|
+
* - yaml_standard: YAML file with mcpServers key (LibreChat)
|
|
37
|
+
*/
|
|
38
|
+
export type McpConfigFormat =
|
|
39
|
+
| 'standard'
|
|
40
|
+
| 'context_servers'
|
|
41
|
+
| 'mcp'
|
|
42
|
+
| 'amp_mcp'
|
|
43
|
+
| 'yaml_standard';
|
|
44
|
+
|
|
45
|
+
export interface McpConfigPaths {
|
|
46
|
+
/** Config file path */
|
|
47
|
+
configPath: string;
|
|
48
|
+
/** Whether this target supports project scope */
|
|
49
|
+
supportsProjectScope: boolean;
|
|
50
|
+
/** Config format for this target */
|
|
51
|
+
configFormat: McpConfigFormat;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface McpServerEntry {
|
|
55
|
+
command: string;
|
|
56
|
+
args: string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface McpPathOptions {
|
|
60
|
+
target: McpTarget;
|
|
61
|
+
scope?: McpScope;
|
|
62
|
+
/** Override cwd for project scope (testing) */
|
|
63
|
+
cwd?: string;
|
|
64
|
+
/** Override home dir (testing) */
|
|
65
|
+
homeDir?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
// Constants
|
|
70
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
export const MCP_SERVER_NAME = 'gno';
|
|
73
|
+
|
|
74
|
+
/** All supported MCP targets */
|
|
75
|
+
export const MCP_TARGETS: McpTarget[] = [
|
|
76
|
+
'claude-desktop',
|
|
77
|
+
'claude-code',
|
|
78
|
+
'codex',
|
|
79
|
+
'cursor',
|
|
80
|
+
'zed',
|
|
81
|
+
'windsurf',
|
|
82
|
+
'opencode',
|
|
83
|
+
'amp',
|
|
84
|
+
'lmstudio',
|
|
85
|
+
'librechat',
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
/** Targets that support project scope */
|
|
89
|
+
export const TARGETS_WITH_PROJECT_SCOPE: McpTarget[] = [
|
|
90
|
+
'claude-code',
|
|
91
|
+
'codex',
|
|
92
|
+
'cursor',
|
|
93
|
+
'opencode',
|
|
94
|
+
'librechat',
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
/** Get config format for a target */
|
|
98
|
+
export function getTargetConfigFormat(target: McpTarget): McpConfigFormat {
|
|
99
|
+
switch (target) {
|
|
100
|
+
case 'zed':
|
|
101
|
+
return 'context_servers';
|
|
102
|
+
case 'opencode':
|
|
103
|
+
return 'mcp';
|
|
104
|
+
case 'amp':
|
|
105
|
+
return 'amp_mcp';
|
|
106
|
+
case 'librechat':
|
|
107
|
+
return 'yaml_standard';
|
|
108
|
+
default:
|
|
109
|
+
return 'standard';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Regex to extract entry script path from command path */
|
|
114
|
+
const COMMANDS_PATH_PATTERN = /\/commands\/.*$/;
|
|
115
|
+
|
|
116
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
117
|
+
// Config Path Resolution
|
|
118
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Resolve Claude Desktop config path based on platform.
|
|
122
|
+
*/
|
|
123
|
+
function resolveClaudeDesktopPath(home: string): string {
|
|
124
|
+
const plat = platform();
|
|
125
|
+
|
|
126
|
+
if (plat === 'darwin') {
|
|
127
|
+
return join(
|
|
128
|
+
home,
|
|
129
|
+
'Library/Application Support/Claude/claude_desktop_config.json'
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
if (plat === 'win32') {
|
|
133
|
+
const appData = process.env.APPDATA || join(home, 'AppData/Roaming');
|
|
134
|
+
return join(appData, 'Claude/claude_desktop_config.json');
|
|
135
|
+
}
|
|
136
|
+
// Linux and other Unix
|
|
137
|
+
return join(home, '.config/Claude/claude_desktop_config.json');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Resolve Claude Code config path.
|
|
142
|
+
*/
|
|
143
|
+
function resolveClaudeCodePath(
|
|
144
|
+
scope: McpScope,
|
|
145
|
+
home: string,
|
|
146
|
+
cwd: string
|
|
147
|
+
): string {
|
|
148
|
+
if (scope === 'user') {
|
|
149
|
+
return join(home, '.claude.json');
|
|
150
|
+
}
|
|
151
|
+
return join(cwd, '.mcp.json');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Resolve Codex config path.
|
|
156
|
+
*/
|
|
157
|
+
function resolveCodexPath(scope: McpScope, home: string, cwd: string): string {
|
|
158
|
+
if (scope === 'user') {
|
|
159
|
+
return join(home, '.codex.json');
|
|
160
|
+
}
|
|
161
|
+
return join(cwd, '.codex/.mcp.json');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Resolve Cursor config path.
|
|
166
|
+
*/
|
|
167
|
+
function resolveCursorPath(scope: McpScope, home: string, cwd: string): string {
|
|
168
|
+
if (scope === 'user') {
|
|
169
|
+
const plat = platform();
|
|
170
|
+
if (plat === 'win32') {
|
|
171
|
+
return join(home, '.cursor', 'mcp.json');
|
|
172
|
+
}
|
|
173
|
+
return join(home, '.cursor/mcp.json');
|
|
174
|
+
}
|
|
175
|
+
return join(cwd, '.cursor/mcp.json');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Resolve Zed config path (macOS/Linux only, no project scope).
|
|
180
|
+
*/
|
|
181
|
+
function resolveZedPath(home: string): string {
|
|
182
|
+
const plat = platform();
|
|
183
|
+
if (plat === 'win32') {
|
|
184
|
+
// Zed not available on Windows, but provide path anyway
|
|
185
|
+
return join(home, '.config/zed/settings.json');
|
|
186
|
+
}
|
|
187
|
+
// macOS and Linux use XDG or fallback
|
|
188
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME;
|
|
189
|
+
if (xdgConfig) {
|
|
190
|
+
return join(xdgConfig, 'zed/settings.json');
|
|
191
|
+
}
|
|
192
|
+
return join(home, '.config/zed/settings.json');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Resolve Windsurf config path.
|
|
197
|
+
*/
|
|
198
|
+
function resolveWindsurfPath(home: string): string {
|
|
199
|
+
const plat = platform();
|
|
200
|
+
if (plat === 'win32') {
|
|
201
|
+
return join(home, '.codeium', 'windsurf', 'mcp_config.json');
|
|
202
|
+
}
|
|
203
|
+
return join(home, '.codeium/windsurf/mcp_config.json');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Resolve OpenCode config path.
|
|
208
|
+
*/
|
|
209
|
+
function resolveOpenCodePath(
|
|
210
|
+
scope: McpScope,
|
|
211
|
+
home: string,
|
|
212
|
+
cwd: string
|
|
213
|
+
): string {
|
|
214
|
+
if (scope === 'user') {
|
|
215
|
+
const plat = platform();
|
|
216
|
+
if (plat === 'win32') {
|
|
217
|
+
return join(home, '.config', 'opencode', 'config.json');
|
|
218
|
+
}
|
|
219
|
+
return join(home, '.config/opencode/config.json');
|
|
220
|
+
}
|
|
221
|
+
// Project scope: opencode.json in project root
|
|
222
|
+
return join(cwd, 'opencode.json');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Resolve Amp config path.
|
|
227
|
+
*/
|
|
228
|
+
function resolveAmpPath(home: string): string {
|
|
229
|
+
const plat = platform();
|
|
230
|
+
if (plat === 'win32') {
|
|
231
|
+
return join(home, '.config', 'amp', 'settings.json');
|
|
232
|
+
}
|
|
233
|
+
return join(home, '.config/amp/settings.json');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Resolve LM Studio config path.
|
|
238
|
+
*/
|
|
239
|
+
function resolveLmStudioPath(home: string): string {
|
|
240
|
+
const plat = platform();
|
|
241
|
+
if (plat === 'win32') {
|
|
242
|
+
return join(home, '.lmstudio', 'mcp.json');
|
|
243
|
+
}
|
|
244
|
+
return join(home, '.lmstudio/mcp.json');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Resolve LibreChat config path.
|
|
249
|
+
* LibreChat uses librechat.yaml in project root (project scope only).
|
|
250
|
+
*/
|
|
251
|
+
function resolveLibreChatPath(cwd: string): string {
|
|
252
|
+
return join(cwd, 'librechat.yaml');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Resolve MCP config path for a given target and scope.
|
|
257
|
+
*/
|
|
258
|
+
export function resolveMcpConfigPath(opts: McpPathOptions): McpConfigPaths {
|
|
259
|
+
const {
|
|
260
|
+
target,
|
|
261
|
+
scope = 'user',
|
|
262
|
+
cwd = process.cwd(),
|
|
263
|
+
homeDir = homedir(),
|
|
264
|
+
} = opts;
|
|
265
|
+
|
|
266
|
+
const configFormat = getTargetConfigFormat(target);
|
|
267
|
+
const supportsProjectScope = TARGETS_WITH_PROJECT_SCOPE.includes(target);
|
|
268
|
+
|
|
269
|
+
switch (target) {
|
|
270
|
+
case 'claude-desktop':
|
|
271
|
+
return {
|
|
272
|
+
configPath: resolveClaudeDesktopPath(homeDir),
|
|
273
|
+
supportsProjectScope,
|
|
274
|
+
configFormat,
|
|
275
|
+
};
|
|
276
|
+
case 'claude-code':
|
|
277
|
+
return {
|
|
278
|
+
configPath: resolveClaudeCodePath(scope, homeDir, cwd),
|
|
279
|
+
supportsProjectScope,
|
|
280
|
+
configFormat,
|
|
281
|
+
};
|
|
282
|
+
case 'codex':
|
|
283
|
+
return {
|
|
284
|
+
configPath: resolveCodexPath(scope, homeDir, cwd),
|
|
285
|
+
supportsProjectScope,
|
|
286
|
+
configFormat,
|
|
287
|
+
};
|
|
288
|
+
case 'cursor':
|
|
289
|
+
return {
|
|
290
|
+
configPath: resolveCursorPath(scope, homeDir, cwd),
|
|
291
|
+
supportsProjectScope,
|
|
292
|
+
configFormat,
|
|
293
|
+
};
|
|
294
|
+
case 'zed':
|
|
295
|
+
return {
|
|
296
|
+
configPath: resolveZedPath(homeDir),
|
|
297
|
+
supportsProjectScope,
|
|
298
|
+
configFormat,
|
|
299
|
+
};
|
|
300
|
+
case 'windsurf':
|
|
301
|
+
return {
|
|
302
|
+
configPath: resolveWindsurfPath(homeDir),
|
|
303
|
+
supportsProjectScope,
|
|
304
|
+
configFormat,
|
|
305
|
+
};
|
|
306
|
+
case 'opencode':
|
|
307
|
+
return {
|
|
308
|
+
configPath: resolveOpenCodePath(scope, homeDir, cwd),
|
|
309
|
+
supportsProjectScope,
|
|
310
|
+
configFormat,
|
|
311
|
+
};
|
|
312
|
+
case 'amp':
|
|
313
|
+
return {
|
|
314
|
+
configPath: resolveAmpPath(homeDir),
|
|
315
|
+
supportsProjectScope,
|
|
316
|
+
configFormat,
|
|
317
|
+
};
|
|
318
|
+
case 'lmstudio':
|
|
319
|
+
return {
|
|
320
|
+
configPath: resolveLmStudioPath(homeDir),
|
|
321
|
+
supportsProjectScope,
|
|
322
|
+
configFormat,
|
|
323
|
+
};
|
|
324
|
+
case 'librechat':
|
|
325
|
+
return {
|
|
326
|
+
configPath: resolveLibreChatPath(cwd),
|
|
327
|
+
supportsProjectScope,
|
|
328
|
+
configFormat,
|
|
329
|
+
};
|
|
330
|
+
default: {
|
|
331
|
+
const _exhaustive: never = target;
|
|
332
|
+
throw new Error(`Unknown target: ${_exhaustive}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Resolve paths for all targets.
|
|
339
|
+
*/
|
|
340
|
+
export function resolveAllMcpPaths(
|
|
341
|
+
scope: McpScope | 'all' = 'all',
|
|
342
|
+
target: McpTarget | 'all' = 'all',
|
|
343
|
+
overrides?: { cwd?: string; homeDir?: string }
|
|
344
|
+
): Array<{ target: McpTarget; scope: McpScope; paths: McpConfigPaths }> {
|
|
345
|
+
const targets: McpTarget[] = target === 'all' ? MCP_TARGETS : [target];
|
|
346
|
+
const results: Array<{
|
|
347
|
+
target: McpTarget;
|
|
348
|
+
scope: McpScope;
|
|
349
|
+
paths: McpConfigPaths;
|
|
350
|
+
}> = [];
|
|
351
|
+
|
|
352
|
+
for (const t of targets) {
|
|
353
|
+
const supportsProject = TARGETS_WITH_PROJECT_SCOPE.includes(t);
|
|
354
|
+
|
|
355
|
+
if (supportsProject) {
|
|
356
|
+
// Targets that support both scopes
|
|
357
|
+
const scopes: McpScope[] =
|
|
358
|
+
scope === 'all' ? ['user', 'project'] : [scope];
|
|
359
|
+
for (const s of scopes) {
|
|
360
|
+
results.push({
|
|
361
|
+
target: t,
|
|
362
|
+
scope: s,
|
|
363
|
+
paths: resolveMcpConfigPath({ target: t, scope: s, ...overrides }),
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
} else {
|
|
367
|
+
// User scope only - skip if filtering by project
|
|
368
|
+
if (scope === 'project') {
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
results.push({
|
|
372
|
+
target: t,
|
|
373
|
+
scope: 'user',
|
|
374
|
+
paths: resolveMcpConfigPath({ target: t, scope: 'user', ...overrides }),
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return results;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
383
|
+
// Bun & GNO Path Discovery
|
|
384
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Find absolute path to bun executable.
|
|
388
|
+
* Cross-platform: uses process.execPath since we're already running under Bun.
|
|
389
|
+
*/
|
|
390
|
+
export function findBunPath(): string {
|
|
391
|
+
// process.execPath is the absolute path to the Bun binary running this process.
|
|
392
|
+
// This is cross-platform and doesn't require shelling out to `which`.
|
|
393
|
+
return process.execPath;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Detect how gno should be invoked and return the MCP server entry.
|
|
398
|
+
* Uses absolute paths because Claude Desktop has a limited PATH.
|
|
399
|
+
* Cross-platform: avoids shelling out to `which`.
|
|
400
|
+
*/
|
|
401
|
+
export function buildMcpServerEntry(): McpServerEntry {
|
|
402
|
+
const bunPath = findBunPath();
|
|
403
|
+
const home = homedir();
|
|
404
|
+
const isWindows = platform() === 'win32';
|
|
405
|
+
|
|
406
|
+
// 1. Check if running from source (dev mode)
|
|
407
|
+
const scriptPath = process.argv[1];
|
|
408
|
+
if (
|
|
409
|
+
scriptPath?.includes('/gno/src/cli/') ||
|
|
410
|
+
scriptPath?.includes('\\gno\\src\\cli\\')
|
|
411
|
+
) {
|
|
412
|
+
// Dev mode: run the entry script directly with bun
|
|
413
|
+
const entryScript = scriptPath.replace(COMMANDS_PATH_PATTERN, '/index.ts');
|
|
414
|
+
return { command: bunPath, args: ['run', entryScript, 'mcp'] };
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// 2. Check common gno install locations (cross-platform)
|
|
418
|
+
const gnoCandidates = isWindows
|
|
419
|
+
? [
|
|
420
|
+
join(home, '.bun\\bin\\gno.exe'),
|
|
421
|
+
join(home, 'AppData\\Roaming\\npm\\gno.cmd'),
|
|
422
|
+
]
|
|
423
|
+
: [
|
|
424
|
+
join(home, '.bun/bin/gno'),
|
|
425
|
+
'/usr/local/bin/gno',
|
|
426
|
+
'/opt/homebrew/bin/gno',
|
|
427
|
+
];
|
|
428
|
+
|
|
429
|
+
for (const gnoPath of gnoCandidates) {
|
|
430
|
+
if (existsSync(gnoPath)) {
|
|
431
|
+
return { command: bunPath, args: [gnoPath, 'mcp'] };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// 3. Fallback to bunx (works if gno is published to npm)
|
|
436
|
+
// Note: This may trigger network access on first run
|
|
437
|
+
return { command: bunPath, args: ['x', '@gmickel/gno', 'mcp'] };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Get display name for a target.
|
|
442
|
+
*/
|
|
443
|
+
export function getTargetDisplayName(target: McpTarget): string {
|
|
444
|
+
switch (target) {
|
|
445
|
+
case 'claude-desktop':
|
|
446
|
+
return 'Claude Desktop';
|
|
447
|
+
case 'claude-code':
|
|
448
|
+
return 'Claude Code';
|
|
449
|
+
case 'codex':
|
|
450
|
+
return 'Codex';
|
|
451
|
+
case 'cursor':
|
|
452
|
+
return 'Cursor';
|
|
453
|
+
case 'zed':
|
|
454
|
+
return 'Zed';
|
|
455
|
+
case 'windsurf':
|
|
456
|
+
return 'Windsurf';
|
|
457
|
+
case 'opencode':
|
|
458
|
+
return 'OpenCode';
|
|
459
|
+
case 'amp':
|
|
460
|
+
return 'Amp';
|
|
461
|
+
case 'lmstudio':
|
|
462
|
+
return 'LM Studio';
|
|
463
|
+
case 'librechat':
|
|
464
|
+
return 'LibreChat';
|
|
465
|
+
default: {
|
|
466
|
+
const _exhaustive: never = target;
|
|
467
|
+
throw new Error(`Unknown target: ${_exhaustive}`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Show MCP server installation status across all targets.
|
|
3
|
+
*
|
|
4
|
+
* @module src/cli/commands/mcp/status
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { getGlobals } from '../../program.js';
|
|
8
|
+
import {
|
|
9
|
+
type AnyMcpConfig,
|
|
10
|
+
getServerEntry,
|
|
11
|
+
isYamlFormat,
|
|
12
|
+
type OpenCodeMcpEntry,
|
|
13
|
+
type StandardMcpEntry,
|
|
14
|
+
} from './config.js';
|
|
15
|
+
import {
|
|
16
|
+
getTargetDisplayName,
|
|
17
|
+
MCP_SERVER_NAME,
|
|
18
|
+
MCP_TARGETS,
|
|
19
|
+
type McpScope,
|
|
20
|
+
type McpTarget,
|
|
21
|
+
resolveMcpConfigPath,
|
|
22
|
+
TARGETS_WITH_PROJECT_SCOPE,
|
|
23
|
+
} from './paths.js';
|
|
24
|
+
|
|
25
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
26
|
+
// Types
|
|
27
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
export interface StatusOptions {
|
|
30
|
+
target?: McpTarget | 'all';
|
|
31
|
+
scope?: McpScope | 'all';
|
|
32
|
+
/** Override cwd (testing) */
|
|
33
|
+
cwd?: string;
|
|
34
|
+
/** Override home dir (testing) */
|
|
35
|
+
homeDir?: string;
|
|
36
|
+
/** JSON output */
|
|
37
|
+
json?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface TargetStatus {
|
|
41
|
+
target: McpTarget;
|
|
42
|
+
scope: McpScope;
|
|
43
|
+
configPath: string;
|
|
44
|
+
configured: boolean;
|
|
45
|
+
serverEntry?: { command: string; args: string[] };
|
|
46
|
+
error?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface StatusResult {
|
|
50
|
+
targets: TargetStatus[];
|
|
51
|
+
summary: {
|
|
52
|
+
configured: number;
|
|
53
|
+
total: number;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
58
|
+
// Config Reading
|
|
59
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Normalize entry to standard format for display.
|
|
63
|
+
*/
|
|
64
|
+
function normalizeEntry(
|
|
65
|
+
entry: StandardMcpEntry | OpenCodeMcpEntry
|
|
66
|
+
): StandardMcpEntry {
|
|
67
|
+
if ('type' in entry && entry.type === 'local') {
|
|
68
|
+
// OpenCode format: command is array [command, ...args]
|
|
69
|
+
const [command = '', ...args] = entry.command;
|
|
70
|
+
return { command, args };
|
|
71
|
+
}
|
|
72
|
+
return entry as StandardMcpEntry;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function checkTargetStatus(
|
|
76
|
+
target: McpTarget,
|
|
77
|
+
scope: McpScope,
|
|
78
|
+
options: { cwd?: string; homeDir?: string }
|
|
79
|
+
): Promise<TargetStatus> {
|
|
80
|
+
const { cwd, homeDir } = options;
|
|
81
|
+
|
|
82
|
+
const { configPath, configFormat } = resolveMcpConfigPath({
|
|
83
|
+
target,
|
|
84
|
+
scope,
|
|
85
|
+
cwd,
|
|
86
|
+
homeDir,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const file = Bun.file(configPath);
|
|
90
|
+
const exists = await file.exists();
|
|
91
|
+
|
|
92
|
+
if (!exists) {
|
|
93
|
+
return { target, scope, configPath, configured: false };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const content = await file.text();
|
|
98
|
+
if (!content.trim()) {
|
|
99
|
+
return { target, scope, configPath, configured: false };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const useYaml = isYamlFormat(configFormat);
|
|
103
|
+
const config = useYaml
|
|
104
|
+
? (Bun.YAML.parse(content) as AnyMcpConfig)
|
|
105
|
+
: (JSON.parse(content) as AnyMcpConfig);
|
|
106
|
+
const entry = getServerEntry(config, MCP_SERVER_NAME, configFormat);
|
|
107
|
+
|
|
108
|
+
if (entry) {
|
|
109
|
+
const serverEntry = normalizeEntry(entry);
|
|
110
|
+
return { target, scope, configPath, configured: true, serverEntry };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { target, scope, configPath, configured: false };
|
|
114
|
+
} catch {
|
|
115
|
+
const format = isYamlFormat(configFormat) ? 'YAML' : 'JSON';
|
|
116
|
+
return {
|
|
117
|
+
target,
|
|
118
|
+
scope,
|
|
119
|
+
configPath,
|
|
120
|
+
configured: false,
|
|
121
|
+
error: `Malformed ${format}`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
127
|
+
// Status Command
|
|
128
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get globals safely.
|
|
132
|
+
*/
|
|
133
|
+
function safeGetGlobals(): { json: boolean } {
|
|
134
|
+
try {
|
|
135
|
+
return getGlobals();
|
|
136
|
+
} catch {
|
|
137
|
+
return { json: false };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Show MCP installation status.
|
|
143
|
+
*/
|
|
144
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: status display with multiple targets and scopes
|
|
145
|
+
export async function statusMcp(opts: StatusOptions = {}): Promise<void> {
|
|
146
|
+
const targetFilter = opts.target ?? 'all';
|
|
147
|
+
const scopeFilter = opts.scope ?? 'all';
|
|
148
|
+
const globals = safeGetGlobals();
|
|
149
|
+
const json = opts.json ?? globals.json;
|
|
150
|
+
|
|
151
|
+
const targets: McpTarget[] =
|
|
152
|
+
targetFilter === 'all' ? MCP_TARGETS : [targetFilter];
|
|
153
|
+
|
|
154
|
+
const results: TargetStatus[] = [];
|
|
155
|
+
|
|
156
|
+
for (const target of targets) {
|
|
157
|
+
const supportsProject = TARGETS_WITH_PROJECT_SCOPE.includes(target);
|
|
158
|
+
|
|
159
|
+
if (supportsProject) {
|
|
160
|
+
// Targets that support both scopes
|
|
161
|
+
const scopes: McpScope[] =
|
|
162
|
+
scopeFilter === 'all' ? ['user', 'project'] : [scopeFilter];
|
|
163
|
+
|
|
164
|
+
for (const scope of scopes) {
|
|
165
|
+
results.push(
|
|
166
|
+
await checkTargetStatus(target, scope, {
|
|
167
|
+
cwd: opts.cwd,
|
|
168
|
+
homeDir: opts.homeDir,
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
} else if (scopeFilter === 'all' || scopeFilter === 'user') {
|
|
173
|
+
// User scope only - skip if filtering by project
|
|
174
|
+
results.push(
|
|
175
|
+
await checkTargetStatus(target, 'user', {
|
|
176
|
+
cwd: opts.cwd,
|
|
177
|
+
homeDir: opts.homeDir,
|
|
178
|
+
})
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const configured = results.filter((r) => r.configured).length;
|
|
184
|
+
const statusResult: StatusResult = {
|
|
185
|
+
targets: results,
|
|
186
|
+
summary: { configured, total: results.length },
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Output
|
|
190
|
+
if (json) {
|
|
191
|
+
process.stdout.write(`${JSON.stringify(statusResult, null, 2)}\n`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Terminal output
|
|
196
|
+
process.stdout.write('MCP Server Status\n');
|
|
197
|
+
process.stdout.write(`${'─'.repeat(50)}\n\n`);
|
|
198
|
+
|
|
199
|
+
for (const status of results) {
|
|
200
|
+
const targetName = getTargetDisplayName(status.target);
|
|
201
|
+
const scopeLabel = status.scope === 'project' ? ' (project)' : '';
|
|
202
|
+
const statusIcon = status.configured ? '✓' : '✗';
|
|
203
|
+
const statusText = status.configured ? 'configured' : 'not configured';
|
|
204
|
+
|
|
205
|
+
process.stdout.write(
|
|
206
|
+
`${statusIcon} ${targetName}${scopeLabel}: ${statusText}\n`
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
if (status.configured && status.serverEntry) {
|
|
210
|
+
process.stdout.write(` Command: ${status.serverEntry.command}\n`);
|
|
211
|
+
process.stdout.write(` Args: ${status.serverEntry.args.join(' ')}\n`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (status.error) {
|
|
215
|
+
process.stdout.write(` Error: ${status.error}\n`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
process.stdout.write(` Config: ${status.configPath}\n\n`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
process.stdout.write(`${configured}/${results.length} targets configured\n`);
|
|
222
|
+
}
|