fraim 2.0.140 → 2.0.142

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.
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.createHubEvent = exports.createHubMessage = exports.ScriptedHostRuntime = exports.FakeHostRuntime = exports.CliHostRuntime = void 0;
4
7
  exports.parseSeekMentoringSignal = parseSeekMentoringSignal;
@@ -10,6 +13,9 @@ exports.buildContinuePlan = buildContinuePlan;
10
13
  exports.parseHostLine = parseHostLine;
11
14
  const crypto_1 = require("crypto");
12
15
  const child_process_1 = require("child_process");
16
+ const fs_1 = __importDefault(require("fs"));
17
+ const os_1 = __importDefault(require("os"));
18
+ const path_1 = __importDefault(require("path"));
13
19
  // Parse a single line of host stdout looking for a seekMentoring tool-use
14
20
  // signal. Returns null if the line does not contain one. Supports both
15
21
  // hosts FRAIM ships against today:
@@ -247,19 +253,63 @@ function detectEmployees() {
247
253
  };
248
254
  });
249
255
  }
256
+ // Rewrite a /fraim or $fraim job invocation to a direct MCP tool call instruction.
257
+ // Gemini CLI has a /fraim command but it conflicts when both workspace-level and
258
+ // user-level fraim.toml commands exist (Gemini renames both), making /fraim
259
+ // unrecognised. Sending an explicit get_fraim_job instruction bypasses the slash
260
+ // command entirely and works regardless of whether the command exists.
261
+ function transformGeminiMessage(message) {
262
+ const match = message.match(/^[/$]fraim\s+(\S+)\n?([\s\S]*)$/);
263
+ if (!match)
264
+ return message;
265
+ const jobId = match[1];
266
+ const instructions = match[2].trim();
267
+ const parts = [
268
+ `Call the get_fraim_job MCP tool with job "${jobId}" to get the full job instructions, then follow them exactly.`,
269
+ ];
270
+ if (instructions)
271
+ parts.push(`\n\nUser instructions: ${instructions}`);
272
+ return parts.join('');
273
+ }
274
+ // If ~/.gemini/settings.json has a wrong/test FRAIM_API_KEY, patch it with the
275
+ // real key from ~/.fraim/config.json so the FRAIM MCP server can authenticate.
276
+ // This self-heals when a test run accidentally writes a test key to global config.
277
+ function ensureGeminiApiKey() {
278
+ try {
279
+ const home = os_1.default.homedir();
280
+ const fraimConfigPath = path_1.default.join(home, '.fraim', 'config.json');
281
+ const geminiSettingsPath = path_1.default.join(home, '.gemini', 'settings.json');
282
+ if (!fs_1.default.existsSync(fraimConfigPath) || !fs_1.default.existsSync(geminiSettingsPath))
283
+ return;
284
+ const fraim = JSON.parse(fs_1.default.readFileSync(fraimConfigPath, 'utf8'));
285
+ const realKey = fraim.apiKey;
286
+ if (!realKey)
287
+ return;
288
+ const settings = JSON.parse(fs_1.default.readFileSync(geminiSettingsPath, 'utf8'));
289
+ const fraimServer = settings?.mcpServers?.fraim;
290
+ if (!fraimServer?.env)
291
+ return;
292
+ if (fraimServer.env.FRAIM_API_KEY !== realKey) {
293
+ fraimServer.env.FRAIM_API_KEY = realKey;
294
+ fs_1.default.writeFileSync(geminiSettingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
295
+ }
296
+ }
297
+ catch { /* best-effort: never crash the Hub over a config patch */ }
298
+ }
250
299
  function buildStartPlan(hostId, message) {
251
300
  if (hostId === 'codex') {
252
301
  return {
253
302
  command: executableName('codex'),
254
- args: ['exec', '--json', '--skip-git-repo-check', '--dangerously-bypass-approvals-and-sandbox', '--model', 'gpt-4o'],
303
+ args: ['exec', '--json', '--skip-git-repo-check', '--dangerously-bypass-approvals-and-sandbox', '--model', 'o4-mini'],
255
304
  stdin: message,
256
305
  };
257
306
  }
258
307
  if (hostId === 'gemini') {
308
+ ensureGeminiApiKey();
259
309
  return {
260
310
  command: executableName('gemini'),
261
311
  args: ['--yolo', '--skip-trust'],
262
- stdin: message,
312
+ stdin: transformGeminiMessage(message),
263
313
  };
264
314
  }
265
315
  return {
@@ -272,7 +322,7 @@ function buildContinuePlan(hostId, sessionId, message) {
272
322
  if (hostId === 'codex') {
273
323
  return {
274
324
  command: executableName('codex'),
275
- args: ['exec', 'resume', '--json', '--skip-git-repo-check', '--dangerously-bypass-approvals-and-sandbox', '--model', 'gpt-4o', sessionId],
325
+ args: ['exec', 'resume', '--json', '--skip-git-repo-check', '--dangerously-bypass-approvals-and-sandbox', '--model', 'o4-mini', sessionId],
276
326
  stdin: message,
277
327
  };
278
328
  }
@@ -387,7 +437,7 @@ function spawnHostProcess(hostId, plan, projectPath, handlers) {
387
437
  const child = (0, child_process_1.spawn)(invocation.command, invocation.args, {
388
438
  cwd: projectPath,
389
439
  stdio: ['pipe', 'pipe', 'pipe'],
390
- env: process.env,
440
+ env: plan.env ? { ...process.env, ...plan.env } : process.env,
391
441
  });
392
442
  if (typeof plan.stdin === 'string') {
393
443
  child.stdin.write(plan.stdin);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim",
3
- "version": "2.0.140",
3
+ "version": "2.0.142",
4
4
  "description": "FRAIM CLI - Framework for Rigor-based AI Management (alias for fraim-framework)",
5
5
  "main": "index.js",
6
6
  "bin": {