fraim-framework 2.0.174 → 2.0.176

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.
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.expandPath = exports.findIDEByName = exports.getAllSupportedIDEs = exports.detectInstalledIDEs = exports.IDE_CONFIGS = void 0;
6
+ exports.expandPath = exports.findIDEByName = exports.getAllSupportedIDEs = exports.detectInstalledIDEs = exports.CLI_PROBE_CONFIGTYPES = exports.IDE_CONFIGS = void 0;
7
7
  exports.getAdapterConfigType = getAdapterConfigType;
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
@@ -71,9 +71,9 @@ const detectGeminiCli = () => {
71
71
  };
72
72
  const detectGeminiSurface = () => {
73
73
  const paths = [
74
- '~/.gemini',
75
- '~/AppData/Roaming/gemini',
76
- '~/.config/gemini'
74
+ '~/.gemini/settings.json',
75
+ '~/AppData/Roaming/gemini/settings.json',
76
+ '~/.config/gemini/settings.json'
77
77
  ];
78
78
  return checkMultiplePaths(paths);
79
79
  };
@@ -103,7 +103,8 @@ exports.IDE_CONFIGS = [
103
103
  alternativePaths: [
104
104
  '~/.claude/settings.json'
105
105
  ],
106
- description: 'Anthropic Claude Code local surfaces (CLI and Desktop Code tab)'
106
+ description: 'Anthropic Claude Code local surfaces (CLI and Desktop Code tab)',
107
+ downloadUrl: 'https://claude.ai/code',
107
108
  },
