fraim-framework 2.0.26 โ†’ 2.0.30

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.
Files changed (104) hide show
  1. package/.github/workflows/deploy-fraim.yml +1 -1
  2. package/dist/registry/scripts/build-scripts-generator.js +205 -0
  3. package/dist/registry/scripts/cleanup-branch.js +258 -0
  4. package/dist/registry/scripts/evaluate-code-quality.js +66 -0
  5. package/dist/registry/scripts/exec-with-timeout.js +142 -0
  6. package/dist/registry/scripts/fraim-config.js +61 -0
  7. package/dist/registry/scripts/generate-engagement-emails.js +630 -0
  8. package/dist/registry/scripts/generic-issues-api.js +100 -0
  9. package/dist/registry/scripts/newsletter-helpers.js +731 -0
  10. package/dist/registry/scripts/openapi-generator.js +664 -0
  11. package/dist/registry/scripts/performance/profile-server.js +390 -0
  12. package/dist/registry/scripts/run-thank-you-workflow.js +92 -0
  13. package/dist/registry/scripts/send-newsletter-simple.js +85 -0
  14. package/dist/registry/scripts/send-thank-you-emails.js +54 -0
  15. package/dist/registry/scripts/validate-openapi-limits.js +311 -0
  16. package/dist/registry/scripts/validate-test-coverage.js +262 -0
  17. package/dist/registry/scripts/verify-test-coverage.js +66 -0
  18. package/dist/src/cli/commands/init.js +14 -12
  19. package/dist/src/cli/commands/sync.js +19 -2
  20. package/dist/src/cli/fraim.js +24 -22
  21. package/dist/src/cli/setup/first-run.js +13 -6
  22. package/dist/src/fraim/config-loader.js +0 -8
  23. package/dist/src/fraim/db-service.js +26 -15
  24. package/dist/src/fraim/issues.js +67 -0
  25. package/dist/src/fraim/setup-wizard.js +1 -69
  26. package/dist/src/fraim/types.js +0 -11
  27. package/dist/src/fraim-mcp-server.js +272 -18
  28. package/dist/src/utils/git-utils.js +1 -1
  29. package/dist/src/utils/version-utils.js +32 -0
  30. package/dist/tests/debug-tools.js +79 -0
  31. package/dist/tests/esm-compat.js +11 -0
  32. package/dist/tests/test-chalk-esm-issue.js +159 -0
  33. package/dist/tests/test-chalk-real-world.js +265 -0
  34. package/dist/tests/test-chalk-regression.js +327 -0
  35. package/dist/tests/test-chalk-resolution-issue.js +304 -0
  36. package/dist/tests/test-cli.js +0 -2
  37. package/dist/tests/test-fraim-install-chalk-issue.js +254 -0
  38. package/dist/tests/test-fraim-issues.js +59 -0
  39. package/dist/tests/test-genericization.js +1 -3
  40. package/dist/tests/test-mcp-connection.js +166 -0
  41. package/dist/tests/test-mcp-issue-integration.js +144 -0
  42. package/dist/tests/test-mcp-lifecycle-methods.js +312 -0
  43. package/dist/tests/test-node-compatibility.js +71 -0
  44. package/dist/tests/test-npm-install.js +66 -0
  45. package/dist/tests/test-npm-resolution-diagnostic.js +140 -0
  46. package/dist/tests/test-session-rehydration.js +145 -0
  47. package/dist/tests/test-standalone.js +2 -8
  48. package/dist/tests/test-sync-version-update.js +93 -0
  49. package/dist/tests/test-telemetry.js +190 -0
  50. package/package.json +10 -8
  51. package/registry/agent-guardrails.md +62 -54
  52. package/registry/rules/agent-success-criteria.md +52 -0
  53. package/registry/rules/agent-testing-guidelines.md +502 -502
  54. package/registry/rules/communication.md +121 -121
  55. package/registry/rules/continuous-learning.md +54 -54
  56. package/registry/rules/ephemeral-execution.md +10 -5
  57. package/registry/rules/hitl-ppe-record-analysis.md +302 -302
  58. package/registry/rules/local-development.md +251 -251
  59. package/registry/rules/software-development-lifecycle.md +104 -104
  60. package/registry/rules/successful-debugging-patterns.md +482 -478
  61. package/registry/rules/telemetry.md +67 -0
  62. package/registry/scripts/build-scripts-generator.ts +216 -215
  63. package/registry/scripts/cleanup-branch.ts +303 -284
  64. package/registry/scripts/code-quality-check.sh +559 -559
  65. package/registry/scripts/detect-tautological-tests.sh +38 -38
  66. package/registry/scripts/evaluate-code-quality.ts +1 -1
  67. package/registry/scripts/generate-engagement-emails.ts +744 -744
  68. package/registry/scripts/generic-issues-api.ts +110 -150
  69. package/registry/scripts/newsletter-helpers.ts +874 -874
  70. package/registry/scripts/openapi-generator.ts +695 -693
  71. package/registry/scripts/performance/profile-server.ts +5 -3
  72. package/registry/scripts/prep-issue.sh +468 -455
  73. package/registry/scripts/validate-openapi-limits.ts +366 -365
  74. package/registry/scripts/validate-test-coverage.ts +280 -280
  75. package/registry/scripts/verify-pr-comments.sh +70 -70
  76. package/registry/scripts/verify-test-coverage.ts +1 -1
  77. package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -53
  78. package/registry/templates/evidence/Implementation-BugEvidence.md +85 -85
  79. package/registry/templates/evidence/Implementation-FeatureEvidence.md +120 -120
  80. package/registry/templates/marketing/HBR-ARTICLE-TEMPLATE.md +66 -0
  81. package/registry/workflows/bootstrap/create-architecture.md +2 -2
  82. package/registry/workflows/bootstrap/evaluate-code-quality.md +3 -3
  83. package/registry/workflows/bootstrap/verify-test-coverage.md +2 -2
  84. package/registry/workflows/customer-development/insight-analysis.md +156 -156
  85. package/registry/workflows/customer-development/interview-preparation.md +421 -421
  86. package/registry/workflows/customer-development/strategic-brainstorming.md +146 -146
  87. package/registry/workflows/customer-development/thank-customers.md +193 -191
  88. package/registry/workflows/customer-development/weekly-newsletter.md +362 -352
  89. package/registry/workflows/improve-fraim/contribute.md +32 -0
  90. package/registry/workflows/improve-fraim/file-issue.md +32 -0
  91. package/registry/workflows/marketing/hbr-article.md +73 -0
  92. package/registry/workflows/performance/analyze-performance.md +63 -59
  93. package/registry/workflows/product-building/design.md +3 -2
  94. package/registry/workflows/product-building/implement.md +4 -3
  95. package/registry/workflows/product-building/prep-issue.md +28 -17
  96. package/registry/workflows/product-building/resolve.md +3 -2
  97. package/registry/workflows/product-building/retrospect.md +3 -2
  98. package/registry/workflows/product-building/spec.md +5 -4
  99. package/registry/workflows/product-building/test.md +3 -2
  100. package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +562 -562
  101. package/registry/workflows/replicate/website-discovery-analysis.md +3 -3
  102. package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +632 -632
  103. package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +669 -669
  104. package/tsconfig.json +2 -1
