fraim-hub 2.0.204

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 (120) hide show
  1. package/README.md +467 -0
  2. package/bin/fraim-hub.js +17 -0
  3. package/dist/src/ai-hub/brand-store.js +17 -0
  4. package/dist/src/ai-hub/catalog.js +412 -0
  5. package/dist/src/ai-hub/cert-store.js +70 -0
  6. package/dist/src/ai-hub/cli.js +212 -0
  7. package/dist/src/ai-hub/configured-agents.js +283 -0
  8. package/dist/src/ai-hub/conversation-store.js +428 -0
  9. package/dist/src/ai-hub/desktop-main.js +449 -0
  10. package/dist/src/ai-hub/hosts.js +1669 -0
  11. package/dist/src/ai-hub/hub-latest-version.js +52 -0
  12. package/dist/src/ai-hub/hub-launch-decision.js +57 -0
  13. package/dist/src/ai-hub/hub-runtime-file.js +43 -0
  14. package/dist/src/ai-hub/managed-browser.js +269 -0
  15. package/dist/src/ai-hub/manager-turns.js +64 -0
  16. package/dist/src/ai-hub/office-sideload.js +156 -0
  17. package/dist/src/ai-hub/openclaw-bridge.js +250 -0
  18. package/dist/src/ai-hub/preferences.js +201 -0
  19. package/dist/src/ai-hub/remote-hub-gateway.js +88 -0
  20. package/dist/src/ai-hub/server.js +4013 -0
  21. package/dist/src/ai-hub/types.js +2 -0
  22. package/dist/src/ai-hub/url-safety.js +19 -0
  23. package/dist/src/api/admin/payments.js +33 -0
  24. package/dist/src/api/admin/sales-leads.js +21 -0
  25. package/dist/src/api/payment/create-session.js +338 -0
  26. package/dist/src/api/payment/dashboard-link.js +149 -0
  27. package/dist/src/api/payment/session-details.js +31 -0
  28. package/dist/src/api/payment/webhook.js +587 -0
  29. package/dist/src/api/sales/contact.js +44 -0
  30. package/dist/src/cli/api/get-provider-client.js +41 -0
  31. package/dist/src/cli/api/provider-client.js +107 -0
  32. package/dist/src/cli/commands/add-ide.js +462 -0
  33. package/dist/src/cli/fraim-hub.js +50 -0
  34. package/dist/src/cli/internal/device-flow-service.js +83 -0
  35. package/dist/src/cli/mcp/command-resolution.js +81 -0
  36. package/dist/src/cli/mcp/fraim-mcp-latest-launcher.js +136 -0
  37. package/dist/src/cli/mcp/ide-formats.js +317 -0
  38. package/dist/src/cli/mcp/mcp-server-builder.js +48 -0
  39. package/dist/src/cli/mcp/mcp-server-registry.js +173 -0
  40. package/dist/src/cli/providers/local-provider-registry.js +165 -0
  41. package/dist/src/cli/providers/provider-registry.js +230 -0
  42. package/dist/src/cli/setup/claude-code-telemetry.js +59 -0
  43. package/dist/src/cli/setup/codex-local-config.js +37 -0
  44. package/dist/src/cli/setup/ide-detector.js +383 -0
  45. package/dist/src/cli/setup/ide-global-integration.js +100 -0
  46. package/dist/src/cli/setup/ide-invocation-surfaces.js +160 -0
  47. package/dist/src/cli/setup/mcp-config-generator.js +225 -0
  48. package/dist/src/cli/setup/provider-prompts.js +339 -0
  49. package/dist/src/cli/utils/managed-agent-paths.js +118 -0
  50. package/dist/src/cli/utils/script-sync-utils.js +221 -0
  51. package/dist/src/cli/utils/user-config.js +100 -0
  52. package/dist/src/cli/utils/version-utils.js +35 -0
  53. package/dist/src/config/ai-manager-hiring.js +121 -0
  54. package/dist/src/config/feature-flags.js +25 -0
  55. package/dist/src/config/persona-capability-bundles.js +277 -0
  56. package/dist/src/config/persona-hiring.js +270 -0
  57. package/dist/src/config/portfolio-slug-overrides.js +17 -0
  58. package/dist/src/config/pricing.js +37 -0
  59. package/dist/src/config/stripe.js +43 -0
  60. package/dist/src/core/brand-store.js +232 -0
  61. package/dist/src/core/quality-evidence.js +331 -0
  62. package/dist/src/core/utils/git-utils.js +194 -0
  63. package/dist/src/core/utils/ports.js +32 -0
  64. package/dist/src/core/utils/project-fraim-paths.js +220 -0
  65. package/dist/src/db/payment-repository.js +61 -0
  66. package/dist/src/first-run/types.js +93 -0
  67. package/dist/src/fraim/db-service.js +2409 -0
  68. package/dist/src/local-mcp-server/agent-token-prices.js +167 -0
  69. package/dist/src/local-mcp-server/learning-context-builder.js +975 -0
  70. package/dist/src/middleware/rate-limit.js +110 -0
  71. package/dist/src/models/payment.js +2 -0
  72. package/dist/src/routes/payment-routes.js +186 -0
  73. package/dist/src/routes/persona-catalog-routes.js +84 -0
  74. package/dist/src/services/dashboard-access.js +27 -0
  75. package/dist/src/services/email-service.js +951 -0
  76. package/dist/src/services/persona-entitlement-service.js +352 -0
  77. package/dist/src/services/workspace-identity.js +21 -0
  78. package/dist/src/types/analytics.js +2 -0
  79. package/dist/src/utils/payment-calculator.js +52 -0
  80. package/extensions/office-word/favicon.ico +0 -0
  81. package/extensions/office-word/icon-64.png +0 -0
  82. package/extensions/office-word/manifest.xml +33 -0
  83. package/extensions/office-word/taskpane.html +242 -0
  84. package/index.js +85 -0
  85. package/package.json +106 -0
  86. package/public/ai-hub/index.html +910 -0
  87. package/public/ai-hub/powerpoint-taskpane/icon-64.png +0 -0
  88. package/public/ai-hub/powerpoint-taskpane/index.html +236 -0
  89. package/public/ai-hub/powerpoint-taskpane/manifest.xml +30 -0
  90. package/public/ai-hub/review.css +444 -0
  91. package/public/ai-hub/script.js +11737 -0
  92. package/public/ai-hub/styles.css +5102 -0
  93. package/public/first-run/error-frame.js +100 -0
  94. package/public/first-run/index.html +35 -0
  95. package/public/first-run/script.js +739 -0
  96. package/public/first-run/styles.css +929 -0
  97. package/public/portfolio/ashley.html +523 -0
  98. package/public/portfolio/auditya.html +83 -0
  99. package/public/portfolio/banke.html +83 -0
  100. package/public/portfolio/beza.html +659 -0
  101. package/public/portfolio/careena.html +632 -0
  102. package/public/portfolio/casey.html +568 -0
  103. package/public/portfolio/celia.html +490 -0
  104. package/public/portfolio/deidre.html +642 -0
  105. package/public/portfolio/gautam.html +597 -0
  106. package/public/portfolio/hari.html +469 -0
  107. package/public/portfolio/huxley.html +1354 -0
  108. package/public/portfolio/index.html +741 -0
  109. package/public/portfolio/maestro.html +518 -0
  110. package/public/portfolio/mandy.html +590 -0
  111. package/public/portfolio/mona.html +597 -0
  112. package/public/portfolio/pam.html +887 -0
  113. package/public/portfolio/procella.html +107 -0
  114. package/public/portfolio/qasm.html +569 -0
  115. package/public/portfolio/ricardo.html +489 -0
  116. package/public/portfolio/sade.html +560 -0
  117. package/public/portfolio/sam.html +654 -0
  118. package/public/portfolio/sechar.html +580 -0
  119. package/public/portfolio/sreya.html +599 -0
  120. package/public/portfolio/swen.html +601 -0
