@phantom-pm/mcp-server 1.0.0 → 2.0.0-alpha.2

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.
@@ -1,49 +1,46 @@
1
- /**
2
- * Self-Discovery Mechanism
3
- *
4
- * When AI agents start, they check for available MCP servers.
5
- * PHANTOM registers itself so agents automatically know it exists.
6
- */
7
- export declare class PhantomDiscovery {
8
- static readonly AGENT_CONFIG_PATHS: {
9
- 'claude-code': string;
10
- cursor: string;
11
- zed: string;
12
- vscode: string;
13
- codex: string;
14
- antigravity: string;
15
- qoder: string;
1
+ type SupportedAgent = 'claude-code' | 'cursor' | 'zed' | 'vscode' | 'codex' | 'gemini-cli';
2
+ interface InstalledAgent {
3
+ type: SupportedAgent;
4
+ name: string;
5
+ installed: boolean;
6
+ running: boolean;
7
+ confidence: number;
8
+ status: 'running' | 'installed' | 'available';
9
+ }
10
+ interface RegisteredAgent extends InstalledAgent {
11
+ configPath: string;
12
+ registered: boolean;
13
+ registrationError?: string;
14
+ }
15
+ interface HealthReport {
16
+ timestamp: string;
17
+ mcp_server: {
18
+ status: 'running' | 'not_responding';
16
19
  };
17
- private static readonly AGENT_NAMES;
18
- /**
19
- * Register PHANTOM with agent's MCP registry
20
- * This happens automatically when PHANTOM is installed
21
- */
22
- static registerWithAgent(agentType: keyof typeof PhantomDiscovery.AGENT_CONFIG_PATHS): Promise<boolean>;
23
- /**
24
- * Auto-detection during agent startup
25
- * Agent checks: "Is PHANTOM available?"
26
- */
20
+ agents: RegisteredAgent[];
21
+ issues: string[];
22
+ }
23
+ export declare class PhantomDiscovery {
24
+ static get AGENT_CONFIG_PATHS(): Record<SupportedAgent, string>;
25
+ private static get AGENT_NAMES();
27
26
  static detectPhantom(): Promise<boolean>;
28
- /**
29
- * Detect all installed agents
30
- */
31
- static detectInstalledAgents(): Array<{
32
- type: string;
33
- name: string;
34
- installed: boolean;
27
+ static detectInstalledAgents(): Promise<InstalledAgent[]>;
28
+ static detectRegisteredAgents(): Promise<RegisteredAgent[]>;
29
+ static registerAgent(type: SupportedAgent): Promise<{
30
+ success: boolean;
31
+ message: string;
32
+ }>;
33
+ static registerWithAgent(type: SupportedAgent): Promise<boolean>;
34
+ static registerAll(agentTypes?: SupportedAgent[]): Promise<{
35
+ total: number;
36
+ success: number;
37
+ failed: number;
35
38
  }>;
36
- /**
37
- * Auto-register with all detected agents
38
- */
39
+ static forceRegisterAgents(agentTypes: SupportedAgent[]): Promise<void>;
39
40
  static autoRegisterAll(): Promise<void>;
40
- /**
41
- * Suggest installation if not found
42
- */
43
41
  static getSuggestedInstall(): string;
44
- /**
45
- * Health check - verify MCP server and agent registrations
46
- */
42
+ static healthReport(): Promise<HealthReport>;
47
43
  static healthCheck(): Promise<void>;
48
44
  }
45
+ export {};
49
46
  //# sourceMappingURL=discovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AAEH,qBAAa,gBAAgB;IAC3B,MAAM,CAAC,QAAQ,CAAC,kBAAkB;;;;;;;;MAQhC;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAQjC;IAEF;;;OAGG;WACU,iBAAiB,CAAC,SAAS,EAAE,MAAM,OAAO,gBAAgB,CAAC,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAwD7G;;;OAGG;WACU,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAY9C;;OAEG;IACH,MAAM,CAAC,qBAAqB,IAAI,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAC,CAAC;IA+CvF;;OAEG;WACU,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC7C;;OAEG;IACH,MAAM,CAAC,mBAAmB,IAAI,MAAM;IAUpC;;OAEG;WACU,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;CA2B1C"}
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAOA,KAAK,cAAc,GAAG,aAAa,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,YAAY,CAAC;AAQ3F,UAAU,cAAc;IACtB,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,WAAW,CAAC;CAC/C;AAED,UAAU,eAAgB,SAAQ,cAAc;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,UAAU,YAAY;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE;QACV,MAAM,EAAE,SAAS,GAAG,gBAAgB,CAAC;KACtC,CAAC;IACF,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAgKD,qBAAa,gBAAgB;IAC3B,MAAM,KAAK,kBAAkB,IAAI,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAS9D;IAED,OAAO,CAAC,MAAM,KAAK,WAAW,GAS7B;WAEY,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;WAYjC,qBAAqB,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;WAmBlD,sBAAsB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;WAkBpD,aAAa,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;WAiDnF,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;WAUzD,WAAW,CAAC,UAAU,CAAC,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;WAsBvG,mBAAmB,CAAC,UAAU,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;WAKhE,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB7C,MAAM,CAAC,mBAAmB,IAAI,MAAM;WAUvB,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC;WAwBrC,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;CAmB1C"}
package/dist/discovery.js CHANGED
@@ -1,96 +1,184 @@
1
- // PHANTOM Agent Discovery System
2
- // Auto-detects installed AI agents and registers PHANTOM with them
1
+ // PHANTOM Agent/MCP Discovery and Registration
3
2
  import { spawnSync } from 'child_process';
4
- import { writeFileSync, existsSync, readFileSync } from 'fs';
5
- import { join } from 'path';
3
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
4
+ import { dirname, join } from 'path';
6
5
  import { homedir } from 'os';
7
- /**
8
- * Self-Discovery Mechanism
9
- *
10
- * When AI agents start, they check for available MCP servers.
11
- * PHANTOM registers itself so agents automatically know it exists.
12
- */
13
- export class PhantomDiscovery {
14
- static AGENT_CONFIG_PATHS = {
15
- 'claude-code': join(homedir(), '.claude', 'settings.json'),
16
- 'cursor': join(homedir(), '.cursor/settings.json'),
17
- 'zed': join(homedir(), '.config/zed/settings.json'),
18
- 'vscode': join(homedir(), 'Library/Application Support/Code/User/settings.json'),
19
- 'codex': join(homedir(), 'Library/Application Support/Codex/User/settings.json'),
20
- 'antigravity': join(homedir(), 'Library/Application Support/Antigravity/User/settings.json'),
21
- 'qoder': join(homedir(), 'Library/Application Support/Qoder/User/settings.json'),
22
- };
23
- static AGENT_NAMES = {
24
- 'claude-code': 'Claude Code',
25
- 'cursor': 'Cursor',
26
- 'zed': 'Zed Editor',
27
- 'vscode': 'Visual Studio Code',
28
- 'codex': 'Codex AI',
29
- 'antigravity': 'AntiGravity AI',
30
- 'qoder': 'Qoder AI',
31
- };
32
- /**
33
- * Register PHANTOM with agent's MCP registry
34
- * This happens automatically when PHANTOM is installed
35
- */
36
- static async registerWithAgent(agentType) {
37
- try {
38
- const configPath = this.AGENT_CONFIG_PATHS[agentType];
39
- const agentName = this.AGENT_NAMES[agentType];
40
- if (!configPath) {
41
- console.log(`❌ Unknown agent type: ${agentType}`);
42
- return false;
6
+ import { AgentDiscovery } from '@phantom-pm/core';
7
+ const ADAPTERS = {
8
+ 'claude-code': {
9
+ type: 'claude-code',
10
+ name: 'Claude Code',
11
+ configPathByPlatform: {
12
+ darwin: join(homedir(), '.claude', 'settings.json'),
13
+ linux: join(homedir(), '.claude', 'settings.json'),
14
+ win32: join(homedir(), '.claude', 'settings.json'),
15
+ },
16
+ },
17
+ cursor: {
18
+ type: 'cursor',
19
+ name: 'Cursor',
20
+ configPathByPlatform: {
21
+ darwin: join(homedir(), '.cursor', 'settings.json'),
22
+ linux: join(homedir(), '.cursor', 'settings.json'),
23
+ win32: join(homedir(), '.cursor', 'settings.json'),
24
+ },
25
+ },
26
+ zed: {
27
+ type: 'zed',
28
+ name: 'Zed Editor',
29
+ configPathByPlatform: {
30
+ darwin: join(homedir(), '.config', 'zed', 'settings.json'),
31
+ linux: join(homedir(), '.config', 'zed', 'settings.json'),
32
+ win32: join(homedir(), '.config', 'zed', 'settings.json'),
33
+ },
34
+ },
35
+ vscode: {
36
+ type: 'vscode',
37
+ name: 'Visual Studio Code',
38
+ configPathByPlatform: {
39
+ darwin: join(homedir(), 'Library', 'Application Support', 'Code', 'User', 'settings.json'),
40
+ linux: join(homedir(), '.config', 'Code', 'User', 'settings.json'),
41
+ win32: join(homedir(), 'AppData', 'Roaming', 'Code', 'User', 'settings.json'),
42
+ },
43
+ },
44
+ codex: {
45
+ type: 'codex',
46
+ name: 'Codex AI',
47
+ configPathByPlatform: {
48
+ darwin: join(homedir(), 'Library', 'Application Support', 'Codex', 'User', 'settings.json'),
49
+ linux: join(homedir(), '.config', 'Codex', 'User', 'settings.json'),
50
+ win32: join(homedir(), 'AppData', 'Roaming', 'Codex', 'User', 'settings.json'),
51
+ },
52
+ },
53
+ 'gemini-cli': {
54
+ type: 'gemini-cli',
55
+ name: 'Gemini CLI',
56
+ configPathByPlatform: {
57
+ darwin: join(homedir(), '.gemini', 'settings.json'),
58
+ linux: join(homedir(), '.gemini', 'settings.json'),
59
+ win32: join(homedir(), '.gemini', 'settings.json'),
60
+ },
61
+ },
62
+ };
63
+ function getConfigPath(type) {
64
+ const adapter = ADAPTERS[type];
65
+ const fromPlatform = adapter.configPathByPlatform[process.platform];
66
+ if (fromPlatform)
67
+ return fromPlatform;
68
+ return Object.values(adapter.configPathByPlatform)[0] || join(homedir(), `.${type}`, 'settings.json');
69
+ }
70
+ function stripJsonComments(input) {
71
+ let result = '';
72
+ let index = 0;
73
+ let inString = false;
74
+ let escaped = false;
75
+ while (index < input.length) {
76
+ const current = input[index];
77
+ const next = input[index + 1];
78
+ if (inString) {
79
+ result += current;
80
+ if (escaped) {
81
+ escaped = false;
43
82
  }
44
- // Check if agent config exists
45
- if (!existsSync(configPath)) {
46
- console.log(`○ ${agentName} config not found (skipped)`);
47
- return false;
83
+ else if (current === '\\') {
84
+ escaped = true;
48
85
  }
49
- // Read existing config
50
- let config = {};
51
- try {
52
- const configContent = readFileSync(configPath, 'utf8');
53
- config = JSON.parse(configContent);
86
+ else if (current === '"') {
87
+ inString = false;
54
88
  }
55
- catch (error) {
56
- config = {};
89
+ index += 1;
90
+ continue;
91
+ }
92
+ if (current === '"') {
93
+ inString = true;
94
+ result += current;
95
+ index += 1;
96
+ continue;
97
+ }
98
+ if (current === '/' && next === '/') {
99
+ while (index < input.length && input[index] !== '\n') {
100
+ index += 1;
57
101
  }
58
- // Ensure mcpServers object exists
59
- if (!config.mcpServers) {
60
- config.mcpServers = {};
102
+ continue;
103
+ }
104
+ if (current === '/' && next === '*') {
105
+ index += 2;
106
+ while (index < input.length) {
107
+ if (input[index] === '*' && input[index + 1] === '/') {
108
+ index += 2;
109
+ break;
110
+ }
111
+ index += 1;
61
112
  }
62
- // Register PHANTOM MCP server
63
- config.mcpServers.phantom = {
64
- command: 'phantom',
65
- args: ['mcp', 'serve'],
66
- description: 'PHANTOM PM Operating System - Product management superpowers',
67
- capabilities: [
68
- 'Generate PRDs',
69
- 'Analyze product decisions',
70
- 'Create user stories',
71
- 'Plan sprints',
72
- 'Access product context',
73
- ],
74
- };
75
- // Write updated config
76
- writeFileSync(configPath, JSON.stringify(config, null, 2));
77
- console.log(`✓ Registered with ${agentName} (MCP enabled)`);
78
- return true;
113
+ continue;
79
114
  }
80
- catch (error) {
81
- console.error(`❌ Failed to register with ${this.AGENT_NAMES[agentType]}:`, error);
82
- return false;
115
+ result += current;
116
+ index += 1;
117
+ }
118
+ return result;
119
+ }
120
+ function parseJsonc(input) {
121
+ const withoutComments = stripJsonComments(input);
122
+ const normalized = withoutComments.replace(/,\s*([}\]])/g, '$1');
123
+ return JSON.parse(normalized);
124
+ }
125
+ function readAgentConfig(configPath) {
126
+ if (!existsSync(configPath)) {
127
+ return { data: {} };
128
+ }
129
+ try {
130
+ const raw = readFileSync(configPath, 'utf8');
131
+ const parsed = parseJsonc(raw);
132
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
133
+ return { data: {}, error: 'Config is not an object' };
83
134
  }
135
+ return { data: parsed };
136
+ }
137
+ catch (error) {
138
+ return {
139
+ data: {},
140
+ error: error instanceof Error ? error.message : 'Failed to parse config',
141
+ };
142
+ }
143
+ }
144
+ function createBackup(configPath) {
145
+ if (!existsSync(configPath))
146
+ return null;
147
+ const backupPath = `${configPath}.bak.${Date.now()}`;
148
+ copyFileSync(configPath, backupPath);
149
+ return backupPath;
150
+ }
151
+ function restoreBackup(configPath, backupPath) {
152
+ if (!backupPath || !existsSync(backupPath))
153
+ return;
154
+ copyFileSync(backupPath, configPath);
155
+ }
156
+ export class PhantomDiscovery {
157
+ static get AGENT_CONFIG_PATHS() {
158
+ return {
159
+ 'claude-code': getConfigPath('claude-code'),
160
+ cursor: getConfigPath('cursor'),
161
+ zed: getConfigPath('zed'),
162
+ vscode: getConfigPath('vscode'),
163
+ codex: getConfigPath('codex'),
164
+ 'gemini-cli': getConfigPath('gemini-cli'),
165
+ };
166
+ }
167
+ static get AGENT_NAMES() {
168
+ return {
169
+ 'claude-code': ADAPTERS['claude-code'].name,
170
+ cursor: ADAPTERS.cursor.name,
171
+ zed: ADAPTERS.zed.name,
172
+ vscode: ADAPTERS.vscode.name,
173
+ codex: ADAPTERS.codex.name,
174
+ 'gemini-cli': ADAPTERS['gemini-cli'].name,
175
+ };
84
176
  }
85
- /**
86
- * Auto-detection during agent startup
87
- * Agent checks: "Is PHANTOM available?"
88
- */
89
177
  static async detectPhantom() {
90
178
  try {
91
179
  const result = spawnSync('phantom', ['--version'], {
92
180
  stdio: 'pipe',
93
- timeout: 5000
181
+ timeout: 5000,
94
182
  });
95
183
  return result.status === 0;
96
184
  }
@@ -98,129 +186,181 @@ export class PhantomDiscovery {
98
186
  return false;
99
187
  }
100
188
  }
101
- /**
102
- * Detect all installed agents
103
- */
104
- static detectInstalledAgents() {
105
- const agents = [
106
- { type: 'claude-code', name: 'Claude Code', command: 'cursor' },
107
- { type: 'cursor', name: 'Cursor', command: 'cursor' },
108
- { type: 'zed', name: 'Zed Editor', command: 'zed' },
109
- { type: 'vscode', name: 'Visual Studio Code', command: 'code' },
110
- { type: 'codex', name: 'Codex AI', command: 'codex' },
111
- { type: 'antigravity', name: 'AntiGravity AI', command: 'antigravity' },
112
- { type: 'qoder', name: 'Qoder AI', command: 'qoder' },
113
- ];
114
- return agents.map(agent => {
115
- try {
116
- // Special handling for apps that might not have CLI commands
117
- if (agent.command === 'codex' || agent.command === 'antigravity' || agent.command === 'qoder') {
118
- // Check if the app is running
119
- const processCheck = spawnSync('pgrep', ['-f', agent.command], {
120
- stdio: 'pipe',
121
- timeout: 3000
122
- });
123
- return {
124
- type: agent.type,
125
- name: agent.name,
126
- installed: processCheck.status === 0 || processCheck.stdout.toString().trim() !== ''
127
- };
128
- }
129
- else {
130
- // Standard CLI command check
131
- const result = spawnSync(agent.command, ['--version'], {
132
- stdio: 'pipe',
133
- timeout: 3000
134
- });
135
- return {
136
- type: agent.type,
137
- name: agent.name,
138
- installed: result.status === 0
139
- };
140
- }
189
+ static async detectInstalledAgents() {
190
+ const discovery = new AgentDiscovery(process.cwd());
191
+ const detected = await discovery.scanSystem();
192
+ const detectedMap = new Map(detected.map(item => [item.signature.id, item]));
193
+ return Object.keys(ADAPTERS).map(type => {
194
+ const found = detectedMap.get(type);
195
+ return {
196
+ type,
197
+ name: ADAPTERS[type].name,
198
+ installed: Boolean(found),
199
+ running: found?.status === 'running',
200
+ confidence: found?.confidence || 0,
201
+ status: found?.status || 'available',
202
+ };
203
+ });
204
+ }
205
+ static async detectRegisteredAgents() {
206
+ const installed = await this.detectInstalledAgents();
207
+ return installed.map(agent => {
208
+ const configPath = this.AGENT_CONFIG_PATHS[agent.type];
209
+ const parsed = readAgentConfig(configPath);
210
+ const mcpServers = parsed.data.mcpServers;
211
+ const registered = Boolean(mcpServers && typeof mcpServers === 'object' && mcpServers.phantom);
212
+ return {
213
+ ...agent,
214
+ configPath,
215
+ registered,
216
+ ...(parsed.error ? { registrationError: parsed.error } : {}),
217
+ };
218
+ });
219
+ }
220
+ static async registerAgent(type) {
221
+ const configPath = this.AGENT_CONFIG_PATHS[type];
222
+ const agentName = this.AGENT_NAMES[type];
223
+ let backupPath = null;
224
+ try {
225
+ mkdirSync(dirname(configPath), { recursive: true });
226
+ backupPath = createBackup(configPath);
227
+ const parsed = readAgentConfig(configPath);
228
+ const config = parsed.data;
229
+ const mcpServers = (config.mcpServers && typeof config.mcpServers === 'object')
230
+ ? config.mcpServers
231
+ : {};
232
+ if (mcpServers.phantom) {
233
+ return { success: true, message: `${agentName} already registered` };
141
234
  }
142
- catch {
143
- return {
144
- type: agent.type,
145
- name: agent.name,
146
- installed: false
147
- };
235
+ const nextConfig = {
236
+ ...config,
237
+ mcpServers: {
238
+ ...mcpServers,
239
+ phantom: {
240
+ command: 'phantom',
241
+ args: ['mcp', 'serve'],
242
+ description: 'PHANTOM PM Operating System - Product management superpowers',
243
+ capabilities: [
244
+ 'Generate PRDs',
245
+ 'Analyze product decisions',
246
+ 'Create user stories',
247
+ 'Plan sprints',
248
+ 'Access product context',
249
+ ],
250
+ },
251
+ },
252
+ };
253
+ writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, 'utf8');
254
+ return { success: true, message: `Registered with ${agentName}` };
255
+ }
256
+ catch (error) {
257
+ restoreBackup(configPath, backupPath);
258
+ return {
259
+ success: false,
260
+ message: error instanceof Error ? `Failed to register ${agentName}: ${error.message}` : `Failed to register ${agentName}`,
261
+ };
262
+ }
263
+ }
264
+ static async registerWithAgent(type) {
265
+ const result = await this.registerAgent(type);
266
+ if (result.success) {
267
+ console.log(`✓ ${result.message}`);
268
+ return true;
269
+ }
270
+ console.log(`○ ${result.message}`);
271
+ return false;
272
+ }
273
+ static async registerAll(agentTypes) {
274
+ const targets = agentTypes || (await this.detectInstalledAgents()).filter(agent => agent.installed).map(agent => agent.type);
275
+ const uniqueTargets = Array.from(new Set(targets));
276
+ let success = 0;
277
+ for (const target of uniqueTargets) {
278
+ const result = await this.registerAgent(target);
279
+ if (result.success) {
280
+ success += 1;
281
+ console.log(`✓ ${result.message}`);
148
282
  }
149
- });
283
+ else {
284
+ console.log(`○ ${result.message}`);
285
+ }
286
+ }
287
+ return {
288
+ total: uniqueTargets.length,
289
+ success,
290
+ failed: uniqueTargets.length - success,
291
+ };
292
+ }
293
+ static async forceRegisterAgents(agentTypes) {
294
+ const summary = await this.registerAll(agentTypes);
295
+ console.log(`✓ Successfully registered with ${summary.success}/${summary.total} agents`);
150
296
  }
151
- /**
152
- * Auto-register with all detected agents
153
- */
154
297
  static async autoRegisterAll() {
155
298
  console.log('🔍 Detecting installed AI agents...\n');
156
- const agents = this.detectInstalledAgents();
157
- const installedAgents = agents.filter(agent => agent.installed);
158
- if (installedAgents.length === 0) {
299
+ const installed = await this.detectInstalledAgents();
300
+ const detected = installed.filter(agent => agent.installed);
301
+ if (detected.length === 0) {
159
302
  console.log('No supported AI agents detected.');
160
- console.log(this.getSuggestedInstall());
303
+ console.log('Creating baseline configs for common agents...\n');
304
+ await this.forceRegisterAgents(['codex', 'cursor', 'claude-code']);
161
305
  return;
162
306
  }
163
- console.log(`Found ${installedAgents.length} installed agent${installedAgents.length > 1 ? 's' : ''}:`);
164
- installedAgents.forEach(agent => {
165
- console.log(` ✓ ${agent.name}`);
166
- });
167
- console.log('');
168
- console.log('🔌 Registering PHANTOM with agents...\n');
169
- let successCount = 0;
170
- for (const agent of installedAgents) {
171
- const success = await this.registerWithAgent(agent.type);
172
- if (success)
173
- successCount++;
307
+ console.log(`Found ${detected.length} installed agent${detected.length > 1 ? 's' : ''}:`);
308
+ for (const agent of detected) {
309
+ console.log(` ✓ ${agent.name} (${agent.status}, confidence ${agent.confidence}%)`);
174
310
  }
311
+ console.log('');
312
+ const summary = await this.registerAll(detected.map(agent => agent.type));
175
313
  console.log(`\n🎯 Registration complete!`);
176
- console.log(`✓ Successfully registered with ${successCount}/${installedAgents.length} agents`);
177
- if (successCount > 0) {
178
- console.log('\n✨ Your AI agents now have PM superpowers!');
179
- console.log('Next time you work on a feature in your IDE,');
180
- console.log('your agent will automatically use PHANTOM.');
181
- }
314
+ console.log(`✓ Successfully registered with ${summary.success}/${summary.total} agents`);
182
315
  }
183
- /**
184
- * Suggest installation if not found
185
- */
186
316
  static getSuggestedInstall() {
187
317
  return `
188
318
  # PHANTOM not found. Install with:
189
- npm install -g https://codeload.github.com/sir-ad/Phantom/tar.gz/refs/heads/main
319
+ npm install -g @phantom-pm/cli
190
320
 
191
321
  # Or:
192
- curl -fsSL https://raw.githubusercontent.com/sir-ad/Phantom/main/scripts/install.sh | sh
322
+ curl -fsSL https://raw.githubusercontent.com/PhantomPM/phantom/main/scripts/install.sh | sh
193
323
  `.trim();
194
324
  }
195
- /**
196
- * Health check - verify MCP server and agent registrations
197
- */
198
- static async healthCheck() {
199
- console.log('🏥 PHANTOM Health Check\n');
200
- // Check MCP server
325
+ static async healthReport() {
201
326
  const mcpRunning = await this.detectPhantom();
202
- console.log(`◉ MCP Server: ${mcpRunning ? 'Running' : 'Not responding'}`);
203
- // Check agent registrations
204
- const agents = this.detectInstalledAgents();
327
+ const agents = await this.detectRegisteredAgents();
328
+ const issues = [];
205
329
  for (const agent of agents) {
206
- if (agent.installed) {
207
- const configPath = this.AGENT_CONFIG_PATHS[agent.type];
208
- let registered = false;
209
- if (configPath && existsSync(configPath)) {
210
- try {
211
- const config = JSON.parse(readFileSync(configPath, 'utf8'));
212
- registered = config.mcpServers?.phantom !== undefined;
213
- }
214
- catch {
215
- registered = false;
216
- }
217
- }
218
- console.log(`◉ ${agent.name}: ${registered ? 'Connected' : 'Not registered'}`);
330
+ if (agent.installed && !agent.registered) {
331
+ issues.push(`${agent.type}:detected_not_registered`);
332
+ }
333
+ if (agent.registrationError) {
334
+ issues.push(`${agent.type}:config_error`);
335
+ }
336
+ }
337
+ return {
338
+ timestamp: new Date().toISOString(),
339
+ mcp_server: {
340
+ status: mcpRunning ? 'running' : 'not_responding',
341
+ },
342
+ agents,
343
+ issues,
344
+ };
345
+ }
346
+ static async healthCheck() {
347
+ const report = await this.healthReport();
348
+ console.log('🏥 PHANTOM Health Check\n');
349
+ console.log(`◉ MCP Server: ${report.mcp_server.status === 'running' ? 'Running' : 'Not responding'}`);
350
+ for (const agent of report.agents) {
351
+ const status = agent.installed
352
+ ? (agent.registered ? 'Connected' : 'Installed but not registered')
353
+ : 'Not installed';
354
+ console.log(`◉ ${agent.name}: ${status}`);
355
+ }
356
+ if (report.issues.length > 0) {
357
+ console.log('\nIssues:');
358
+ for (const issue of report.issues) {
359
+ console.log(` - ${issue}`);
219
360
  }
220
361
  }
221
362
  }
222
363
  }
223
- // Post-install hook - automatically register with detected agents
224
364
  if (import.meta.url === `file://${process.argv[1]}`) {
225
365
  PhantomDiscovery.autoRegisterAll().catch(console.error);
226
366
  }