agentinit 1.7.0 → 1.8.1

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