@@ -0,0 +1,383 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.expandPath = exports.findIDEByName = exports.getAllSupportedIDEs = exports.detectInstalledIDEs = exports.CLI_PROBE_CONFIGTYPES = exports.IDE_CONFIGS = void 0;
7
+ exports.getAdapterConfigType = getAdapterConfigType;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const os_1 = __importDefault(require("os"));
11
+ const child_process_1 = require("child_process");
12
+ /** Returns the configType to use for adapter file filtering. Falls back to configType when adapterConfigType is not set. */
13
+ function getAdapterConfigType(ide) {
14
+ return ide.adapterConfigType ?? ide.configType;
15
+ }
16
+ const expandPath = (filePath) => {
17
+ if (filePath.startsWith('~/')) {
18
+ return path_1.default.join(os_1.default.homedir(), filePath.slice(2));
19
+ }
20
+ return filePath;
21
+ };
22
+ exports.expandPath = expandPath;
23
+ const checkMultiplePaths = (paths) => {
24
+ return paths.some(p => fs_1.default.existsSync(expandPath(p)));
25
+ };
26
+ // Issue #646 (Bug 2): a config directory left behind after uninstall (e.g. ~/.cursor,
27
+ // ~/Library/Application Support/Cursor) made config-surface detection report Cursor
28
+ // as "Installed" when it was not. App evidence is the reliable signal for Cursor;
29
+ // other GUI surfaces keep their existing config-surface checks for compatibility
30
+ // with the #640 first-run detection contract.
31
+ const appBundlePaths = (appName) => [
32
+ `/Applications/${appName}.app`,
33
+ `~/Applications/${appName}.app`,
34
+ ];
35
+ const windowsUserAppPaths = (appName) => {
36
+ switch (appName) {
37
+ case 'Cursor':
38
+ return [
39
+ '~/AppData/Local/Programs/Cursor/Cursor.exe',
40
+ '~/AppData/Local/Cursor/Cursor.exe',
41
+ ];
42
+ default:
43
+ return [];
44
+ }
45
+ };
46
+ // Issue #646 follow-up: a user may run a GUI app from a non-standard location
47
+ // (e.g. straight from a mounted DMG: /Volumes/.../Cursor.app) that the
48
+ // /Applications checks miss. A running process of the app is definitive proof it
49
+ // is installed and in use, and a stale config dir alone has no such process — so
50
+ // this catches real installs without re-introducing the stale-dir false positive.
51
+ const isMacAppRunning = (appName) => {
52
+ if (process.platform !== 'darwin')
53
+ return false;
54
+ // Test seam: `pgrep` is system-global (not HOME-scoped), so tests that simulate
55
+ // "app not installed" via a temp HOME set this to keep results deterministic.
56
+ if (process.env.FRAIM_DETECT_DISABLE_PROCESS_CHECK === '1')
57
+ return false;
58
+ const result = (0, child_process_1.spawnSync)('pgrep', ['-f', `${appName}.app/Contents`], { encoding: 'utf8', timeout: 1500 });
59
+ return result.status === 0 && Boolean((result.stdout || '').trim());
60
+ };
61
+ const hasAppEvidence = (appName) => {
62
+ const evidencePaths = process.platform === 'win32'
63
+ ? [...appBundlePaths(appName), ...windowsUserAppPaths(appName)]
64
+ : appBundlePaths(appName);
65
+ return checkMultiplePaths(evidencePaths) || isMacAppRunning(appName);
66
+ };
67
+ const guiAppDetect = (configSurfaceCheck, appName, options = {}) => {
68
+ return () => {
69
+ if (options.requireAppEvidence) {
70
+ return hasAppEvidence(appName);
71
+ }
72
+ if (process.platform === 'darwin') {
73
+ return hasAppEvidence(appName);
74
+ }
75
+ return configSurfaceCheck();
76
+ };
77
+ };
78
+ const availableByVersionProbe = (command) => {
79
+ const result = process.platform === 'win32'
80
+ ? (0, child_process_1.spawnSync)('cmd.exe', ['/d', '/s', '/c', `${command} --version`], { encoding: 'utf8', timeout: 1500 })
81
+ : (0, child_process_1.spawnSync)(command, ['--version'], { encoding: 'utf8', timeout: 1500 });
82
+ return result.status === 0;
83
+ };
84
+ const detectClaude = () => {
85
+ const paths = [
86
+ '~/.claude.json',
87
+ '~/.claude',
88
+ '~/Library/Application Support/Cloud',
89
+ '~/Library/Application Support/Claude',
90
+ '~/AppData/Roaming/Claude'
91
+ ];
92
+ return checkMultiplePaths(paths);
93
+ };
94
+ const detectVSCode = () => {
95
+ const paths = [
96
+ '~/.vscode',
97
+ '~/Library/Application Support/Code',
98
+ '~/AppData/Roaming/Code',
99
+ '~/.config/Code'
100
+ ];
101
+ return checkMultiplePaths(paths);
102
+ };
103
+ const detectCursorMcpConfig = () => {
104
+ const paths = [
105
+ '~/.cursor/mcp.json',
106
+ '~/Library/Application Support/Cursor/mcp.json',
107
+ '~/AppData/Roaming/Cursor/mcp.json',
108
+ '~/.config/Cursor/mcp.json'
109
+ ];
110
+ return checkMultiplePaths(paths);
111
+ };
112
+ const detectWindsurf = () => {
113
+ const paths = [
114
+ '~/.codeium/windsurf',
115
+ '~/Library/Application Support/Windsurf',
116
+ '~/AppData/Roaming/Windsurf',
117
+ '~/.config/windsurf'
118
+ ];
119
+ return checkMultiplePaths(paths);
120
+ };
121
+ const detectGeminiCli = () => {
122
+ return availableByVersionProbe('gemini');
123
+ };
124
+ const detectCopilotCli = () => {
125
+ return availableByVersionProbe('copilot');
126
+ };
127
+ const detectGeminiSurface = () => {
128
+ const paths = [
129
+ '~/.gemini',
130
+ '~/.gemini/settings.json',
131
+ '~/AppData/Roaming/gemini/settings.json',
132
+ '~/.config/gemini/settings.json'
133
+ ];
134
+ return checkMultiplePaths(paths);
135
+ };
136
+ const detectCopilotSurface = () => {
137
+ const paths = [
138
+ process.env.COPILOT_HOME ? path_1.default.join(process.env.COPILOT_HOME, 'mcp-config.json') : '',
139
+ process.env.COPILOT_HOME || '',
140
+ '~/.copilot',
141
+ '~/.copilot/mcp-config.json'
142
+ ].filter(Boolean);
143
+ return detectCopilotCli() || checkMultiplePaths(paths);
144
+ };
145
+ const detectCodexSurface = () => {
146
+ const paths = [
147
+ '~/.codex',
148
+ '~/.codex/config.toml'
149
+ ];
150
+ return checkMultiplePaths(paths);
151
+ };
152
+ const detectGrokSurface = () => {
153
+ const paths = [
154
+ '~/.grok',
155
+ '~/.grok/config.toml'
156
+ ];
157
+ return checkMultiplePaths(paths);
158
+ };
159
+ exports.IDE_CONFIGS = [
160
+ {
161
+ name: 'Claude Code',
162
+ configPath: '~/.claude.json',
163
+ configFormat: 'json',
164
+ configType: 'claude-code',
165
+ invocationProfile: 'claude-slash',
166
+ detectMethod: detectClaude,
167
+ aliases: ['claude-code', 'claude code', 'claude code cli', 'code tab'],
168
+ alternativePaths: [
169
+ '~/.claude/settings.json'
170
+ ],
171
+ description: 'Anthropic Claude Code local surfaces (CLI and Desktop Code tab)',
172
+ downloadUrl: 'https://claude.ai/code',
173
+ },
174
+ {
175
+ name: 'Claude Desktop / Cowork',
176
+ configPath: process.platform === 'win32'
177
+ ? '~/AppData/Roaming/Claude/claude_desktop_config.json'
178
+ : process.platform === 'darwin'
179
+ ? '~/Library/Application Support/Claude/claude_desktop_config.json'
180
+ : '~/.config/Claude/claude_desktop_config.json',
181
+ configFormat: 'json',
182
+ configType: 'claude',
183
+ invocationProfile: 'launch-phrase',
184
+ detectMethod: guiAppDetect(detectClaude, 'Claude'),
185
+ aliases: ['claude', 'claude-desktop', 'claude desktop', 'claude-cowork', 'cowork', 'claude cowork'],
186
+ alternativePaths: [
187
+ '~/AppData/Roaming/Claude/claude_desktop_config.json',
188
+ '~/Library/Application Support/Claude/claude_desktop_config.json',
189
+ ],
190
+ description: 'Anthropic Claude Desktop chat and Cowork surfaces',
191
+ downloadUrl: 'https://claude.ai/download',
192
+ },
193
+ {
194
+ name: 'Antigravity',
195
+ configPath: '~/.gemini/antigravity/mcp_config.json',
196
+ configFormat: 'json',
197
+ configType: 'standard',
198
+ invocationProfile: 'cursor-mention',
199
+ detectMethod: guiAppDetect(() => fs_1.default.existsSync(expandPath('~/.gemini/antigravity')), 'Antigravity'),
200
+ description: 'Google Gemini Antigravity IDE',
201
+ downloadUrl: 'https://antigravity.google/',
202
+ },
203
+ {
204
+ name: 'Gemini CLI',
205
+ configPath: '~/.gemini/settings.json',
206
+ configFormat: 'json',
207
+ configType: 'gemini-cli',
208
+ invocationProfile: 'gemini-command',
209
+ detectMethod: detectGeminiSurface,
210
+ supportsConfigBootstrap: true,
211
+ aliases: ['gemini', 'gemini-cli', 'gemini cli'],
212
+ alternativePaths: [
213
+ '~/AppData/Roaming/gemini/settings.json',
214
+ '~/.config/gemini/settings.json'
215
+ ],
216
+ description: 'Google Gemini CLI local settings',
217
+ downloadUrl: 'https://github.com/google-gemini/gemini-cli',
218
+ },
219
+ {
220
+ name: 'GitHub Copilot CLI',
221
+ configPath: process.env.COPILOT_HOME
222
+ ? path_1.default.join(process.env.COPILOT_HOME, 'mcp-config.json')
223
+ : '~/.copilot/mcp-config.json',
224
+ configFormat: 'json',
225
+ configType: 'copilot-cli',
226
+ invocationProfile: 'instructions-only',
227
+ detectMethod: detectCopilotSurface,
228
+ supportsConfigBootstrap: true,
229
+ aliases: ['copilot', 'copilot-cli', 'github copilot', 'github copilot cli'],
230
+ description: 'GitHub Copilot CLI local MCP settings',
231
+ downloadUrl: 'https://docs.github.com/copilot/how-tos/use-copilot-agents/use-copilot-cli',
232
+ },
233
+ {
234
+ name: 'Kiro',
235
+ configPath: '~/.kiro/settings/mcp.json',
236
+ configFormat: 'json',
237
+ configType: 'kiro',
238
+ invocationProfile: 'kiro-hashtag',
239
+ detectMethod: guiAppDetect(() => fs_1.default.existsSync(expandPath('~/.kiro')), 'Kiro'),
240
+ description: 'Kiro AI-powered IDE',
241
+ downloadUrl: 'https://kiro.dev/',
242
+ },
243
+ {
244
+ name: 'Cursor',
245
+ configPath: '~/.cursor/mcp.json',
246
+ configFormat: 'json',
247
+ configType: 'kiro',
248
+ adapterConfigType: 'cursor',
249
+ invocationProfile: 'cursor-mention',
250
+ detectMethod: () => detectCursorMcpConfig() || hasAppEvidence('Cursor'),
251
+ alternativePaths: [
252
+ '~/Library/Application Support/Cursor/mcp.json',
253
+ '~/AppData/Roaming/Cursor/mcp.json',
254
+ '~/.config/Cursor/mcp.json'
255
+ ],
256
+ description: 'Cursor AI code editor',
257
+ downloadUrl: 'https://cursor.com/',
258
+ },
259
+ {
260
+ name: 'VSCode',
261
+ configPath: process.platform === 'win32'
262
+ ? '~/AppData/Roaming/Code/User/mcp.json'
263
+ : process.platform === 'darwin'
264
+ ? '~/Library/Application Support/Code/User/mcp.json'
265
+ : '~/.config/Code/User/mcp.json',
266
+ configFormat: 'json',
267
+ configType: 'vscode',
268
+ invocationProfile: 'vscode-prompt',
269
+ detectMethod: guiAppDetect(detectVSCode, 'Visual Studio Code'),
270
+ alternativePaths: [
271
+ '~/Library/Application Support/Code/User/mcp.json',
272
+ '~/AppData/Roaming/Code/User/mcp.json',
273
+ '~/.config/Code/User/mcp.json'
274
+ ],
275
+ description: 'Visual Studio Code',
276
+ downloadUrl: 'https://code.visualstudio.com/',
277
+ },
278
+ {
279
+ name: 'Codex',
280
+ configPath: '~/.codex/config.toml',
281
+ configFormat: 'toml',
282
+ configType: 'codex',
283
+ invocationProfile: 'codex-skill',
284
+ detectMethod: detectCodexSurface,
285
+ description: 'Codex AI development environment',
286
+ downloadUrl: 'https://github.com/openai/codex',
287
+ },
288
+ {
289
+ name: 'Grok',
290
+ configPath: '~/.grok/config.toml',
291
+ configFormat: 'toml',
292
+ configType: 'grok',
293
+ invocationProfile: 'grok-skill',
294
+ detectMethod: detectGrokSurface,
295
+ supportsConfigBootstrap: true,
296
+ aliases: ['grok', 'grok-cli', 'grok cli', 'grok build'],
297
+ description: 'xAI Grok Build CLI local settings',
298
+ downloadUrl: 'https://x.ai/grok',
299
+ },
300
+ {
301
+ name: 'Windsurf',
302
+ configPath: '~/.codeium/windsurf/mcp_config.json',
303
+ configFormat: 'json',
304
+ configType: 'windsurf',
305
+ invocationProfile: 'windsurf-command',
306
+ detectMethod: guiAppDetect(detectWindsurf, 'Windsurf'),
307
+ alternativePaths: [
308
+ '~/Library/Application Support/Windsurf/mcp_config.json',
309
+ '~/AppData/Roaming/Windsurf/mcp_config.json',
310
+ '~/.config/windsurf/mcp_config.json'
311
+ ],
312
+ description: 'Codeium Windsurf IDE',
313
+ downloadUrl: 'https://windsurf.com/',
314
+ }
315
+ ];
316
+ const findBestConfigPath = (ide) => {
317
+ if (fs_1.default.existsSync(expandPath(ide.configPath))) {
318
+ return ide.configPath;
319
+ }
320
+ if (ide.alternativePaths) {
321
+ for (const altPath of ide.alternativePaths) {
322
+ if (fs_1.default.existsSync(expandPath(altPath))) {
323
+ return altPath;
324
+ }
325
+ }
326
+ }
327
+ return ide.configPath;
328
+ };
329
+ const _cachedIDEs = new Map();
330
+ const _cacheTimestamps = new Map();
331
+ const _cacheHomeDirs = new Map();
332
+ const DETECT_CACHE_TTL_MS = 5000;
333
+ /** configTypes that require a binary probe in cli-runnable mode; config folder alone is insufficient. */
334
+ exports.CLI_PROBE_CONFIGTYPES = new Set(['claude-code', 'codex', 'grok', 'gemini-cli', 'copilot-cli']);
335
+ const isDetectedForMode = (ide, mode) => {
336
+ if (mode === 'cli-runnable') {
337
+ switch (ide.configType) {
338
+ case 'claude-code':
339
+ return availableByVersionProbe('claude');
340
+ case 'codex':
341
+ return availableByVersionProbe('codex');
342
+ case 'grok':
343
+ return availableByVersionProbe('grok');
344
+ case 'gemini-cli':
345
+ return detectGeminiCli();
346
+ case 'copilot-cli':
347
+ return detectCopilotCli();
348
+ default:
349
+ return false;
350
+ }
351
+ }
352
+ return ide.detectMethod();
353
+ };
354
+ const detectInstalledIDEs = (mode = 'config-surface') => {
355
+ const now = Date.now();
356
+ const currentHome = os_1.default.homedir();
357
+ const cached = _cachedIDEs.get(mode);
358
+ const cacheTimestamp = _cacheTimestamps.get(mode) || 0;
359
+ const cacheHomeDir = _cacheHomeDirs.get(mode) || '';
360
+ if (cached !== undefined && cacheHomeDir === currentHome && (now - cacheTimestamp) < DETECT_CACHE_TTL_MS) {
361
+ return cached;
362
+ }
363
+ const detected = exports.IDE_CONFIGS.filter((ide) => isDetectedForMode(ide, mode)).map(ide => ({
364
+ ...ide,
365
+ configPath: findBestConfigPath(ide)
366
+ }));
367
+ _cachedIDEs.set(mode, detected);
368
+ _cacheTimestamps.set(mode, now);
369
+ _cacheHomeDirs.set(mode, currentHome);
370
+ return detected;
371
+ };
372
+ exports.detectInstalledIDEs = detectInstalledIDEs;
373
+ const getAllSupportedIDEs = () => {
374
+ return exports.IDE_CONFIGS;
375
+ };
376
+ exports.getAllSupportedIDEs = getAllSupportedIDEs;
377
+ const findIDEByName = (name) => {
378
+ const normalized = name.toLowerCase();
379
+ return exports.IDE_CONFIGS.find(ide => ide.name.toLowerCase().includes(normalized) ||
380
+ normalized.includes(ide.name.toLowerCase()) ||
381
+ ide.aliases?.some(alias => alias.toLowerCase().includes(normalized) || normalized.includes(alias.toLowerCase())));
382
+ };
383
+ exports.findIDEByName = findIDEByName;
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.describeConfiguredInvocationSurfaces = describeConfiguredInvocationSurfaces;
7
+ exports.describeOnboardingInvocationSurfaces = describeOnboardingInvocationSurfaces;
8
+ exports.installSlashCommands = installSlashCommands;
9
+ exports.installGlobalRules = installGlobalRules;
10
+ /**
11
+ * IDE Global Integration
12
+ *
13
+ * Installs user-level FRAIM invocation artifacts for supported IDEs.
14
+ * These surfaces make FRAIM discoverable without requiring `fraim init-project`.
15
+ */
16
+ const fs_1 = __importDefault(require("fs"));
17
+ const os_1 = __importDefault(require("os"));
18
+ const path_1 = __importDefault(require("path"));
19
+ const chalk_1 = __importDefault(require("chalk"));
20
+ const ide_invocation_surfaces_1 = require("./ide-invocation-surfaces");
21
+ function describeConfiguredInvocationSurfaces(installedIDEs) {
22
+ return installedIDEs.map((ide) => (0, ide_invocation_surfaces_1.describeInvocationSurface)(ide.name, ide.invocationProfile));
23
+ }
24
+ // Returns IDE-specific onboarding commands for the post-setup routing screen.
25
+ // Maps each IDE's invocation prefix to the project-onboarding task string so
26
+ // the user sees the exact command to type, not a generic FRAIM invocation.
27
+ function describeOnboardingInvocationSurfaces(installedIDEs) {
28
+ if (installedIDEs.length === 0)
29
+ return ['/fraim onboard this project'];
30
+ return installedIDEs.map((ide) => {
31
+ const profile = ide.invocationProfile;
32
+ if (profile === 'cursor-mention')
33
+ return `${ide.name}: @fraim onboard this project`;
34
+ if (profile === 'launch-phrase')
35
+ return `${ide.name}: onboard this project`;
36
+ if (profile === 'instructions-only')
37
+ return `${ide.name}: "onboard this project"`;
38
+ // claude-slash, vscode-prompt, codex-skill, grok-skill, windsurf-command, kiro-hashtag, gemini-command
39
+ return `${ide.name}: /fraim onboard this project`;
40
+ });
41
+ }
42
+ /**
43
+ * Install Claude FRAIM discovery artifacts at the user level.
44
+ * Writes a skill to ~/.claude/skills/fraim/SKILL.md and a compatibility command
45
+ * to ~/.claude/commands/fraim.md. Existing user files are preserved.
46
+ */
47
+ async function installSlashCommands(homeDir) {
48
+ const home = homeDir || os_1.default.homedir();
49
+ const claudeDir = path_1.default.join(home, '.claude');
50
+ if (!fs_1.default.existsSync(claudeDir)) {
51
+ return;
52
+ }
53
+ installFileIfMissing(path_1.default.join(claudeDir, 'skills', 'fraim', 'SKILL.md'), (0, ide_invocation_surfaces_1.buildClaudeSkillContent)(), 'Claude FRAIM skill (~/.claude/skills/fraim/SKILL.md)');
54
+ installFileIfMissing(path_1.default.join(claudeDir, 'commands', 'fraim.md'), (0, ide_invocation_surfaces_1.buildClaudeCommandShimContent)(), 'Claude compatibility command (~/.claude/commands/fraim.md)');
55
+ }
56
+ /**
57
+ * Install FRAIM invocation artifacts for non-Claude IDEs.
58
+ * Supports: Cursor, Codex, Grok, Gemini CLI, Copilot CLI, Windsurf, Kiro
59
+ * Does not overwrite existing files.
60
+ */
61
+ async function installGlobalRules(homeDir) {
62
+ const home = homeDir || os_1.default.homedir();
63
+ const cursorDir = path_1.default.join(home, '.cursor');
64
+ if (fs_1.default.existsSync(cursorDir)) {
65
+ installFileIfMissing(path_1.default.join(cursorDir, 'rules', 'fraim.mdc'), (0, ide_invocation_surfaces_1.buildCursorMentionRuleContent)(), 'Cursor FRAIM rule (~/.cursor/rules/fraim.mdc)');
66
+ }
67
+ const codexDir = path_1.default.join(home, '.codex');
68
+ if (fs_1.default.existsSync(codexDir)) {
69
+ installFileIfMissing(path_1.default.join(codexDir, 'skills', 'fraim', 'SKILL.md'), (0, ide_invocation_surfaces_1.buildCodexSkillContent)(), 'Codex FRAIM skill (~/.codex/skills/fraim/SKILL.md)');
70
+ }
71
+ const grokDir = path_1.default.join(home, '.grok');
72
+ if (fs_1.default.existsSync(grokDir)) {
73
+ installFileIfMissing(path_1.default.join(grokDir, 'skills', 'fraim', 'SKILL.md'), (0, ide_invocation_surfaces_1.buildGrokSkillContent)(), 'Grok FRAIM skill (~/.grok/skills/fraim/SKILL.md)');
74
+ }
75
+ const geminiDir = path_1.default.join(home, '.gemini');
76
+ if (fs_1.default.existsSync(geminiDir)) {
77
+ installFileIfMissing(path_1.default.join(geminiDir, 'commands', 'fraim.toml'), (0, ide_invocation_surfaces_1.buildGeminiCommandContent)(), 'Gemini CLI FRAIM command (~/.gemini/commands/fraim.toml)');
78
+ }
79
+ const antigravityDir = path_1.default.join(home, '.gemini', 'antigravity');
80
+ if (fs_1.default.existsSync(antigravityDir)) {
81
+ installFileIfMissing(path_1.default.join(antigravityDir, 'commands', 'fraim.md'), (0, ide_invocation_surfaces_1.buildAntigravityCommandContent)(), 'Antigravity FRAIM command (~/.gemini/antigravity/commands/fraim.md)');
82
+ }
83
+ const windsurfDir = path_1.default.join(home, '.codeium', 'windsurf');
84
+ if (fs_1.default.existsSync(windsurfDir)) {
85
+ installFileIfMissing(path_1.default.join(windsurfDir, 'commands', 'fraim.md'), (0, ide_invocation_surfaces_1.buildWindsurfCommandContent)(), 'Windsurf FRAIM command (~/.codeium/windsurf/commands/fraim.md)');
86
+ }
87
+ const kiroDir = path_1.default.join(home, '.kiro');
88
+ if (fs_1.default.existsSync(kiroDir)) {
89
+ installFileIfMissing(path_1.default.join(kiroDir, 'commands', 'fraim.md'), (0, ide_invocation_surfaces_1.buildKiroCommandContent)(), 'Kiro FRAIM command (~/.kiro/commands/fraim.md)');
90
+ }
91
+ }
92
+ function installFileIfMissing(filePath, content, displayName) {
93
+ if (fs_1.default.existsSync(filePath)) {
94
+ console.log(chalk_1.default.gray(` ${displayName.split('(')[0].trim()} already exists — skipping`));
95
+ return;
96
+ }
97
+ fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
98
+ fs_1.default.writeFileSync(filePath, content, 'utf8');
99
+ console.log(chalk_1.default.green(` ✅ Installed ${displayName}`));
100
+ }
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FRAIM_INVOCATION_BODY = exports.FRAIM_DEFERRED_TOOL_PRELOAD = exports.CURSOR_MDC_FRONTMATTER = exports.FRAIM_LAUNCH_PHRASE = void 0;
4
+ exports.buildFraimInvocationBody = buildFraimInvocationBody;
5
+ exports.buildClaudeSkillContent = buildClaudeSkillContent;
6
+ exports.buildClaudeCommandShimContent = buildClaudeCommandShimContent;
7
+ exports.buildClaudeSlashCommandContent = buildClaudeSlashCommandContent;
8
+ exports.buildCursorMentionRuleContent = buildCursorMentionRuleContent;
9
+ exports.buildCodexSkillContent = buildCodexSkillContent;
10
+ exports.buildGrokSkillContent = buildGrokSkillContent;
11
+ exports.buildWindsurfCommandContent = buildWindsurfCommandContent;
12
+ exports.buildKiroCommandContent = buildKiroCommandContent;
13
+ exports.buildAntigravityCommandContent = buildAntigravityCommandContent;
14
+ exports.buildGeminiCommandContent = buildGeminiCommandContent;
15
+ exports.describeInvocationSurface = describeInvocationSurface;
16
+ exports.FRAIM_LAUNCH_PHRASE = 'Use FRAIM for <job or task>';
17
+ exports.CURSOR_MDC_FRONTMATTER = `---
18
+ description: FRAIM discovery and execution contract
19
+ alwaysApply: true
20
+ ---`;
21
+ exports.FRAIM_DEFERRED_TOOL_PRELOAD = [
22
+ 'fraim_connect',
23
+ 'list_fraim_jobs',
24
+ 'get_fraim_job',
25
+ 'get_fraim_file',
26
+ 'seekMentoring'
27
+ ];
28
+ function buildDeferredToolBootstrapSection(profile) {
29
+ if (profile === 'none') {
30
+ return '';
31
+ }
32
+ const preloadList = exports.FRAIM_DEFERRED_TOOL_PRELOAD.map((toolName) => `\`${toolName}\``).join(', ');
33
+ switch (profile) {
34
+ case 'claude-toolsearch':
35
+ return `0. **Preload deferred FRAIM tools when needed**:
36
+ - If FRAIM MCP tools are unavailable because this host lazily loads deferred tool schemas, call \`ToolSearch\` once to load ${preloadList}.
37
+ - Do the preload as one batched discovery step, not one search per tool.
38
+
39
+ `;
40
+ case 'codex-tool-search':
41
+ return `0. **Preload deferred FRAIM tools when needed**:
42
+ - If FRAIM MCP tools are unavailable because this host lazily loads deferred tool schemas, call \`tool_search\` once to load ${preloadList}.
43
+ - Do the preload as one batched discovery step, not one search per tool.
44
+
45
+ `;
46
+ case 'generic-tool-discovery':
47
+ return `0. **Preload deferred FRAIM tools when needed**:
48
+ - If FRAIM MCP tools are unavailable because this host lazily loads deferred tool schemas, use the host's tool discovery surface once to load ${preloadList}.
49
+ - Do the preload as one batched discovery step, not one search per tool.
50
+
51
+ `;
52
+ default:
53
+ return '';
54
+ }
55
+ }
56
+ function buildFraimInvocationBody(profile = 'none') {
57
+ return `Follow this process:
58
+
59
+ ${buildDeferredToolBootstrapSection(profile)}1. **If the user did not specify a FRAIM job or topic**:
60
+ If local FRAIM job stubs are present in the workspace, inspect those first and match the request locally. Also inspect \`fraim/personalized-employee/jobs/\` for local overrides or repo-specific jobs. If local files are missing or you cannot inspect workspace files, call \`list_fraim_jobs()\` to view the full catalog, including any proxy-discoverable personalized jobs.
61
+
62
+ 2. **Find the match**:
63
+ Match the user's request to a FRAIM job from the local stub catalog, \`fraim/personalized-employee/jobs/\`, or the full \`list_fraim_jobs()\` response. If no job matches, try a likely FRAIM skill with \`get_fraim_file({ path: "skills/<likely-category>/<argument>.md" })\` and confirm the match with the user.
64
+
65
+ 3. **Load the full content**:
66
+ - For jobs, call \`get_fraim_job({ job: "<matched-job-name>" })\`.
67
+ - For skills, use the content returned by \`get_fraim_file(...)\`.
68
+
69
+ 4. **Execute**:
70
+ - For jobs, follow the phased instructions and use \`seekMentoring\` when the job requires phase transitions.
71
+ - For skills, apply the skill steps directly to the user's current context.
72
+ `;
73
+ }
74
+ exports.FRAIM_INVOCATION_BODY = buildFraimInvocationBody();
75
+ function buildClaudeSkillContent() {
76
+ return `# FRAIM
77
+
78
+ ${buildFraimInvocationBody('claude-toolsearch')}`;
79
+ }
80
+ function buildClaudeCommandShimContent() {
81
+ return `# FRAIM Compatibility Command
82
+
83
+ Use the FRAIM skill when Claude exposes skills directly. This compatibility command keeps \`/fraim\` working on surfaces that still discover legacy command files.
84
+
85
+ ${buildFraimInvocationBody('claude-toolsearch')}`;
86
+ }
87
+ function buildClaudeSlashCommandContent() {
88
+ return buildClaudeCommandShimContent();
89
+ }
90
+ function buildCursorMentionRuleContent() {
91
+ return `${exports.CURSOR_MDC_FRONTMATTER}
92
+
93
+ # FRAIM
94
+
95
+ ${buildFraimInvocationBody('generic-tool-discovery')}
96
+ `;
97
+ }
98
+ function buildCodexSkillContent() {
99
+ return `# FRAIM
100
+
101
+ ${buildFraimInvocationBody('codex-tool-search')}`;
102
+ }
103
+ function buildGrokSkillContent() {
104
+ return `# FRAIM
105
+
106
+ ${buildFraimInvocationBody('generic-tool-discovery')}`;
107
+ }
108
+ function buildWindsurfCommandContent() {
109
+ return `# FRAIM
110
+
111
+ ${buildFraimInvocationBody('generic-tool-discovery')}`;
112
+ }
113
+ function buildKiroCommandContent() {
114
+ return `# FRAIM
115
+
116
+ ${buildFraimInvocationBody('generic-tool-discovery')}`;
117
+ }
118
+ function escapeTomlMultiline(value) {
119
+ return value.replace(/"""/g, '\\"""');
120
+ }
121
+ function buildAntigravityCommandContent() {
122
+ return `# FRAIM
123
+
124
+ ${buildFraimInvocationBody('generic-tool-discovery')}`;
125
+ }
126
+ function buildGeminiCommandContent() {
127
+ return `description = "Discover and execute FRAIM jobs and skills"
128
+ prompt = """
129
+ # FRAIM
130
+
131
+ ${escapeTomlMultiline(buildFraimInvocationBody('generic-tool-discovery'))}
132
+ """`;
133
+ }
134
+ function describeInvocationSurface(ideName, invocationProfile) {
135
+ switch (invocationProfile) {
136
+ case 'claude-slash':
137
+ return ideName === 'Claude Code'
138
+ ? 'Claude Code (CLI/Desktop): /fraim'
139
+ : `${ideName}: /fraim`;
140
+ case 'launch-phrase':
141
+ return `${ideName}: "${exports.FRAIM_LAUNCH_PHRASE}"`;
142
+ case 'cursor-mention':
143
+ return `${ideName}: @fraim`;
144
+ case 'vscode-prompt':
145
+ return `${ideName}: /fraim via workspace prompt`;
146
+ case 'codex-skill':
147
+ return `${ideName}: /fraim, $fraim`;
148
+ case 'grok-skill':
149
+ return `${ideName}: /fraim`;
150
+ case 'windsurf-command':
151
+ return `${ideName}: /fraim`;
152
+ case 'kiro-hashtag':
153
+ return `${ideName}: #fraim`;
154
+ case 'gemini-command':
155
+ return `${ideName}: /fraim`;
156
+ case 'instructions-only':
157
+ default:
158
+ return `${ideName}: natural language`;
159
+ }
160
+ }