fraim 2.0.281 → 2.0.284

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.
@@ -48,6 +48,7 @@ const ide_detector_1 = require("../cli/setup/ide-detector");
48
48
  const ide_global_integration_1 = require("../cli/setup/ide-global-integration");
49
49
  const auto_mcp_setup_1 = require("../cli/setup/auto-mcp-setup");
50
50
  const setup_1 = require("../cli/commands/setup");
51
+ const fraim_mcp_latest_launcher_1 = require("../cli/mcp/fraim-mcp-latest-launcher");
51
52
  const script_sync_utils_1 = require("../cli/utils/script-sync-utils");
52
53
  const managed_agent_paths_1 = require("../cli/utils/managed-agent-paths");
53
54
  const managed_agent_install_1 = require("../cli/utils/managed-agent-install");
@@ -118,13 +119,18 @@ function buildPersistShellPathWindowsCommand(bins) {
118
119
  `[Environment]::SetEnvironmentVariable('PATH', $cur, 'User')`,
119
120
  ].join('; ');
120
121
  }
121
- function persistShellPath() {
122
+ function persistShellPath(includePackagedFraimBin = false) {
122
123
  const marker = '# FRAIM managed binaries';
123
124
  const legacyExportLine = 'export PATH="$HOME/.fraim/node/bin:$PATH"';
124
- const exportLine = 'export PATH="$PATH:$HOME/.fraim/node/bin"';
125
+ const exportLine = includePackagedFraimBin
126
+ ? 'export PATH="$PATH:$HOME/.fraim/bin:$HOME/.fraim/node/bin"'
127
+ : 'export PATH="$PATH:$HOME/.fraim/node/bin"';
125
128
  const stanza = `\n${marker}\n${exportLine}\n`;
126
129
  if (process.platform === 'win32') {
127
- const psCmd = buildPersistShellPathWindowsCommand((0, managed_agent_paths_1.getManagedAgentBinDirs)());
130
+ const bins = includePackagedFraimBin
131
+ ? [path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'bin'), ...(0, managed_agent_paths_1.getManagedAgentBinDirs)()]
132
+ : (0, managed_agent_paths_1.getManagedAgentBinDirs)();
133
+ const psCmd = buildPersistShellPathWindowsCommand(bins);
128
134
  (0, child_process_1.spawnSync)('powershell', ['-NoProfile', '-NonInteractive', '-Command', psCmd], { encoding: 'utf8' });
129
135
  return;
130
136
  }
@@ -146,8 +152,12 @@ function persistShellPath() {
146
152
  fs_1.default.writeFileSync(profile, content.replace(legacyExportLine, exportLine), 'utf8');
147
153
  continue;
148
154
  }
149
- if (content.includes(marker))
155
+ if (content.includes(marker)) {
156
+ if (includePackagedFraimBin && !content.includes('$HOME/.fraim/bin')) {
157
+ fs_1.default.writeFileSync(profile, content.replace(exportLine.replace(':$HOME/.fraim/bin', ''), exportLine), 'utf8');
158
+ }
150
159
  continue;
160
+ }
151
161
  fs_1.default.appendFileSync(profile, stanza, 'utf8');
152
162
  }
