agentinit 1.7.0 → 1.8.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.
Files changed (148) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +127 -104
  3. package/dist/agents/Agent.d.ts +44 -0
  4. package/dist/agents/Agent.d.ts.map +1 -1
  5. package/dist/agents/Agent.js +111 -22
  6. package/dist/agents/Agent.js.map +1 -1
  7. package/dist/agents/AiderAgent.d.ts +9 -0
  8. package/dist/agents/AiderAgent.d.ts.map +1 -0
  9. package/dist/agents/AiderAgent.js +135 -0
  10. package/dist/agents/AiderAgent.js.map +1 -0
  11. package/dist/agents/ClaudeAgent.d.ts +4 -0
  12. package/dist/agents/ClaudeAgent.d.ts.map +1 -1
  13. package/dist/agents/ClaudeAgent.js +33 -18
  14. package/dist/agents/ClaudeAgent.js.map +1 -1
  15. package/dist/agents/ClaudeDesktopAgent.d.ts +5 -0
  16. package/dist/agents/ClaudeDesktopAgent.d.ts.map +1 -1
  17. package/dist/agents/ClaudeDesktopAgent.js +31 -1
  18. package/dist/agents/ClaudeDesktopAgent.js.map +1 -1
  19. package/dist/agents/ClineAgent.d.ts +8 -0
  20. package/dist/agents/ClineAgent.d.ts.map +1 -0
  21. package/dist/agents/ClineAgent.js +42 -0
  22. package/dist/agents/ClineAgent.js.map +1 -0
  23. package/dist/agents/CodexCliAgent.d.ts +4 -0
  24. package/dist/agents/CodexCliAgent.d.ts.map +1 -1
  25. package/dist/agents/CodexCliAgent.js +38 -8
  26. package/dist/agents/CodexCliAgent.js.map +1 -1
  27. package/dist/agents/CopilotAgent.d.ts +9 -0
  28. package/dist/agents/CopilotAgent.d.ts.map +1 -0
  29. package/dist/agents/CopilotAgent.js +131 -0
  30. package/dist/agents/CopilotAgent.js.map +1 -0
  31. package/dist/agents/CursorAgent.d.ts +4 -4
  32. package/dist/agents/CursorAgent.d.ts.map +1 -1
  33. package/dist/agents/CursorAgent.js +32 -43
  34. package/dist/agents/CursorAgent.js.map +1 -1
  35. package/dist/agents/DroidAgent.d.ts +5 -0
  36. package/dist/agents/DroidAgent.d.ts.map +1 -1
  37. package/dist/agents/DroidAgent.js +37 -18
  38. package/dist/agents/DroidAgent.js.map +1 -1
  39. package/dist/agents/GeminiCliAgent.d.ts +4 -0
  40. package/dist/agents/GeminiCliAgent.d.ts.map +1 -1
  41. package/dist/agents/GeminiCliAgent.js +37 -8
  42. package/dist/agents/GeminiCliAgent.js.map +1 -1
  43. package/dist/agents/MarkdownRulesAgent.d.ts +13 -0
  44. package/dist/agents/MarkdownRulesAgent.d.ts.map +1 -0
  45. package/dist/agents/MarkdownRulesAgent.js +53 -0
  46. package/dist/agents/MarkdownRulesAgent.js.map +1 -0
  47. package/dist/agents/RooCodeAgent.d.ts +9 -0
  48. package/dist/agents/RooCodeAgent.d.ts.map +1 -0
  49. package/dist/agents/RooCodeAgent.js +131 -0
  50. package/dist/agents/RooCodeAgent.js.map +1 -0
  51. package/dist/agents/WindsurfAgent.d.ts +9 -0
  52. package/dist/agents/WindsurfAgent.d.ts.map +1 -0
  53. package/dist/agents/WindsurfAgent.js +127 -0
  54. package/dist/agents/WindsurfAgent.js.map +1 -0
  55. package/dist/agents/ZedAgent.d.ts +9 -0
  56. package/dist/agents/ZedAgent.d.ts.map +1 -0
  57. package/dist/agents/ZedAgent.js +127 -0
  58. package/dist/agents/ZedAgent.js.map +1 -0
  59. package/dist/cli.js +30587 -11981
  60. package/dist/cli.js.map +1 -1
  61. package/dist/commands/apply.d.ts +10 -0
  62. package/dist/commands/apply.d.ts.map +1 -1
  63. package/dist/commands/apply.js +134 -3
  64. package/dist/commands/apply.js.map +1 -1
  65. package/dist/commands/detect.d.ts.map +1 -1
  66. package/dist/commands/detect.js +25 -0
  67. package/dist/commands/detect.js.map +1 -1
  68. package/dist/commands/init.js +1 -1
  69. package/dist/commands/init.js.map +1 -1
  70. package/dist/commands/mcp.d.ts +2 -7
  71. package/dist/commands/mcp.d.ts.map +1 -1
  72. package/dist/commands/mcp.js +541 -110
  73. package/dist/commands/mcp.js.map +1 -1
  74. package/dist/commands/plugins.d.ts +3 -0
  75. package/dist/commands/plugins.d.ts.map +1 -0
  76. package/dist/commands/plugins.js +309 -0
  77. package/dist/commands/plugins.js.map +1 -0
  78. package/dist/commands/revert.d.ts +7 -0
  79. package/dist/commands/revert.d.ts.map +1 -0
  80. package/dist/commands/revert.js +48 -0
  81. package/dist/commands/revert.js.map +1 -0
  82. package/dist/commands/rules.d.ts +3 -0
  83. package/dist/commands/rules.d.ts.map +1 -0
  84. package/dist/commands/rules.js +354 -0
  85. package/dist/commands/rules.js.map +1 -0
  86. package/dist/commands/skills.d.ts +3 -0
  87. package/dist/commands/skills.d.ts.map +1 -0
  88. package/dist/commands/skills.js +179 -0
  89. package/dist/commands/skills.js.map +1 -0
  90. package/dist/commands/sync.d.ts +1 -0
  91. package/dist/commands/sync.d.ts.map +1 -1
  92. package/dist/commands/sync.js +18 -1
  93. package/dist/commands/sync.js.map +1 -1
  94. package/dist/commands/verifyMcp.js +1 -1
  95. package/dist/commands/verifyMcp.js.map +1 -1
  96. package/dist/core/agentDetector.d.ts.map +1 -1
  97. package/dist/core/agentDetector.js +8 -2
  98. package/dist/core/agentDetector.js.map +1 -1
  99. package/dist/core/agentManager.d.ts.map +1 -1
  100. package/dist/core/agentManager.js +12 -0
  101. package/dist/core/agentManager.js.map +1 -1
  102. package/dist/core/gitignoreManager.d.ts +8 -0
  103. package/dist/core/gitignoreManager.d.ts.map +1 -0
  104. package/dist/core/gitignoreManager.js +114 -0
  105. package/dist/core/gitignoreManager.js.map +1 -0
  106. package/dist/core/managedState.d.ts +42 -0
  107. package/dist/core/managedState.d.ts.map +1 -0
  108. package/dist/core/managedState.js +194 -0
  109. package/dist/core/managedState.js.map +1 -0
  110. package/dist/core/pluginManager.d.ts +134 -0
  111. package/dist/core/pluginManager.d.ts.map +1 -0
  112. package/dist/core/pluginManager.js +845 -0
  113. package/dist/core/pluginManager.js.map +1 -0
  114. package/dist/core/projectSkills.d.ts +19 -0
  115. package/dist/core/projectSkills.d.ts.map +1 -0
  116. package/dist/core/projectSkills.js +105 -0
  117. package/dist/core/projectSkills.js.map +1 -0
  118. package/dist/core/propagator.d.ts +8 -1
  119. package/dist/core/propagator.d.ts.map +1 -1
  120. package/dist/core/propagator.js +179 -36
  121. package/dist/core/propagator.js.map +1 -1
  122. package/dist/core/rulesApplicator.d.ts +0 -4
  123. package/dist/core/rulesApplicator.d.ts.map +1 -1
  124. package/dist/core/rulesApplicator.js +8 -39
  125. package/dist/core/rulesApplicator.js.map +1 -1
  126. package/dist/core/rulesTemplateLoader.js +2 -2
  127. package/dist/core/rulesTemplateLoader.js.map +1 -1
  128. package/dist/core/skillsManager.d.ts +61 -0
  129. package/dist/core/skillsManager.d.ts.map +1 -0
  130. package/dist/core/skillsManager.js +407 -0
  131. package/dist/core/skillsManager.js.map +1 -0
  132. package/dist/types/index.d.ts +14 -0
  133. package/dist/types/index.d.ts.map +1 -1
  134. package/dist/types/index.js.map +1 -1
  135. package/dist/types/plugins.d.ts +161 -0
  136. package/dist/types/plugins.d.ts.map +1 -0
  137. package/dist/types/plugins.js +2 -0
  138. package/dist/types/plugins.js.map +1 -0
  139. package/dist/types/skills.d.ts +50 -0
  140. package/dist/types/skills.d.ts.map +1 -0
  141. package/dist/types/skills.js +2 -0
  142. package/dist/types/skills.js.map +1 -0
  143. package/package.json +7 -2
  144. package/dist/agentinit-1.7.0.tgz +0 -0
  145. package/dist/registry/mcpRegistry.d.ts +0 -12
  146. package/dist/registry/mcpRegistry.d.ts.map +0 -1
  147. package/dist/registry/mcpRegistry.js +0 -114
  148. package/dist/registry/mcpRegistry.js.map +0 -1
