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.
- package/README.md +467 -0
- package/bin/fraim-hub.js +17 -0
- package/dist/src/ai-hub/brand-store.js +17 -0
- package/dist/src/ai-hub/catalog.js +412 -0
- package/dist/src/ai-hub/cert-store.js +70 -0
- package/dist/src/ai-hub/cli.js +212 -0
- package/dist/src/ai-hub/configured-agents.js +283 -0
- package/dist/src/ai-hub/conversation-store.js +428 -0
- package/dist/src/ai-hub/desktop-main.js +449 -0
- package/dist/src/ai-hub/hosts.js +1669 -0
- package/dist/src/ai-hub/hub-latest-version.js +52 -0
- package/dist/src/ai-hub/hub-launch-decision.js +57 -0
- package/dist/src/ai-hub/hub-runtime-file.js +43 -0
- package/dist/src/ai-hub/managed-browser.js +269 -0
- package/dist/src/ai-hub/manager-turns.js +64 -0
- package/dist/src/ai-hub/office-sideload.js +156 -0
- package/dist/src/ai-hub/openclaw-bridge.js +250 -0
- package/dist/src/ai-hub/preferences.js +201 -0
- package/dist/src/ai-hub/remote-hub-gateway.js +88 -0
- package/dist/src/ai-hub/server.js +4013 -0
- package/dist/src/ai-hub/types.js +2 -0
- package/dist/src/ai-hub/url-safety.js +19 -0
- package/dist/src/api/admin/payments.js +33 -0
- package/dist/src/api/admin/sales-leads.js +21 -0
- package/dist/src/api/payment/create-session.js +338 -0
- package/dist/src/api/payment/dashboard-link.js +149 -0
- package/dist/src/api/payment/session-details.js +31 -0
- package/dist/src/api/payment/webhook.js +587 -0
- package/dist/src/api/sales/contact.js +44 -0
- package/dist/src/cli/api/get-provider-client.js +41 -0
- package/dist/src/cli/api/provider-client.js +107 -0
- package/dist/src/cli/commands/add-ide.js +462 -0
- package/dist/src/cli/fraim-hub.js +50 -0
- package/dist/src/cli/internal/device-flow-service.js +83 -0
- package/dist/src/cli/mcp/command-resolution.js +81 -0
- package/dist/src/cli/mcp/fraim-mcp-latest-launcher.js +136 -0
- package/dist/src/cli/mcp/ide-formats.js +317 -0
- package/dist/src/cli/mcp/mcp-server-builder.js +48 -0
- package/dist/src/cli/mcp/mcp-server-registry.js +173 -0
- package/dist/src/cli/providers/local-provider-registry.js +165 -0
- package/dist/src/cli/providers/provider-registry.js +230 -0
- package/dist/src/cli/setup/claude-code-telemetry.js +59 -0
- package/dist/src/cli/setup/codex-local-config.js +37 -0
- package/dist/src/cli/setup/ide-detector.js +383 -0
- package/dist/src/cli/setup/ide-global-integration.js +100 -0
- package/dist/src/cli/setup/ide-invocation-surfaces.js +160 -0
- package/dist/src/cli/setup/mcp-config-generator.js +225 -0
- package/dist/src/cli/setup/provider-prompts.js +339 -0
- package/dist/src/cli/utils/managed-agent-paths.js +118 -0
- package/dist/src/cli/utils/script-sync-utils.js +221 -0
- package/dist/src/cli/utils/user-config.js +100 -0
- package/dist/src/cli/utils/version-utils.js +35 -0
- package/dist/src/config/ai-manager-hiring.js +121 -0
- package/dist/src/config/feature-flags.js +25 -0
- package/dist/src/config/persona-capability-bundles.js +277 -0
- package/dist/src/config/persona-hiring.js +270 -0
- package/dist/src/config/portfolio-slug-overrides.js +17 -0
- package/dist/src/config/pricing.js +37 -0
- package/dist/src/config/stripe.js +43 -0
- package/dist/src/core/brand-store.js +232 -0
- package/dist/src/core/quality-evidence.js +331 -0
- package/dist/src/core/utils/git-utils.js +194 -0
- package/dist/src/core/utils/ports.js +32 -0
- package/dist/src/core/utils/project-fraim-paths.js +220 -0
- package/dist/src/db/payment-repository.js +61 -0
- package/dist/src/first-run/types.js +93 -0
- package/dist/src/fraim/db-service.js +2409 -0
- package/dist/src/local-mcp-server/agent-token-prices.js +167 -0
- package/dist/src/local-mcp-server/learning-context-builder.js +975 -0
- package/dist/src/middleware/rate-limit.js +110 -0
- package/dist/src/models/payment.js +2 -0
- package/dist/src/routes/payment-routes.js +186 -0
- package/dist/src/routes/persona-catalog-routes.js +84 -0
- package/dist/src/services/dashboard-access.js +27 -0
- package/dist/src/services/email-service.js +951 -0
- package/dist/src/services/persona-entitlement-service.js +352 -0
- package/dist/src/services/workspace-identity.js +21 -0
- package/dist/src/types/analytics.js +2 -0
- package/dist/src/utils/payment-calculator.js +52 -0
- package/extensions/office-word/favicon.ico +0 -0
- package/extensions/office-word/icon-64.png +0 -0
- package/extensions/office-word/manifest.xml +33 -0
- package/extensions/office-word/taskpane.html +242 -0
- package/index.js +85 -0
- package/package.json +106 -0
- package/public/ai-hub/index.html +910 -0
- package/public/ai-hub/powerpoint-taskpane/icon-64.png +0 -0
- package/public/ai-hub/powerpoint-taskpane/index.html +236 -0
- package/public/ai-hub/powerpoint-taskpane/manifest.xml +30 -0
- package/public/ai-hub/review.css +444 -0
- package/public/ai-hub/script.js +11737 -0
- package/public/ai-hub/styles.css +5102 -0
- package/public/first-run/error-frame.js +100 -0
- package/public/first-run/index.html +35 -0
- package/public/first-run/script.js +739 -0
- package/public/first-run/styles.css +929 -0
- package/public/portfolio/ashley.html +523 -0
- package/public/portfolio/auditya.html +83 -0
- package/public/portfolio/banke.html +83 -0
- package/public/portfolio/beza.html +659 -0
- package/public/portfolio/careena.html +632 -0
- package/public/portfolio/casey.html +568 -0
- package/public/portfolio/celia.html +490 -0
- package/public/portfolio/deidre.html +642 -0
- package/public/portfolio/gautam.html +597 -0
- package/public/portfolio/hari.html +469 -0
- package/public/portfolio/huxley.html +1354 -0
- package/public/portfolio/index.html +741 -0
- package/public/portfolio/maestro.html +518 -0
- package/public/portfolio/mandy.html +590 -0
- package/public/portfolio/mona.html +597 -0
- package/public/portfolio/pam.html +887 -0
- package/public/portfolio/procella.html +107 -0
- package/public/portfolio/qasm.html +569 -0
- package/public/portfolio/ricardo.html +489 -0
- package/public/portfolio/sade.html +560 -0
- package/public/portfolio/sam.html +654 -0
- package/public/portfolio/sechar.html +580 -0
- package/public/portfolio/sreya.html +599 -0
- 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
|
+
}
|