@@ -45,6 +45,8 @@ const path_1 = require("path");
45
45
  const git_utils_1 = require("./utils/git-utils");
46
46
  const config_loader_1 = require("./fraim/config-loader");
47
47
  const db_service_1 = require("./fraim/db-service");
48
+ const issues_1 = require("./fraim/issues");
49
+ const crypto_1 = require("crypto");
48
50
  const dotenv = __importStar(require("dotenv"));
49
51
  // Load environment variables
50
52
  dotenv.config();
@@ -62,17 +64,82 @@ console.log('๐Ÿš€ FRAIM MCP Server starting up...');
62
64
  console.log(`๐Ÿ“‚ Working Directory: ${process.cwd()}`);
63
65
  console.log(`๐Ÿ  Dirname: ${__dirname}`);
64
66
  try {
65
- const fs = require('fs');
66
- const path = require('path');
67
- console.log('files in current dir:', fs.readdirSync(process.cwd()).join(', '));
68
- const distPath = path.join(process.cwd(), 'dist');
69
- if (fs.existsSync(distPath)) {
70
- console.log('files in dist:', fs.readdirSync(distPath).join(', '));
67
+ console.log('files in current dir:', (0, fs_1.readdirSync)(process.cwd()).join(', '));
68
+ const distPath = (0, path_1.join)(process.cwd(), 'dist');
69
+ if ((0, fs_1.existsSync)(distPath)) {
70
+ console.log('files in dist:', (0, fs_1.readdirSync)(distPath).join(', '));
71
71
  }
72
72
  }
73
73
  catch (e) {
74
74
  console.warn('โš ๏ธ Could not log directory structure:', e.message);
75
75
  }
76
+ class SessionManager {
77
+ constructor(dbService) {
78
+ this.sessions = new Map();
79
+ this.dbService = dbService;
80
+ // Default 1m, configurable for testing
81
+ this.FLUSH_INTERVAL_MS = process.env.FRAIM_TELEMETRY_FLUSH_INTERVAL
82
+ ? parseInt(process.env.FRAIM_TELEMETRY_FLUSH_INTERVAL)
83
+ : 60 * 1000;
84
+ // Flush on shutdown
85
+ const cleanup = async () => {
86
+ console.log('๐Ÿ›‘ Flushing telemetry sessions before shutdown...');
87
+ // Race flush with a 2s timeout to guarantee exit
88
+ const timeout = new Promise(resolve => setTimeout(resolve, 2000));
89
+ await Promise.race([this.flushAll(), timeout]);
90
+ console.log('๐Ÿ›‘ Shutdown flush complete (or timed out). Exiting.');
91
+ process.exit(0);
92
+ };
93
+ process.on('SIGTERM', cleanup);
94
+ process.on('SIGINT', cleanup);
95
+ }
96
+ registerSession(apiKey, sessionId) {
97
+ const now = Date.now();
98
+ this.sessions.set(apiKey, { sessionId, lastWrite: now, lastActive: now });
99
+ }
100
+ async updateActivity(apiKey) {
101
+ let session = this.sessions.get(apiKey);
102
+ const now = Date.now();
103
+ if (!session) {
104
+ // Re-hydration: Check DB for an active session for this API Key
105
+ try {
106
+ const dbSession = await this.dbService.getActiveSessionByApiKey(apiKey);
107
+ if (dbSession) {
108
+ console.log(`๐Ÿ”„ Re-hydrating session for API Key: ${apiKey}`);
109
+ session = {
110
+ sessionId: dbSession.sessionId,
111
+ lastWrite: now,
112
+ lastActive: now
113
+ };
114
+ this.sessions.set(apiKey, session);
115
+ }
116
+ else {
117
+ return false;
118
+ }
119
+ }
120
+ catch (e) {
121
+ console.error('โš ๏ธ Failed to re-hydrate session from DB:', e);
122
+ return false;
123
+ }
124
+ }
125
+ session.lastActive = now;
126
+ // Write-behind: Only flush to DB if > Interval elapsed
127
+ if (now - session.lastWrite > this.FLUSH_INTERVAL_MS) {
128
+ // Async write
129
+ this.dbService.updateSessionActivity(session.sessionId, new Date(now)).catch(e => console.error('โš ๏ธ Failed to flush session activity:', e));
130
+ session.lastWrite = now;
131
+ }
132
+ return true;
133
+ }
134
+ async flushAll() {
135
+ const promises = [];
136
+ for (const [_, data] of this.sessions) {
137
+ // Flush the memory lastActive time
138
+ promises.push(this.dbService.updateSessionActivity(data.sessionId, new Date(data.lastActive)));
139
+ }
140
+ await Promise.allSettled(promises);
141
+ }
142
+ }
76
143
  class FraimMCPServer {
77
144
  constructor() {
78
145
  this.fileIndex = new Map();
@@ -101,6 +168,7 @@ class FraimMCPServer {
101
168
  }
102
169
  // Initialize database service
103
170
  this.dbService = new db_service_1.FraimDbService();
171
+ this.sessionManager = new SessionManager(this.dbService);
104
172
  // Load FRAIM configuration
105
173
  this.config = (0, config_loader_1.loadFraimConfig)();
106
174
  // Find registry directory (check dist first for production, then source)
@@ -121,6 +189,7 @@ class FraimMCPServer {
121
189
  });
122
190
  });