@@ -1,127 +1,558 @@
1
1
  import ora from 'ora';
2
- import prompts from 'prompts';
3
- import { exec } from 'child_process';
4
- import { promisify } from 'util';
2
+ import { green, cyan, yellow, red } from 'kleur/colors';
5
3
  import { logger } from '../utils/logger.js';
6
- import { MCPRegistryManager } from '../registry/mcpRegistry.js';
7
- import { StackDetector } from '../core/stackDetector.js';
8
- const execAsync = promisify(exec);
9
- export async function mcpCommand(options) {
10
- const cwd = process.cwd();
11
- logger.title('📦 AgentInit - MCP Management');
12
- const registryManager = new MCPRegistryManager();
13
- if (options.search) {
14
- await searchMCPs(registryManager, options.search);
15
- return;
16
- }
17
- if (options.install) {
18
- await installMCP(registryManager, options.install);
19
- return;
20
- }
21
- if (options.interactive) {
22
- await interactiveMCPSelection(registryManager, cwd);
23
- return;
24
- }
25
- // Default: show top MCPs
26
- showTopMCPs(registryManager);
27
- }
28
- async function searchMCPs(registry, query) {
29
- const results = registry.searchMCPs(query);
30
- if (results.length === 0) {
31
- logger.info(`No MCPs found matching "${query}"`);
32
- return;
4
+ import { MCPParser, MCPParseError } from '../core/mcpParser.js';
5
+ import { MCPVerifier } from '../core/mcpClient.js';
6
+ import { MCPFilter } from '../core/mcpFilter.js';
7
+ import { AgentManager } from '../core/agentManager.js';
8
+ import { DEFAULT_CONNECTION_TIMEOUT_MS } from '../constants/index.js';
9
+ /**
10
+ * Options that `mcp add` recognizes and must be stripped before
11
+ * passing the remaining argv to MCPParser.
12
+ *
13
+ * Map key = the flag itself
14
+ * Map value = true if the flag takes a value argument, false if boolean
15
+ */
16
+ const KNOWN_ADD_OPTIONS = {
17
+ '--agent': true,
18
+ '--global': false,
19
+ '--verify': false,
20
+ '--timeout': true,
21
+ };
22
+ /**
23
+ * Strip known CLI options (and their values) from the raw arg list
24
+ * so that only `--mcp-*` flags and their modifiers remain.
25
+ */
26
+ function filterMcpArgs(args) {
27
+ const filtered = [];
28
+ let i = 0;
29
+ while (i < args.length) {
30
+ const arg = args[i];
31
+ const meta = KNOWN_ADD_OPTIONS[arg];
32
+ if (meta !== undefined) {
33
+ // Skip the flag itself
34
+ i++;
35
+ // If it takes a value, also skip the next token
36
+ if (meta && i < args.length) {
37
+ i++;
38
+ }
39
+ continue;
40
+ }
41
+ filtered.push(arg);
42
+ i++;
33
43
  }
34
- logger.success(`Found ${results.length} MCPs matching "${query}":`);
35
- results.forEach(mcp => displayMCP(mcp));
44
+ return filtered;
36
45
  }
37
- async function installMCP(registry, name) {
38
- const mcp = registry.getMCPByName(name);
39
- if (!mcp) {
40
- logger.error(`MCP "${name}" not found in registry`);
41
- return;
46
+ /**
47
+ * Resolve the list of agents to operate on.
48
+ *
49
+ * - If `agentIds` is provided, look up each one explicitly.
50
+ * - If `isGlobal` is set without agents, error out (global requires explicit agent).
51
+ * - Otherwise auto-detect agents in the current project.
52
+ */
53
+ async function resolveAgents(agentManager, agentIds, isGlobal, cwd) {
54
+ if (agentIds && agentIds.length > 0) {
55
+ const results = [];
56
+ for (const id of agentIds) {
57
+ const agent = agentManager.getAgentById(id);
58
+ if (!agent) {
59
+ logger.error(`Unknown agent: ${id}`);
60
+ logger.info(`Supported agents: ${agentManager.getSupportedAgentIds().join(', ')}`);
61
+ process.exit(1);
62
+ }
63
+ if (isGlobal && !agent.supportsGlobalConfig()) {
64
+ logger.error(`Agent ${agent.name} does not support global configuration`);
65
+ process.exit(1);
66
+ }
67
+ if (!isGlobal) {
68
+ const detection = await agent.detectPresence(cwd);
69
+ if (!detection) {
70
+ logger.warning(`Agent ${id} not detected in project, skipping`);
71
+ continue;
72
+ }
73
+ }
74
+ results.push({ agent, isGlobal });
75
+ }
76
+ return results;
42
77
  }
43
- const spinner = ora(`Installing ${mcp.name}...`).start();
44
- try {
45
- await execAsync(mcp.installCommand);
46
- spinner.succeed(`${mcp.name} installed successfully`);
47
- logger.info('Agent Instructions:');
48
- logger.info(mcp.agentInstructions);
78
+ if (isGlobal) {
79
+ logger.error('--global requires --agent to specify target agent(s)');
80
+ process.exit(1);
49
81
  }
50
- catch (error) {
51
- spinner.fail(`Failed to install ${mcp.name}`);
52
- logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
82
+ // Auto-detect
83
+ const detected = await agentManager.detectAgents(cwd);
84
+ if (detected.length === 0) {
85
+ logger.warning('No AI coding agents detected in this project');
86
+ logger.info('Supported agents:');
87
+ agentManager.getAllAgents().forEach(a => {
88
+ logger.info(` - ${a.name} (${a.id})`);
89
+ });
90
+ logger.info('');
91
+ logger.info('To target a specific agent, use: --agent <agent-id>');
92
+ return [];
53
93
  }
94
+ return detected.map(d => ({ agent: d.agent, isGlobal: false }));
54
95
  }
55
- async function interactiveMCPSelection(registry, projectPath) {
56
- // Detect stack to show relevant MCPs
57
- const stackDetector = new StackDetector();
58
- const stack = await stackDetector.detectStack(projectPath);
59
- logger.info(`Detected stack: ${stack.language}${stack.framework ? ` with ${stack.framework}` : ''}`);
60
- // Get recommendations based on stack
61
- const stackMCPs = registry.getMCPsForStack(stack.language);
62
- const topMCPs = registry.getTopMCPs(4);
63
- // Combine and deduplicate
64
- const recommendedMCPs = [...new Set([...stackMCPs, ...topMCPs])].slice(0, 6);
65
- const choices = recommendedMCPs.map(mcp => ({
66
- title: `${mcp.name} - ${mcp.description}`,
67
- value: mcp,
68
- selected: false
69
- }));
70
- choices.push({
71
- title: '🔍 Search for more MCPs',
72
- value: 'search',
73
- selected: false
96
+ // ---------------------------------------------------------------------------
97
+ // Subcommand: mcp add
98
+ // ---------------------------------------------------------------------------
99
+ function registerAddCommand(mcp) {
100
+ mcp
101
+ .command('add')
102
+ .description('Add MCP server(s) to agent configurations')
103
+ .allowUnknownOption(true)
104
+ .option('--agent <agents...>', 'Target specific agent(s)')
105
+ .option('-g, --global', 'Apply to global configuration')
106
+ .option('--verify', 'Verify MCP servers after adding')
107
+ .option('--timeout <ms>', 'Connection timeout for verification (ms)')
108
+ .action(async (options, command) => {
109
+ const cwd = process.cwd();
110
+ // Extract raw args that come after "mcp add"
111
+ const allArgs = command.parent.parent.rawArgs;
112
+ const addIdx = allArgs.indexOf('add');
113
+ if (addIdx < 0) {
114
+ logger.error('Internal error: could not locate "add" in raw arguments');
115
+ process.exit(1);
116
+ }
117
+ const rawArgs = allArgs.slice(addIdx + 1);
118
+ // Strip known options so only --mcp-* flags remain
119
+ const mcpArgs = filterMcpArgs(rawArgs);
120
+ if (mcpArgs.length === 0 || !mcpArgs.some(a => a.startsWith('--mcp-'))) {
121
+ logger.info('Usage: agentinit mcp add [options] --mcp-stdio|--mcp-http|--mcp-sse <name> <command|url> [modifiers]');
122
+ logger.info('');
123
+ logger.info('Options:');
124
+ logger.info(' --agent <agents...> Target specific agent(s)');
125
+ logger.info(' -g, --global Apply to global configuration');
126
+ logger.info(' --verify Verify MCP servers after adding');
127
+ logger.info(` --timeout <ms> Verification timeout (default: ${DEFAULT_CONNECTION_TIMEOUT_MS})`);
128
+ logger.info('');
129
+ logger.info('Examples:');
130
+ logger.info(' agentinit mcp add --mcp-stdio exa "npx -y @exa/mcp-server" --env "EXA_API_KEY=key"');
131
+ logger.info(' agentinit mcp add --agent claude --mcp-http github "https://api.github.com/mcp" --auth "Bearer token"');
132
+ logger.info(' agentinit mcp add --global --agent claude --mcp-stdio fs "npx -y @modelcontextprotocol/server-filesystem" --args "/home"');
133
+ return;
134
+ }
135
+ const spinner = ora('Parsing MCP configuration...').start();
136
+ try {
137
+ const parsed = MCPParser.parseArguments(mcpArgs);
138
+ if (parsed.servers.length === 0) {
139
+ spinner.warn('No MCP servers found in arguments');
140
+ return;
141
+ }
142
+ spinner.text = 'Resolving target agents...';
143
+ const agentManager = new AgentManager();
144
+ const isGlobal = !!options.global;
145
+ const targets = await resolveAgents(agentManager, options.agent, isGlobal, cwd);
146
+ if (targets.length === 0) {
147
+ spinner.warn('No target agents resolved');
148
+ return;
149
+ }
150
+ let totalApplied = 0;
151
+ for (const { agent, isGlobal: global } of targets) {
152
+ spinner.text = `Applying to ${agent.name}...`;
153
+ const filtered = MCPFilter.filterForAgent(agent, parsed.servers);
154
+ if (filtered.servers.length === 0) {
155
+ logger.warning(`${agent.name}: No compatible MCP servers`);
156
+ continue;
157
+ }
158
+ if (global) {
159
+ await agent.applyGlobalMCPConfig(filtered.servers);
160
+ }
161
+ else {
162
+ await agent.applyMCPConfig(cwd, filtered.servers);
163
+ }
164
+ totalApplied += filtered.servers.length;
165
+ const configPath = global
166
+ ? agent.getGlobalMcpPath()
167
+ : agent.getNativeMcpPath(cwd);
168
+ logger.info(` ${green('+')} ${agent.name}: ${filtered.servers.length} server(s) added`);
169
+ if (configPath) {
170
+ logger.info(` Config: ${configPath}`);
171
+ }
172
+ if (filtered.transformations.length > 0) {
173
+ filtered.transformations.forEach(t => {
174
+ logger.info(` ${yellow('~')} ${t.original.name}: ${t.reason}`);
175
+ });
176
+ }
177
+ }
178
+ if (totalApplied === 0) {
179
+ spinner.warn('No servers were applied (all filtered out)');
180
+ }
181
+ else {
182
+ spinner.succeed(`Added ${totalApplied} MCP server(s)`);
183
+ // List what was added
184
+ parsed.servers.forEach(server => {
185
+ const typeLabel = server.type.toUpperCase();
186
+ if (server.type === 'stdio' && server.command) {
187
+ logger.info(` ${cyan(server.name)} (${typeLabel}): ${server.command} ${server.args?.join(' ') || ''}`);
188
+ }
189
+ else if (server.url) {
190
+ logger.info(` ${cyan(server.name)} (${typeLabel}): ${server.url}`);
191
+ }
192
+ });
193
+ }
194
+ // Verify if requested
195
+ if (options.verify && parsed.servers.length > 0) {
196
+ logger.info('');
197
+ const timeout = options.timeout ? parseInt(options.timeout, 10) : undefined;
198
+ await runVerification(parsed.servers, timeout);
199
+ }
200
+ }
201
+ catch (error) {
202
+ spinner.fail('Failed to add MCP servers');
203
+ if (error instanceof MCPParseError) {
204
+ logger.error(error.message);
205
+ }
206
+ else {
207
+ logger.error(`${error instanceof Error ? error.message : 'Unknown error'}`);
208
+ }
209
+ process.exit(1);
210
+ }
74
211
  });
75
- const response = await prompts({
76
- type: 'multiselect',
77
- name: 'mcps',
78
- message: '🔥 Select MCPs to install:',
79
- choices,
80
- hint: 'Space to select, Enter to confirm'
212
+ }
213
+ // ---------------------------------------------------------------------------
214
+ // Subcommand: mcp list (alias: ls)
215
+ // ---------------------------------------------------------------------------
216
+ function registerListCommand(mcp) {
217
+ mcp
218
+ .command('list')
219
+ .alias('ls')
220
+ .description('List configured MCP servers')
221
+ .option('--agent <agents...>', 'Target specific agent(s)')
222
+ .option('-g, --global', 'List global configuration')
223
+ .action(async (options) => {
224
+ const cwd = process.cwd();
225
+ const agentManager = new AgentManager();
226
+ const isGlobal = !!options.global;
227
+ const spinner = ora('Reading MCP configurations...').start();
228
+ try {
229
+ const targets = await resolveAgents(agentManager, options.agent, isGlobal, cwd);
230
+ if (targets.length === 0) {
231
+ spinner.warn('No target agents resolved');
232
+ return;
233
+ }
234
+ spinner.stop();
235
+ let totalServers = 0;
236
+ for (const { agent, isGlobal: global } of targets) {
237
+ let servers = [];
238
+ try {
239
+ if (global) {
240
+ servers = await agent.getGlobalMCPServers();
241
+ }
242
+ else {
243
+ servers = await agent.getMCPServers(cwd);
244
+ }
245
+ }
246
+ catch {
247
+ // Agent may not have MCP config yet
248
+ }
249
+ const scope = global ? ' (global)' : '';
250
+ logger.subtitle(`${agent.name}${scope}:`);
251
+ if (servers.length === 0) {
252
+ logger.info(' No MCP servers configured');
253
+ }
254
+ else {
255
+ for (const server of servers) {
256
+ const typeLabel = server.type.toUpperCase();
257
+ if (server.type === 'stdio' && server.command) {
258
+ logger.info(` ${cyan(server.name)} (${typeLabel})`);
259
+ logger.info(` Command: ${server.command} ${server.args?.join(' ') || ''}`);
260
+ }
261
+ else if (server.url) {
262
+ // Sanitize URL
263
+ let sanitized;
264
+ try {
265
+ const u = new URL(server.url);
266
+ u.username = '';
267
+ u.password = '';
268
+ u.search = '';
269
+ sanitized = u.toString();
270
+ }
271
+ catch {
272
+ sanitized = server.url.split('?')[0] || server.url;
273
+ }
274
+ logger.info(` ${cyan(server.name)} (${typeLabel})`);
275
+ logger.info(` URL: ${sanitized}`);
276
+ }
277
+ else {
278
+ logger.info(` ${cyan(server.name)} (${typeLabel})`);
279
+ }
280
+ if (server.env && Object.keys(server.env).length > 0) {
281
+ const keys = Object.keys(server.env).join(', ');
282
+ logger.info(` Env: ${keys}`);
283
+ }
284
+ }
285
+ }
286
+ totalServers += servers.length;
287
+ logger.info('');
288
+ }
289
+ logger.info(`Total: ${totalServers} MCP server(s) across ${targets.length} agent(s)`);
290
+ }
291
+ catch (error) {
292
+ spinner.fail('Failed to list MCP servers');
293
+ logger.error(`${error instanceof Error ? error.message : 'Unknown error'}`);
294
+ process.exit(1);
295
+ }
81
296
  });
82
- if (!response.mcps || response.mcps.length === 0) {
83
- logger.info('No MCPs selected');
84
- return;
85
- }
86
- // Handle search option
87
- if (response.mcps.includes('search')) {
88
- const searchResponse = await prompts({
89
- type: 'text',
90
- name: 'query',
91
- message: 'Search MCPs:'
92
- });
93
- if (searchResponse.query) {
94
- await searchMCPs(registry, searchResponse.query);
297
+ }
298
+ // ---------------------------------------------------------------------------
299
+ // Subcommand: mcp remove <name>
300
+ // ---------------------------------------------------------------------------
301
+ function registerRemoveCommand(mcp) {
302
+ mcp
303
+ .command('remove <name>')
304
+ .description('Remove an MCP server by name')
305
+ .option('--agent <agents...>', 'Target specific agent(s)')
306
+ .option('-g, --global', 'Remove from global configuration')
307
+ .action(async (name, options) => {
308
+ const cwd = process.cwd();
309
+ const agentManager = new AgentManager();
310
+ const isGlobal = !!options.global;
311
+ const spinner = ora(`Removing MCP server "${name}"...`).start();
312
+ try {
313
+ const targets = await resolveAgents(agentManager, options.agent, isGlobal, cwd);
314
+ if (targets.length === 0) {
315
+ spinner.warn('No target agents resolved');
316
+ return;
317
+ }
318
+ let removedCount = 0;
319
+ let failedCount = 0;
320
+ for (const { agent, isGlobal: global } of targets) {
321
+ try {
322
+ let removed;
323
+ if (global) {
324
+ removed = await agent.removeGlobalMCPServer(name);
325
+ }
326
+ else {
327
+ removed = await agent.removeMCPServer(cwd, name);
328
+ }
329
+ if (removed) {
330
+ removedCount++;
331
+ logger.info(` ${green('-')} ${agent.name}: removed "${name}"`);
332
+ }
333
+ else {
334
+ logger.info(` ${yellow('~')} ${agent.name}: "${name}" not found`);
335
+ }
336
+ }
337
+ catch (err) {
338
+ failedCount++;
339
+ logger.info(` ${red('x')} ${agent.name}: ${err instanceof Error ? err.message : 'unknown error'}`);
340
+ }
341
+ }
342
+ if (removedCount > 0) {
343
+ spinner.succeed(`Removed "${name}" from ${removedCount} agent(s)`);
344
+ }
345
+ else if (failedCount > 0) {
346
+ spinner.fail(`Failed to remove "${name}"`);
347
+ }
348
+ else {
349
+ spinner.warn(`"${name}" was not found in any agent configuration`);
350
+ }
95
351
  }
96
- return;
97
- }
98
- // Install selected MCPs
99
- for (const mcp of response.mcps) {
100
- if (typeof mcp === 'object') {
101
- await installMCP(registry, mcp.name);
352
+ catch (error) {
353
+ spinner.fail(`Failed to remove MCP server "${name}"`);
354
+ logger.error(`${error instanceof Error ? error.message : 'Unknown error'}`);
355
+ process.exit(1);
102
356
  }
103
- }
104
- logger.success('✨ MCP installation complete!');
105
- logger.info('Next steps:');
106
- logger.info(' 1. Update your agents.md with MCP-specific instructions');
107
- logger.info(' 2. Run `agentinit sync` to apply changes to agent configs');
357
+ });
108
358
  }
109
- function showTopMCPs(registry) {
110
- const topMCPs = registry.getTopMCPs(5);
111
- logger.subtitle('🔥 Top Recommended MCPs:');
112
- topMCPs.forEach(mcp => displayMCP(mcp));
113
- logger.info('');
114
- logger.info('Commands:');
115
- logger.info(' agentinit mcp --interactive Interactive MCP selection');
116
- logger.info(' agentinit mcp --search <query> Search for MCPs');
117
- logger.info(' agentinit mcp --install <name> Install specific MCP');
359
+ // ---------------------------------------------------------------------------
360
+ // Subcommand: mcp verify
361
+ // ---------------------------------------------------------------------------
362
+ function registerVerifyCommand(mcp) {
363
+ mcp
364
+ .command('verify')
365
+ .description('Verify MCP server connectivity and capabilities')
366
+ .allowUnknownOption(true)
367
+ .option('--all', 'Verify all configured MCP servers')
368
+ .option('--name <name>', 'Verify a specific MCP server by name')
369
+ .option('--timeout <ms>', 'Connection timeout in milliseconds')
370
+ .action(async (options, command) => {
371
+ const cwd = process.cwd();
372
+ // Determine if raw MCP args were provided for direct verification
373
+ const allArgs = command.parent.parent.rawArgs;
374
+ const verifyIdx = allArgs.indexOf('verify');
375
+ const rawArgs = verifyIdx >= 0 ? allArgs.slice(verifyIdx + 1) : [];
376
+ const hasMcpArgs = rawArgs.some((a) => a.startsWith('--mcp-'));
377
+ const timeout = options.timeout ? parseInt(options.timeout, 10) : undefined;
378
+ // Validate mutually exclusive options
379
+ if (hasMcpArgs && (options.all || options.name)) {
380
+ logger.error('Cannot mix direct MCP server args (--mcp-*) with --all or --name');
381
+ process.exit(1);
382
+ }
383
+ if (options.all && options.name) {
384
+ logger.error('Cannot use --all and --name together');
385
+ process.exit(1);
386
+ }
387
+ // If no options at all, show usage
388
+ if (!hasMcpArgs && !options.all && !options.name) {
389
+ logger.info('Usage: agentinit mcp verify [options]');
390
+ logger.info('');
391
+ logger.info('Verify existing configurations:');
392
+ logger.info(' --all Verify all configured MCP servers');
393
+ logger.info(' --name <name> Verify a specific MCP server by name');
394
+ logger.info(` --timeout <ms> Connection timeout (default: ${DEFAULT_CONNECTION_TIMEOUT_MS})`);
395
+ logger.info('');
396
+ logger.info('Verify a server directly:');
397
+ logger.info(' --mcp-stdio <name> <command> Verify STDIO server');
398
+ logger.info(' --mcp-http <name> <url> Verify HTTP server');
399
+ logger.info(' --mcp-sse <name> <url> Verify SSE server');
400
+ logger.info('');
401
+ logger.info('Examples:');
402
+ logger.info(' agentinit mcp verify --all');
403
+ logger.info(' agentinit mcp verify --name exa');
404
+ logger.info(' agentinit mcp verify --mcp-stdio everything "npx -y @modelcontextprotocol/server-everything"');
405
+ return;
406
+ }
407
+ let serversToVerify = [];
408
+ let serverSources = [];
409
+ const spinner = ora('Preparing verification...').start();
410
+ try {
411
+ if (hasMcpArgs) {
412
+ // Direct verification: parse --mcp-* args from the raw args
413
+ const mcpOnlyArgs = filterMcpArgs(rawArgs);
414
+ const parsed = MCPParser.parseArguments(mcpOnlyArgs);
415
+ if (parsed.servers.length === 0) {
416
+ spinner.warn('No MCP servers found in arguments');
417
+ return;
418
+ }
419
+ serversToVerify = parsed.servers;
420
+ spinner.text = `Verifying ${serversToVerify.length} MCP server(s) from arguments...`;
421
+ }
422
+ else {
423
+ // Existing configuration verification
424
+ spinner.text = 'Detecting agents and MCP configurations...';
425
+ const agentManager = new AgentManager();
426
+ const detectedAgents = await agentManager.detectAgents(cwd);
427
+ if (detectedAgents.length === 0) {
428
+ spinner.warn('No AI coding agents detected in this project');
429
+ logger.info('Run `agentinit detect` to see which agents are supported');
430
+ return;
431
+ }
432
+ // Collect servers from all detected agents
433
+ const allServers = [];
434
+ const allSources = [];
435
+ for (const { agent } of detectedAgents) {
436
+ try {
437
+ const servers = await agent.getMCPServers(cwd);
438
+ for (const server of servers) {
439
+ allServers.push(server);
440
+ allSources.push({
441
+ server,
442
+ agentName: agent.name,
443
+ configPath: agent.getNativeMcpPath(cwd),
444
+ });
445
+ }
446
+ }
447
+ catch {
448
+ // Agent may not have MCP config
449
+ }
450
+ }
451
+ if (allServers.length === 0) {
452
+ spinner.warn('No MCP servers found in any agent configuration');
453
+ logger.info('Use `agentinit mcp add` to add MCP servers');
454
+ return;
455
+ }
456
+ if (options.all) {
457
+ serversToVerify = allServers;
458
+ serverSources = allSources;
459
+ }
460
+ else if (options.name) {
461
+ const matchName = options.name.toLowerCase();
462
+ serversToVerify = allServers.filter(s => s.name.toLowerCase() === matchName);
463
+ serverSources = allSources.filter(s => s.server.name.toLowerCase() === matchName);
464
+ if (serversToVerify.length === 0) {
465
+ spinner.fail(`MCP server "${options.name}" not found`);
466
+ logger.info('Available MCP servers:');
467
+ allServers.forEach(s => {
468
+ const src = allSources.find(x => x.server.name === s.name);
469
+ logger.info(` - ${s.name} (${s.type}) [${src?.agentName || 'unknown'}]`);
470
+ });
471
+ return;
472
+ }
473
+ }
474
+ spinner.text = `Verifying ${serversToVerify.length} MCP server(s)...`;
475
+ }
476
+ // Run verification
477
+ await runVerification(serversToVerify, timeout, spinner, serverSources);
478
+ }
479
+ catch (error) {
480
+ spinner.fail('Verification failed');
481
+ if (error instanceof MCPParseError) {
482
+ logger.error(error.message);
483
+ }
484
+ else {
485
+ logger.error(`${error instanceof Error ? error.message : 'Unknown error'}`);
486
+ }
487
+ process.exit(1);
488
+ }
489
+ });
118
490
  }
119
- function displayMCP(mcp) {
120
- const verification = mcp.verified ? '✓' : '?';
121
- logger.info(` ${verification} ${mcp.name} (${mcp.category})`);
122
- logger.info(` ${mcp.description}`);
123
- logger.info(` Install: ${mcp.installCommand}`);
124
- logger.info(` Stack: ${mcp.stackCompatibility.join(', ')}`);
491
+ // ---------------------------------------------------------------------------
492
+ // Shared verification helper
493
+ // ---------------------------------------------------------------------------
494
+ async function runVerification(servers, timeout, spinner, serverSources) {
495
+ const ownSpinner = spinner || ora(`Verifying ${servers.length} MCP server(s)...`).start();
496
+ const verifier = new MCPVerifier(timeout);
497
+ const results = await verifier.verifyServers(servers, timeout ? { timeout } : undefined);
498
+ const successCount = results.filter(r => r.status === 'success').length;
499
+ const errorCount = results.filter(r => r.status === 'error').length;
500
+ const timeoutCount = results.filter(r => r.status === 'timeout').length;
501
+ if (successCount === results.length) {
502
+ ownSpinner.succeed(`All ${results.length} MCP server(s) verified successfully!`);
503
+ }
504
+ else if (successCount > 0) {
505
+ ownSpinner.warn(`${successCount}/${results.length} MCP server(s) verified successfully`);
506
+ }
507
+ else {
508
+ ownSpinner.fail('Failed to verify any MCP servers');
509
+ }
125
510
  logger.info('');
511
+ const formattedOutput = verifier.formatResults(results);
512
+ console.log(formattedOutput);
513
+ // Summary for multiple servers
514
+ if (results.length > 1) {
515
+ logger.info('Summary:');
516
+ logger.info(` Successful: ${successCount}`);
517
+ if (errorCount > 0)
518
+ logger.info(` Failed: ${errorCount}`);
519
+ if (timeoutCount > 0)
520
+ logger.info(` Timeout: ${timeoutCount}`);
521
+ logger.info('');
522
+ }
523
+ // Show source info for failed servers when we have sources
524
+ if (serverSources && serverSources.length > 0) {
525
+ const failed = results.filter(r => r.status !== 'success');
526
+ if (failed.length > 0) {
527
+ logger.info('Configuration Sources:');
528
+ for (const result of failed) {
529
+ const src = serverSources.find(s => s.server.name === result.server.name);
530
+ if (src) {
531
+ logger.info(` - ${result.server.name}: ${src.configPath} (${src.agentName})`);
532
+ }
533
+ }
534
+ logger.info('');
535
+ }
536
+ }
537
+ // Troubleshooting tips for failures
538
+ if (successCount < results.length) {
539
+ logger.info('Troubleshooting:');
540
+ logger.info(' 1. Ensure MCP server packages are installed');
541
+ logger.info(' 2. Check environment variables are set correctly');
542
+ logger.info(' 3. Verify network connectivity for HTTP/SSE servers');
543
+ logger.info(' 4. Try increasing timeout with --timeout <ms>');
544
+ }
545
+ }
546
+ // ---------------------------------------------------------------------------
547
+ // Main registration
548
+ // ---------------------------------------------------------------------------
549
+ export function registerMcpCommand(program) {
550
+ const mcp = program
551
+ .command('mcp')
552
+ .description('Manage MCP server configurations');
553
+ registerAddCommand(mcp);
554
+ registerListCommand(mcp);
555
+ registerRemoveCommand(mcp);
556
+ registerVerifyCommand(mcp);
126
557
  }
127
558
  //# sourceMappingURL=mcp.js.map