fraim 2.0.335 → 2.0.338
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/registry/scripts/learning/entry-format.js +27 -0
- package/dist/registry/scripts/learning/learning-domains.json +78 -0
- package/dist/src/cli/commands/doctor.js +24 -1
- package/dist/src/cli/commands/sync.js +10 -14
- package/dist/src/cli/doctor/checks/fraim-mcp-cache-repair.js +188 -0
- package/dist/src/cli/doctor/checks/mcp-connectivity-checks.js +38 -2
- package/dist/src/cli/utils/pack-home.js +4 -2
- package/dist/src/config/learning-domains.js +10 -113
- package/dist/src/core/utils/local-registry-resolver.js +6 -0
- package/dist/src/local-mcp-server/stdio-server.js +19 -23
- package/package.json +3 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Read the shared author's current entry format for identity and update guards.
|
|
4
|
+
function learningEntryBlocks(body) {
|
|
5
|
+
const headings = [...body.matchAll(/^#{2,6}\s+\[P-[A-Z]+\].*$/gm)];
|
|
6
|
+
return headings.map((heading, index) => {
|
|
7
|
+
const start = heading.index;
|
|
8
|
+
const next = headings[index + 1]?.index ?? body.length;
|
|
9
|
+
const ownDepth = heading[0].match(/^#+/)[0].length;
|
|
10
|
+
const afterHeading = start + heading[0].length;
|
|
11
|
+
const shallower = [...body.slice(afterHeading, next).matchAll(/^(#{1,6})\s+.*$/gm)].find(m => m[1].length <= ownDepth);
|
|
12
|
+
const limit = shallower ? afterHeading + shallower.index : next;
|
|
13
|
+
const text = body.slice(start, limit).trimEnd();
|
|
14
|
+
return { start, end: start + text.length, text, id: text.match(/^\*\*Id\*\*:\s*(L-[a-f0-9]{10})\s*$/m)?.[1] ?? null };
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function learningEntryMetadata(content) {
|
|
19
|
+
const metadata = {};
|
|
20
|
+
for (const match of content.matchAll(/^\*\*(Recurrences|First recorded|First synthesized)\*\*:\s*([^\r\n]+)$/gm)) {
|
|
21
|
+
if (metadata[match[1]] !== undefined && metadata[match[1]] !== match[2]) throw new Error('entry-metadata-conflict');
|
|
22
|
+
metadata[match[1]] = match[2];
|
|
23
|
+
}
|
|
24
|
+
return metadata;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = { learningEntryMetadata, learningEntryBlocks };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"mapVersion": 1,
|
|
4
|
+
"domains": [
|
|
5
|
+
"engineering",
|
|
6
|
+
"product",
|
|
7
|
+
"go-to-market",
|
|
8
|
+
"people-ops",
|
|
9
|
+
"finance",
|
|
10
|
+
"legal",
|
|
11
|
+
"compliance",
|
|
12
|
+
"customer",
|
|
13
|
+
"company-building",
|
|
14
|
+
"operations"
|
|
15
|
+
],
|
|
16
|
+
"jobDomainMap": {
|
|
17
|
+
"product-building": "engineering",
|
|
18
|
+
"dev-ops": "engineering",
|
|
19
|
+
"quality-assurance": "engineering",
|
|
20
|
+
"migration": "engineering",
|
|
21
|
+
"replicate": "engineering",
|
|
22
|
+
"reviewer": "engineering",
|
|
23
|
+
"security": "engineering",
|
|
24
|
+
"delivery-ops": "engineering",
|
|
25
|
+
"salesforce": "engineering",
|
|
26
|
+
"ai-engineering": "engineering",
|
|
27
|
+
"product-management": "product",
|
|
28
|
+
"customer-development": "product",
|
|
29
|
+
"brainstorming": "product",
|
|
30
|
+
"marketing": "go-to-market",
|
|
31
|
+
"branding": "go-to-market",
|
|
32
|
+
"sales": "go-to-market",
|
|
33
|
+
"go-to-market": "go-to-market",
|
|
34
|
+
"business-development": "go-to-market",
|
|
35
|
+
"commercial-ops": "go-to-market",
|
|
36
|
+
"people-ops": "people-ops",
|
|
37
|
+
"inclusion-ops": "people-ops",
|
|
38
|
+
"career": "people-ops",
|
|
39
|
+
"finance": "finance",
|
|
40
|
+
"invoicing": "finance",
|
|
41
|
+
"banking": "finance",
|
|
42
|
+
"legal": "legal",
|
|
43
|
+
"compliance": "compliance",
|
|
44
|
+
"customer-success": "customer",
|
|
45
|
+
"customer-communication": "customer",
|
|
46
|
+
"company-creation": "company-building",
|
|
47
|
+
"bootstrap": "company-building",
|
|
48
|
+
"fundraising": "company-building",
|
|
49
|
+
"business-ops": "operations",
|
|
50
|
+
"procurement": "operations",
|
|
51
|
+
"project-management": "operations"
|
|
52
|
+
},
|
|
53
|
+
"globalJobCategories": [
|
|
54
|
+
"manager",
|
|
55
|
+
"fraim",
|
|
56
|
+
"1-1",
|
|
57
|
+
"coaching",
|
|
58
|
+
"delegation",
|
|
59
|
+
"hiring",
|
|
60
|
+
"productivity",
|
|
61
|
+
"project-setup",
|
|
62
|
+
"setup-guardrails",
|
|
63
|
+
"team-learning",
|
|
64
|
+
"verification"
|
|
65
|
+
],
|
|
66
|
+
"domainVoice": {
|
|
67
|
+
"engineering": "Precise and technical; file paths, commands, and test names are appropriate.",
|
|
68
|
+
"product": "Product and user-outcome language; behavior and flow, not code symbols.",
|
|
69
|
+
"go-to-market": "Marketing and sales language; campaigns, funnels, messaging, audience.",
|
|
70
|
+
"people-ops": "Plain HR and recruiting language; no engineering jargon.",
|
|
71
|
+
"finance": "Finance and accounting language; figures, close, forecasts.",
|
|
72
|
+
"legal": "Contract and IP language; obligations, clauses, risk.",
|
|
73
|
+
"compliance": "Controls, evidence, and regulation language.",
|
|
74
|
+
"customer": "Customer-relationship and support language.",
|
|
75
|
+
"company-building": "Founder and operations language; setup, entity, funding.",
|
|
76
|
+
"operations": "Business-operations language; process, vendors, planning, coordination."
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.doctorCommand = void 0;
|
|
4
4
|
exports.getAllChecks = getAllChecks;
|
|
5
5
|
exports.runFixMcpRepair = runFixMcpRepair;
|
|
6
|
+
exports.runRepairMcpCache = runRepairMcpCache;
|
|
6
7
|
const commander_1 = require("commander");
|
|
7
8
|
const fs_1 = require("fs");
|
|
8
9
|
const path_1 = require("path");
|
|
@@ -17,6 +18,8 @@ const mcp_connectivity_checks_1 = require("../doctor/checks/mcp-connectivity-che
|
|
|
17
18
|
const scripts_checks_1 = require("../doctor/checks/scripts-checks");
|
|
18
19
|
const agent_cli_health_checks_1 = require("../doctor/checks/agent-cli-health-checks");
|
|
19
20
|
const llm_auth_route_checks_1 = require("../doctor/checks/llm-auth-route-checks");
|
|
21
|
+
const fraim_mcp_cache_repair_1 = require("../doctor/checks/fraim-mcp-cache-repair");
|
|
22
|
+
const fraim_mcp_latest_launcher_1 = require("../mcp/fraim-mcp-latest-launcher");
|
|
20
23
|
const add_ide_1 = require("./add-ide");
|
|
21
24
|
// Read version from package.json
|
|
22
25
|
const getFramVersion = () => {
|
|
@@ -85,18 +88,34 @@ async function runFixMcpRepair(options, repair = add_ide_1.runAddIDE) {
|
|
|
85
88
|
console.log = originalLog;
|
|
86
89
|
}
|
|
87
90
|
}
|
|
91
|
+
async function runRepairMcpCache(options, dependencies = {}) {
|
|
92
|
+
if (!options.repairMcpCache) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const ensureShims = dependencies.ensureShims ?? fraim_mcp_latest_launcher_1.ensureFraimMcpLatestLauncher;
|
|
96
|
+
const repairCache = dependencies.repairCache ?? fraim_mcp_cache_repair_1.repairFraimMcpPackageCache;
|
|
97
|
+
const warmPackage = dependencies.warmPackage ?? fraim_mcp_cache_repair_1.warmFraimPackage;
|
|
98
|
+
ensureShims();
|
|
99
|
+
const result = repairCache({});
|
|
100
|
+
if (!options.json) {
|
|
101
|
+
console.log(`FRAIM MCP package cache repair removed ${result.removedBrokenNpxEntries.length} broken _npx entr${result.removedBrokenNpxEntries.length === 1 ? 'y' : 'ies'} and ${result.removedStaleRetryCaches.length} stale retry cache${result.removedStaleRetryCaches.length === 1 ? '' : 's'}.`);
|
|
102
|
+
}
|
|
103
|
+
await warmPackage(FRAIM_VERSION === 'unknown' ? 'latest' : FRAIM_VERSION);
|
|
104
|
+
}
|
|
88
105
|
exports.doctorCommand = new commander_1.Command('doctor')
|
|
89
106
|
.description('Validate FRAIM installation and configuration')
|
|
90
107
|
.option('--test-mcp', 'Test only MCP server connectivity')
|
|
91
108
|
.option('--test-config', 'Validate only configuration files')
|
|
92
109
|
.option('--test-jobs', 'Check only job status')
|
|
93
110
|
.option('--fix-mcp', 'Repair FRAIM-owned MCP server entries in detected IDE configs, then validate MCP connectivity')
|
|
111
|
+
.option('--repair-mcp-cache', 'Repair corrupted FRAIM MCP package cache entries, then validate MCP connectivity')
|
|
94
112
|
.option('--verbose', 'Show detailed output including successful checks')
|
|
95
113
|
.option('--json', 'Output results as JSON')
|
|
96
114
|
.action(async (cmdOptions) => {
|
|
97
115
|
const startTime = Date.now();
|
|
98
116
|
const options = {
|
|
99
|
-
testMcp: cmdOptions.testMcp || cmdOptions.fixMcp,
|
|
117
|
+
testMcp: cmdOptions.testMcp || cmdOptions.fixMcp || cmdOptions.repairMcpCache,
|
|
118
|
+
repairMcpCache: cmdOptions.repairMcpCache,
|
|
100
119
|
testConfig: cmdOptions.testConfig,
|
|
101
120
|
testJobs: cmdOptions.testJobs,
|
|
102
121
|
fixMcp: cmdOptions.fixMcp,
|
|
@@ -110,6 +129,7 @@ exports.doctorCommand = new commander_1.Command('doctor')
|
|
|
110
129
|
testConfig: options.testConfig || false,
|
|
111
130
|
testJobs: options.testJobs || false,
|
|
112
131
|
fixMcp: options.fixMcp || false,
|
|
132
|
+
repairMcpCache: options.repairMcpCache || false,
|
|
113
133
|
verbose: options.verbose || false,
|
|
114
134
|
json: options.json || false
|
|
115
135
|
}
|
|
@@ -125,12 +145,15 @@ exports.doctorCommand = new commander_1.Command('doctor')
|
|
|
125
145
|
trackMetric('doctor.flags.test_jobs', 1);
|
|
126
146
|
if (options.fixMcp)
|
|
127
147
|
trackMetric('doctor.flags.fix_mcp', 1);
|
|
148
|
+
if (options.repairMcpCache)
|
|
149
|
+
trackMetric('doctor.flags.repair_mcp_cache', 1);
|
|
128
150
|
if (options.verbose)
|
|
129
151
|
trackMetric('doctor.flags.verbose', 1);
|
|
130
152
|
if (options.json)
|
|
131
153
|
trackMetric('doctor.flags.json', 1);
|
|
132
154
|
try {
|
|
133
155
|
await runFixMcpRepair(options);
|
|
156
|
+
await runRepairMcpCache(options);
|
|
134
157
|
// Collect all checks
|
|
135
158
|
const checks = getAllChecks();
|
|
136
159
|
// Run checks
|
|
@@ -311,13 +311,11 @@ const runSync = async (options) => {
|
|
|
311
311
|
if (ignoreUpdate.gitignoreUpdated) {
|
|
312
312
|
console.log(chalk_1.default.green('Removed legacy FRAIM sync block from .gitignore'));
|
|
313
313
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
console.log(chalk_1.default.green(`Updated FRAIM agent adapter files: ${adapterUpdates.join(', ')}`));
|
|
320
|
-
}
|
|
314
|
+
const allowedLocalTypes = resolveAllowedConfigTypes();
|
|
315
|
+
await cleanupStaleAdapterFiles(projectRoot, allowedLocalTypes);
|
|
316
|
+
const localAdapterUpdates = (0, agent_adapters_1.ensureAgentAdapterFiles)(projectRoot, allowedLocalTypes);
|
|
317
|
+
if (localAdapterUpdates.length > 0) {
|
|
318
|
+
console.log(chalk_1.default.green(`Updated FRAIM agent adapter files: ${localAdapterUpdates.join(', ')}`));
|
|
321
319
|
}
|
|
322
320
|
await refreshOrgCache(localUrl, 'local-dev');
|
|
323
321
|
await refreshManagerCache(localUrl, 'local-dev');
|
|
@@ -437,13 +435,11 @@ const runSync = async (options) => {
|
|
|
437
435
|
if (ignoreUpdate.gitignoreUpdated) {
|
|
438
436
|
console.log(chalk_1.default.green('Removed legacy FRAIM sync block from .gitignore'));
|
|
439
437
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
console.log(chalk_1.default.green(`Updated FRAIM agent adapter files: ${adapterUpdates.join(', ')}`));
|
|
446
|
-
}
|
|
438
|
+
const allowedTypes = resolveAllowedConfigTypes();
|
|
439
|
+
await cleanupStaleAdapterFiles(projectRoot, allowedTypes);
|
|
440
|
+
const adapterUpdates = (0, agent_adapters_1.ensureAgentAdapterFiles)(projectRoot, allowedTypes);
|
|
441
|
+
if (adapterUpdates.length > 0) {
|
|
442
|
+
console.log(chalk_1.default.green(`Updated FRAIM agent adapter files: ${adapterUpdates.join(', ')}`));
|
|
447
443
|
}
|
|
448
444
|
console.log(chalk_1.default.green('✅ Project-level sync complete.'));
|
|
449
445
|
};
|
|
@@ -0,0 +1,188 @@
|
|
|
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.DEFAULT_RETRY_CACHE_AGE_FLOOR_MS = exports.CORRUPTED_NPX_PACKAGE_CACHE_CLASSIFICATION = exports.FRAIM_MCP_CACHE_REPAIR_DIAGNOSTIC_VERSION = void 0;
|
|
7
|
+
exports.getFraimMcpCacheRoot = getFraimMcpCacheRoot;
|
|
8
|
+
exports.inspectFraimMcpPackageCache = inspectFraimMcpPackageCache;
|
|
9
|
+
exports.repairFraimMcpPackageCache = repairFraimMcpPackageCache;
|
|
10
|
+
exports.buildFraimMcpCacheDiagnostic = buildFraimMcpCacheDiagnostic;
|
|
11
|
+
exports.warmFraimPackage = warmFraimPackage;
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
|
+
const os_1 = __importDefault(require("os"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const child_process_1 = require("child_process");
|
|
16
|
+
const fraim_mcp_latest_launcher_1 = require("../../mcp/fraim-mcp-latest-launcher");
|
|
17
|
+
exports.FRAIM_MCP_CACHE_REPAIR_DIAGNOSTIC_VERSION = 'fraim-mcp-cache-repair-v1';
|
|
18
|
+
exports.CORRUPTED_NPX_PACKAGE_CACHE_CLASSIFICATION = 'corrupted-npx-package-cache';
|
|
19
|
+
exports.DEFAULT_RETRY_CACHE_AGE_FLOOR_MS = 7 * 24 * 60 * 60 * 1000;
|
|
20
|
+
const FRAIM_MCP_CACHE_REPAIR_COMMAND = 'npx --yes fraim@latest doctor --repair-mcp-cache';
|
|
21
|
+
const VALID_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/;
|
|
22
|
+
function userDir() {
|
|
23
|
+
return process.env.FRAIM_USER_DIR || path_1.default.join(os_1.default.homedir(), '.fraim');
|
|
24
|
+
}
|
|
25
|
+
function getFraimMcpCacheRoot() {
|
|
26
|
+
return path_1.default.join(userDir(), 'npm-cache', 'mcp');
|
|
27
|
+
}
|
|
28
|
+
function versionFromCacheDir(versionCacheDir) {
|
|
29
|
+
const base = path_1.default.basename(versionCacheDir);
|
|
30
|
+
if (!base.startsWith('fraim-'))
|
|
31
|
+
return null;
|
|
32
|
+
const version = base.slice('fraim-'.length);
|
|
33
|
+
return VALID_VERSION_PATTERN.test(version) ? version : null;
|
|
34
|
+
}
|
|
35
|
+
function safeChild(parent, child) {
|
|
36
|
+
const parentResolved = path_1.default.resolve(parent);
|
|
37
|
+
const childResolved = path_1.default.resolve(child);
|
|
38
|
+
return childResolved === parentResolved || childResolved.startsWith(parentResolved + path_1.default.sep);
|
|
39
|
+
}
|
|
40
|
+
function directoryExists(dir) {
|
|
41
|
+
try {
|
|
42
|
+
return fs_1.default.statSync(dir).isDirectory();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function findBrokenNpxEntries(versionCacheDir) {
|
|
49
|
+
const version = versionFromCacheDir(versionCacheDir);
|
|
50
|
+
if (!version)
|
|
51
|
+
return [];
|
|
52
|
+
const npxRoot = path_1.default.join(versionCacheDir, '_npx');
|
|
53
|
+
if (!directoryExists(npxRoot))
|
|
54
|
+
return [];
|
|
55
|
+
return fs_1.default.readdirSync(npxRoot, { withFileTypes: true })
|
|
56
|
+
.filter(entry => entry.isDirectory())
|
|
57
|
+
.map(entry => path_1.default.join(npxRoot, entry.name))
|
|
58
|
+
.filter(entryPath => safeChild(npxRoot, entryPath))
|
|
59
|
+
.filter(entryPath => (directoryExists(path_1.default.join(entryPath, 'node_modules')) &&
|
|
60
|
+
!fs_1.default.existsSync(path_1.default.join(entryPath, 'package.json'))))
|
|
61
|
+
.map(entryPath => ({ version, path: entryPath }));
|
|
62
|
+
}
|
|
63
|
+
function findStaleRetryCaches(cacheRoot, options = {}) {
|
|
64
|
+
if (!directoryExists(cacheRoot))
|
|
65
|
+
return [];
|
|
66
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
67
|
+
const ageFloorMs = options.ageFloorMs ?? exports.DEFAULT_RETRY_CACHE_AGE_FLOOR_MS;
|
|
68
|
+
return fs_1.default.readdirSync(cacheRoot, { withFileTypes: true })
|
|
69
|
+
.filter(entry => entry.isDirectory() && /^retry-\d+-\d+$/.test(entry.name))
|
|
70
|
+
.map(entry => {
|
|
71
|
+
const entryPath = path_1.default.join(cacheRoot, entry.name);
|
|
72
|
+
const stat = fs_1.default.statSync(entryPath);
|
|
73
|
+
return { path: entryPath, mtimeMs: stat.mtimeMs, ageMs: nowMs - stat.mtimeMs };
|
|
74
|
+
})
|
|
75
|
+
.filter(entry => entry.ageMs >= ageFloorMs && safeChild(cacheRoot, entry.path));
|
|
76
|
+
}
|
|
77
|
+
function inspectFraimMcpPackageCache(options = {}) {
|
|
78
|
+
const cacheRoot = options.cacheRoot ?? getFraimMcpCacheRoot();
|
|
79
|
+
const brokenNpxEntries = [];
|
|
80
|
+
if (directoryExists(cacheRoot)) {
|
|
81
|
+
for (const entry of fs_1.default.readdirSync(cacheRoot, { withFileTypes: true })) {
|
|
82
|
+
if (!entry.isDirectory())
|
|
83
|
+
continue;
|
|
84
|
+
const versionCacheDir = path_1.default.join(cacheRoot, entry.name);
|
|
85
|
+
brokenNpxEntries.push(...findBrokenNpxEntries(versionCacheDir));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const counts = new Map();
|
|
89
|
+
for (const entry of brokenNpxEntries) {
|
|
90
|
+
counts.set(entry.version, (counts.get(entry.version) ?? 0) + 1);
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
cacheRoot,
|
|
94
|
+
brokenNpxEntries,
|
|
95
|
+
staleRetryCaches: findStaleRetryCaches(cacheRoot, {
|
|
96
|
+
nowMs: options.nowMs,
|
|
97
|
+
ageFloorMs: options.retryAgeFloorMs,
|
|
98
|
+
}),
|
|
99
|
+
affectedVersions: [...counts.entries()]
|
|
100
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
101
|
+
.map(([version, brokenNpxEntryCount]) => ({ version, brokenNpxEntryCount })),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function repairFraimMcpPackageCache(options = {}) {
|
|
105
|
+
const inspection = inspectFraimMcpPackageCache(options);
|
|
106
|
+
const removedBrokenNpxEntries = [];
|
|
107
|
+
const removedStaleRetryCaches = [];
|
|
108
|
+
if (!options.dryRun) {
|
|
109
|
+
for (const entry of inspection.brokenNpxEntries) {
|
|
110
|
+
if (!safeChild(inspection.cacheRoot, entry.path))
|
|
111
|
+
continue;
|
|
112
|
+
fs_1.default.rmSync(entry.path, { recursive: true, force: true });
|
|
113
|
+
removedBrokenNpxEntries.push(entry);
|
|
114
|
+
}
|
|
115
|
+
for (const entry of inspection.staleRetryCaches) {
|
|
116
|
+
if (!safeChild(inspection.cacheRoot, entry.path))
|
|
117
|
+
continue;
|
|
118
|
+
fs_1.default.rmSync(entry.path, { recursive: true, force: true });
|
|
119
|
+
removedStaleRetryCaches.push(entry);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
...inspection,
|
|
124
|
+
removedBrokenNpxEntries,
|
|
125
|
+
removedStaleRetryCaches,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function buildFraimMcpCacheDiagnostic(options = {}) {
|
|
129
|
+
const inspection = inspectFraimMcpPackageCache({
|
|
130
|
+
cacheRoot: options.cacheRoot,
|
|
131
|
+
nowMs: options.nowMs,
|
|
132
|
+
});
|
|
133
|
+
if (inspection.brokenNpxEntries.length === 0) {
|
|
134
|
+
return {
|
|
135
|
+
status: 'passed',
|
|
136
|
+
message: 'FRAIM MCP package cache has no corrupted _npx entries',
|
|
137
|
+
details: {
|
|
138
|
+
diagnosticVersion: exports.FRAIM_MCP_CACHE_REPAIR_DIAGNOSTIC_VERSION,
|
|
139
|
+
cacheRoot: inspection.cacheRoot,
|
|
140
|
+
affectedVersions: [],
|
|
141
|
+
staleRetryCacheCount: inspection.staleRetryCaches.length,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const status = options.mcpRuntimeStatus === 'error' ? 'error' : 'warning';
|
|
146
|
+
return {
|
|
147
|
+
status,
|
|
148
|
+
message: 'FRAIM MCP package cache is corrupted',
|
|
149
|
+
suggestion: `Run: ${FRAIM_MCP_CACHE_REPAIR_COMMAND}`,
|
|
150
|
+
details: {
|
|
151
|
+
diagnosticVersion: exports.FRAIM_MCP_CACHE_REPAIR_DIAGNOSTIC_VERSION,
|
|
152
|
+
classification: exports.CORRUPTED_NPX_PACKAGE_CACHE_CLASSIFICATION,
|
|
153
|
+
cacheRoot: inspection.cacheRoot,
|
|
154
|
+
affectedVersions: inspection.affectedVersions,
|
|
155
|
+
brokenNpxEntryCount: inspection.brokenNpxEntries.length,
|
|
156
|
+
staleRetryCacheCount: inspection.staleRetryCaches.length,
|
|
157
|
+
repairCommand: FRAIM_MCP_CACHE_REPAIR_COMMAND,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function readRecordedLauncherVersion() {
|
|
162
|
+
try {
|
|
163
|
+
const state = JSON.parse(fs_1.default.readFileSync((0, fraim_mcp_latest_launcher_1.getFraimMcpLauncherStatePath)(), 'utf8'));
|
|
164
|
+
const version = typeof state?.version === 'string' ? state.version : '';
|
|
165
|
+
return VALID_VERSION_PATTERN.test(version) ? version : null;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function versionCacheDirName(version) {
|
|
172
|
+
return `fraim-${version.replace(/[^a-zA-Z0-9._-]/g, '_')}`;
|
|
173
|
+
}
|
|
174
|
+
async function warmFraimPackage(versionFallback) {
|
|
175
|
+
const version = readRecordedLauncherVersion() || versionFallback;
|
|
176
|
+
const cacheDir = path_1.default.join(getFraimMcpCacheRoot(), versionCacheDirName(version));
|
|
177
|
+
const command = process.platform === 'win32' ? (process.env.ComSpec || 'cmd.exe') : 'npx';
|
|
178
|
+
const args = process.platform === 'win32'
|
|
179
|
+
? ['/d', '/s', '/c', `npx --yes --package fraim@${version} fraim --version`]
|
|
180
|
+
: ['--yes', '--package', `fraim@${version}`, 'fraim', '--version'];
|
|
181
|
+
fs_1.default.mkdirSync(cacheDir, { recursive: true });
|
|
182
|
+
(0, child_process_1.execFileSync)(command, args, {
|
|
183
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
184
|
+
windowsHide: true,
|
|
185
|
+
env: { ...process.env, npm_config_cache: cacheDir },
|
|
186
|
+
timeout: 120000,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
@@ -59,6 +59,7 @@ const redact_secrets_1 = require("../../../core/utils/redact-secrets");
|
|
|
59
59
|
const ide_detector_1 = require("../../setup/ide-detector");
|
|
60
60
|
const fraim_mcp_latest_launcher_1 = require("../../mcp/fraim-mcp-latest-launcher");
|
|
61
61
|
const fraim_mcp_diagnostics_1 = require("./fraim-mcp-diagnostics");
|
|
62
|
+
const fraim_mcp_cache_repair_1 = require("./fraim-mcp-cache-repair");
|
|
62
63
|
// Cache the npm major version so execFileSync is called at most once per process.
|
|
63
64
|
// Without caching, each IDE config check calls diagnoseFraimMcpLaunchPlan which calls
|
|
64
65
|
// getNpmMajorVersion, and execFileSync blocks the event loop for ~2s on Windows.
|
|
@@ -1001,8 +1002,18 @@ function getStdioMCPRuntimeChecks() {
|
|
|
1001
1002
|
/**
|
|
1002
1003
|
* Get all MCP connectivity checks
|
|
1003
1004
|
*/
|
|
1004
|
-
function getMCPConnectivityChecks() {
|
|
1005
|
+
function getMCPConnectivityChecks(dependencies = {}) {
|
|
1005
1006
|
const checks = [];
|
|
1007
|
+
const ensureLauncher = dependencies.ensureLauncher ?? fraim_mcp_latest_launcher_1.ensureFraimMcpLatestLauncher;
|
|
1008
|
+
const runStdioServer = dependencies.testStdioServer ?? testStdioMCPServer;
|
|
1009
|
+
ensureLauncher();
|
|
1010
|
+
const npx = dependencies.npxPath ?? (0, fraim_mcp_latest_launcher_1.getFraimNpxShimPath)();
|
|
1011
|
+
const fraimMcp = dependencies.fraimMcpPath ?? (0, fraim_mcp_latest_launcher_1.getFraimMcpShimPath)();
|
|
1012
|
+
let fraimRuntimeCheck = null;
|
|
1013
|
+
const runFraimRuntimeCheck = () => {
|
|
1014
|
+
fraimRuntimeCheck ??= runStdioServer('fraim', fraimMcp, []);
|
|
1015
|
+
return fraimRuntimeCheck;
|
|
1016
|
+
};
|
|
1006
1017
|
// Check 1: Test FRAIM server connectivity with actual API key
|
|
1007
1018
|
checks.push({
|
|
1008
1019
|
name: 'FRAIM server connectivity',
|
|
@@ -1016,6 +1027,14 @@ function getMCPConnectivityChecks() {
|
|
|
1016
1027
|
critical: false,
|
|
1017
1028
|
run: testFraimLocalProxyTroubleshooting
|
|
1018
1029
|
});
|
|
1030
|
+
checks.push({
|
|
1031
|
+
name: 'FRAIM MCP package cache health',
|
|
1032
|
+
category: 'mcpConnectivity',
|
|
1033
|
+
critical: false,
|
|
1034
|
+
run: async () => (0, fraim_mcp_cache_repair_1.buildFraimMcpCacheDiagnostic)({
|
|
1035
|
+
mcpRuntimeStatus: (await runFraimRuntimeCheck()).status
|
|
1036
|
+
})
|
|
1037
|
+
});
|
|
1019
1038
|
// Check 2: Validate each installed IDE's MCP config
|
|
1020
1039
|
const installedIDEs = (0, ide_detector_1.detectInstalledIDEs)();
|
|
1021
1040
|
for (const ide of installedIDEs) {
|
|
@@ -1029,6 +1048,23 @@ function getMCPConnectivityChecks() {
|
|
|
1029
1048
|
// Check 3 (Issue #532): Confirm stdio MCP servers respond at runtime.
|
|
1030
1049
|
// "Disconnected" in mcp list is expected on-demand-spawn behavior. These checks
|
|
1031
1050
|
// verify that each server can actually start and complete a JSON-RPC handshake.
|
|
1032
|
-
checks.push(
|
|
1051
|
+
checks.push({
|
|
1052
|
+
name: 'git stdio runtime check',
|
|
1053
|
+
category: 'mcpConnectivity',
|
|
1054
|
+
critical: false,
|
|
1055
|
+
run: () => runStdioServer('git', npx, ['-y', '@cyanheads/git-mcp-server'])
|
|
1056
|
+
});
|
|
1057
|
+
checks.push({
|
|
1058
|
+
name: 'playwright stdio runtime check',
|
|
1059
|
+
category: 'mcpConnectivity',
|
|
1060
|
+
critical: false,
|
|
1061
|
+
run: () => runStdioServer('playwright', npx, ['-y', '@playwright/mcp'])
|
|
1062
|
+
});
|
|
1063
|
+
checks.push({
|
|
1064
|
+
name: 'fraim stdio runtime check',
|
|
1065
|
+
category: 'mcpConnectivity',
|
|
1066
|
+
critical: false,
|
|
1067
|
+
run: runFraimRuntimeCheck
|
|
1068
|
+
});
|
|
1033
1069
|
return checks;
|
|
1034
1070
|
}
|
|
@@ -113,12 +113,14 @@ function defaultCloneDir(layer) {
|
|
|
113
113
|
* Resolve where this layer's details live. Never throws and never touches the
|
|
114
114
|
* network; call `ensurePackClone` separately when the git working copy has to
|
|
115
115
|
* be present and current.
|
|
116
|
+
* Read-only inventory callers set `migrateLegacy: false` to suppress the
|
|
117
|
+
* otherwise backwards-compatible flat org layout migration.
|
|
116
118
|
*/
|
|
117
|
-
function resolvePackHome(layer) {
|
|
119
|
+
function resolvePackHome(layer, options = {}) {
|
|
118
120
|
const config = (0, user_config_1.getLayerBackend)(layer);
|
|
119
121
|
const standard = standardHome(layer);
|
|
120
122
|
const make = (root, extra) => {
|
|
121
|
-
if (layer === 'org')
|
|
123
|
+
if (layer === 'org' && options.migrateLegacy !== false)
|
|
122
124
|
migrateOrgFlatContent(root);
|
|
123
125
|
return { layer, root, contentRoot: path_1.default.join(root, 'personalized-employee'), ...extra };
|
|
124
126
|
};
|
|
@@ -12,130 +12,27 @@
|
|
|
12
12
|
* the current job's-domain learnings only, so a marketing job no longer loads
|
|
13
13
|
* engineering learnings.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
* registry-category -> domain map, the per-domain authoring voice (R3)
|
|
17
|
-
* resolver used at the injection sites
|
|
15
|
+
* The shared registry/scripts/learning/learning-domains.json owns the domain list, the
|
|
16
|
+
* registry-category -> domain map, and the per-domain authoring voice (R3).
|
|
17
|
+
* This module provides the pure resolver used at the injection sites, following the
|
|
18
18
|
* config-module convention of `src/config/portfolio-slug-overrides.ts`.
|
|
19
19
|
*/
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.DOMAIN_VOICE = exports.GLOBAL_JOB_CATEGORIES = exports.JOB_DOMAIN_MAP = exports.LEARNING_DOMAINS = void 0;
|
|
21
|
+
exports.DOMAIN_VOICE = exports.GLOBAL_JOB_CATEGORIES = exports.JOB_DOMAIN_MAP = exports.LEARNING_DOMAINS = exports.LEARNING_DOMAIN_MAP_VERSION = void 0;
|
|
22
22
|
exports.isLearningDomain = isLearningDomain;
|
|
23
23
|
exports.jobCategoryFromRegistryPath = jobCategoryFromRegistryPath;
|
|
24
24
|
exports.resolveJobDomain = resolveJobDomain;
|
|
25
25
|
exports.domainFromJobFrontmatter = domainFromJobFrontmatter;
|
|
26
26
|
exports.resolveDomainForJob = resolveDomainForJob;
|
|
27
|
-
|
|
28
|
-
exports.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
'finance',
|
|
34
|
-
'legal',
|
|
35
|
-
'compliance',
|
|
36
|
-
'customer',
|
|
37
|
-
'company-building',
|
|
38
|
-
'operations',
|
|
39
|
-
];
|
|
27
|
+
const learningDomainData = require('../../registry/scripts/learning/learning-domains.json');
|
|
28
|
+
exports.LEARNING_DOMAIN_MAP_VERSION = learningDomainData.mapVersion;
|
|
29
|
+
exports.LEARNING_DOMAINS = Object.freeze(learningDomainData.domains);
|
|
30
|
+
exports.JOB_DOMAIN_MAP = Object.freeze(learningDomainData.jobDomainMap);
|
|
31
|
+
exports.GLOBAL_JOB_CATEGORIES = new Set(learningDomainData.globalJobCategories);
|
|
32
|
+
exports.DOMAIN_VOICE = Object.freeze(learningDomainData.domainVoice);
|
|
40
33
|
function isLearningDomain(value) {
|
|
41
34
|
return typeof value === 'string' && exports.LEARNING_DOMAINS.includes(value);
|
|
42
35
|
}
|
|
43
|
-
/**
|
|
44
|
-
* Registry job category -> learning domain. Seeded from the registry taxonomy and
|
|
45
|
-
* the `PERSONA_CAPABILITY_BUNDLES` groupings. Every domain-scoped category maps
|
|
46
|
-
* here; cross-cutting categories are listed in `GLOBAL_JOB_CATEGORIES` instead.
|
|
47
|
-
*
|
|
48
|
-
* A build-time test (`tests/test-unit-learning-domains.ts`) asserts that every
|
|
49
|
-
* registry job category (from `registry/jobs/**`) is covered by exactly one of
|
|
50
|
-
* this map or `GLOBAL_JOB_CATEGORIES`, so the taxonomy cannot silently drift as
|
|
51
|
-
* new job categories are added.
|
|
52
|
-
*/
|
|
53
|
-
exports.JOB_DOMAIN_MAP = {
|
|
54
|
-
// engineering
|
|
55
|
-
'product-building': 'engineering',
|
|
56
|
-
'dev-ops': 'engineering',
|
|
57
|
-
'quality-assurance': 'engineering',
|
|
58
|
-
'migration': 'engineering',
|
|
59
|
-
'replicate': 'engineering',
|
|
60
|
-
'reviewer': 'engineering',
|
|
61
|
-
'security': 'engineering',
|
|
62
|
-
'delivery-ops': 'engineering',
|
|
63
|
-
'salesforce': 'engineering',
|
|
64
|
-
'ai-engineering': 'engineering',
|
|
65
|
-
// product
|
|
66
|
-
'product-management': 'product',
|
|
67
|
-
'customer-development': 'product',
|
|
68
|
-
'brainstorming': 'product',
|
|
69
|
-
// go-to-market
|
|
70
|
-
'marketing': 'go-to-market',
|
|
71
|
-
'branding': 'go-to-market',
|
|
72
|
-
'sales': 'go-to-market',
|
|
73
|
-
'go-to-market': 'go-to-market',
|
|
74
|
-
'business-development': 'go-to-market',
|
|
75
|
-
'commercial-ops': 'go-to-market',
|
|
76
|
-
// people-ops
|
|
77
|
-
'people-ops': 'people-ops',
|
|
78
|
-
'inclusion-ops': 'people-ops',
|
|
79
|
-
'career': 'people-ops',
|
|
80
|
-
// finance
|
|
81
|
-
'finance': 'finance',
|
|
82
|
-
'invoicing': 'finance',
|
|
83
|
-
'banking': 'finance',
|
|
84
|
-
// legal
|
|
85
|
-
'legal': 'legal',
|
|
86
|
-
// compliance
|
|
87
|
-
'compliance': 'compliance',
|
|
88
|
-
// customer
|
|
89
|
-
'customer-success': 'customer',
|
|
90
|
-
'customer-communication': 'customer',
|
|
91
|
-
// company-building
|
|
92
|
-
'company-creation': 'company-building',
|
|
93
|
-
'bootstrap': 'company-building',
|
|
94
|
-
'fundraising': 'company-building',
|
|
95
|
-
// operations
|
|
96
|
-
'business-ops': 'operations',
|
|
97
|
-
'procurement': 'operations',
|
|
98
|
-
'project-management': 'operations',
|
|
99
|
-
};
|
|
100
|
-
/**
|
|
101
|
-
* Cross-cutting job categories that intentionally resolve to `global` (no domain).
|
|
102
|
-
* Management and framework-meta learnings apply regardless of the work domain, so
|
|
103
|
-
* they are never hidden from any job. Listed explicitly so the completeness test
|
|
104
|
-
* can tell an intentional global category apart from a forgotten mapping.
|
|
105
|
-
*/
|
|
106
|
-
exports.GLOBAL_JOB_CATEGORIES = new Set([
|
|
107
|
-
// ai-employee cross-cutting
|
|
108
|
-
'manager',
|
|
109
|
-
'fraim',
|
|
110
|
-
// ai-manager (all management categories)
|
|
111
|
-
'1-1',
|
|
112
|
-
'coaching',
|
|
113
|
-
'delegation',
|
|
114
|
-
'hiring',
|
|
115
|
-
'productivity',
|
|
116
|
-
'project-setup',
|
|
117
|
-
'setup-guardrails',
|
|
118
|
-
'team-learning',
|
|
119
|
-
'verification',
|
|
120
|
-
]);
|
|
121
|
-
/**
|
|
122
|
-
* One-line authoring voice per domain (R3). Consumed as documentation and by the
|
|
123
|
-
* synthesis jobs/skills (`sleep-on-learnings`, `organizational-learning-synthesis`,
|
|
124
|
-
* `l1-signal-classification`) so a domain-specific learning is written in that
|
|
125
|
-
* domain's language and stays human-auditable.
|
|
126
|
-
*/
|
|
127
|
-
exports.DOMAIN_VOICE = {
|
|
128
|
-
'engineering': 'Precise and technical; file paths, commands, and test names are appropriate.',
|
|
129
|
-
'product': 'Product and user-outcome language; behavior and flow, not code symbols.',
|
|
130
|
-
'go-to-market': 'Marketing and sales language; campaigns, funnels, messaging, audience.',
|
|
131
|
-
'people-ops': 'Plain HR and recruiting language; no engineering jargon.',
|
|
132
|
-
'finance': 'Finance and accounting language; figures, close, forecasts.',
|
|
133
|
-
'legal': 'Contract and IP language; obligations, clauses, risk.',
|
|
134
|
-
'compliance': 'Controls, evidence, and regulation language.',
|
|
135
|
-
'customer': 'Customer-relationship and support language.',
|
|
136
|
-
'company-building': 'Founder and operations language; setup, entity, funding.',
|
|
137
|
-
'operations': 'Business-operations language; process, vendors, planning, coordination.',
|
|
138
|
-
};
|
|
139
36
|
/**
|
|
140
37
|
* Extract the registry job category from a job's registry path.
|
|
141
38
|
*
|
|
@@ -102,11 +102,17 @@ class LocalRegistryResolver {
|
|
|
102
102
|
(0, path_1.join)(this.managerCacheRoot, dir),
|
|
103
103
|
(0, path_1.join)(this.orgCacheRoot, dir),
|
|
104
104
|
(0, path_1.join)(this.workspaceRoot, 'registry', dir),
|
|
105
|
+
// Installed projects have categorized job stubs in the synced roles,
|
|
106
|
+
// without a source registry checkout. Keep authored overrides first.
|
|
107
|
+
...(type === 'jobs' ? [this.getFraimPath('ai-employee', dir), this.getFraimPath('ai-manager', dir)] : []),
|
|
105
108
|
];
|
|
106
109
|
for (const root of searchRoots) {
|
|
107
110
|
const found = this.searchFileRecursively(root, baseName);
|
|
108
111
|
if (!found)
|
|
109
112
|
continue;
|
|
113
|
+
const syncedRole = ['ai-employee', 'ai-manager'].find(role => root === this.getFraimPath(role, dir));
|
|
114
|
+
if (syncedRole)
|
|
115
|
+
return `${dir}/${syncedRole}/${found.slice(root.length + 1).replace(/\\/g, '/')}`;
|
|
110
116
|
// Convert absolute back to a registry-relative path.
|
|
111
117
|
const rel = found.replace(/\\/g, '/');
|
|
112
118
|
const dirMarker = `/${dir}/`;
|
|
@@ -1612,17 +1612,8 @@ class FraimLocalMCPServer {
|
|
|
1612
1612
|
// 4. After get_fraim_job succeeds, inject learning context with job-focus frame.
|
|
1613
1613
|
if (toolName === 'get_fraim_job' && !finalizedResponse.error) {
|
|
1614
1614
|
const text = finalizedResponse.result?.content?.[0]?.text;
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
const workspaceRoot = this.findProjectRoot() || process.cwd();
|
|
1618
|
-
const jobDomain = await this.resolveJobDomainForJob(args.job, requestSessionId);
|
|
1619
|
-
const learningSection = (0, learning_context_builder_js_1.buildLearningContextSection)(workspaceRoot, userEmail, true, jobDomain);
|
|
1620
|
-
const teamContextSection = (0, learning_context_builder_js_1.buildTeamContextSection)(workspaceRoot, true);
|
|
1621
|
-
if (learningSection || teamContextSection) {
|
|
1622
|
-
finalizedResponse.result.content[0].text = text + `\n\n---` + learningSection + teamContextSection;
|
|
1623
|
-
this.log(`[req:${requestId}] Injected job-focus learning/team context for ${userEmail} (domain: ${jobDomain ?? 'global'})`);
|
|
1624
|
-
}
|
|
1625
|
-
this.recordUsageOffers(workspaceRoot, userEmail, true, jobDomain, String(args.job ?? 'unknown'), requestId);
|
|
1615
|
+
if (typeof text === 'string') {
|
|
1616
|
+
finalizedResponse.result.content[0].text = await this.appendJobContext(text, args.job, requestSessionId, requestId);
|
|
1626
1617
|
}
|
|
1627
1618
|
// Issue #1345: forward executionMode from get_fraim_job response to Hub so
|
|
1628
1619
|
// the chip reads from run data (no local file read, no separate endpoint needed).
|
|
@@ -1640,6 +1631,22 @@ class FraimLocalMCPServer {
|
|
|
1640
1631
|
}
|
|
1641
1632
|
return this.processResponseWithHydration(finalizedResponse, requestSessionId);
|
|
1642
1633
|
}
|
|
1634
|
+
/** Keep context delivery and offer attribution together for local and remote jobs. */
|
|
1635
|
+
async appendJobContext(text, jobName, requestSessionId, requestId) {
|
|
1636
|
+
const userEmail = this.ensureEngine().getUserEmail();
|
|
1637
|
+
if (!userEmail)
|
|
1638
|
+
return text;
|
|
1639
|
+
const workspaceRoot = this.findProjectRoot() || process.cwd();
|
|
1640
|
+
const jobDomain = await this.resolveJobDomainForJob(jobName, requestSessionId);
|
|
1641
|
+
const learningSection = (0, learning_context_builder_js_1.buildLearningContextSection)(workspaceRoot, userEmail, true, jobDomain);
|
|
1642
|
+
const teamContextSection = (0, learning_context_builder_js_1.buildTeamContextSection)(workspaceRoot, true);
|
|
1643
|
+
if (learningSection || teamContextSection) {
|
|
1644
|
+
text += `\n\n---` + learningSection + teamContextSection;
|
|
1645
|
+
this.log(`[req:${requestId}] Injected job-focus learning/team context for ${userEmail} (domain: ${jobDomain ?? 'global'})`);
|
|
1646
|
+
}
|
|
1647
|
+
this.recordUsageOffers(workspaceRoot, userEmail, true, jobDomain, String(jobName ?? 'unknown'), requestId);
|
|
1648
|
+
return text;
|
|
1649
|
+
}
|
|
1643
1650
|
/**
|
|
1644
1651
|
* Issue #1103 R1, R2 and R3: record what this context injection delivered.
|
|
1645
1652
|
*
|
|
@@ -2627,18 +2634,7 @@ class FraimLocalMCPServer {
|
|
|
2627
2634
|
responseText = `${mentor.getCompactPhaseAuthority()}\n\n${responseText}`;
|
|
2628
2635
|
responseText += `\n\n---\n\n**Job ID:** \`${jobId}\`\n\n**This job has phases.** Use \`seekMentoring\` with the jobId above to get phase-specific instructions.`;
|
|
2629
2636
|
}
|
|
2630
|
-
|
|
2631
|
-
const userEmail = this.ensureEngine().getUserEmail();
|
|
2632
|
-
if (userEmail) {
|
|
2633
|
-
const workspaceRoot = this.findProjectRoot() || process.cwd();
|
|
2634
|
-
const jobDomain = await this.resolveJobDomainForJob(name, requestSessionId);
|
|
2635
|
-
const learningSection = (0, learning_context_builder_js_1.buildLearningContextSection)(workspaceRoot, userEmail, true, jobDomain);
|
|
2636
|
-
const teamContextSection = (0, learning_context_builder_js_1.buildTeamContextSection)(workspaceRoot, true);
|
|
2637
|
-
if (learningSection || teamContextSection) {
|
|
2638
|
-
responseText += `\n\n---` + learningSection + teamContextSection;
|
|
2639
|
-
this.log(`✅ Injected job-focus learning/team context for ${userEmail} (local override path, domain: ${jobDomain ?? 'global'})`);
|
|
2640
|
-
}
|
|
2641
|
-
}
|
|
2637
|
+
responseText = await this.appendJobContext(responseText, name, requestSessionId, requestId);
|
|
2642
2638
|
return await this.finalizeLocalToolTextResponse(request, requestSessionId, requestId, responseText);
|
|
2643
2639
|
}
|
|
2644
2640
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fraim",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.338",
|
|
4
4
|
"description": "FRAIM core CLI and MCP package.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"fraim": "bin/fraim.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
+
"dist/registry/scripts/learning/learning-domains.json",
|
|
11
|
+
"dist/registry/scripts/learning/entry-format.js",
|
|
10
12
|
"dist/src/local-mcp-server/",
|
|
11
13
|
"dist/src/cli/",
|
|
12
14
|
"!dist/src/cli/fraim-hub.js",
|