@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.
- package/dist/discovery.d.ts +38 -41
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +318 -178
- package/dist/discovery.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +420 -7
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/dist/discovery.d.ts
CHANGED
|
@@ -1,49 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
static
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
package/dist/discovery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"
|
|
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
|
|
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 {
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
console.log(`○ ${agentName} config not found (skipped)`);
|
|
47
|
-
return false;
|
|
83
|
+
else if (current === '\\') {
|
|
84
|
+
escaped = true;
|
|
48
85
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
const configContent = readFileSync(configPath, 'utf8');
|
|
53
|
-
config = JSON.parse(configContent);
|
|
86
|
+
else if (current === '"') {
|
|
87
|
+
inString = false;
|
|
54
88
|
}
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
{
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
|
157
|
-
const
|
|
158
|
-
if (
|
|
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(
|
|
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 ${
|
|
164
|
-
|
|
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 ${
|
|
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
|
|
319
|
+
npm install -g @phantom-pm/cli
|
|
190
320
|
|
|
191
321
|
# Or:
|
|
192
|
-
curl -fsSL https://raw.githubusercontent.com/
|
|
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
|
-
|
|
203
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
}
|