123
191
  this.app.use(this.authMiddleware.bind(this));
192
+ this.app.use(this.telemetryMiddleware.bind(this));
124
193
  this.setupRoutes();
125
194
  }
126
195
  /**
@@ -139,16 +208,19 @@ class FraimMCPServer {
139
208
  console.info(`${keyStr} ${method} ${path} ${status} ${duration}ms ${params}`);
140
209
  }
141
210
  // Log usage if authenticated
211
+ // DISABLE: Granular logging disabled in favor of Session Keep-Alive
212
+ /*
142
213
  if (apiKey) {
143
- this.dbService.logUsage({
144
- keyId: apiKey.key,
145
- userId: apiKey.userId,
146
- method,
147
- path,
148
- status,
149
- duration
150
- }).catch(e => console.error('โš ๏ธ Failed to log usage:', e.message));
214
+ this.dbService.logUsage({
215
+ keyId: apiKey.key,
216
+ userId: apiKey.userId,
217
+ method,
218
+ path,
219
+ status,
220
+ duration
221
+ }).catch(e => console.error('โš ๏ธ Failed to log usage:', (e as Error).message));
151
222
  }
223
+ */
152
224
  });
153
225
  next();
154
226
  }
@@ -246,6 +318,60 @@ class FraimMCPServer {
246
318
  }