108
109
  {
109
110
  name: 'Claude Desktop / Cowork',
@@ -116,7 +117,8 @@ exports.IDE_CONFIGS = [
116
117
  alternativePaths: [
117
118
  '~/Library/Application Support/Claude/claude_desktop_config.json'
118
119
  ],
119
- description: 'Anthropic Claude Desktop chat and Cowork surfaces'
120
+ description: 'Anthropic Claude Desktop chat and Cowork surfaces',
121
+ downloadUrl: 'https://claude.ai/download',
120
122
  },
121
123
  {
122
124
  name: 'Antigravity',
@@ -125,7 +127,8 @@ exports.IDE_CONFIGS = [
125
127
  configType: 'standard',
126
128
  invocationProfile: 'cursor-mention',
127
129
  detectMethod: () => fs_1.default.existsSync(expandPath('~/.gemini/antigravity')),
128
- description: 'Google Gemini Antigravity IDE'
130
+ description: 'Google Gemini Antigravity IDE',
131
+ downloadUrl: 'https://deepmind.google/technologies/gemini/',
129
132
  },
130
133
  {
131
134
  name: 'Gemini CLI',
@@ -140,7 +143,8 @@ exports.IDE_CONFIGS = [
140
143
  '~/AppData/Roaming/gemini/settings.json',
141
144
  '~/.config/gemini/settings.json'
142
145
  ],
143
- description: 'Google Gemini CLI local settings'
146
+ description: 'Google Gemini CLI local settings',
147
+ downloadUrl: 'https://github.com/google-gemini/gemini-cli',
144
148
  },
145
149
  {
146
150
  name: 'Kiro',
@@ -149,7 +153,8 @@ exports.IDE_CONFIGS = [
149
153
  configType: 'kiro',
150
154
  invocationProfile: 'kiro-hashtag',
151
155
  detectMethod: () => fs_1.default.existsSync(expandPath('~/.kiro')),
152
- description: 'Kiro AI-powered IDE'
156
+ description: 'Kiro AI-powered IDE',
157
+ downloadUrl: 'https://kiro.dev/',
153
158
  },
154
159
  {
155
160
  name: 'Cursor',
@@ -164,7 +169,8 @@ exports.IDE_CONFIGS = [
164
169
  '~/AppData/Roaming/Cursor/mcp.json',
165
170
  '~/.config/Cursor/mcp.json'
166
171
  ],
167
- description: 'Cursor AI code editor'
172
+ description: 'Cursor AI code editor',
173
+ downloadUrl: 'https://cursor.com/',
168
174
  },
169
175
  {
170
176
  name: 'VSCode',
@@ -182,7 +188,8 @@ exports.IDE_CONFIGS = [
182
188
  '~/AppData/Roaming/Code/User/mcp.json',
183
189
  '~/.config/Code/User/mcp.json'
184
190
  ],
185
- description: 'Visual Studio Code'
191
+ description: 'Visual Studio Code',
192
+ downloadUrl: 'https://code.visualstudio.com/',
186
193
  },
187
194
  {
188
195
  name: 'Codex',
@@ -191,7 +198,8 @@ exports.IDE_CONFIGS = [
191
198
  configType: 'codex',
192
199
  invocationProfile: 'codex-skill',
193
200
  detectMethod: detectCodexSurface,
194
- description: 'Codex AI development environment'
201
+ description: 'Codex AI development environment',
202
+ downloadUrl: 'https://github.com/openai/codex',
195
203
  },
196
204
  {
197
205
  name: 'Grok',
@@ -202,7 +210,8 @@ exports.IDE_CONFIGS = [
202
210
  detectMethod: detectGrokSurface,
203
211
  supportsConfigBootstrap: true,
204
212
  aliases: ['grok', 'grok-cli', 'grok cli', 'grok build'],
205
- description: 'xAI Grok Build CLI local settings'
213
+ description: 'xAI Grok Build CLI local settings',
214
+ downloadUrl: 'https://x.ai/grok',
206
215
  },
207
216
  {
208
217
  name: 'Windsurf',
@@ -216,7 +225,8 @@ exports.IDE_CONFIGS = [
216
225
  '~/AppData/Roaming/Windsurf/mcp_config.json',
217
226
  '~/.config/windsurf/mcp_config.json'
218
227
  ],
219
- description: 'Codeium Windsurf IDE'
228
+ description: 'Codeium Windsurf IDE',
229
+ downloadUrl: 'https://windsurf.com/',
220
230
  }
221
231
  ];
222
232
  const findBestConfigPath = (ide) => {
@@ -236,6 +246,8 @@ const _cachedIDEs = new Map();
236
246
  const _cacheTimestamps = new Map();
237
247
  const _cacheHomeDirs = new Map();
238
248
  const DETECT_CACHE_TTL_MS = 5000;
249
+ /** configTypes that require a binary probe in cli-runnable mode; config folder alone is insufficient. */
250
+ exports.CLI_PROBE_CONFIGTYPES = new Set(['claude-code', 'codex', 'grok', 'gemini-cli']);
239
251
  const isDetectedForMode = (ide, mode) => {
240
252
  if (mode === 'cli-runnable') {
241
253
  switch (ide.configType) {
@@ -175,7 +175,13 @@ function normalizeRows(rows) {
175
175
  }));
176
176
  }
177
177
  function buildRunnableAgentSurfaces() {
178
- const ides = (0, ide_detector_1.detectInstalledIDEs)('cli-runnable');
178
+ // GUI agents (Claude Desktop, VSCode, Cursor, Windsurf, Kiro) are detected by config-surface:
179
+ // their config folder is sufficient evidence of installation.
180
+ // CLI agents require a binary on PATH; a config folder alone does not mean the agent is
181
+ // runnable, so they are gated through cli-runnable as well.
182
+ const configSurface = (0, ide_detector_1.detectInstalledIDEs)('config-surface');
183
+ const cliRunnableTypes = new Set((0, ide_detector_1.detectInstalledIDEs)('cli-runnable').map((ide) => ide.configType));
184
+ const ides = configSurface.filter((ide) => !ide_detector_1.CLI_PROBE_CONFIGTYPES.has(ide.configType) || cliRunnableTypes.has(ide.configType));
179
185
  const hints = (0, ide_global_integration_1.describeOnboardingInvocationSurfaces)(ides);
180
186
  return ides.map((ide, index) => ({
181
187
  id: ide.configType,
@@ -426,6 +432,13 @@ class FirstRunSessionService {
426
432
  getSession() {
427
433
  this.detectRowsOnLoad();
428
434
  this.persist();
435
+ const detectedNames = new Set((this.state.setupResult?.configuredSurfaces ?? []).map((s) => s.name));
436
+ const supportedAgents = ide_detector_1.IDE_CONFIGS
437
+ .map((ide) => ({
438
+ name: ide.name,
439
+ detected: detectedNames.has(ide.name),
440
+ downloadUrl: ide.downloadUrl ?? null,
441
+ }));
429
442
  return {
430
443
  state: this.state,
431
444
  rows: this.state.rows,
@@ -435,6 +448,7 @@ class FirstRunSessionService {
435
448
  requestToken: this.requestToken,
436
449
  agentOptions: types_1.FIRST_RUN_AGENT_OPTIONS,
437
450
  currentAgentId: this.state.agentId,
451
+ supportedAgents,
438
452
  };
439
453
  }
440
454
  respond(message, ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim-framework",
3
- "version": "2.0.174",
3
+ "version": "2.0.176",
4
4
  "description": "FRAIM: AI Workforce Infrastructure — the organizational capability that turns AI agents into an accountable workforce, their operators into capable AI managers, and executives into leaders with clear optics on AI proficiency.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -13,6 +13,7 @@
13
13
  configureError: null,
14
14
  };
15
15
 
16
+
16
17
  const STEP_LABELS = {
17
18
  prereqs: 'Prerequisites',
18
19
  agents: 'Local Agents',
@@ -86,15 +87,6 @@
86
87
  return configuredSurfaces().some((surface) => surface && (surface.id === opt.id || surface.name === opt.label));
87
88
  }
88
89
 
89
- function readyAgentLabels() {
90
- const labels = new Set();
91
- for (const agent of readyAgents()) labels.add(agent.label);
92
- for (const surface of configuredSurfaces()) {
93
- if (surface && surface.name) labels.add(surface.name);
94
- }
95
- return Array.from(labels);
96
- }
97
-
98
90
  function detectedAgentCount() {
99
91
  const setupResult = state.session && state.session.state ? state.session.state.setupResult : null;
100
92
  return (setupResult && setupResult.detectedSurfaceCount) || readyAgents().length;
@@ -216,60 +208,74 @@
216
208
  h.textContent = 'Locally installed AI agents';
217
209
  pane.appendChild(h);
218
210
 
211
+ const anyDetected = detectedAgentCount() > 0;
219
212
  const p = document.createElement('p');
220
213
  p.className = 'pane-copy';
221
- p.textContent = detectedAgentCount() > 0
222
- ? 'At least one local AI agent is ready. You can add more agents or continue to configure FRAIM.'
223
- : 'No problem, we will install AI agents next.';
214
+ p.textContent = anyDetected
215
+ ? 'An AI tool is ready. You can add more or continue to configure FRAIM.'
216
+ : 'Install any of the AI tools below, then click Rescan once it is running.';
224
217
  pane.appendChild(p);
225
218
 
226
- const readyLabels = readyAgentLabels();
227
- if (readyLabels.length > 0) {
228
- const readyList = document.createElement('div');
229
- readyList.className = 'ready-strip';
230
- readyList.setAttribute('data-testid', 'ready-agents');
231
- readyList.textContent = `Already installed: ${readyLabels.join(', ')}`;
232
- pane.appendChild(readyList);
233
- } else {
234
- const readyList = document.createElement('div');
235
- readyList.className = 'ready-strip ready-strip--muted';
236
- readyList.setAttribute('data-testid', 'ready-agents');
237
- readyList.textContent = 'Already installed: none detected yet';
238
- pane.appendChild(readyList);
239
- }
219
+ // Server-driven list: session.supportedAgents contains every GUI-detectable IDE
220
+ // with detection status and download URL already resolved.
221
+ const agents = (state.session && state.session.supportedAgents) || [];
240
222
 
241
- const grid = document.createElement('div');
242
- grid.className = 'agent-grid';
243
- const agentOptions = state.session.agentOptions || [];
244
- for (const opt of agentOptions) {
245
- const card = document.createElement('div');
246
- card.className = 'user-type-card recruit-card';
247
- card.setAttribute('data-agent-id', opt.id);
248
- card.setAttribute('data-testid', `agent-card-${opt.id}`);
249
- const agentReady = isAgentReady(opt);
250
- const title = document.createElement('strong');
251
- title.className = 'card-title';
252
- title.textContent = opt.label;
253
- const desc = document.createElement('p');
254
- desc.className = 'card-desc';
255
- desc.textContent = agentReady
256
- ? 'Already installed and ready for FRAIM.'
257
- : `Available to set up: install, sign in, and verify ${opt.label}.`;
258
- const action = button(agentReady ? 'Ready' : 'Set up', 'secondary');
259
- action.disabled = agentReady;
260
- action.setAttribute('data-testid', `install-${opt.id}`);
261
- action.addEventListener('click', () => openAgentModal(opt));
262
- card.appendChild(title);
263
- card.appendChild(desc);
264
- card.appendChild(action);
265
- grid.appendChild(card);
223
+ const list = document.createElement('ul');
224
+ list.className = 'agent-list';
225
+ list.setAttribute('data-testid', 'agent-list');
226
+
227
+ for (const agent of agents) {
228
+ const li = document.createElement('li');
229
+ li.className = 'agent-row' + (agent.detected ? ' agent-row--detected' : '');
230
+ li.setAttribute('data-testid', `agent-row-${agent.name.toLowerCase().replace(/\s+/g, '-')}`);
231
+
232
+ const nameSpan = document.createElement('span');
233
+ nameSpan.className = 'agent-row__name';
234
+ nameSpan.textContent = agent.name;
235
+
236
+ const actionSpan = document.createElement('span');
237
+ actionSpan.className = 'agent-row__action';
238
+
239
+ if (agent.detected) {
240
+ const badge = document.createElement('span');
241
+ badge.className = 'agent-badge agent-badge--installed';
242
+ badge.setAttribute('data-testid', `agent-installed-${agent.name.toLowerCase().replace(/\s+/g, '-')}`);
243
+ badge.textContent = 'Installed';
244
+ actionSpan.appendChild(badge);
245
+ } else if (agent.downloadUrl) {
246
+ const link = document.createElement('a');
247
+ link.className = 'agent-download-link';
248
+ link.href = agent.downloadUrl;
249
+ link.target = '_blank';
250
+ link.rel = 'noopener noreferrer';
251
+ link.textContent = 'Download';
252
+ link.setAttribute('data-testid', `agent-download-${agent.name.toLowerCase().replace(/\s+/g, '-')}`);
253
+ actionSpan.appendChild(link);
254
+ }
255
+
256
+ li.appendChild(nameSpan);
257
+ li.appendChild(actionSpan);
258
+ list.appendChild(li);
266
259
  }
267
260
 
268
- const byoa = document.createElement('div');
269
- byoa.className = 'user-type-card recruit-card byoa-card';
270
- byoa.innerHTML = '<strong class="card-title">Bring Your Own Agent</strong><p class="card-desc">Use Cursor, Windsurf, Kiro, VS Code, or another supported local AI tool. If you install a new agent later and want FRAIM to use it, run this command from your project.</p><div class="cmd-block">npx fraim add-ide</div>';
271
- grid.appendChild(byoa);
272
- pane.appendChild(grid);
261
+ pane.appendChild(list);
262
+
263
+ const otherNote = document.createElement('p');
264
+ otherNote.className = 'pane-copy';
265
+ otherNote.innerHTML = 'Using a different tool? Run <code>npx fraim add-ide</code> from your project directory after setup.';
266
+ pane.appendChild(otherNote);
267
+
268
+ const rescanBtn = button('Rescan installed tools', 'ghost');
269
+ rescanBtn.setAttribute('data-testid', 'rescan-agents');
270
+ rescanBtn.addEventListener('click', async () => {
271
+ rescanBtn.disabled = true;
272
+ rescanBtn.textContent = 'Scanning...';
273
+ try { await loadSession(false); } finally {
274
+ state.activeStep = 'agents';
275
+ render();
276
+ }
277
+ });
278
+ pane.appendChild(rescanBtn);
273
279
 
274
280
  const done = button('Next', 'primary');
275
281
  done.setAttribute('data-testid', 'done-installing-agents');
@@ -516,6 +516,53 @@ body {
516
516
  grid-template-columns: repeat(2, minmax(0, 1fr));
517
517
  gap: 12px;
518
518
  }
519
+
520
+ /* Compact server-driven agent list */
521
+ .agent-list {
522
+ list-style: none;
523
+ margin: 0 0 12px;
524
+ padding: 0;
525
+ border: 1px solid var(--line);
526
+ border-radius: 10px;
527
+ overflow: hidden;
528
+ }
529
+ .agent-row {
530
+ display: flex;
531
+ align-items: center;
532
+ justify-content: space-between;
533
+ padding: 12px 16px;
534
+ border-bottom: 1px solid var(--line);
535
+ background: var(--surface);
536
+ gap: 12px;
537
+ }
538
+ .agent-row:last-child { border-bottom: 0; }
539
+ .agent-row--detected { background: var(--accent-soft); }
540
+ .agent-row__name {
541
+ font-size: 14px;
542
+ font-weight: 500;
543
+ color: var(--text);
544
+ flex: 1;
545
+ }
546
+ .agent-row--detected .agent-row__name { color: var(--accent-strong); }
547
+ .agent-row__action { flex-shrink: 0; }
548
+ .agent-badge {
549
+ font-size: 12px;
550
+ font-weight: 600;
551
+ padding: 3px 10px;
552
+ border-radius: 20px;
553
+ }
554
+ .agent-badge--installed {
555
+ background: var(--accent);
556
+ color: #fff;
557
+ }
558
+ .agent-download-link {
559
+ font-size: 13px;
560
+ font-weight: 500;
561
+ color: var(--accent-strong);
562
+ text-decoration: none;
563
+ }
564
+ .agent-download-link:hover { text-decoration: underline; }
565
+
519
566
  .ready-strip {
520
567
  background: var(--accent-soft);
521
568
  border: 1px solid var(--accent);