fraim 2.0.203 → 2.0.205

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 (79) hide show
  1. package/README.md +3 -3
  2. package/bin/fraim.js +8 -18
  3. package/dist/src/cli/commands/first-run.js +2 -37
  4. package/dist/src/cli/doctor/checks/mcp-connectivity-checks.js +1 -1
  5. package/dist/src/cli/fraim.js +0 -2
  6. package/dist/src/cli/setup/ide-invocation-surfaces.js +6 -6
  7. package/dist/src/cli/utils/managed-agent-paths.js +76 -6
  8. package/dist/src/cli/utils/version-utils.js +2 -2
  9. package/dist/src/config/persona-capability-bundles.js +3 -3
  10. package/dist/src/core/job-phases.js +168 -0
  11. package/dist/src/core/utils/ports.js +32 -0
  12. package/dist/src/core/utils/stub-generator.js +24 -2
  13. package/dist/src/first-run/session-service.js +106 -24
  14. package/dist/src/local-mcp-server/learning-context-builder.js +1 -1
  15. package/dist/src/local-mcp-server/stdio-server.js +41 -9
  16. package/dist/src/routes/analytics.js +2 -2
  17. package/dist/src/services/registry-service.js +2 -2
  18. package/index.js +1 -1
  19. package/package.json +61 -181
  20. package/dist/src/ai-hub/catalog.js +0 -406
  21. package/dist/src/ai-hub/cert-store.js +0 -70
  22. package/dist/src/ai-hub/conversation-store.js +0 -359
  23. package/dist/src/ai-hub/desktop-main.js +0 -449
  24. package/dist/src/ai-hub/hosts.js +0 -1664
  25. package/dist/src/ai-hub/hub-latest-version.js +0 -52
  26. package/dist/src/ai-hub/hub-launch-decision.js +0 -57
  27. package/dist/src/ai-hub/hub-runtime-file.js +0 -43
  28. package/dist/src/ai-hub/managed-browser.js +0 -269
  29. package/dist/src/ai-hub/manager-turns.js +0 -64
  30. package/dist/src/ai-hub/office-sideload.js +0 -156
  31. package/dist/src/ai-hub/openclaw-bridge.js +0 -250
  32. package/dist/src/ai-hub/preferences.js +0 -201
  33. package/dist/src/ai-hub/remote-hub-gateway.js +0 -88
  34. package/dist/src/ai-hub/server.js +0 -3827
  35. package/dist/src/ai-hub/types.js +0 -2
  36. package/dist/src/ai-hub/word-sideload.js +0 -95
  37. package/dist/src/cli/commands/hub.js +0 -238
  38. package/dist/src/cli/commands/test-mcp.js +0 -171
  39. package/dist/src/cli/setup/first-run.js +0 -242
  40. package/dist/src/core/config-writer.js +0 -75
  41. package/dist/src/core/utils/job-aliases.js +0 -47
  42. package/dist/src/core/utils/workflow-parser.js +0 -174
  43. package/dist/src/services/email-service-clean.js +0 -782
  44. package/extensions/office-word/favicon.ico +0 -0
  45. package/extensions/office-word/icon-64.png +0 -0
  46. package/extensions/office-word/manifest.xml +0 -33
  47. package/extensions/office-word/taskpane.html +0 -242
  48. package/public/ai-hub/index.html +0 -894
  49. package/public/ai-hub/powerpoint-taskpane/icon-64.png +0 -0
  50. package/public/ai-hub/powerpoint-taskpane/index.html +0 -236
  51. package/public/ai-hub/powerpoint-taskpane/manifest.xml +0 -30
  52. package/public/ai-hub/review.css +0 -381
  53. package/public/ai-hub/script.js +0 -11015
  54. package/public/ai-hub/styles.css +0 -4873
  55. package/public/portfolio/ashley.html +0 -523
  56. package/public/portfolio/auditya.html +0 -83
  57. package/public/portfolio/banke.html +0 -83
  58. package/public/portfolio/beza.html +0 -659
  59. package/public/portfolio/careena.html +0 -632
  60. package/public/portfolio/casey.html +0 -568
  61. package/public/portfolio/celia.html +0 -490
  62. package/public/portfolio/deidre.html +0 -642
  63. package/public/portfolio/gautam.html +0 -597
  64. package/public/portfolio/hari.html +0 -469
  65. package/public/portfolio/huxley.html +0 -1354
  66. package/public/portfolio/index.html +0 -741
  67. package/public/portfolio/maestro.html +0 -518
  68. package/public/portfolio/mandy.html +0 -590
  69. package/public/portfolio/mona.html +0 -597
  70. package/public/portfolio/pam.html +0 -887
  71. package/public/portfolio/procella.html +0 -107
  72. package/public/portfolio/qasm.html +0 -569
  73. package/public/portfolio/ricardo.html +0 -489
  74. package/public/portfolio/sade.html +0 -560
  75. package/public/portfolio/sam.html +0 -654
  76. package/public/portfolio/sechar.html +0 -580
  77. package/public/portfolio/sreya.html +0 -599
  78. package/public/portfolio/swen.html +0 -601
  79. /package/dist/src/{ai-hub → core}/brand-store.js +0 -0
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,95 +0,0 @@
1
- "use strict";
2
- /**
3
- * Sideloads the Word add-in manifest so it appears in Word's Developer Add-ins
4
- * list without admin rights or AppSource publishing.
5
- *
6
- * Windows: writes a registry value under HKCU\SOFTWARE\Microsoft\Office\16.0\WEF\Developer
7
- * macOS: writes an entry to ~/Library/Containers/com.microsoft.Word/Data/Documents/wef/
8
- *
9
- * Both paths are non-admin and survive app updates (keyed by manifest GUID).
10
- * Safe to call multiple times — checks before writing.
11
- */
12
- var __importDefault = (this && this.__importDefault) || function (mod) {
13
- return (mod && mod.__esModule) ? mod : { "default": mod };
14
- };
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.isSideloaded = isSideloaded;
17
- exports.sideloadManifest = sideloadManifest;
18
- exports.removeSideload = removeSideload;
19
- const fs_1 = __importDefault(require("fs"));
20
- const path_1 = __importDefault(require("path"));
21
- const os_1 = __importDefault(require("os"));
22
- const child_process_1 = require("child_process");
23
- const MANIFEST_GUID = 'd1090951-50cf-4cf2-9d12-b0f8541d265c';
24
- function resolveManifestPath(projectPath) {
25
- const candidates = [
26
- path_1.default.resolve(projectPath, 'extensions/office-word/manifest.xml'),
27
- path_1.default.resolve(__dirname, '..', '..', 'extensions/office-word/manifest.xml'),
28
- path_1.default.resolve(__dirname, '..', '..', '..', 'extensions/office-word/manifest.xml'),
29
- ];
30
- return candidates.find(c => fs_1.default.existsSync(c)) ?? null;
31
- }
32
- function isSideloaded() {
33
- if (process.platform === 'win32') {
34
- const result = (0, child_process_1.spawnSync)('reg', [
35
- 'query',
36
- `HKCU\\SOFTWARE\\Microsoft\\Office\\16.0\\WEF\\Developer`,
37
- '/v', MANIFEST_GUID,
38
- ], { encoding: 'utf8' });
39
- return result.status === 0 && result.stdout.includes(MANIFEST_GUID);
40
- }
41
- if (process.platform === 'darwin') {
42
- const wefDir = path_1.default.join(os_1.default.homedir(), 'Library', 'Containers', 'com.microsoft.Word', 'Data', 'Documents', 'wef');
43
- return fs_1.default.existsSync(path_1.default.join(wefDir, `${MANIFEST_GUID}.xml`));
44
- }
45
- return false;
46
- }
47
- function sideloadManifest(projectPath) {
48
- const manifestPath = resolveManifestPath(projectPath);
49
- if (!manifestPath) {
50
- return { ok: false, reason: 'Manifest file not found — is extensions/office-word/ present?' };
51
- }
52
- if (process.platform === 'win32') {
53
- const result = (0, child_process_1.spawnSync)('reg', [
54
- 'add',
55
- `HKCU\\SOFTWARE\\Microsoft\\Office\\16.0\\WEF\\Developer`,
56
- '/v', MANIFEST_GUID,
57
- '/t', 'REG_SZ',
58
- '/d', manifestPath,
59
- '/f',
60
- ], { encoding: 'utf8' });
61
- if (result.status !== 0) {
62
- return { ok: false, reason: result.stderr || 'reg add failed' };
63
- }
64
- return { ok: true };
65
- }
66
- if (process.platform === 'darwin') {
67
- const wefDir = path_1.default.join(os_1.default.homedir(), 'Library', 'Containers', 'com.microsoft.Word', 'Data', 'Documents', 'wef');
68
- try {
69
- fs_1.default.mkdirSync(wefDir, { recursive: true });
70
- fs_1.default.copyFileSync(manifestPath, path_1.default.join(wefDir, `${MANIFEST_GUID}.xml`));
71
- return { ok: true };
72
- }
73
- catch (err) {
74
- return { ok: false, reason: String(err) };
75
- }
76
- }
77
- return { ok: false, reason: `Unsupported platform: ${process.platform}` };
78
- }
79
- function removeSideload() {
80
- if (process.platform === 'win32') {
81
- (0, child_process_1.spawnSync)('reg', [
82
- 'delete',
83
- `HKCU\\SOFTWARE\\Microsoft\\Office\\16.0\\WEF\\Developer`,
84
- '/v', MANIFEST_GUID,
85
- '/f',
86
- ], { encoding: 'utf8' });
87
- return;
88
- }
89
- if (process.platform === 'darwin') {
90
- const wefDir = path_1.default.join(os_1.default.homedir(), 'Library', 'Containers', 'com.microsoft.Word', 'Data', 'Documents', 'wef');
91
- const target = path_1.default.join(wefDir, `${MANIFEST_GUID}.xml`);
92
- if (fs_1.default.existsSync(target))
93
- fs_1.default.unlinkSync(target);
94
- }
95
- }
@@ -1,238 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.hubCommand = void 0;
40
- const commander_1 = require("commander");
41
- // ai-hub/server pulls in server-only code (persona-hiring etc.) that is not
42
- // shipped in the client package; it is imported lazily inside the action below
43
- // to keep it out of the CLI startup graph so the packed client loads (issue #422).
44
- const git_utils_1 = require("../../core/utils/git-utils");
45
- const path_1 = __importDefault(require("path"));
46
- const child_process_1 = require("child_process");
47
- const fs_1 = __importDefault(require("fs"));
48
- const net_1 = __importDefault(require("net"));
49
- const http_1 = __importDefault(require("http"));
50
- // #755: light, server-free modules — safe to import eagerly in the packed client
51
- // (unlike ai-hub/server, which is lazy-required per #422).
52
- const hub_launch_decision_1 = require("../../ai-hub/hub-launch-decision");
53
- const hub_runtime_file_1 = require("../../ai-hub/hub-runtime-file");
54
- const project_fraim_paths_1 = require("../../core/utils/project-fraim-paths");
55
- const version_utils_1 = require("../utils/version-utils");
56
- function resolveElectronBinary() {
57
- try {
58
- // eslint-disable-next-line @typescript-eslint/no-var-requires
59
- return require('electron');
60
- }
61
- catch {
62
- return null;
63
- }
64
- }
65
- function resolveDesktopEntry() {
66
- const candidates = [
67
- path_1.default.resolve(__dirname, '..', '..', 'ai-hub', 'desktop-main.js'),
68
- path_1.default.resolve(process.cwd(), 'dist', 'src', 'ai-hub', 'desktop-main.js'),
69
- ];
70
- for (const candidate of candidates) {
71
- try {
72
- fs_1.default.accessSync(candidate);
73
- return candidate;
74
- }
75
- catch {
76
- continue;
77
- }
78
- }
79
- return null;
80
- }
81
- function openDesktopWindow(projectPath, preferredPort) {
82
- const electronBinary = resolveElectronBinary();
83
- const desktopEntry = resolveDesktopEntry();
84
- if (!electronBinary || !desktopEntry) {
85
- return false;
86
- }
87
- const child = (0, child_process_1.spawn)(electronBinary, [desktopEntry, '--project-path', projectPath, '--port', String(preferredPort)], {
88
- detached: true,
89
- stdio: 'ignore',
90
- });
91
- child.unref();
92
- return true;
93
- }
94
- function openBrowser(url) {
95
- if (process.platform === 'win32') {
96
- const child = (0, child_process_1.spawn)('cmd', ['/c', 'start', '', url], { detached: true, stdio: 'ignore' });
97
- child.unref();
98
- return;
99
- }
100
- if (process.platform === 'darwin') {
101
- const child = (0, child_process_1.spawn)('open', [url], { detached: true, stdio: 'ignore' });
102
- child.unref();
103
- return;
104
- }
105
- const child = (0, child_process_1.spawn)('xdg-open', [url], { detached: true, stdio: 'ignore' });
106
- child.unref();
107
- }
108
- // #755: version-aware kill-and-replace. Electron's single-instance lock otherwise
109
- // re-focuses a stale running build instead of launching a newly-released one.
110
- function isProcessAlive(pid) {
111
- try {
112
- process.kill(pid, 0);
113
- return true;
114
- }
115
- catch (e) {
116
- // EPERM => the process exists but we can't signal it (still "alive").
117
- return !!(e && e.code === 'EPERM');
118
- }
119
- }
120
- function killPid(pid) {
121
- try {
122
- if (process.platform === 'win32') {
123
- (0, child_process_1.execFileSync)('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore' });
124
- }
125
- else {
126
- process.kill(pid, 'SIGTERM');
127
- }
128
- }
129
- catch {
130
- /* already gone */
131
- }
132
- }
133
- function isPortFree(port, timeoutMs = 500) {
134
- return new Promise((resolve) => {
135
- const sock = net_1.default.connect({ host: '127.0.0.1', port }, () => { sock.destroy(); resolve(false); });
136
- sock.on('error', () => resolve(true));
137
- sock.setTimeout(timeoutMs, () => { sock.destroy(); resolve(true); });
138
- });
139
- }
140
- async function waitForPortFree(port, totalMs = 5000) {
141
- const start = Date.now();
142
- while (Date.now() - start < totalMs) {
143
- if (await isPortFree(port))
144
- return;
145
- await new Promise((r) => setTimeout(r, 200));
146
- }
147
- }
148
- /**
149
- * Reconcile a running desktop Hub with the version about to launch. Replaces a
150
- * stale/older instance (or any instance under --restart) so the new build takes
151
- * over; keeps the running instance under --keep-running.
152
- */
153
- /** Confirm the recorded port actually serves a FRAIM Hub, returning its authoritative version. */
154
- function fetchRunningHubVersion(port) {
155
- return new Promise((resolve) => {
156
- const req = http_1.default.get({ host: '127.0.0.1', port, path: '/api/ai-hub/version', timeout: 1000 }, (res) => {
157
- if (res.statusCode !== 200) {
158
- res.resume();
159
- return resolve(null);
160
- }
161
- let body = '';
162
- res.on('data', (c) => { body += c; });
163
- res.on('end', () => { try {
164
- resolve(JSON.parse(body).version ?? null);
165
- }
166
- catch {
167
- resolve(null);
168
- } });
169
- });
170
- req.on('error', () => resolve(null));
171
- req.on('timeout', () => { req.destroy(); resolve(null); });
172
- });
173
- }
174
- async function reconcileRunningHub(flags) {
175
- const running = (0, hub_runtime_file_1.readHubRuntimeFile)((0, project_fraim_paths_1.getUserFraimDirPath)());
176
- // Confirm the recorded port genuinely serves a FRAIM Hub before trusting the file.
177
- // This (a) prevents killing an unrelated process that reused a crashed Hub's pid,
178
- // and (b) yields the authoritative running version (the file may be stale).
179
- const confirmedVersion = running ? await fetchRunningHubVersion(running.port) : null;
180
- const live = !!(running && confirmedVersion && isProcessAlive(running.pid));
181
- const effective = live && running ? { ...running, version: confirmedVersion } : null;
182
- const decision = (0, hub_launch_decision_1.decideHubLaunch)({
183
- running: effective,
184
- pidAlive: live,
185
- cliVersion: (0, version_utils_1.getFraimVersion)(),
186
- restart: flags.restart,
187
- noRestart: flags.keepRunning,
188
- });
189
- if (decision.action === 'replace' && effective) {
190
- console.log(`Replacing running Hub (v${effective.version}, pid ${effective.pid}) — ${decision.reason}`);
191
- killPid(effective.pid);
192
- await waitForPortFree(effective.port);
193
- }
194
- else if (decision.action === 'focus-existing' && effective) {
195
- console.log(`A Hub (v${effective.version}) is already running — focusing it. Use --restart to replace it.`);
196
- }
197
- }
198
- exports.hubCommand = new commander_1.Command('hub')
199
- .description('Start the AI Hub local companion for running FRAIM jobs through Codex or Claude Code')
200
- .option('--port <port>', 'Preferred local port for the hub', (value) => Number(value), 43091)
201
- .option('--project-path <path>', 'Initial project path for job discovery', process.cwd())
202
- .option('--no-open', 'Do not open the hub after startup')
203
- .option('--browser', 'Open in the default browser instead of the desktop shell')
204
- .option('--restart', 'Replace any running Hub instance with this build (even at the same version)')
205
- .option('--keep-running', 'Do not replace a running Hub; keep single-instance focus (default replaces only an older/stale instance)')
206
- .action(async (options) => {
207
- const { AiHubServer, findAvailablePort } = await Promise.resolve().then(() => __importStar(require('../../ai-hub/server')));
208
- const preferredPort = options.port || (0, git_utils_1.getPort)() + 100;
209
- const projectPath = path_1.default.resolve(options.projectPath || process.cwd());
210
- if (options.open) {
211
- const wantDesktop = !options.browser;
212
- // #755: before spawning Electron (whose single-instance lock would re-focus a
213
- // stale build), replace an older/forced running instance so the new build wins.
214
- if (wantDesktop) {
215
- await reconcileRunningHub({ restart: !!options.restart, keepRunning: !!options.keepRunning });
216
- }
217
- const openedDesktop = wantDesktop && openDesktopWindow(projectPath, preferredPort);
218
- if (!openedDesktop) {
219
- const port = await findAvailablePort(preferredPort);
220
- const server = new AiHubServer({ projectPath });
221
- await server.start(port);
222
- const url = `http://127.0.0.1:${port}/ai-hub/`;
223
- console.log(`AI Hub running at ${url}`);
224
- console.log(`Project path: ${projectPath}`);
225
- openBrowser(url);
226
- return;
227
- }
228
- console.log('AI Hub desktop shell launched.');
229
- console.log(`Project path: ${projectPath}`);
230
- return;
231
- }
232
- const port = await findAvailablePort(preferredPort);
233
- const server = new AiHubServer({ projectPath });
234
- await server.start(port);
235
- const url = `http://127.0.0.1:${port}/ai-hub/`;
236
- console.log(`AI Hub running at ${url}`);
237
- console.log(`Project path: ${projectPath}`);
238
- });
@@ -1,171 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.testMCPCommand = exports.runTestMCP = void 0;
40
- const commander_1 = require("commander");
41
- const chalk_1 = __importDefault(require("chalk"));
42
- const fs_1 = __importDefault(require("fs"));
43
- const path_1 = __importDefault(require("path"));
44
- const ide_detector_1 = require("../setup/ide-detector");
45
- const script_sync_utils_1 = require("../utils/script-sync-utils");
46
- const testIDEConfig = async (ide) => {
47
- const result = {
48
- ide: ide.name,
49
- configExists: false,
50
- configValid: false,
51
- mcpServers: [],
52
- errors: []
53
- };
54
- const configPath = (0, ide_detector_1.expandPath)(ide.configPath);
55
- if (!fs_1.default.existsSync(configPath)) {
56
- result.errors.push('Config file does not exist');
57
- return result;
58
- }
59
- result.configExists = true;
60
- try {
61
- if (ide.configFormat === 'json') {
62
- const configContent = fs_1.default.readFileSync(configPath, 'utf8');
63
- const config = JSON.parse(configContent);
64
- const servers = ide.configType === 'vscode' ? config.servers : config.mcpServers;
65
- if (servers) {
66
- result.configValid = true;
67
- result.mcpServers = Object.keys(servers);
68
- }
69
- else {
70
- const expectedKey = ide.configType === 'vscode' ? 'servers' : 'mcpServers';
71
- result.errors.push(`No ${expectedKey} section found`);
72
- }
73
- }
74
- else if (ide.configFormat === 'toml') {
75
- const configContent = fs_1.default.readFileSync(configPath, 'utf8');
76
- // Simple TOML parsing for MCP servers
77
- const serverMatches = configContent.match(/\[mcp_servers\.(\w+)\]/g);
78
- if (serverMatches) {
79
- result.configValid = true;
80
- result.mcpServers = serverMatches.map(match => match.replace(/\[mcp_servers\.(\w+)\]/, '$1'));
81
- }
82
- else {
83
- result.errors.push('No mcp_servers sections found');
84
- }
85
- }
86
- }
87
- catch (error) {
88
- result.errors.push(`Failed to parse config: ${error instanceof Error ? error.message : 'Unknown error'}`);
89
- }
90
- return result;
91
- };
92
- const checkGlobalSetup = () => {
93
- const globalConfigPath = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
94
- return fs_1.default.existsSync(globalConfigPath);
95
- };
96
- const runTestMCP = async () => {
97
- console.log(chalk_1.default.blue('šŸ” Testing MCP configuration...\n'));
98
- // Check global setup
99
- if (!checkGlobalSetup()) {
100
- console.log(chalk_1.default.red('āŒ Global FRAIM setup not found.'));
101
- console.log(chalk_1.default.yellow('Please run: fraim setup --key=<your-fraim-key>'));
102
- return;
103
- }
104
- console.log(chalk_1.default.green('āœ… Global FRAIM setup found'));
105
- // Detect IDEs
106
- const detectedIDEs = (0, ide_detector_1.detectInstalledIDEs)();
107
- if (detectedIDEs.length === 0) {
108
- console.log(chalk_1.default.yellow('āš ļø No supported IDEs detected.'));
109
- return;
110
- }
111
- console.log(chalk_1.default.blue(`\nšŸ” Testing ${detectedIDEs.length} detected IDEs...\n`));
112
- const results = await Promise.all(detectedIDEs.map(ide => testIDEConfig(ide)));
113
- let totalConfigured = 0;
114
- let totalWithFRAIM = 0;
115
- for (const result of results) {
116
- console.log(chalk_1.default.white(`šŸ“± ${result.ide}`));
117
- if (!result.configExists) {
118
- console.log(chalk_1.default.red(' āŒ No MCP config found'));
119
- console.log(chalk_1.default.gray(` šŸ’” Run: fraim setup --ide=${result.ide.toLowerCase()}`));
120
- }
121
- else if (!result.configValid) {
122
- console.log(chalk_1.default.yellow(' āš ļø Config exists but invalid'));
123
- result.errors.forEach(error => {
124
- console.log(chalk_1.default.red(` āŒ ${error}`));
125
- });
126
- }
127
- else {
128
- totalConfigured++;
129
- console.log(chalk_1.default.green(` āœ… MCP config valid (${result.mcpServers.length} servers)`));
130
- // Check for essential servers
131
- const { BASE_MCP_SERVERS } = await Promise.resolve().then(() => __importStar(require('../mcp/mcp-server-registry')));
132
- const essentialServers = BASE_MCP_SERVERS.map(s => s.id); // fraim, git, playwright
133
- const hasEssential = essentialServers.filter(server => result.mcpServers.includes(server));
134
- if (hasEssential.includes('fraim')) {
135
- totalWithFRAIM++;
136
- console.log(chalk_1.default.green(' āœ… FRAIM server configured'));
137
- }
138
- else {
139
- console.log(chalk_1.default.yellow(' āš ļø FRAIM server missing'));
140
- }
141
- if (hasEssential.length > 1) {
142
- console.log(chalk_1.default.green(` āœ… ${hasEssential.length - 1} additional servers: ${hasEssential.filter(s => s !== 'fraim').join(', ')}`));
143
- }
144
- const missingEssential = essentialServers.filter(server => !result.mcpServers.includes(server));
145
- if (missingEssential.length > 0) {
146
- console.log(chalk_1.default.yellow(` āš ļø Missing servers: ${missingEssential.join(', ')}`));
147
- }
148
- }
149
- console.log(); // Empty line
150
- }
151
- // Summary
152
- console.log(chalk_1.default.blue('šŸ“Š Summary:'));
153
- console.log(chalk_1.default.green(` āœ… ${totalConfigured}/${detectedIDEs.length} IDEs have valid MCP configs`));
154
- console.log(chalk_1.default.green(` āœ… ${totalWithFRAIM}/${detectedIDEs.length} IDEs have FRAIM configured`));
155
- if (totalWithFRAIM === 0) {
156
- console.log(chalk_1.default.red('\nāŒ No IDEs have FRAIM configured!'));
157
- console.log(chalk_1.default.yellow('šŸ’” Run: fraim setup --key=<your-fraim-key>'));
158
- }
159
- else if (totalWithFRAIM < detectedIDEs.length) {
160
- console.log(chalk_1.default.yellow(`\nāš ļø ${detectedIDEs.length - totalWithFRAIM} IDEs missing FRAIM configuration`));
161
- console.log(chalk_1.default.yellow('šŸ’” Run: fraim setup to configure remaining IDEs'));
162
- }
163
- else {
164
- console.log(chalk_1.default.green('\nšŸŽ‰ All detected IDEs have FRAIM configured!'));
165
- console.log(chalk_1.default.blue('šŸ’” Try running: fraim init-project in any project'));
166
- }
167
- };
168
- exports.runTestMCP = runTestMCP;
169
- exports.testMCPCommand = new commander_1.Command('test-mcp')
170
- .description('Test MCP server configurations for all detected IDEs')
171
- .action(exports.runTestMCP);