247
319
  return next();
248
320
  }
321
+ /**
322
+ * Middleware to enforce Session Handshake and track activity
323
+ */
324
+ async telemetryMiddleware(req, res, next) {
325
+ // Skip for non-authenticated or exempt paths
326
+ const exemptPaths = ['/health', '/admin', '/mcp'];
327
+ if (exemptPaths.some(p => req.path.startsWith(p)) && req.method === 'GET') {
328
+ return next();
329
+ }
330
+ // 1. Standard MCP Lifecycle & Discovery methods (Initialize, List Tools/Resources/Prompts, etc.)
331
+ const exemptMethods = [
332
+ 'initialize',
333
+ 'notifications/initialized',
334
+ 'tools/list',
335
+ 'resources/list',
336
+ 'prompts/list',
337
+ 'logging/setLevel'
338
+ ];
339
+ if (req.body && exemptMethods.includes(req.body.method)) {
340
+ return next();
341
+ }
342
+ // 2. FRAIM Bootstrap/Discovery Tools that don't need an active session
343
+ if (req.body && req.body.method === 'tools/call') {
344
+ const toolName = req.body.params?.name;
345
+ const safeTools = [
346
+ 'fraim_connect',
347
+ 'get_fraim_init',
348
+ 'list_fraim_workflows',
349
+ 'fraim_get_local_config'
350
+ ];
351
+ if (safeTools.includes(toolName))
352
+ return next();
353
+ }
354
+ const apiKey = req.apiKeyData?.key;
355
+ if (!apiKey)
356
+ return next(); // Auth middleware handles 401 earlier
357
+ // Check Session
358
+ const isActive = await this.sessionManager.updateActivity(apiKey);
359
+ if (!isActive) {
360
+ console.log(`โ›” Telemetry blocked request: ${req.method} ${req.path} (No Session for ${apiKey})`);
361
+ // Enforce Handshake
362
+ res.status(400).json({
363
+ jsonrpc: '2.0',
364
+ error: {
365
+ code: -32600,
366
+ message: "โ›” Session Not Started. Please call the 'fraim_connect' tool first to register your environment and begin the session."
367
+ },
368
+ id: req.body?.id || null
369
+ });
370
+ return;
371
+ }
372
+ console.log(`โœ… Telemetry active for ${apiKey}`);
373
+ return next();
374
+ }
249
375
  /**
250
376
  * Get the Express application for mounting in another server
251
377
  * This allows FRAIM routes to be accessible on the main server port (e.g., on Azure)
@@ -584,8 +710,18 @@ class FraimMCPServer {
584
710
  'Connection': 'keep-alive',
585
711
  'Access-Control-Allow-Origin': '*'
586
712
  });
587
- res.write(': FRAIM MCP Server\n\n');
588
- res.write('data: connected\n\n');
713
+ // Handle client disconnects to prevent server crash
714
+ res.on('error', (err) => {
715
+ console.error('โš ๏ธ SSE stream error:', err);
716
+ });
717
+ res.write('event: endpoint\n');
718
+ // Use absolute URL to be safe
719
+ const protocol = req.protocol;
720
+ const host = req.get('host');
721
+ res.write(`data: ${protocol}://${host}/mcp\n\n`);
722
+ // Remove confusing "connected" message that might interfere with protocol
723
+ // res.write(': FRAIM MCP Server\n\n');
724
+ // res.write('data: connected\n\n');
589
725
  return;
590
726
  }
591
727
  // Regular GET request
@@ -631,8 +767,23 @@ class FraimMCPServer {
631
767
  else if (method === 'tools/list') {
632
768
  result = this.handleListTools();
633
769
  }
770
+ else if (method === 'resources/list') {
771
+ result = this.handleListResources();
772
+ }
773
+ else if (method === 'prompts/list') {
774
+ result = this.handleListPrompts();
775
+ }
634
776
  else if (method === 'tools/call') {
635
- result = await this.handleToolCall(params);
777
+ // Pass API Key context to handleToolCall
778
+ const context = {
779
+ apiKey: req.apiKeyData?.key,
780
+ userId: req.apiKeyData?.userId
781
+ };
782
+ result = await this.handleToolCall(params, context);
783
+ }
784
+ else if (method === 'notifications/initialized') {
785
+ // Client initialized notification - no response needed but must not error
786
+ return res.json({ jsonrpc: '2.0', id: id || null, result: true });
636
787
  }
637
788
  else {
638
789
  throw new Error(`Unknown method: ${method}`);
@@ -699,6 +850,28 @@ Call this before fetching any workflow or file to understand the system.`,
699
850
  required: []
700
851
  }
701
852
  },
853
+ {
854
+ name: 'fraim_connect',
855
+ description: `Handshake to start a FRAIM session. MUST be called at the start of every session.
856
+ Registers your environment (machine/repo) for telemetry and unlocks other tools.`,
857
+ inputSchema: {
858
+ type: 'object',
859
+ properties: {
860
+ machine: {
861
+ type: 'object',
862
+ description: 'Machine specs (hostname, platform, memory, cpus)',
863
+ additionalProperties: true
864
+ },
865
+ repo: {
866
+ type: 'object',
867
+ description: 'Repository context (owner, name, url)',
868
+ required: ['url'],
869
+ additionalProperties: true
870
+ }
871
+ },
872
+ required: ['machine', 'repo']
873
+ }
874
+ },
702
875
  {
703
876
  name: 'get_fraim_file',
704
877
  description: `Get a specific file from FRAIM by path.
@@ -776,11 +949,51 @@ Use this to discover which workflow to call with get_fraim_workflow.`,
776
949
  properties: {},
777
950
  required: []
778
951
  }
952
+ },
953
+ {
954
+ name: 'file_issue',
955
+ description: `Create a GitHub issue in the FRAIM repository.
956
+
957
+ Use this tool when you need to report a bug, request a feature, or track a task.
958
+ Supports dry-run mode to preview the operation.`,
959
+ inputSchema: {
960
+ type: 'object',
961
+ properties: {
962
+ title: {
963
+ type: 'string',
964
+ description: 'Title of the issue'
965
+ },
966
+ body: {
967
+ type: 'string',
968
+ description: 'Body/Content of the issue'
969
+ },
970
+ labels: {
971
+ type: 'array',
972
+ items: { type: 'string' },
973
+ description: 'List of labels to apply'
974
+ },
975
+ dryRun: {
976
+ type: 'boolean',
977
+ description: 'If true, simulates the creation without actually notifying GitHub'
978
+ }
979
+ },
980
+ required: ['title', 'body']
981
+ }
779
982
  }
780
983
  ]
781
984
  };
782
985
  }
783
- async handleToolCall(params) {
986
+ handleListResources() {
987
+ return {
988
+ resources: []
989
+ };
990
+ }
991
+ handleListPrompts() {
992
+ return {
993
+ prompts: []
994
+ };
995
+ }
996
+ async handleToolCall(params, context = {}) {
784
997
  const { name: toolName, arguments: toolArgs } = params;
785
998
  switch (toolName) {
786
999
  case 'get_fraim_init':
@@ -793,6 +1006,16 @@ Use this to discover which workflow to call with get_fraim_workflow.`,
793
1006
  return await this.handleListWorkflows();
794
1007
  case 'fraim_get_local_config':
795
1008
  return await this.handleGetLocalConfig();
1009
+ case 'file_issue':
1010
+ const result = await (0, issues_1.fileFraimIssue)(toolArgs);
1011
+ return {
1012
+ content: [{
1013
+ type: 'text',
1014
+ text: JSON.stringify(result, null, 2)
1015
+ }]
1016
+ };
1017
+ case 'fraim_connect':
1018
+ return await this.handleFraimConnect(toolArgs, context.apiKey, context.userId);
796
1019
  default:
797
1020
  throw new Error(`Unknown tool: ${toolName} `);
798
1021
  }
@@ -1196,6 +1419,37 @@ If \`.fraim/config.json\` doesn't exist:
1196
1419
  ]
1197
1420
  };
1198
1421
  }
1422
+ async handleFraimConnect(args, apiKey, userId) {
1423
+ if (!apiKey) {
1424
+ throw new Error('No API Key found in context for fraim_connect');
1425
+ }
1426
+ // 1. Generate Session ID
1427
+ const sessionId = (0, crypto_1.randomUUID)();
1428
+ // userId passed from context
1429
+ const finalUserId = userId || 'unknown';
1430
+ // 2. Create Session in DB
1431
+ const session = {
1432
+ sessionId,
1433
+ userId: finalUserId,
1434
+ machine: args.machine || {},
1435
+ repo: args.repo || {},
1436
+ startTime: new Date(),
1437
+ lastActive: new Date()
1438
+ };
1439
+ const keyData = await this.dbService.verifyApiKey(apiKey);
1440
+ if (keyData)
1441
+ session.userId = keyData.userId;
1442
+ await this.dbService.createSession(session);
1443
+ // 3. Register in SessionManager
1444
+ this.sessionManager.registerSession(apiKey, sessionId);
1445
+ return {
1446
+ content: [{
1447
+ type: 'text',
1448
+ text: `โœ… Connected! Session ID: ${sessionId}. Telemetry active.`
1449
+ }],
1450
+ sessionId: sessionId
1451
+ };
1452
+ }
1199
1453
  async start(port = 3002) {
1200
1454
  try {
1201
1455
  // Connect to database before starting server
@@ -40,7 +40,7 @@ function determineDatabaseName() {
40
40
  catch (e) {
41
41
  // Silently fail
42
42
  }
43
- return process.env.MONGODB_DB_NAME || 'fraim_dev';
43
+ return process.env.MONGODB_DB_NAME || (process.env.NODE_ENV === 'production' ? 'fraim_prod' : 'fraim_dev');
44
44
  }
45
45
  /**
46
46
  * Gets the current git branch name
@@ -0,0 +1,32 @@
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.getFraimVersion = getFraimVersion;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ function getFraimVersion() {
10
+ // Try reliable paths to find package.json relative to this file
11
+ // locally: src/utils/version-utils.ts -> package.json is ../../package.json
12
+ // dist: dist/src/utils/version-utils.js -> package.json is ../../../package.json
13
+ const possiblePaths = [
14
+ path_1.default.join(__dirname, '../../package.json'), // Local dev (src)
15
+ path_1.default.join(__dirname, '../../../package.json'), // Dist (dist/src)
16
+ path_1.default.join(process.cwd(), 'package.json') // Fallback to CWD (unlikely to be correct for global install but safe fallback)
17
+ ];
18
+ for (const pkgPath of possiblePaths) {
19
+ if (fs_1.default.existsSync(pkgPath)) {
20
+ try {
21
+ const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf-8'));
22
+ if (pkg.name === 'fraim-framework') {
23
+ return pkg.version;
24
+ }
25
+ }
26
+ catch (e) {
27
+ // Ignore parsing errors
28
+ }
29
+ }
30
+ }
31
+ return '1.0.0'; // Fallback
32
+ }
@@ -0,0 +1,79 @@
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
+ const node_child_process_1 = require("node:child_process");
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const db_service_js_1 = require("../src/fraim/db-service.js");
9
+ const test_utils_1 = require("./test-utils");
10
+ const tree_kill_1 = __importDefault(require("tree-kill"));
11
+ const path_1 = __importDefault(require("path"));
12
+ async function debugListTools() {
13
+ console.log(' ๐Ÿ” Debugging Available Tools...');
14
+ let fraimProcess;
15
+ let dbService;
16
+ const PORT = 10003;
17
+ const TEST_API_KEY = 'debug-tools-key';
18
+ const BASE_URL = `http://localhost:${PORT}`;
19
+ try {
20
+ // 1. Seed API key
21
+ dbService = new db_service_js_1.FraimDbService();
22
+ await dbService.connect();
23
+ const db = dbService.db;
24
+ await db.collection('fraim_api_keys').updateOne({ key: TEST_API_KEY }, { $set: { userId: 'debug@test.com', orgId: 'debug-org', isActive: true, createdAt: new Date() } }, { upsert: true });
25
+ // 2. Start server
26
+ const npxCommand = process.platform === 'win32' ? 'npx.cmd' : 'npx';
27
+ const serverScript = path_1.default.resolve(__dirname, '../src/fraim-mcp-server.ts');
28
+ fraimProcess = (0, node_child_process_1.spawn)(npxCommand, ['tsx', `"${serverScript}"`], {
29
+ env: { ...process.env, FRAIM_MCP_PORT: PORT.toString(), FRAIM_SKIP_INDEX_ON_START: 'true' },
30
+ shell: true
31
+ });
32
+ // 3. Wait for start
33
+ let started = false;
34
+ for (let i = 0; i < 15; i++) {
35
+ try {
36
+ await axios_1.default.get(`${BASE_URL}/health`, { timeout: 1000 });
37
+ started = true;
38
+ break;
39
+ }
40
+ catch (e) {
41
+ await new Promise(r => setTimeout(r, 1000));
42
+ }
43
+ }
44
+ if (!started)
45
+ return false;
46
+ // 4. Call tools/list
47
+ const response = await axios_1.default.post(`${BASE_URL}/mcp`, {
48
+ jsonrpc: '2.0',
49
+ id: 1,
50
+ method: 'tools/list',
51
+ params: {}
52
+ }, { headers: { 'x-api-key': TEST_API_KEY } });
53
+ console.log(' ๐Ÿ› ๏ธ Tools found:');
54
+ response.data.result.tools.forEach((t) => {
55
+ console.log(` - ${t.name}: ${t.description.split('\n')[0]}`);
56
+ });
57
+ const hasFraimConnect = response.data.result.tools.some((t) => t.name === 'fraim_connect');
58
+ console.log(` โ“ Has fraim_connect: ${hasFraimConnect}`);
59
+ return true;
60
+ }
61
+ catch (e) {
62
+ console.error(e);
63
+ return false;
64
+ }
65
+ finally {
66
+ if (dbService)
67
+ await dbService.close().catch(() => { });
68
+ if (fraimProcess && fraimProcess.pid)
69
+ (0, tree_kill_1.default)(fraimProcess.pid);
70
+ }
71
+ }
72
+ async function runTest(testCase) {
73
+ return await testCase.testFunction();
74
+ }
75
+ (0, test_utils_1.runTests)([{
76
+ name: 'Debug Tools List',
77
+ testFunction: debugListTools,
78
+ tags: ['debug']
79
+ }], runTest, 'Debug Tools');
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ /**
3
+ * ESM Compatibility Helper
4
+ * Provides __dirname and __filename for ESM modules
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.__dirname = exports.__filename = void 0;
8
+ const url_1 = require("url");
9
+ const path_1 = require("path");
10
+ exports.__filename = (0, url_1.fileURLToPath)(import.meta.url);
11
+ exports.__dirname = (0, path_1.dirname)(exports.__filename);
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ /**
3
+ * Test to reproduce the ERR_REQUIRE_ESM issue with chalk
4
+ *
5
+ * This test simulates what happens when npm resolves chalk to v5 (ESM-only)
6
+ * while the code tries to use require() (CommonJS)
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const node_child_process_1 = require("node:child_process");
13
+ const test_utils_1 = require("./test-utils");
14
+ const fs_1 = __importDefault(require("fs"));
15
+ const path_1 = __importDefault(require("path"));
16
+ const os_1 = __importDefault(require("os"));
17
+ async function testChalkESMIssue() {
18
+ console.log(' ๐Ÿงช Testing Chalk ESM Issue Reproduction...');
19
+ // Create a temp directory for the test
20
+ const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'chalk-esm-test-'));
21
+ console.log(` ๐Ÿ“‚ Created temp dir: ${tempDir}`);
22
+ try {
23
+ // 1. Create a minimal package.json that forces chalk v5
24
+ const packageJson = {
25
+ name: 'chalk-esm-test',
26
+ version: '1.0.0',
27
+ type: 'commonjs', // Using CommonJS
28
+ dependencies: {
29
+ 'chalk': '^5.0.0' // Force chalk v5 (ESM-only)
30
+ }
31
+ };
32
+ fs_1.default.writeFileSync(path_1.default.join(tempDir, 'package.json'), JSON.stringify(packageJson, null, 2));
33
+ // 2. Create a test script that uses require('chalk')
34
+ const testScript = `
35
+ const chalk = require('chalk');
36
+ console.log(chalk.blue('This should fail with ERR_REQUIRE_ESM'));
37
+ `;
38
+ fs_1.default.writeFileSync(path_1.default.join(tempDir, 'test.js'), testScript);
39
+ // 3. Install dependencies
40
+ console.log(' ๐Ÿ“ฆ Installing chalk v5...');
41
+ const installResult = await new Promise((resolve) => {
42
+ const proc = (0, node_child_process_1.spawn)('npm', ['install', '--silent'], {
43
+ cwd: tempDir,
44
+ stdio: 'pipe',
45
+ shell: true
46
+ });
47
+ proc.on('close', (code) => {
48
+ resolve({ code });
49
+ });
50
+ });
51
+ if (installResult.code !== 0) {
52
+ console.log(' โš ๏ธ npm install failed (expected in some environments)');
53
+ return true; // Skip test if npm install fails
54
+ }
55
+ // 4. Try to run the script - should fail with ERR_REQUIRE_ESM
56
+ console.log(' ๐Ÿ”ฅ Attempting to require() chalk v5 (should fail)...');
57
+ const runResult = await new Promise((resolve) => {
58
+ const proc = (0, node_child_process_1.spawn)('node', ['test.js'], {
59
+ cwd: tempDir,
60
+ stdio: 'pipe',
61
+ shell: true
62
+ });
63
+ let stderr = '';
64
+ proc.stderr?.on('data', (data) => {
65
+ stderr += data.toString();
66
+ });
67
+ proc.on('close', (code) => {
68
+ resolve({ code, stderr });
69
+ });
70
+ });
71
+ // 5. Verify we got the ERR_REQUIRE_ESM error
72
+ console.log(' ๐Ÿ“‹ Checking for ERR_REQUIRE_ESM error...');
73
+ if (runResult.code === 0) {
74
+ console.log(' โŒ Script succeeded when it should have failed!');
75
+ return false;
76
+ }
77
+ if (!runResult.stderr.includes('ERR_REQUIRE_ESM')) {
78
+ console.log(' โŒ Got wrong error. Expected ERR_REQUIRE_ESM');
79
+ console.log(` Stderr: ${runResult.stderr.substring(0, 200)}`);
80
+ return false;
81
+ }
82
+ console.log(' โœ… Successfully reproduced ERR_REQUIRE_ESM error!');
83
+ console.log(' โœ… This confirms chalk v5 cannot be used with require()');
84
+ // 6. Now test with chalk v4 (should work)
85
+ console.log(' ๐Ÿ”„ Testing with chalk v4 (should work)...');
86
+ const packageJsonV4 = {
87
+ name: 'chalk-esm-test',
88
+ version: '1.0.0',
89
+ type: 'commonjs',
90
+ dependencies: {
91
+ 'chalk': '4.1.2' // Exact version 4.1.2
92
+ }
93
+ };
94
+ fs_1.default.writeFileSync(path_1.default.join(tempDir, 'package.json'), JSON.stringify(packageJsonV4, null, 2));
95
+ // Remove node_modules and reinstall
96
+ fs_1.default.rmSync(path_1.default.join(tempDir, 'node_modules'), { recursive: true, force: true });
97
+ const installV4Result = await new Promise((resolve) => {
98
+ const proc = (0, node_child_process_1.spawn)('npm', ['install', '--silent'], {
99
+ cwd: tempDir,
100
+ stdio: 'pipe',
101
+ shell: true
102
+ });
103
+ proc.on('close', (code) => {
104
+ resolve({ code });
105
+ });
106
+ });
107
+ if (installV4Result.code !== 0) {
108
+ console.log(' โš ๏ธ npm install v4 failed');
109
+ return true; // Skip this part
110
+ }
111
+ const runV4Result = await new Promise((resolve) => {
112
+ const proc = (0, node_child_process_1.spawn)('node', ['test.js'], {
113
+ cwd: tempDir,
114
+ stdio: 'pipe',
115
+ shell: true
116
+ });
117
+ let stdout = '';
118
+ proc.stdout?.on('data', (data) => {
119
+ stdout += data.toString();
120
+ });
121
+ proc.on('close', (code) => {
122
+ resolve({ code, stdout });
123
+ });
124
+ });
125
+ if (runV4Result.code !== 0) {
126
+ console.log(' โŒ Chalk v4 failed when it should have worked!');
127
+ return false;
128
+ }
129
+ console.log(' โœ… Chalk v4 works correctly with require()');
130
+ console.log(' โœ… Test confirms: pinning to 4.1.2 prevents the issue');
131
+ return true;
132
+ }
133
+ catch (error) {
134
+ console.error(' โŒ Test failed with error:', error);
135
+ return false;
136
+ }
137
+ finally {
138
+ // Cleanup
139
+ try {
140
+ fs_1.default.rmSync(tempDir, { recursive: true, force: true });
141
+ console.log(' ๐Ÿงน Cleaned up temp directory');
142
+ }
143
+ catch (e) {
144
+ console.log(' โš ๏ธ Could not clean up temp directory');
145
+ }
146
+ }
147
+ }
148
+ async function runChalkTest(testCase) {
149
+ return await testCase.testFunction();
150
+ }
151
+ const testCases = [
152
+ {
153
+ name: 'Chalk ESM Issue Reproduction',
154
+ description: 'Reproduces the ERR_REQUIRE_ESM error when chalk v5 is used with require()',
155
+ testFunction: testChalkESMIssue,
156
+ tags: ['chalk', 'esm', 'reproduction']
157
+ }
158
+ ];
159
+ (0, test_utils_1.runTests)(testCases, runChalkTest, 'Chalk ESM Issue Test');