fraim 2.0.358-next.1 → 2.0.358

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 CHANGED
@@ -343,7 +343,7 @@ fraim/
343
343
  Use `fraim override` to create a local starting point:
344
344
 
345
345
  ```bash
346
- fraim override --inherit jobs/product-building/feature-implementation.md
346
+ fraim override --inherit jobs/engineering/feature-implementation.md
347
347
  fraim override --copy rules/engineering/architecture-standards.md
348
348
  ```
349
349
 
@@ -15,6 +15,7 @@ const fs_1 = __importDefault(require("fs"));
15
15
  const os_1 = __importDefault(require("os"));
16
16
  const path_1 = __importDefault(require("path"));
17
17
  const windows_shim_path_search_1 = require("../../core/utils/windows-shim-path-search");
18
+ const user_config_1 = require("../utils/user-config");
18
19
  const LAUNCHER_VERSION = 2;
19
20
  /**
20
21
  * Issue #1354: how long a resolved version stays good before the launcher asks npm again.
@@ -82,7 +83,8 @@ const CHANNEL = (() => {
82
83
  return '';
83
84
  })();
84
85
  const DIST_TAG = CHANNEL.toUpperCase() === 'PPE' ? 'next' : 'latest';
85
- const STATE_PATH = path.join(STATE_DIR, 'latest-' + DIST_TAG + '.json');
86
+ // Preserve the stable cache written by earlier launchers and explicit sync.
87
+ const STATE_PATH = path.join(STATE_DIR, DIST_TAG === 'latest' ? 'latest.json' : 'latest-next.json');
86
88
  const CACHE_ROOT = path.join(USER_DIR, 'npm-cache', 'mcp');
87
89
  const REFRESH_LOCK = path.join(STATE_DIR, 'refresh-' + DIST_TAG + '.lock');
88
90
  const REFRESH_LOCK_STALE_MS = 10 * 60 * 1000;
@@ -114,11 +116,17 @@ function ensureDir(dir) {
114
116
  }
115
117
 
116
118
  function readState() {
117
- try {
118
- return JSON.parse(fs.readFileSync(STATE_PATH, 'utf8'));
119
- } catch {
120
- return null;
121
- }
119
+ // Stable releases have shipped both filenames. Preserve the latest selection
120
+ // across upgrades, including an explicit sync that deliberately selects an older version.
121
+ const paths = DIST_TAG === 'latest'
122
+ ? [STATE_PATH, path.join(STATE_DIR, 'latest-latest.json')]
123
+ : [STATE_PATH, path.join(STATE_DIR, 'latest.json')];
124
+ const states = paths.flatMap(file => {
125
+ try { const state = JSON.parse(fs.readFileSync(file, 'utf8')); return isUsableState(state) ? [state] : []; }
126
+ catch { return []; }
127
+ });
128
+ states.sort((a, b) => (Date.parse(b.updatedAt) || 0) - (Date.parse(a.updatedAt) || 0));
129
+ return states[0] || null;
122
130
  }
123
131
 
124
132
  function writeState(state) {
@@ -161,7 +169,8 @@ function isUsableState(state) {
161
169
  state &&
162
170
  state.packageName === 'fraim' &&
163
171
  state.binName === 'fraim' &&
164
- (state.distTag || 'latest') === DIST_TAG &&
172
+ // Older explicit sync wrote PPE versions into latest.json without a tag.
173
+ (state.distTag || (typeof state.version === 'string' && /-next(?:[.]|$)/.test(state.version) ? 'next' : 'latest')) === DIST_TAG &&
165
174
  typeof state.version === 'string' &&
166
175
  /^\\d+\\.\\d+\\.\\d+/.test(state.version)
167
176
  );
@@ -359,7 +368,7 @@ function getPackagedFraimRuntimePath() {
359
368
  return path_1.default.join(process.env.FRAIM_USER_DIR || path_1.default.join(os_1.default.homedir(), '.fraim'), 'bin', 'packaged-fraim-runtime.json');
360
369
  }
361
370
  function getFraimMcpLauncherStatePath() {
362
- return path_1.default.join(process.env.FRAIM_USER_DIR || path_1.default.join(os_1.default.homedir(), '.fraim'), 'mcp-launcher', 'latest.json');
371
+ return path_1.default.join(process.env.FRAIM_USER_DIR || path_1.default.join(os_1.default.homedir(), '.fraim'), 'mcp-launcher', (0, user_config_1.getReleaseChannel)() === 'ppe' ? 'latest-next.json' : 'latest.json');
363
372
  }
364
373
  /**
365
374
  * Record `version` as the version the launcher should run, resetting the freshness window.
@@ -394,6 +403,7 @@ function recordFraimMcpLauncherVersion(version, source = 'explicit-sync') {
394
403
  binName: 'fraim',
395
404
  version,
396
405
  fromCache: false,
406
+ distTag: (0, user_config_1.getReleaseChannel)() === 'ppe' ? 'next' : 'latest',
397
407
  launcherVersion: LAUNCHER_VERSION,
398
408
  updatedAt: new Date().toISOString()
399
409
  }, null, 2));
@@ -325,6 +325,15 @@ const GENERIC_WORKER_OWNED_JOBS = new Set([
325
325
  // specialist hire, matching the existing no-employee attribution of adhoc runs.
326
326
  exports.UNOWNED_EXEMPT_JOBS = new Set([
327
327
  'adhoc-prompt',
328
+ // #2109: moving these existing operations into ai-employee folders does
329
+ // not change their prior null owner, attribution or ungated access.
330
+ 'evolve-employee',
331
+ 'graduate-job',
332
+ 'generate-productivity-report',
333
+ 'project-onboarding',
334
+ 'sleep-on-learnings',
335
+ 'share-with-others',
336
+ 'video-demo-to-sop',
328
337
  ]);
329
338
  function getPersonaCapabilityBundle(personaKey) {
330
339
  return exports.PERSONA_CAPABILITY_BUNDLES[personaKey];
@@ -604,10 +604,7 @@ class FraimLocalMCPServer {
604
604
  }
605
605
  // Fallback to user config file
606
606
  try {
607
- const homeDir = process.env.HOME || process.env.USERPROFILE || '';
608
- if (!homeDir)
609
- return '';
610
- const configPath = (0, path_1.join)(homeDir, '.fraim', 'config.json');
607
+ const configPath = (0, path_1.join)((0, project_fraim_paths_1.getUserFraimDirPath)(), 'config.json');
611
608
  if ((0, fs_1.existsSync)(configPath)) {
612
609
  const config = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf8'));
613
610
  if (config.apiKey) {
@@ -628,10 +625,7 @@ class FraimLocalMCPServer {
628
625
  if (process.env.FRAIM_RELEASE_CHANNEL)
629
626
  return process.env.FRAIM_RELEASE_CHANNEL;
630
627
  try {
631
- const homeDir = process.env.HOME || process.env.USERPROFILE || '';
632
- if (!homeDir)
633
- return '';
634
- const config = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(homeDir, '.fraim', 'config.json'), 'utf8'));
628
+ const config = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)((0, project_fraim_paths_1.getUserFraimDirPath)(), 'config.json'), 'utf8'));
635
629
  if (config.releaseChannel)
636
630
  return config.releaseChannel;
637
631
  }
@@ -1203,8 +1197,7 @@ class FraimLocalMCPServer {
1203
1197
  return this.config.workingStyle;
1204
1198
  }
1205
1199
  try {
1206
- const homeDir = process.env.HOME || process.env.USERPROFILE || '';
1207
- const configPath = (0, path_1.join)(homeDir, '.fraim', 'config.json');
1200
+ const configPath = (0, path_1.join)((0, project_fraim_paths_1.getUserFraimDirPath)(), 'config.json');
1208
1201
  if ((0, fs_1.existsSync)(configPath)) {
1209
1202
  const config = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf8'));
1210
1203
  return config.workingStyle || 'PR';
@@ -1829,8 +1822,9 @@ class FraimLocalMCPServer {
1829
1822
  return null;
1830
1823
  // Read the job file so a domain it self-declares in frontmatter can override
1831
1824
  // the category map (extensible for newly-taught jobs); the pure resolver
1832
- // owns the decision logic.
1833
- const resolved = await resolver.resolveFile(registryPath).catch(() => null);
1825
+ // owns the decision logic. Display metadata would prepend an HTML comment
1826
+ // to personalized content, hiding its frontmatter from the domain parser.
1827
+ const resolved = await resolver.resolveFile(registryPath, { includeMetadata: false }).catch(() => null);
1834
1828
  return (0, learning_domains_js_1.resolveDomainForJob)(registryPath, resolved?.content ?? null);
1835
1829
  }
1836
1830
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim",
3
- "version": "2.0.358-next.1",
3
+ "version": "2.0.358",
4
4
  "description": "FRAIM core CLI and MCP package.",
5
5
  "main": "index.js",
6
6
  "bin": {