153
163
  catch {
@@ -246,6 +256,7 @@ class FirstRunSessionService {
246
256
  // wizard prompts the user to paste their key (see setKey / needsKey).
247
257
  this.key = options.key || '';
248
258
  this.headless = options.headless === true;
259
+ this.embeddedDesktop = options.embeddedDesktop === true;
249
260
  this.fakeMode = getFakeStateMode();
250
261
  this.fakeStderr =
251
262
  process.env.FRAIM_FIRST_RUN_FAKE_STDERR ??
@@ -310,10 +321,10 @@ class FirstRunSessionService {
310
321
  }
311
322
  // Real detection — populate row statuses from `command -v` style probes.
312
323
  const nodeRow = this.getRow('node');
313
- const nodeVer = commandVersion('node');
324
+ const nodeVer = this.embeddedDesktop ? process.version : commandVersion('node');
314
325
  if (nodeVer) {
315
326
  nodeRow.status = 'ok';
316
- nodeRow.verb = `${nodeVer} detected`;
327
+ nodeRow.verb = this.embeddedDesktop ? `${nodeVer} bundled with FRAIM` : `${nodeVer} detected`;
317
328
  }
318
329
  else {
319
330
  nodeRow.status = 'pending';
@@ -330,7 +341,7 @@ class FirstRunSessionService {
330
341
  gitRow.verb = 'optional — only needed for code delivery workflows';
331
342
  }
332
343
  const fraimRow = this.getRow('fraim');
333
- if (commandVersion('npx') !== null) {
344
+ if (this.embeddedDesktop || commandVersion('npx') !== null) {
334
345
  // Only mark ok if setup has previously completed (config.json is written after autoConfigureMCP).
335
346
  // Without this, the wizard skips runFraimRow entirely and autoConfigureMCP never runs for IDEs.
336
347
  const globalConfigPath = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
@@ -342,7 +353,7 @@ class FirstRunSessionService {
342
353
  fraimRow.status = 'pending';
343
354
  fraimRow.verb = "we'll configure your IDEs";
344
355
  }
345
- persistShellPath();
356
+ persistShellPath(this.embeddedDesktop);
346
357
  }
347
358
  else {
348
359
  fraimRow.status = 'pending';
@@ -570,6 +581,12 @@ class FirstRunSessionService {
570
581
  }
571
582
  async runNodeRow() {
572
583
  const row = this.getRow('node');
584
+ if (this.embeddedDesktop) {
585
+ row.status = 'ok';
586
+ row.verb = `${process.version} bundled with FRAIM`;
587
+ this.persist();
588
+ return this.respond(`FRAIM includes its own runtime (${process.version}).`, true);
589
+ }
573
590
  // Node is bootstrapped by the install template before the wizard starts.
574
591
  // If node is missing here something is severely wrong; fall through to error.
575
592
  const ver = commandVersion('node');
@@ -631,7 +648,10 @@ class FirstRunSessionService {
631
648
  return this.respond('FRAIM configured.', true);
632
649
  }
633
650
  try {
634
- if (!forceConfigure && !commandVersion('fraim')) {
651
+ if (this.embeddedDesktop) {
652
+ (0, fraim_mcp_latest_launcher_1.ensureFraimMcpLatestLauncher)();
653
+ }
654
+ else if (!forceConfigure && !commandVersion('fraim')) {
635
655
  const prefix = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'node');
636
656
  fs_1.default.mkdirSync(prefix, { recursive: true });
637
657
  row.streamOutput = 'Installing FRAIM on this machine...';
@@ -645,7 +665,7 @@ class FirstRunSessionService {
645
665
  this.persist();
646
666
  await runProcess('npm', ['install', '-g', 'fraim@latest'], { npm_config_prefix: prefix });
647
667
  }
648
- persistShellPath();
668
+ persistShellPath(this.embeddedDesktop);
649
669
  const detectedIDEs = (0, ide_detector_1.detectInstalledIDEs)();
650
670
  if (detectedIDEs.length > 0) {
651
671
  await (0, auto_mcp_setup_1.autoConfigureMCP)(this.key, detectedIDEs.map((ide) => ide.name));
@@ -660,7 +680,9 @@ class FirstRunSessionService {
660
680
  await installSlashCommands();
661
681
  await installGlobalRules();
662
682
  row.status = 'ok';
663
- row.verb = 'Ready — open a new terminal before running fraim commands.';
683
+ row.verb = this.embeddedDesktop
684
+ ? 'Ready — bundled with FRAIM Hub.'
685
+ : 'Ready — open a new terminal before running fraim commands.';
664
686
  delete row.streamOutput;
665
687
  const surfaces = this.getConfiguredAgentSurfaces();
666
688
  this.state.setupResult = {
@@ -671,7 +693,9 @@ class FirstRunSessionService {
671
693
  completedAt: new Date().toISOString(),
672
694
  };
673
695
  this.persist();
674
- return this.respond('FRAIM is ready. Open a new terminal window so your PATH update takes effect before running fraim commands.', true);
696
+ return this.respond(this.embeddedDesktop
697
+ ? 'FRAIM and FRAIM Hub are ready in this installed application.'
698
+ : 'FRAIM is ready. Open a new terminal window so your PATH update takes effect before running fraim commands.', true);
675
699
  }
676
700
  catch (error) {
677
701
  const detail = error instanceof Error ? error.message : 'Unknown error';
@@ -931,6 +955,18 @@ class FirstRunSessionService {
931
955
  message: "No AI agent (Claude Code or Codex) found on this machine. Let's install one first.",
932
956
  };
933
957
  }
958
+ if (this.embeddedDesktop) {
959
+ (0, fraim_mcp_latest_launcher_1.ensureFraimMcpLatestLauncher)();
960
+ if (!fs_1.default.existsSync((0, fraim_mcp_latest_launcher_1.getFraimCliShimPath)())) {
961
+ return {
962
+ ok: false,
963
+ needsAgentSetup: true,
964
+ message: 'FRAIM CLI was not installed into the packaged desktop profile. Re-run FRAIM setup before opening Hub.',
965
+ };
966
+ }
967
+ appendInstallLog('hub-opened embedded-desktop');
968
+ return { ok: true, desktop: true, message: 'Opening FRAIM Hub in this app.' };
969
+ }
934
970
  try {
935
971
  const npxBinary = process.platform === 'win32' ? 'npx.cmd' : 'npx';
936
972
  // Issue #866 R1/R2: launch the FRAIM Hub desktop (Electron) app, not a
@@ -45,8 +45,8 @@ async function fileFraimIssue(params) {
45
45
  if (!token) {
46
46
  return {
47
47
  success: false,
48
- message: `GitHub integration requires GITHUB_TOKEN environment variable.
49
-
48
+ message: `GitHub integration requires GITHUB_TOKEN environment variable.
49
+
50
50
  Please set GITHUB_TOKEN environment variable to file issues in ${owner}/${repo}.`
51
51
  };
52
52
  }
@@ -102,8 +102,8 @@ async function listFraimIssuesByReporter(params) {
102
102
  if (!token) {
103
103
  return {
104
104
  success: false,
105
- message: `GitHub integration requires GITHUB_TOKEN environment variable.
106
-
105
+ message: `GitHub integration requires GITHUB_TOKEN environment variable.
106
+
107
107
  Please set GITHUB_TOKEN environment variable to query issues in ${FRAIM_REPO_OWNER}/${FRAIM_REPO_NAME}.`
108
108
  };
109
109
  }
@@ -42,19 +42,19 @@ const getToolDefinitions = (options = {}) => {
42
42
  ? ['sessionId', ...fields]
43
43
  : fields;
44
44
  const fraimConnectDescription = isLocalProxySurface
45
- ? `Bootstrap and initialize a FRAIM session and obtain the sessionId used by active FRAIM workflow tools. Use this after explicit FRAIM activation: the user invoked FRAIM, named a FRAIM job, asked for FRAIM job recommendations, or the active surface selected a FRAIM job. Must be called before any FRAIM workflow tool calls. For ordinary requests, do not start a FRAIM session or scan the catalog.
46
-
47
- Example:
48
- {
49
- "agent": {"name": "Claude", "model": "claude-3.5-sonnet"}
45
+ ? `Bootstrap and initialize a FRAIM session and obtain the sessionId used by active FRAIM workflow tools. Use this after explicit FRAIM activation: the user invoked FRAIM, named a FRAIM job, asked for FRAIM job recommendations, or the active surface selected a FRAIM job. Must be called before any FRAIM workflow tool calls. For ordinary requests, do not start a FRAIM session or scan the catalog.
46
+
47
+ Example:
48
+ {
49
+ "agent": {"name": "Claude", "model": "claude-3.5-sonnet"}
50
50
  }`
51
- : `Bootstrap and initialize a FRAIM session and obtain the sessionId used by active FRAIM workflow tools. Use this after explicit FRAIM activation: the user invoked FRAIM, named a FRAIM job, asked for FRAIM job recommendations, or the active surface selected a FRAIM job. Must be called before any FRAIM workflow tool calls. For ordinary requests, do not start a FRAIM session or scan the catalog.
52
-
53
- Hosted marketplace clients may call this with only agent information. If machine or repository context is omitted, FRAIM creates a hosted marketplace session context automatically.
54
-
55
- Example:
56
- {
57
- "agent": {"name": "ChatGPT", "model": "gpt-5"}
51
+ : `Bootstrap and initialize a FRAIM session and obtain the sessionId used by active FRAIM workflow tools. Use this after explicit FRAIM activation: the user invoked FRAIM, named a FRAIM job, asked for FRAIM job recommendations, or the active surface selected a FRAIM job. Must be called before any FRAIM workflow tool calls. For ordinary requests, do not start a FRAIM session or scan the catalog.
52
+
53
+ Hosted marketplace clients may call this with only agent information. If machine or repository context is omitted, FRAIM creates a hosted marketplace session context automatically.
54
+
55
+ Example:
56
+ {
57
+ "agent": {"name": "ChatGPT", "model": "gpt-5"}
58
58
  }`;
59
59
  return [
60
60
  {
@@ -198,12 +198,12 @@ Example:
198
198
  },
199
199
  {
200
200
  name: 'get_fraim_file',
201
- description: `Get a specific skill, rule, or reference file from the FRAIM registry by path.
202
-
203
- For running FRAIM jobs, use get_fraim_job instead — do NOT call get_fraim_file for job execution.
204
-
205
- Examples:
206
- - get_fraim_file({ path: "skills/communication/active-listening.md" })
201
+ description: `Get a specific skill, rule, or reference file from the FRAIM registry by path.
202
+
203
+ For running FRAIM jobs, use get_fraim_job instead — do NOT call get_fraim_file for job execution.
204
+
205
+ Examples:
206
+ - get_fraim_file({ path: "skills/communication/active-listening.md" })
207
207
  - get_fraim_file({ path: "rules/local-development.md" })`,
208
208
  annotations: readOnlyClosedWorldAnnotations,
209
209
  inputSchema: {
@@ -224,13 +224,13 @@ Examples:
224
224
  },
225
225
  {
226
226
  name: 'get_fraim_job',
227
- description: `Execute a named FRAIM job after explicit FRAIM activation. Call this tool whenever the user asks to run, start, or execute a named FRAIM job. Returns phased instructions — follow each phase and call seekMentoring to advance phases.
228
-
229
- Do NOT use get_fraim_file to load job content. Always use get_fraim_job for job execution.
230
-
231
- Examples:
232
- - get_fraim_job({ job: "feature-specification" })
233
- - get_fraim_job({ job: "technical-design" })
227
+ description: `Execute a named FRAIM job after explicit FRAIM activation. Call this tool whenever the user asks to run, start, or execute a named FRAIM job. Returns phased instructions — follow each phase and call seekMentoring to advance phases.
228
+
229
+ Do NOT use get_fraim_file to load job content. Always use get_fraim_job for job execution.
230
+
231
+ Examples:
232
+ - get_fraim_job({ job: "feature-specification" })
233
+ - get_fraim_job({ job: "technical-design" })
234
234
  - get_fraim_job({ job: "feature-implementation" })`,
235
235
  annotations: readOnlyClosedWorldAnnotations,
236
236
  inputSchema: {
@@ -263,11 +263,11 @@ Examples:
263
263
  },
264
264
  {
265
265
  name: 'file_fraim_github_issue',
266
- description: `Create a GitHub issue in the FRAIM repository.
267
-
268
- Use this tool when you need to report a bug, request a feature from FRAIM. Do not use this tool to file issues in other repositories.
269
- Supports dry-run mode to preview the operation.
270
-
266
+ description: `Create a GitHub issue in the FRAIM repository.
267
+
268
+ Use this tool when you need to report a bug, request a feature from FRAIM. Do not use this tool to file issues in other repositories.
269
+ Supports dry-run mode to preview the operation.
270
+
271
271
  This tool accepts text only. If visual evidence is needed, upload images to a stable shared HTTPS URL first and include those URLs in the issue body as markdown images or plain links. Do not include local file paths or base64-encoded image data in the issue body.`,
272
272
  annotations: writeOpenWorldAnnotations,
273
273
  inputSchema: {
@@ -297,16 +297,16 @@ This tool accepts text only. If visual evidence is needed, upload images to a st
297
297
  },
298
298
  {
299
299
  name: 'list_my_fraim_github_issues',
300
- description: `List issues the current FRAIM user filed into the FRAIM GitHub repository.
301
-
302
- This tool is the read-side companion to file_fraim_github_issue. Use it when you need a simple list of issues filed by the current FRAIM user into the FRAIM repository.
303
-
304
- Returns only:
305
- - issue number
306
- - title
307
- - status
308
- - created date
309
-
300
+ description: `List issues the current FRAIM user filed into the FRAIM GitHub repository.
301
+
302
+ This tool is the read-side companion to file_fraim_github_issue. Use it when you need a simple list of issues filed by the current FRAIM user into the FRAIM repository.
303
+
304
+ Returns only:
305
+ - issue number
306
+ - title
307
+ - status
308
+ - created date
309
+
310
310
  Do not use this tool for other repositories or external project issue trackers.`,
311
311
  annotations: readOnlyOpenWorldAnnotations,
312
312
  inputSchema: {
@@ -84,9 +84,9 @@ class TelemetryMiddleware {
84
84
  jsonrpc: '2.0',
85
85
  error: {
86
86
  code: -32600,
87
- message: `Missing Session ID. REQUIRED ACTION:
88
- 1. Call 'fraim_connect' first
89
- 2. Capture the returned sessionId
87
+ message: `Missing Session ID. REQUIRED ACTION:
88
+ 1. Call 'fraim_connect' first
89
+ 2. Capture the returned sessionId
90
90
  3. Include that sessionId on every subsequent FRAIM tool call (params.sessionId or params.arguments.sessionId).`
91
91
  },
92
92
  id: req.body?.id || null
@@ -97,24 +97,24 @@ class TelemetryMiddleware {
97
97
  if (!isActive) {
98
98
  const sessionStartGuidance = isProxiedAgentCall
99
99
  ? `Session Not Started. REQUIRED ACTION: Call 'fraim_connect' tool first, then include the returned sessionId on every subsequent FRAIM tool call.`
100
- : `Session Not Started. REQUIRED ACTION: Call 'fraim_connect' tool first with these parameters:
101
-
102
- {
103
- "agent": {
104
- "name": "YourAgentName",
105
- "model": "your-model-name"
106
- },
107
- "machine": {
108
- "hostname": "use os.hostname()",
109
- "platform": "use process.platform"
110
- },
111
- "repo": {
112
- "url": "git remote URL",
113
- "owner": "repo owner",
114
- "name": "repo name"
115
- }
116
- }
117
-
100
+ : `Session Not Started. REQUIRED ACTION: Call 'fraim_connect' tool first with these parameters:
101
+
102
+ {
103
+ "agent": {
104
+ "name": "YourAgentName",
105
+ "model": "your-model-name"
106
+ },
107
+ "machine": {
108
+ "hostname": "use os.hostname()",
109
+ "platform": "use process.platform"
110
+ },
111
+ "repo": {
112
+ "url": "git remote URL",
113
+ "owner": "repo owner",
114
+ "name": "repo name"
115
+ }
116
+ }
117
+
118
118
  After successful fraim_connect, all other FRAIM tools will work. This is required for telemetry and session management.`;
119
119
  return res.status(400).json({
120
120
  jsonrpc: '2.0',