fraim 2.0.166 → 2.0.168
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/ai-hub/catalog.js +43 -36
- package/dist/src/ai-hub/server.js +28 -5
- package/dist/src/cli/commands/init-project.js +1 -98
- package/dist/src/cli/commands/manager.js +40 -0
- package/dist/src/cli/commands/sync.js +17 -21
- package/dist/src/cli/fraim.js +2 -0
- package/dist/src/cli/utils/github-workflow-sync.js +12 -146
- package/dist/src/cli/utils/manager-pack-sync.js +188 -0
- package/dist/src/cli/utils/manager-publish.js +76 -0
- package/dist/src/cli/utils/user-config.js +20 -0
- package/dist/src/core/config-loader.js +9 -5
- package/dist/src/core/fraim-config-schema.generated.js +85 -31
- package/dist/src/core/manager-pack.js +26 -0
- package/dist/src/core/utils/local-registry-resolver.js +8 -1
- package/dist/src/first-run/install-state.js +1 -0
- package/dist/src/first-run/server.js +9 -0
- package/dist/src/first-run/session-service.js +117 -23
- package/dist/src/first-run/types.js +2 -5
- package/dist/src/local-mcp-server/learning-context-builder.js +45 -8
- package/dist/src/local-mcp-server/stdio-server.js +28 -0
- package/index.js +1 -1
- package/package.json +4 -1
- package/public/ai-hub/powerpoint-taskpane/index.html +236 -236
- package/public/ai-hub/powerpoint-taskpane/manifest.xml +29 -29
- package/public/ai-hub/review.css +13 -0
- package/public/ai-hub/script.js +199 -5
- package/public/ai-hub/styles.css +28 -0
- package/public/first-run/index.html +1 -1
- package/public/first-run/script.js +459 -530
- package/public/first-run/styles.css +288 -73
- 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
- package/dist/src/ai-hub/word-sideload.js +0 -95
- package/dist/src/cli/commands/test-mcp.js +0 -171
- package/dist/src/cli/setup/first-run.js +0 -242
- package/dist/src/core/config-writer.js +0 -75
- package/dist/src/core/utils/job-aliases.js +0 -47
- package/dist/src/core/utils/workflow-parser.js +0 -174
|
@@ -138,14 +138,6 @@ exports.FRAIM_CONFIG_SCHEMA = {
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
},
|
|
141
|
-
"githubWorkflows": {
|
|
142
|
-
"kind": "object",
|
|
143
|
-
"properties": {
|
|
144
|
-
"enabled": {
|
|
145
|
-
"kind": "boolean"
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
141
|
"validation": {
|
|
150
142
|
"kind": "object",
|
|
151
143
|
"properties": {
|
|
@@ -312,6 +304,78 @@ exports.FRAIM_CONFIG_SCHEMA = {
|
|
|
312
304
|
]
|
|
313
305
|
}
|
|
314
306
|
}
|
|
307
|
+
},
|
|
308
|
+
"recruiting": {
|
|
309
|
+
"kind": "object",
|
|
310
|
+
"properties": {
|
|
311
|
+
"ats": {
|
|
312
|
+
"kind": "object",
|
|
313
|
+
"properties": {
|
|
314
|
+
"provider": {
|
|
315
|
+
"kind": "enum",
|
|
316
|
+
"values": [
|
|
317
|
+
"greenhouse",
|
|
318
|
+
"lever",
|
|
319
|
+
"ashby",
|
|
320
|
+
"none"
|
|
321
|
+
]
|
|
322
|
+
},
|
|
323
|
+
"pipeline_source": {
|
|
324
|
+
"kind": "enum",
|
|
325
|
+
"values": [
|
|
326
|
+
"api",
|
|
327
|
+
"browser",
|
|
328
|
+
"csv",
|
|
329
|
+
"manual"
|
|
330
|
+
]
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
"leadgen": {
|
|
335
|
+
"kind": "object",
|
|
336
|
+
"properties": {
|
|
337
|
+
"provider": {
|
|
338
|
+
"kind": "enum",
|
|
339
|
+
"values": [
|
|
340
|
+
"gem",
|
|
341
|
+
"linkedin-recruiter",
|
|
342
|
+
"apollo",
|
|
343
|
+
"outreach",
|
|
344
|
+
"none"
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
"pipeline_source": {
|
|
348
|
+
"kind": "enum",
|
|
349
|
+
"values": [
|
|
350
|
+
"api",
|
|
351
|
+
"browser",
|
|
352
|
+
"csv",
|
|
353
|
+
"manual"
|
|
354
|
+
]
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
"staleness_days": {
|
|
359
|
+
"kind": "object",
|
|
360
|
+
"properties": {
|
|
361
|
+
"sourcing": {
|
|
362
|
+
"kind": "number"
|
|
363
|
+
},
|
|
364
|
+
"applied": {
|
|
365
|
+
"kind": "number"
|
|
366
|
+
},
|
|
367
|
+
"screen": {
|
|
368
|
+
"kind": "number"
|
|
369
|
+
},
|
|
370
|
+
"interview": {
|
|
371
|
+
"kind": "number"
|
|
372
|
+
},
|
|
373
|
+
"offer": {
|
|
374
|
+
"kind": "number"
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
315
379
|
}
|
|
316
380
|
}
|
|
317
381
|
},
|
|
@@ -335,23 +399,6 @@ exports.FRAIM_CONFIG_SCHEMA = {
|
|
|
335
399
|
"autonomous"
|
|
336
400
|
]
|
|
337
401
|
},
|
|
338
|
-
"contextResolver": {
|
|
339
|
-
"kind": "object",
|
|
340
|
-
"properties": {
|
|
341
|
-
"scriptPath": {
|
|
342
|
-
"kind": "string"
|
|
343
|
-
},
|
|
344
|
-
"arguments": {
|
|
345
|
-
"kind": "array",
|
|
346
|
-
"element": {
|
|
347
|
-
"kind": "string"
|
|
348
|
-
}
|
|
349
|
-
},
|
|
350
|
-
"timeoutMs": {
|
|
351
|
-
"kind": "number"
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
},
|
|
355
402
|
"playbooks": {
|
|
356
403
|
"kind": "object",
|
|
357
404
|
"properties": {
|
|
@@ -560,8 +607,6 @@ exports.SUPPORTED_FRAIM_CONFIG_PATHS = [
|
|
|
560
607
|
"customizations.designSystem",
|
|
561
608
|
"customizations.designSystem.path",
|
|
562
609
|
"customizations.designSystem.brand",
|
|
563
|
-
"customizations.githubWorkflows",
|
|
564
|
-
"customizations.githubWorkflows.enabled",
|
|
565
610
|
"customizations.validation",
|
|
566
611
|
"customizations.validation.buildCommand",
|
|
567
612
|
"customizations.validation.testSuiteCommand",
|
|
@@ -600,14 +645,23 @@ exports.SUPPORTED_FRAIM_CONFIG_PATHS = [
|
|
|
600
645
|
"integrations.itsm.accessScript",
|
|
601
646
|
"integrations.identity",
|
|
602
647
|
"integrations.identity.provider",
|
|
648
|
+
"integrations.recruiting",
|
|
649
|
+
"integrations.recruiting.ats",
|
|
650
|
+
"integrations.recruiting.ats.provider",
|
|
651
|
+
"integrations.recruiting.ats.pipeline_source",
|
|
652
|
+
"integrations.recruiting.leadgen",
|
|
653
|
+
"integrations.recruiting.leadgen.provider",
|
|
654
|
+
"integrations.recruiting.leadgen.pipeline_source",
|
|
655
|
+
"integrations.recruiting.staleness_days",
|
|
656
|
+
"integrations.recruiting.staleness_days.sourcing",
|
|
657
|
+
"integrations.recruiting.staleness_days.applied",
|
|
658
|
+
"integrations.recruiting.staleness_days.screen",
|
|
659
|
+
"integrations.recruiting.staleness_days.interview",
|
|
660
|
+
"integrations.recruiting.staleness_days.offer",
|
|
603
661
|
"automation",
|
|
604
662
|
"automation.support",
|
|
605
663
|
"automation.support.startMode",
|
|
606
664
|
"automation.support.defaultDecisionMode",
|
|
607
|
-
"automation.support.contextResolver",
|
|
608
|
-
"automation.support.contextResolver.scriptPath",
|
|
609
|
-
"automation.support.contextResolver.arguments",
|
|
610
|
-
"automation.support.contextResolver.timeoutMs",
|
|
611
665
|
"automation.support.playbooks",
|
|
612
666
|
"automation.support.playbooks.directory",
|
|
613
667
|
"automation.support.playbooks.decisionCommand",
|
|
@@ -0,0 +1,26 @@
|
|
|
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.MANAGER_PACK_RELATIVE_PATH_RE = exports.MANAGER_LEARNING_FILE_RE = exports.MANAGER_RULES_RELATIVE_PATH = exports.MANAGER_CONTEXT_RELATIVE_PATH = void 0;
|
|
7
|
+
exports.isManagerPackRelativePath = isManagerPackRelativePath;
|
|
8
|
+
exports.managerPackRelativePathForFileName = managerPackRelativePathForFileName;
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
exports.MANAGER_CONTEXT_RELATIVE_PATH = 'context/manager_context.md';
|
|
11
|
+
exports.MANAGER_RULES_RELATIVE_PATH = 'rules/manager_rules.md';
|
|
12
|
+
exports.MANAGER_LEARNING_FILE_RE = /^(?!org-)[A-Za-z0-9._%+@-]+-(mistake-patterns|preferences|manager-coaching|validated-patterns)\.md$/;
|
|
13
|
+
exports.MANAGER_PACK_RELATIVE_PATH_RE = /^(context\/manager_context\.md|rules\/manager_rules\.md|learnings\/(?!org-)[A-Za-z0-9._%+@-]+-(mistake-patterns|preferences|manager-coaching|validated-patterns)\.md)$/;
|
|
14
|
+
function isManagerPackRelativePath(value) {
|
|
15
|
+
return exports.MANAGER_PACK_RELATIVE_PATH_RE.test(value);
|
|
16
|
+
}
|
|
17
|
+
function managerPackRelativePathForFileName(fileName) {
|
|
18
|
+
const base = path_1.default.basename(fileName);
|
|
19
|
+
if (base === 'manager_context.md')
|
|
20
|
+
return exports.MANAGER_CONTEXT_RELATIVE_PATH;
|
|
21
|
+
if (base === 'manager_rules.md')
|
|
22
|
+
return exports.MANAGER_RULES_RELATIVE_PATH;
|
|
23
|
+
if (exports.MANAGER_LEARNING_FILE_RE.test(base))
|
|
24
|
+
return `learnings/${base}`;
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
@@ -371,9 +371,11 @@ class LocalRegistryResolver {
|
|
|
371
371
|
if (!this.hasLocalOverride(path)) {
|
|
372
372
|
const syncedLocalContent = this.readSyncedLocalFile(path);
|
|
373
373
|
if (syncedLocalContent !== null) {
|
|
374
|
+
// Synced baseline (fraim/ai-employee), not personalized.
|
|
374
375
|
return {
|
|
375
376
|
content: syncedLocalContent,
|
|
376
377
|
source: 'local',
|
|
378
|
+
personalized: false,
|
|
377
379
|
inherited: false
|
|
378
380
|
};
|
|
379
381
|
}
|
|
@@ -384,6 +386,7 @@ class LocalRegistryResolver {
|
|
|
384
386
|
return {
|
|
385
387
|
content,
|
|
386
388
|
source: 'remote',
|
|
389
|
+
personalized: false,
|
|
387
390
|
inherited: false
|
|
388
391
|
};
|
|
389
392
|
}
|
|
@@ -408,6 +411,7 @@ class LocalRegistryResolver {
|
|
|
408
411
|
return {
|
|
409
412
|
content,
|
|
410
413
|
source: 'remote',
|
|
414
|
+
personalized: false,
|
|
411
415
|
inherited: false
|
|
412
416
|
};
|
|
413
417
|
}
|
|
@@ -422,13 +426,16 @@ class LocalRegistryResolver {
|
|
|
422
426
|
return {
|
|
423
427
|
content,
|
|
424
428
|
source: 'remote',
|
|
429
|
+
personalized: false,
|
|
425
430
|
inherited: false
|
|
426
431
|
};
|
|
427
432
|
}
|
|
428
|
-
// Build result
|
|
433
|
+
// Build result — a local override hit means this came from the
|
|
434
|
+
// personalized-employee layer (the manager taught/customized it).
|
|
429
435
|
const result = {
|
|
430
436
|
content: resolved.content,
|
|
431
437
|
source: 'local',
|
|
438
|
+
personalized: true,
|
|
432
439
|
inherited: resolved.imports.length > 0,
|
|
433
440
|
imports: resolved.imports.length > 0 ? resolved.imports : undefined
|
|
434
441
|
};
|
|
@@ -31,6 +31,7 @@ function createInitialFirstRunState(key) {
|
|
|
31
31
|
installKeyRef: maskInstallKey(key),
|
|
32
32
|
platform: process.platform,
|
|
33
33
|
agentId: 'claude-code',
|
|
34
|
+
agentInstalls: {},
|
|
34
35
|
rows: (0, types_1.createInitialRows)(),
|
|
35
36
|
resourcesUrl: types_1.FIRST_RUN_RESOURCES_URL,
|
|
36
37
|
createdAt: now,
|
|
@@ -272,6 +272,15 @@ class FirstRunServer {
|
|
|
272
272
|
return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not check agent.' });
|
|
273
273
|
}
|
|
274
274
|
});
|
|
275
|
+
this.app.post('/api/first-run/done-recruiting', async (_req, res) => {
|
|
276
|
+
try {
|
|
277
|
+
const result = await this.sessionService.finishAgentRecruiting();
|
|
278
|
+
return res.json(result);
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not finish local agent setup.' });
|
|
282
|
+
}
|
|
283
|
+
});
|
|
275
284
|
// Hub-launch helper - starts an AiHubServer for the chosen project and
|
|
276
285
|
// opens the user's browser. v2 (#355) replaces the in-process spawn with
|
|
277
286
|
// a durable launcher binary that survives independently.
|
|
@@ -184,6 +184,14 @@ function buildConfiguredSurfaces() {
|
|
|
184
184
|
status: 'configured',
|
|
185
185
|
}));
|
|
186
186
|
}
|
|
187
|
+
function surfaceForAgent(option) {
|
|
188
|
+
return {
|
|
189
|
+
id: option.id,
|
|
190
|
+
name: option.label,
|
|
191
|
+
invocationHint: `${option.label}: /fraim onboard this project`,
|
|
192
|
+
status: 'configured',
|
|
193
|
+
};
|
|
194
|
+
}
|
|
187
195
|
class FirstRunSessionService {
|
|
188
196
|
constructor(options) {
|
|
189
197
|
this.key = options.key;
|
|
@@ -294,15 +302,15 @@ class FirstRunSessionService {
|
|
|
294
302
|
}
|
|
295
303
|
updateAgentSummaryRow() {
|
|
296
304
|
const agentRow = this.getRow('agent');
|
|
297
|
-
const surfaces = this.
|
|
305
|
+
const surfaces = this.getConfiguredAgentSurfaces();
|
|
298
306
|
if (surfaces.length > 0) {
|
|
299
307
|
agentRow.status = 'ok';
|
|
300
|
-
agentRow.verb = `${surfaces.length} AI
|
|
308
|
+
agentRow.verb = `${surfaces.length} local AI agent${surfaces.length === 1 ? '' : 's'} ready`;
|
|
301
309
|
agentRow.detail = surfaces.map((surface) => surface.name).join(', ');
|
|
302
310
|
}
|
|
303
311
|
else {
|
|
304
312
|
agentRow.status = 'manual-required';
|
|
305
|
-
agentRow.verb =
|
|
313
|
+
agentRow.verb = 'No problem, we will install AI agents next.';
|
|
306
314
|
delete agentRow.detail;
|
|
307
315
|
}
|
|
308
316
|
this.state.setupResult = {
|
|
@@ -313,6 +321,36 @@ class FirstRunSessionService {
|
|
|
313
321
|
completedAt: new Date().toISOString(),
|
|
314
322
|
};
|
|
315
323
|
}
|
|
324
|
+
getReadyInstalledAgentSurfaces() {
|
|
325
|
+
const installs = this.state.agentInstalls || {};
|
|
326
|
+
return types_1.FIRST_RUN_AGENT_OPTIONS
|
|
327
|
+
.filter((option) => installs[option.id]?.status === 'ready')
|
|
328
|
+
.map(surfaceForAgent);
|
|
329
|
+
}
|
|
330
|
+
getConfiguredAgentSurfaces() {
|
|
331
|
+
const byId = new Map();
|
|
332
|
+
for (const surface of this.getReadyInstalledAgentSurfaces()) {
|
|
333
|
+
byId.set(surface.id, surface);
|
|
334
|
+
}
|
|
335
|
+
if (this.fakeMode !== 'no-agents') {
|
|
336
|
+
for (const surface of buildConfiguredSurfaces()) {
|
|
337
|
+
byId.set(surface.id, surface);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return Array.from(byId.values());
|
|
341
|
+
}
|
|
342
|
+
setAgentInstallStatus(agentId, status, message) {
|
|
343
|
+
const option = findAgentOption(agentId);
|
|
344
|
+
if (!option)
|
|
345
|
+
return;
|
|
346
|
+
this.state.agentInstalls = this.state.agentInstalls || {};
|
|
347
|
+
this.state.agentInstalls[agentId] = {
|
|
348
|
+
status,
|
|
349
|
+
label: option.label,
|
|
350
|
+
updatedAt: new Date().toISOString(),
|
|
351
|
+
...(message ? { message } : {}),
|
|
352
|
+
};
|
|
353
|
+
}
|
|
316
354
|
applyFakeStateOnLoad(mode) {
|
|
317
355
|
const setStatus = (rowId, status, verb, detail) => {
|
|
318
356
|
const row = this.getRow(rowId);
|
|
@@ -325,7 +363,7 @@ class FirstRunSessionService {
|
|
|
325
363
|
setStatus('node', 'pending', "we'll install");
|
|
326
364
|
setStatus('git', 'pending', "we'll install");
|
|
327
365
|
setStatus('fraim', 'pending', "we'll install");
|
|
328
|
-
setStatus('agent', 'pending', "we'll check for AI
|
|
366
|
+
setStatus('agent', 'pending', "we'll check for local AI agents");
|
|
329
367
|
delete this.state.setupResult;
|
|
330
368
|
return;
|
|
331
369
|
}
|
|
@@ -333,7 +371,7 @@ class FirstRunSessionService {
|
|
|
333
371
|
setStatus('node', 'ok', 'v20.11.1 detected');
|
|
334
372
|
setStatus('git', 'ok', 'git version 2.45 detected');
|
|
335
373
|
setStatus('fraim', 'ok', 'fraim-framework detected');
|
|
336
|
-
setStatus('agent', 'ok', '1 AI
|
|
374
|
+
setStatus('agent', 'ok', '1 local AI agent ready', 'Claude Code');
|
|
337
375
|
this.state.setupResult = {
|
|
338
376
|
mode: 'conversational',
|
|
339
377
|
configuredSurfaces: [
|
|
@@ -348,8 +386,8 @@ class FirstRunSessionService {
|
|
|
348
386
|
if (mode === 'no-agents') {
|
|
349
387
|
setStatus('node', 'ok', 'v20.11.1 detected');
|
|
350
388
|
setStatus('git', 'ok', 'git version 2.45 detected');
|
|
351
|
-
setStatus('fraim', '
|
|
352
|
-
setStatus('agent', 'manual-required',
|
|
389
|
+
setStatus('fraim', 'pending', "we'll configure FRAIM after local agents are ready");
|
|
390
|
+
setStatus('agent', 'manual-required', 'No problem, we will install AI agents next.');
|
|
353
391
|
this.state.setupResult = {
|
|
354
392
|
mode: 'conversational',
|
|
355
393
|
configuredSurfaces: [],
|
|
@@ -363,7 +401,7 @@ class FirstRunSessionService {
|
|
|
363
401
|
setStatus('node', 'ok', 'v20.11.1 installed');
|
|
364
402
|
setStatus('git', 'ok', '2.45 installed');
|
|
365
403
|
setStatus('fraim', 'ok', 'fraim-framework installed');
|
|
366
|
-
setStatus('agent', 'pending', "we'll check for AI
|
|
404
|
+
setStatus('agent', 'pending', "we'll check for local AI agents");
|
|
367
405
|
delete this.state.setupResult;
|
|
368
406
|
return;
|
|
369
407
|
}
|
|
@@ -371,7 +409,7 @@ class FirstRunSessionService {
|
|
|
371
409
|
setStatus('node', 'ok', 'v20.11.1 detected');
|
|
372
410
|
setStatus('git', 'ok', 'git version 2.45 detected');
|
|
373
411
|
setStatus('fraim', 'ok', 'fake-mode fraim installed');
|
|
374
|
-
setStatus('agent', 'ok', 'fake-mode AI
|
|
412
|
+
setStatus('agent', 'ok', 'fake-mode local AI agent ready', 'Claude Code');
|
|
375
413
|
this.state.setupResult = {
|
|
376
414
|
mode: 'conversational',
|
|
377
415
|
configuredSurfaces: [
|
|
@@ -493,16 +531,31 @@ class FirstRunSessionService {
|
|
|
493
531
|
this.persist();
|
|
494
532
|
return this.respond('git not found — continuing without it. Install git later if you plan to do code delivery work.', true);
|
|
495
533
|
}
|
|
496
|
-
async runFraimRow() {
|
|
534
|
+
async runFraimRow(forceConfigure = false) {
|
|
497
535
|
const row = this.getRow('fraim');
|
|
498
536
|
if (this.fakeMode) {
|
|
499
537
|
row.status = 'ok';
|
|
500
538
|
row.verb = 'FRAIM ready (fake-mode)';
|
|
539
|
+
const surfaces = this.getConfiguredAgentSurfaces();
|
|
540
|
+
this.state.setupResult = {
|
|
541
|
+
mode: 'conversational',
|
|
542
|
+
configuredSurfaces: surfaces,
|
|
543
|
+
failedSurfaces: [],
|
|
544
|
+
detectedSurfaceCount: surfaces.length,
|
|
545
|
+
completedAt: new Date().toISOString(),
|
|
546
|
+
};
|
|
501
547
|
this.persist();
|
|
502
548
|
return this.respond('Fake-mode fraim ok.', true);
|
|
503
549
|
}
|
|
504
550
|
try {
|
|
505
|
-
if (!commandVersion('fraim')) {
|
|
551
|
+
if (!forceConfigure && !commandVersion('fraim')) {
|
|
552
|
+
const prefix = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'node');
|
|
553
|
+
fs_1.default.mkdirSync(prefix, { recursive: true });
|
|
554
|
+
row.streamOutput = 'Installing FRAIM on this machine...';
|
|
555
|
+
this.persist();
|
|
556
|
+
await runProcess('npm', ['install', '-g', 'fraim@latest'], { npm_config_prefix: prefix });
|
|
557
|
+
}
|
|
558
|
+
else if (forceConfigure && !commandVersion('fraim')) {
|
|
506
559
|
const prefix = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'node');
|
|
507
560
|
fs_1.default.mkdirSync(prefix, { recursive: true });
|
|
508
561
|
row.streamOutput = 'Installing FRAIM on this machine...';
|
|
@@ -526,6 +579,14 @@ class FirstRunSessionService {
|
|
|
526
579
|
row.status = 'ok';
|
|
527
580
|
row.verb = 'Ready — open a new terminal before running fraim commands.';
|
|
528
581
|
delete row.streamOutput;
|
|
582
|
+
const surfaces = this.getConfiguredAgentSurfaces();
|
|
583
|
+
this.state.setupResult = {
|
|
584
|
+
mode: 'conversational',
|
|
585
|
+
configuredSurfaces: surfaces,
|
|
586
|
+
failedSurfaces: [],
|
|
587
|
+
detectedSurfaceCount: surfaces.length,
|
|
588
|
+
completedAt: new Date().toISOString(),
|
|
589
|
+
};
|
|
529
590
|
this.persist();
|
|
530
591
|
return this.respond('FRAIM is ready. Open a new terminal window so your PATH update takes effect before running fraim commands.', true);
|
|
531
592
|
}
|
|
@@ -543,7 +604,7 @@ class FirstRunSessionService {
|
|
|
543
604
|
const row = this.getRow('agent');
|
|
544
605
|
if (request.errorActionId === 'skip') {
|
|
545
606
|
row.status = 'manual-required';
|
|
546
|
-
row.verb =
|
|
607
|
+
row.verb = 'No problem, we will install AI agents next.';
|
|
547
608
|
this.state.setupResult = {
|
|
548
609
|
mode: 'conversational',
|
|
549
610
|
configuredSurfaces: [],
|
|
@@ -552,21 +613,21 @@ class FirstRunSessionService {
|
|
|
552
613
|
completedAt: new Date().toISOString(),
|
|
553
614
|
};
|
|
554
615
|
this.persist();
|
|
555
|
-
return this.respond('AI
|
|
616
|
+
return this.respond('A local AI agent is required before FRAIM setup can finish.', false);
|
|
556
617
|
}
|
|
557
618
|
if (this.fakeMode === 'agent-install-fails') {
|
|
558
|
-
this.setRowError(row, 'Checking for installed AI
|
|
619
|
+
this.setRowError(row, 'Checking for installed local AI agents and configuring one for this project', this.fakeStderr, [
|
|
559
620
|
{ id: 'retry', label: 'Retry', variant: 'primary' },
|
|
560
621
|
{ id: 'alternative', label: 'Try alternative', variant: 'secondary' },
|
|
561
|
-
{ id: '
|
|
622
|
+
{ id: 'alternative', label: 'Manual setup help', variant: 'ghost' },
|
|
562
623
|
]);
|
|
563
624
|
this.persist();
|
|
564
|
-
return this.respond('AI
|
|
625
|
+
return this.respond('Local AI agent setup failed.', false);
|
|
565
626
|
}
|
|
566
627
|
this.updateAgentSummaryRow();
|
|
567
628
|
this.persist();
|
|
568
629
|
const count = this.state.setupResult?.detectedSurfaceCount || 0;
|
|
569
|
-
return this.respond(count > 0 ? 'AI
|
|
630
|
+
return this.respond(count > 0 ? 'Local AI agents are ready.' : 'No problem, we will install AI agents next.', true);
|
|
570
631
|
}
|
|
571
632
|
/**
|
|
572
633
|
* Update the current agent selection (inline `Change…` picker).
|
|
@@ -617,7 +678,20 @@ class FirstRunSessionService {
|
|
|
617
678
|
if (!option) {
|
|
618
679
|
return { ok: false, message: `Unknown agent: ${agentId}` };
|
|
619
680
|
}
|
|
681
|
+
this.setAgentInstallStatus(agentId, 'installing', `Installing ${option.label}.`);
|
|
682
|
+
this.persist();
|
|
620
683
|
if (this.fakeMode) {
|
|
684
|
+
if (this.fakeMode === 'agent-install-fails') {
|
|
685
|
+
const stderr = process.env.FRAIM_FIRST_RUN_FAKE_STDERR || `Failed to install ${option.label}.`;
|
|
686
|
+
this.setAgentInstallStatus(agentId, 'failed', stderr);
|
|
687
|
+
this.persist();
|
|
688
|
+
return {
|
|
689
|
+
ok: false,
|
|
690
|
+
message: stderr,
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
this.setAgentInstallStatus(agentId, 'needs-sign-in', `Sign in to ${option.label} to activate it.`);
|
|
694
|
+
this.persist();
|
|
621
695
|
return {
|
|
622
696
|
ok: true,
|
|
623
697
|
message: `${option.label} installed successfully.`,
|
|
@@ -634,11 +708,9 @@ class FirstRunSessionService {
|
|
|
634
708
|
if (!ver) {
|
|
635
709
|
throw new Error(`${option.label} install completed, but the CLI is not runnable from FRAIM's managed PATH.`);
|
|
636
710
|
}
|
|
637
|
-
|
|
638
|
-
if (detectedIDEs.length > 0) {
|
|
639
|
-
await (0, auto_mcp_setup_1.autoConfigureMCP)(this.key, {}, detectedIDEs.map((ide) => ide.name), {});
|
|
640
|
-
}
|
|
711
|
+
this.setAgentInstallStatus(agentId, 'needs-sign-in', `Sign in to ${option.label} to activate it.`);
|
|
641
712
|
appendInstallLog(`agent-installed ${agentId}`);
|
|
713
|
+
this.persist();
|
|
642
714
|
return {
|
|
643
715
|
ok: true,
|
|
644
716
|
message: `${option.label} installed successfully.`,
|
|
@@ -649,6 +721,8 @@ class FirstRunSessionService {
|
|
|
649
721
|
}
|
|
650
722
|
catch (error) {
|
|
651
723
|
const detail = error instanceof Error ? error.message : 'Unknown error';
|
|
724
|
+
this.setAgentInstallStatus(agentId, 'failed', detail);
|
|
725
|
+
this.persist();
|
|
652
726
|
appendInstallLog(`agent-install-failed ${agentId} ${detail}`);
|
|
653
727
|
return { ok: false, message: `Failed to install ${option.label}: ${detail}` };
|
|
654
728
|
}
|
|
@@ -683,20 +757,40 @@ class FirstRunSessionService {
|
|
|
683
757
|
return { ok: false, ready: false, message: `Unknown agent: ${agentId}` };
|
|
684
758
|
}
|
|
685
759
|
if (this.fakeMode) {
|
|
760
|
+
this.setAgentInstallStatus(agentId, 'ready', `${option.label} is ready.`);
|
|
761
|
+
this.updateAgentSummaryRow();
|
|
762
|
+
this.persist();
|
|
686
763
|
return { ok: true, ready: true, message: `${option.label} is ready (fake-mode).` };
|
|
687
764
|
}
|
|
688
765
|
const ver = commandVersion(option.launchCommand, (0, managed_agent_paths_1.getManagedAgentBinDirs)());
|
|
689
766
|
if (ver) {
|
|
767
|
+
this.setAgentInstallStatus(agentId, 'ready', `${option.label} is ready.`);
|
|
690
768
|
this.updateAgentSummaryRow();
|
|
691
769
|
this.persist();
|
|
692
770
|
return { ok: true, ready: true, message: `${option.label} is ready.` };
|
|
693
771
|
}
|
|
772
|
+
this.setAgentInstallStatus(agentId, 'needs-sign-in', `${option.label} is not detected yet.`);
|
|
773
|
+
this.persist();
|
|
694
774
|
return {
|
|
695
775
|
ok: true,
|
|
696
776
|
ready: false,
|
|
697
777
|
message: `${option.label} is not detected yet. Make sure sign-in is complete and try again.`,
|
|
698
778
|
};
|
|
699
779
|
}
|
|
780
|
+
async finishAgentRecruiting() {
|
|
781
|
+
this.updateAgentSummaryRow();
|
|
782
|
+
const readyCount = this.state.setupResult?.detectedSurfaceCount || 0;
|
|
783
|
+
if (readyCount < 1) {
|
|
784
|
+
this.persist();
|
|
785
|
+
return this.respond('At least one local AI agent is required before FRAIM setup can finish.', false);
|
|
786
|
+
}
|
|
787
|
+
const fraimRow = this.getRow('fraim');
|
|
788
|
+
fraimRow.status = 'pending';
|
|
789
|
+
fraimRow.verb = 'configuring FRAIM for local AI agents';
|
|
790
|
+
this.persist();
|
|
791
|
+
appendInstallLog(`agent-recruiting-complete ready=${readyCount}`);
|
|
792
|
+
return this.runFraimRow(true);
|
|
793
|
+
}
|
|
700
794
|
openTerminalWithCommand(command) {
|
|
701
795
|
if (process.platform === 'win32') {
|
|
702
796
|
(0, child_process_1.spawn)('cmd.exe', ['/c', 'start', 'cmd.exe', '/k', command], { detached: true, stdio: 'ignore' }).unref();
|
|
@@ -730,7 +824,7 @@ class FirstRunSessionService {
|
|
|
730
824
|
async openHub() {
|
|
731
825
|
if (this.fakeMode) {
|
|
732
826
|
// Tests don't actually want a Hub server running — just confirm intent.
|
|
733
|
-
return { ok: true, message: 'Fake-mode Hub open requested.', hubUrl: 'http://127.0.0.1:0/ai-hub
|
|
827
|
+
return { ok: true, message: 'Fake-mode Hub open requested.', hubUrl: 'http://127.0.0.1:0/ai-hub/' };
|
|
734
828
|
}
|
|
735
829
|
// Hub launches a real CLI process, so folder-only config surfaces are not
|
|
736
830
|
// enough here. Require a runnable command in the managed or ambient PATH.
|
|
@@ -748,7 +842,7 @@ class FirstRunSessionService {
|
|
|
748
842
|
const port = await findAvailablePort(43091);
|
|
749
843
|
const hubServer = new AiHubServer({ projectPath: this.state.workspacePath });
|
|
750
844
|
await hubServer.start(port);
|
|
751
|
-
const hubUrl = `http://127.0.0.1:${port}/ai-hub
|
|
845
|
+
const hubUrl = `http://127.0.0.1:${port}/ai-hub/`;
|
|
752
846
|
appendInstallLog(`hub-opened ${hubUrl}`);
|
|
753
847
|
return {
|
|
754
848
|
ok: true,
|
|
@@ -16,8 +16,8 @@ exports.derivePrimaryButtonLabel = derivePrimaryButtonLabel;
|
|
|
16
16
|
exports.FIRST_RUN_ROW_IDS = [
|
|
17
17
|
'node',
|
|
18
18
|
'git',
|
|
19
|
-
'fraim',
|
|
20
19
|
'agent',
|
|
20
|
+
'fraim',
|
|
21
21
|
];
|
|
22
22
|
exports.FIRST_RUN_PROMPT = 'Onboard this project';
|
|
23
23
|
exports.FIRST_RUN_RESOURCES_URL = 'https://fraimworks.ai/resources.html';
|
|
@@ -67,8 +67,8 @@ function createInitialRows() {
|
|
|
67
67
|
return [
|
|
68
68
|
{ id: 'node', label: 'Node.js', status: 'pending', verb: "we'll install" },
|
|
69
69
|
{ id: 'git', label: 'git', status: 'pending', verb: "we'll install", optional: true },
|
|
70
|
+
{ id: 'agent', label: 'Locally installed AI agents', status: 'pending', verb: "we'll detect local AI agents" },
|
|
70
71
|
{ id: 'fraim', label: 'FRAIM', status: 'pending', verb: "we'll set up FRAIM" },
|
|
71
|
-
{ id: 'agent', label: 'Execution surfaces', status: 'pending', verb: "we'll detect execution surfaces" },
|
|
72
72
|
];
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
@@ -87,9 +87,6 @@ function derivePrimaryButtonLabel(rows) {
|
|
|
87
87
|
if (allOk)
|
|
88
88
|
return 'Get Started';
|
|
89
89
|
// Skip-path: every required row is ok-or-manual-required — nothing left for the wizard.
|
|
90
|
-
if (required.every((row) => row.status === 'ok' || row.status === 'manual-required')) {
|
|
91
|
-
return 'Get Started';
|
|
92
|
-
}
|
|
93
90
|
if (required.some((row) => row.status === 'ok'))
|
|
94
91
|
return 'Continue';
|
|
95
92
|
return 'Set up FRAIM';
|