fraim 2.0.360-next.0 → 2.0.360-next.2

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.
@@ -205,7 +205,7 @@ For read-only diagnosis or delivery verification, use get_fraim_file({ path: "jo
205
205
 
206
206
  Examples:
207
207
  - get_fraim_file({ path: "skills/communication/active-listening.md" })
208
- - get_fraim_file({ path: "rules/local-development.md" })`,
208
+ - get_fraim_file({ path: "rules/engineering/engineering.md" })`,
209
209
  annotations: readOnlyClosedWorldAnnotations,
210
210
  inputSchema: {
211
211
  type: 'object',
@@ -213,7 +213,7 @@ Examples:
213
213
  ...sessionIdProperty,
214
214
  path: {
215
215
  type: 'string',
216
- description: 'Path to the file (e.g., skills/communication/active-listening.md, rules/local-development.md, templates/specs/FEATURESPEC-TEMPLATE.md)'
216
+ description: 'Path to the file (e.g., skills/communication/active-listening.md, rules/engineering/engineering.md, templates/specs/FEATURESPEC-TEMPLATE.md)'
217
217
  },
218
218
  raw: {
219
219
  type: 'boolean',
@@ -1,44 +1,8 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.requestLogger = requestLogger;
37
4
  const crypto_1 = require("crypto");
38
5
  const redact_secrets_1 = require("../core/utils/redact-secrets");
39
- const fs = __importStar(require("fs"));
40
- const path = __importStar(require("path"));
41
- const LOG_FILE = path.join(process.cwd(), 'server-direct.log');
42
6
  function getBodyForLogging(req) {
43
7
  if (req.path === '/api/ai-hub/brand' && req.body && typeof req.body === 'object') {
44
8
  const body = { ...req.body };
@@ -82,16 +46,8 @@ function requestLogger(req, res, next) {
82
46
  }
83
47
  }
84
48
  const logEntry = (msg) => {
85
- const timestamp = new Date().toISOString();
86
- const entry = `[${timestamp}] ${msg}\n`;
87
- try {
88
- fs.appendFileSync(LOG_FILE, entry);
89
- }
90
- catch (e) {
91
- console.error('Failed to write to log file:', e);
92
- }
93
49
  if (msg.includes('END') && msg.includes(' 400')) {
94
- console.error(msg); // Still log errors to console
50
+ console.error(msg);
95
51
  }
96
52
  else {
97
53
  console.info(msg);
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.McpService = exports.REQUIRED_QUALITY_FIELDS = exports.QUALITY_SCORE_JOBS = exports.QUALITY_PRODUCING_JOBS = exports.DEFAULT_LAUNCH_PHRASE_MAPPINGS = void 0;
37
37
  exports.buildTrainingModeBlock = buildTrainingModeBlock;
38
+ const domain_rule_assembler_1 = require("../core/utils/domain-rule-assembler");
38
39
  const fs_1 = require("fs");
39
40
  const crypto_1 = require("crypto");
40
41
  const job_parser_1 = require("../core/utils/job-parser");
@@ -311,8 +312,8 @@ class McpService {
311
312
  header += `These must be filled by the agent/runtime from the active task context.\n\n`;
312
313
  }
313
314
  if (metadata.type === 'script') {
314
- header += `\n## 🛡️ Ephemeral Execution Mandatory (IP Protection)\n\n`;
315
- header += `This script MUST be executed ephemerally (see rules/ephemeral-execution.md).\n\n`;
315
+ header += `\n## Script execution\n\n`;
316
+ header += `Use the synchronized installed script with the arguments supplied by its owning skill. If it is missing or stale, restore synchronization before execution.\n\n`;
316
317
  }
317
318
  header += `---\n\n`;
318
319
  return {
@@ -421,12 +422,9 @@ class McpService {
421
422
  console.error(`❌ McpService: Failed to fetch executionMode for ${jobName}:`, e);
422
423
  }
423
424
  }
424
- return {
425
- content: [{
426
- type: 'text',
427
- text: response
428
- }]
429
- };
425
+ return this.addRuleDelivery({
426
+ content: [{ type: 'text', text: response }]
427
+ }, jobName);
430
428
  }
431
429
  async handleListJobs() {
432
430
  const jobs = Array.from(this.registryService.getFileIndex().values()).filter((f) => f.type === 'job' && !f.isStub);
@@ -574,7 +572,7 @@ class McpService {
574
572
  }
575
573
  // Learning context is injected by the local proxy (learning-context-builder.ts)
576
574
  // after this response is received, because learning files live in the local workspace.
577
- return {
575
+ return this.addRuleDelivery({
578
576
  content: [{
579
577
  type: 'text',
580
578
  text: lines.join('\n')
@@ -587,7 +585,7 @@ class McpService {
587
585
  repo: session.repo
588
586
  },
589
587
  sessionId: sessionId
590
- };
588
+ });
591
589
  }
592
590
  validateIssueTrackingConfig(issueTracking) {
593
591
  if (!issueTracking.provider) {
@@ -708,7 +706,8 @@ class McpService {
708
706
  };
709
707
  }
710
708
  async handleSeekMentoring(args, userId) {
711
- const personaLock = await this.maybeBuildPersonaLockResponse(args.jobName, userId, undefined, args.status === 'starting');
709
+ const restoringInstructions = args.status === 'starting' && args.requestInstructions === true;
710
+ const personaLock = await this.maybeBuildPersonaLockResponse(args.jobName, userId, undefined, args.status === 'starting' && !restoringInstructions);
712
711
  if (personaLock) {
713
712
  return personaLock;
714
713
  }
@@ -827,7 +826,7 @@ class McpService {
827
826
  let phaseStartWarning = '';
828
827
  const sessionId = typeof args.sessionId === 'string' ? args.sessionId.trim() : '';
829
828
  const isPhaseCompletion = args.status === 'complete' || args.status === 'failure';
830
- if (sessionId && sessionId !== 'unknown') {
829
+ if (!restoringInstructions && sessionId && sessionId !== 'unknown') {
831
830
  const markerKey = JSON.stringify([userId ?? null, sessionId, args.jobName]);
832
831
  const context = {
833
832
  sessionId,
@@ -864,7 +863,7 @@ class McpService {
864
863
  phaseStartWarning = `Phase-start notice: phase \`${args.currentPhase}\` has no identifiable session, so its start cannot be matched to this run. The missing match was flagged, but this completion remains accepted.\n\n`;
865
864
  }
866
865
  // Update job execution tracking using usage events
867
- if (canTrackJob && userId && this.analyticsService) {
866
+ if (!restoringInstructions && canTrackJob && userId && this.analyticsService) {
868
867
  try {
869
868
  const sessionId = args.sessionId || 'unknown';
870
869
  await this.analyticsService.logMentoringCall(args.jobId, args.jobName, userId, sessionId, args.currentPhase, args.status, result.nextPhase, result.message ? true : false);
@@ -885,7 +884,7 @@ class McpService {
885
884
  }
886
885
  // Issue #1345: persist executionModeUpdate from findings when present.
887
886
  const executionModeUpdate = args.findings?.executionModeUpdate;
888
- if (userId && executionModeUpdate && typeof executionModeUpdate['jobName'] === 'string') {
887
+ if (!restoringInstructions && userId && executionModeUpdate && typeof executionModeUpdate['jobName'] === 'string') {
889
888
  try {
890
889
  await this.dbService.upsertJobExecutionMode(userId, executionModeUpdate);
891
890
  }
@@ -893,13 +892,33 @@ class McpService {
893
892
  console.error(`❌ McpService: Failed to upsert executionMode:`, e);
894
893
  }
895
894
  }
896
- return {
895
+ return this.addRuleDelivery({
897
896
  content: [{
898
897
  type: 'text',
899
898
  text: trackingWarning + phaseStartWarning + result.message
900
899
  }],
901
900
  structuredContent: { phaseAccepted: true, nextPhase: result.nextPhase, phaseStartWarning }
902
- };
901
+ }, args.jobName, args);
902
+ }
903
+ async addRuleDelivery(response, jobName, phaseArgs) {
904
+ if (response.isError || response.personaLock)
905
+ return response;
906
+ const context = jobName ? await this.aiMentor.getRuleDeliveryContext(jobName) : null;
907
+ if (jobName && !context)
908
+ return response;
909
+ if (phaseArgs && !(phaseArgs.status === 'starting' && (phaseArgs.currentPhase === 'starting' || phaseArgs.currentPhase === context?.initialPhase || phaseArgs.requestInstructions === true)))
910
+ return response;
911
+ const assembly = await (0, domain_rule_assembler_1.assembleDomainRules)({ jobPath: context?.jobPath, resolveFile: async (path) => {
912
+ const content = await this.registryService.getFile(path);
913
+ if (content === null)
914
+ throw new Error(`Rule source unavailable: ${path}`);
915
+ return { content, source: 'remote', personalized: false, inherited: false };
916
+ } });
917
+ const result = (0, domain_rule_assembler_1.attachRuleAssembly)(response, assembly);
918
+ for (const warning of assembly.metadata.warnings) {
919
+ console.warn('Rule delivery warning:', JSON.stringify(warning));
920
+ }
921
+ return result;
903
922
  }
904
923
  getJobDescription(jobName, fullPath) {
905
924
  if (fullPath && (0, fs_1.existsSync)(fullPath)) {
@@ -308,6 +308,10 @@ class RegistryService {
308
308
  }
309
309
  getSyncBundle() {
310
310
  const files = [];
311
+ const domainCatalog = (0, path_1.join)(this.registryPath, 'domains.json');
312
+ if ((0, fs_1.existsSync)(domainCatalog)) {
313
+ files.push({ path: 'domains.json', content: (0, fs_1.readFileSync)(domainCatalog, 'utf8'), type: 'config' });
314
+ }
311
315
  const allFiles = Array.from(this.fileIndex.values());
312
316
  // 1. Add job STUBS (not full jobs) from registry/stubs/jobs
313
317
  const jobStubs = allFiles.filter(f => f.type === 'job' && f.path.includes('stubs/jobs/'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim",
3
- "version": "2.0.360-next.0",
3
+ "version": "2.0.360-next.2",
4
4
  "description": "FRAIM core CLI and MCP package.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -9,7 +9,8 @@
9
9
  "files": [
10
10
  "dist/registry/scripts/artifact-paths.js",
11
11
  "dist/registry/scripts/learning-usage/local-runtime.js",
12
- "dist/registry/scripts/learning/learning-domains.json",
12
+ "dist/registry/domains.json",
13
+ "dist/registry/scripts/domains/catalog.js",
13
14
  "dist/registry/scripts/learning/entry-format.js",
14
15
  "dist/registry/scripts/learning/completion-state.js",
15
16
  "dist/registry/scripts/learning/corpus-audit.js",
@@ -1,80 +0,0 @@
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
- "engineering": "engineering",
18
- "product-building": "engineering",
19
- "dev-ops": "engineering",
20
- "quality-assurance": "engineering",
21
- "migration": "engineering",
22
- "replicate": "engineering",
23
- "reviewer": "engineering",
24
- "security": "engineering",
25
- "delivery-ops": "engineering",
26
- "salesforce": "engineering",
27
- "ai-engineering": "engineering",
28
- "product-management": "product",
29
- "customer-development": "product",
30
- "brainstorming": "product",
31
- "marketing": "go-to-market",
32
- "branding": "go-to-market",
33
- "sales": "go-to-market",
34
- "go-to-market": "go-to-market",
35
- "business-development": "go-to-market",
36
- "commercial-ops": "go-to-market",
37
- "people-ops": "people-ops",
38
- "recruiting": "people-ops",
39
- "inclusion-ops": "people-ops",
40
- "career": "people-ops",
41
- "finance": "finance",
42
- "invoicing": "finance",
43
- "banking": "finance",
44
- "legal": "legal",
45
- "compliance": "compliance",
46
- "customer-success": "customer",
47
- "customer-communication": "customer",
48
- "company-creation": "company-building",
49
- "bootstrap": "company-building",
50
- "fundraising": "company-building",
51
- "business-ops": "operations",
52
- "procurement": "operations",
53
- "project-management": "operations"
54
- },
55
- "globalJobCategories": [
56
- "manager",
57
- "fraim",
58
- "1-1",
59
- "coaching",
60
- "delegation",
61
- "hiring",
62
- "productivity",
63
- "project-setup",
64
- "setup-guardrails",
65
- "team-learning",
66
- "verification"
67
- ],
68
- "domainVoice": {
69
- "engineering": "Precise and technical; file paths, commands, and test names are appropriate.",
70
- "product": "Product and user-outcome language; behavior and flow, not code symbols.",
71
- "go-to-market": "Marketing and sales language; campaigns, funnels, messaging, audience.",
72
- "people-ops": "Plain HR and recruiting language; no engineering jargon.",
73
- "finance": "Finance and accounting language; figures, close, forecasts.",
74
- "legal": "Contract and IP language; obligations, clauses, risk.",
75
- "compliance": "Controls, evidence, and regulation language.",
76
- "customer": "Customer-relationship and support language.",
77
- "company-building": "Founder and operations language; setup, entity, funding.",
78
- "operations": "Business-operations language; process, vendors, planning, coordination."
79
- }
80
- }