fraim-framework 2.0.37 → 2.0.41

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 (38) hide show
  1. package/README.md +8 -0
  2. package/dist/src/ai-manager/ai-manager.js +162 -0
  3. package/dist/src/cli/commands/init-project.js +74 -0
  4. package/dist/src/cli/commands/setup.js +176 -0
  5. package/dist/src/cli/commands/test-mcp.js +135 -0
  6. package/dist/src/cli/fraim.js +6 -0
  7. package/dist/src/cli/setup/auto-mcp-setup.js +367 -0
  8. package/dist/src/cli/setup/ide-detector.js +165 -0
  9. package/dist/src/cli/setup/mcp-config-generator.js +144 -0
  10. package/dist/src/cli/setup/token-validator.js +49 -0
  11. package/dist/src/fraim-mcp-server.js +198 -0
  12. package/dist/tests/debug-tools.js +2 -2
  13. package/dist/tests/shared-server-utils.js +57 -0
  14. package/dist/tests/test-ai-manager.js +113 -0
  15. package/dist/tests/test-client-scripts-validation.js +27 -5
  16. package/dist/tests/test-complete-setup-flow.js +110 -0
  17. package/dist/tests/test-ide-detector.js +46 -0
  18. package/dist/tests/test-improved-setup.js +121 -0
  19. package/dist/tests/test-mcp-config-generator.js +99 -0
  20. package/dist/tests/test-mcp-connection.js +58 -117
  21. package/dist/tests/test-mcp-issue-integration.js +2 -2
  22. package/dist/tests/test-mcp-lifecycle-methods.js +34 -100
  23. package/dist/tests/test-mcp-shared-server.js +308 -0
  24. package/dist/tests/test-package-size.js +21 -8
  25. package/dist/tests/test-script-location-independence.js +39 -62
  26. package/dist/tests/test-server-utils.js +32 -0
  27. package/dist/tests/test-session-rehydration.js +2 -2
  28. package/dist/tests/test-setup-integration.js +98 -0
  29. package/dist/tests/test-standalone.js +2 -2
  30. package/dist/tests/test-stub-registry.js +23 -7
  31. package/dist/tests/test-telemetry.js +2 -2
  32. package/dist/tests/test-token-validator.js +30 -0
  33. package/dist/tests/test-user-journey.js +2 -1
  34. package/package.json +3 -2
  35. package/registry/scripts/code-quality-check.sh +566 -559
  36. package/registry/scripts/prep-issue.sh +7 -0
  37. package/registry/scripts/verify-pr-comments.sh +74 -70
  38. /package/registry/stubs/workflows/{convert-to-pdf.md → marketing/convert-to-pdf.md} +0 -0
@@ -42,13 +42,15 @@ const path_1 = __importDefault(require("path"));
42
42
  const os_1 = __importDefault(require("os"));
43
43
  async function testScriptLocationIndependence() {
44
44
  console.log(' 🚀 Testing Script Location Independence...');
45
- const tempProjectDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'fraim-location-test-'));
46
- const userFraimDir = path_1.default.join(os_1.default.homedir(), '.fraim-location-test');
45
+ // Use timestamp to ensure unique directories even in concurrent runs
46
+ const timestamp = Date.now();
47
+ const tempProjectDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), `fraim-location-test-${timestamp}-`));
48
+ const userFraimDir = path_1.default.join(os_1.default.homedir(), `.fraim-location-test-${timestamp}`);
47
49
  const userScriptsDir = path_1.default.join(userFraimDir, 'scripts');
48
50
  console.log(` 📂 Created temp project dir: ${tempProjectDir}`);
49
51
  console.log(` 🏠 Using test user dir: ${userFraimDir}`);
50
52
  try {
51
- // Clean up any existing test user directory
53
+ // Clean up any existing test user directory (with timestamp, should be unique)
52
54
  if (fs_1.default.existsSync(userFraimDir)) {
53
55
  fs_1.default.rmSync(userFraimDir, { recursive: true, force: true });
54
56
  }
@@ -75,11 +77,11 @@ async function testScriptLocationIndependence() {
75
77
  const projectFraimDir = path_1.default.join(tempProjectDir, '.fraim');
76
78
  fs_1.default.mkdirSync(projectFraimDir, { recursive: true });
77
79
  const testConfig = {
78
- project: { name: 'location-test' },
80
+ project: { name: `location-test-${timestamp}` },
79
81
  git: {
80
82
  repoOwner: 'test',
81
- repoName: 'location-test',
82
- url: 'https://github.com/test/location-test.git'
83
+ repoName: `location-test-${timestamp}`,
84
+ url: `https://github.com/test/location-test-${timestamp}.git`
83
85
  }
84
86
  };
85
87
  fs_1.default.writeFileSync(path_1.default.join(projectFraimDir, 'config.json'), JSON.stringify(testConfig, null, 2));
@@ -111,7 +113,7 @@ async function testScriptLocationIndependence() {
111
113
  const output = execSync(bashCommand, {
112
114
  cwd: tempProjectDir,
113
115
  encoding: 'utf-8',
114
- timeout: 10000
116
+ timeout: 15000 // Increased timeout for concurrent runs
115
117
  });
116
118
  }
117
119
  else {
@@ -119,13 +121,18 @@ async function testScriptLocationIndependence() {
119
121
  const output = execSync(`"${prepIssueScript}" --help`, {
120
122
  cwd: tempProjectDir,
121
123
  encoding: 'utf-8',
122
- timeout: 10000
124
+ timeout: 15000 // Increased timeout for concurrent runs
123
125
  });
124
126
  }
125
127
  // If we get here, the script at least started successfully
126
128
  console.log(' ✅ prep-issue.sh executed successfully from user directory');
127
129
  }
128
130
  catch (error) {
131
+ // Check for timeout specifically
132
+ if (error.message.includes('ETIMEDOUT') || error.signal === 'SIGTERM') {
133
+ console.log(' ❌ prep-issue.sh failed: spawnSync', error.syscall, 'ETIMEDOUT');
134
+ return false;
135
+ }
129
136
  // The script may fail due to missing repo, but as long as it started, that's OK
130
137
  if (error.message.includes('Repository not found') ||
131
138
  error.message.includes('fatal: repository') ||
@@ -142,54 +149,7 @@ async function testScriptLocationIndependence() {
142
149
  else {
143
150
  console.log(' ⚠️ prep-issue.sh not found, skipping test');
144
151
  }
145
- // Test 2: Try to execute code-quality-check.sh (this will likely fail due to dependencies)
146
- console.log(' Testing code-quality-check.sh execution from user directory...');
147
- const qualityCheckScript = path_1.default.join(userScriptsDir, 'code-quality-check.sh');
148
- if (fs_1.default.existsSync(qualityCheckScript)) {
149
- try {
150
- const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
151
- if (process.platform === 'win32') {
152
- const gitBashPaths = [
153
- 'C:\\Program Files\\Git\\bin\\bash.exe',
154
- 'C:\\Program Files (x86)\\Git\\bin\\bash.exe'
155
- ];
156
- let bashPath = null;
157
- for (const path of gitBashPaths) {
158
- if (fs_1.default.existsSync(path)) {
159
- bashPath = path;
160
- break;
161
- }
162
- }
163
- if (!bashPath) {
164
- console.log(' ⚠️ Git Bash not found on Windows, skipping quality check test');
165
- }
166
- else {
167
- const bashCommand = `"${bashPath}" "${qualityCheckScript}" --help`;
168
- const output = execSync(bashCommand, {
169
- cwd: tempProjectDir,
170
- encoding: 'utf-8',
171
- timeout: 10000
172
- });
173
- console.log(' ✅ code-quality-check.sh executed successfully from user directory');
174
- }
175
- }
176
- else {
177
- const output = execSync(`"${qualityCheckScript}" --help`, {
178
- cwd: tempProjectDir,
179
- encoding: 'utf-8',
180
- timeout: 10000
181
- });
182
- console.log(' ✅ code-quality-check.sh executed successfully from user directory');
183
- }
184
- }
185
- catch (error) {
186
- console.log(' ❌ code-quality-check.sh failed (expected due to dependencies):', error.message);
187
- // This is expected to fail - we'll document this as a known issue
188
- }
189
- }
190
- else {
191
- console.log(' ⚠️ code-quality-check.sh not found, skipping test');
192
- }
152
+ // Skip the second test to reduce interference - the first test is sufficient
193
153
  console.log(' ✅ Script location independence test completed');
194
154
  return true;
195
155
  }
@@ -198,13 +158,30 @@ async function testScriptLocationIndependence() {
198
158
  return false;
199
159
  }
200
160
  finally {
201
- try {
202
- fs_1.default.rmSync(tempProjectDir, { recursive: true, force: true });
203
- if (fs_1.default.existsSync(userFraimDir)) {
204
- fs_1.default.rmSync(userFraimDir, { recursive: true, force: true });
161
+ // Enhanced cleanup with retry logic
162
+ const cleanup = async (retries = 3) => {
163
+ for (let i = 0; i < retries; i++) {
164
+ try {
165
+ if (fs_1.default.existsSync(tempProjectDir)) {
166
+ fs_1.default.rmSync(tempProjectDir, { recursive: true, force: true });
167
+ }
168
+ if (fs_1.default.existsSync(userFraimDir)) {
169
+ fs_1.default.rmSync(userFraimDir, { recursive: true, force: true });
170
+ }
171
+ break; // Success
172
+ }
173
+ catch (e) {
174
+ if (i === retries - 1) {
175
+ console.warn(' ⚠️ Cleanup failed after retries:', e);
176
+ }
177
+ else {
178
+ // Wait a bit before retry
179
+ await new Promise(resolve => setTimeout(resolve, 100));
180
+ }
181
+ }
205
182
  }
206
- }
207
- catch (e) { }
183
+ };
184
+ await cleanup();
208
185
  }
209
186
  }
210
187
  async function runScriptLocationTest(testCase) {
@@ -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.getServerScriptPath = getServerScriptPath;
7
+ const path_1 = __importDefault(require("path"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ /**
10
+ * Utility function to get the correct server script path
11
+ * Handles different execution contexts (running from project root, tests directory, etc.)
12
+ */
13
+ function getServerScriptPath() {
14
+ // Try to find the server script from various possible locations
15
+ const possiblePaths = [
16
+ path_1.default.resolve(__dirname, '../dist/src/fraim-mcp-server.js'),
17
+ path_1.default.resolve(process.cwd(), 'dist/src/fraim-mcp-server.js'),
18
+ path_1.default.resolve(__dirname, '../../dist/src/fraim-mcp-server.js'),
19
+ path_1.default.resolve(__dirname, '../../../dist/src/fraim-mcp-server.js')
20
+ ];
21
+ for (const scriptPath of possiblePaths) {
22
+ if (fs_1.default.existsSync(scriptPath)) {
23
+ console.log(` 📍 Found server script at: ${scriptPath}`);
24
+ return scriptPath;
25
+ }
26
+ }
27
+ // If none found, log all attempted paths and use the first one
28
+ console.warn(' ⚠️ Server script not found at any of these paths:');
29
+ possiblePaths.forEach(p => console.warn(` ${p}`));
30
+ console.warn(' Using first path (may fail)...');
31
+ return possiblePaths[0];
32
+ }
@@ -9,7 +9,7 @@ const db_service_js_1 = require("../src/fraim/db-service.js");
9
9
  const test_utils_1 = require("./test-utils");
10
10
  const node_assert_1 = __importDefault(require("node:assert"));
11
11
  const tree_kill_1 = __importDefault(require("tree-kill"));
12
- const path_1 = __importDefault(require("path"));
12
+ const test_server_utils_1 = require("./test-server-utils");
13
13
  async function testRehydrationAndExemptions() {
14
14
  console.log(' 🚀 Testing Session Re-hydration and Initialization Exemptions...');
15
15
  let fraimProcess;
@@ -21,7 +21,7 @@ async function testRehydrationAndExemptions() {
21
21
  const startServer = async () => {
22
22
  console.log(` Starting server on port ${PORT}...`);
23
23
  const npxCommand = process.platform === 'win32' ? 'npx.cmd' : 'npx';
24
- const serverScript = path_1.default.resolve(__dirname, '../dist/src/fraim-mcp-server.js');
24
+ const serverScript = (0, test_server_utils_1.getServerScriptPath)();
25
25
  const proc = (0, node_child_process_1.spawn)(npxCommand, ['node', `"${serverScript}"`], {
26
26
  env: {
27
27
  ...process.env,
@@ -0,0 +1,98 @@
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_test_1 = require("node:test");
7
+ const node_assert_1 = __importDefault(require("node:assert"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const os_1 = __importDefault(require("os"));
11
+ const ide_detector_1 = require("../src/cli/setup/ide-detector");
12
+ const mcp_config_generator_1 = require("../src/cli/setup/mcp-config-generator");
13
+ const token_validator_1 = require("../src/cli/setup/token-validator");
14
+ (0, node_test_1.test)('Setup integration - token validation flow', async () => {
15
+ // Test valid tokens
16
+ const validFraimKey = 'fraim_test123456789012345';
17
+ const validGithubToken = 'ghp_test123456789012345';
18
+ (0, node_assert_1.default)((0, token_validator_1.isValidTokenFormat)(validFraimKey, 'fraim'), 'Valid FRAIM key format should pass');
19
+ (0, node_assert_1.default)((0, token_validator_1.isValidTokenFormat)(validGithubToken, 'github'), 'Valid GitHub token format should pass');
20
+ (0, node_assert_1.default)(await (0, token_validator_1.validateFraimKey)(validFraimKey), 'Valid FRAIM key should validate');
21
+ // Test invalid tokens
22
+ (0, node_assert_1.default)(!(0, token_validator_1.isValidTokenFormat)('invalid_key', 'fraim'), 'Invalid FRAIM key should fail');
23
+ (0, node_assert_1.default)(!(0, token_validator_1.isValidTokenFormat)('invalid_token', 'github'), 'Invalid GitHub token should fail');
24
+ (0, node_assert_1.default)(!await (0, token_validator_1.validateFraimKey)('invalid_key'), 'Invalid FRAIM key should not validate');
25
+ });
26
+ (0, node_test_1.test)('Setup integration - MCP config generation for all IDE types', () => {
27
+ const testFraimKey = 'fraim_test123456789012345';
28
+ const testGithubToken = 'ghp_test123456789012345';
29
+ // Test all config types
30
+ const standardConfig = (0, mcp_config_generator_1.generateMCPConfig)('standard', testFraimKey, testGithubToken);
31
+ const kiroConfig = (0, mcp_config_generator_1.generateMCPConfig)('kiro', testFraimKey, testGithubToken);
32
+ const codexConfig = (0, mcp_config_generator_1.generateMCPConfig)('codex', testFraimKey, testGithubToken);
33
+ const windsurfConfig = (0, mcp_config_generator_1.generateMCPConfig)('windsurf', testFraimKey, testGithubToken);
34
+ // Verify standard config structure
35
+ (0, node_assert_1.default)(standardConfig.mcpServers, 'Standard config should have mcpServers');
36
+ (0, node_assert_1.default)(standardConfig.mcpServers.fraim.serverUrl, 'Standard should use serverUrl');
37
+ (0, node_assert_1.default)(standardConfig.mcpServers.fraim.headers['x-api-key'] === testFraimKey, 'Should contain FRAIM key');
38
+ // Verify kiro config structure
39
+ (0, node_assert_1.default)(kiroConfig.mcpServers.fraim.url, 'Kiro should use url');
40
+ (0, node_assert_1.default)(!kiroConfig.mcpServers.fraim.serverUrl, 'Kiro should not have serverUrl');
41
+ // Verify codex config is TOML string
42
+ (0, node_assert_1.default)(typeof codexConfig === 'string', 'Codex should return TOML string');
43
+ (0, node_assert_1.default)(codexConfig.includes('[mcp_servers.fraim]'), 'Should contain FRAIM section');
44
+ (0, node_assert_1.default)(codexConfig.includes(testFraimKey), 'Should contain FRAIM key');
45
+ // Verify windsurf config structure
46
+ (0, node_assert_1.default)(windsurfConfig.mcpServers.fraim.command, 'Windsurf should use command');
47
+ (0, node_assert_1.default)(windsurfConfig.mcpServers.fraim.env.FRAIM_API_KEY === testFraimKey, 'Should contain FRAIM key in env');
48
+ });
49
+ (0, node_test_1.test)('Setup integration - IDE detection works', () => {
50
+ const detectedIDEs = (0, ide_detector_1.detectInstalledIDEs)();
51
+ // Should return an array (might be empty in test environment)
52
+ (0, node_assert_1.default)(Array.isArray(detectedIDEs), 'Should return an array');
53
+ // If any IDEs are detected, they should have the correct structure
54
+ detectedIDEs.forEach(ide => {
55
+ (0, node_assert_1.default)(typeof ide.name === 'string', 'IDE should have name');
56
+ (0, node_assert_1.default)(typeof ide.configPath === 'string', 'IDE should have configPath');
57
+ (0, node_assert_1.default)(['json', 'toml'].includes(ide.configFormat), 'IDE should have valid configFormat');
58
+ (0, node_assert_1.default)(['standard', 'kiro', 'codex', 'windsurf'].includes(ide.configType), 'IDE should have valid configType');
59
+ (0, node_assert_1.default)(typeof ide.detectMethod === 'function', 'IDE should have detectMethod');
60
+ });
61
+ });
62
+ (0, node_test_1.test)('Setup integration - global config directory creation', () => {
63
+ const testConfigDir = path_1.default.join(os_1.default.tmpdir(), 'fraim-test-' + Date.now());
64
+ // Simulate creating global config directory
65
+ if (!fs_1.default.existsSync(testConfigDir)) {
66
+ fs_1.default.mkdirSync(testConfigDir, { recursive: true });
67
+ }
68
+ (0, node_assert_1.default)(fs_1.default.existsSync(testConfigDir), 'Config directory should be created');
69
+ // Test config file creation
70
+ const testConfig = {
71
+ version: '2.0.37',
72
+ apiKey: 'fraim_test123456789012345',
73
+ configuredAt: new Date().toISOString()
74
+ };
75
+ const configPath = path_1.default.join(testConfigDir, 'config.json');
76
+ fs_1.default.writeFileSync(configPath, JSON.stringify(testConfig, null, 2));
77
+ (0, node_assert_1.default)(fs_1.default.existsSync(configPath), 'Config file should be created');
78
+ const savedConfig = JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
79
+ (0, node_assert_1.default)(savedConfig.apiKey === testConfig.apiKey, 'Config should contain API key');
80
+ (0, node_assert_1.default)(savedConfig.version === testConfig.version, 'Config should contain version');
81
+ // Cleanup
82
+ fs_1.default.rmSync(testConfigDir, { recursive: true, force: true });
83
+ });
84
+ (0, node_test_1.test)('Setup integration - backup functionality', () => {
85
+ const testDir = path_1.default.join(os_1.default.tmpdir(), 'fraim-backup-test-' + Date.now());
86
+ fs_1.default.mkdirSync(testDir, { recursive: true });
87
+ const originalConfigPath = path_1.default.join(testDir, 'test-config.json');
88
+ const originalConfig = { existing: 'config' };
89
+ fs_1.default.writeFileSync(originalConfigPath, JSON.stringify(originalConfig, null, 2));
90
+ // Simulate backup creation
91
+ const backupPath = `${originalConfigPath}.fraim-backup-${Date.now()}`;
92
+ fs_1.default.copyFileSync(originalConfigPath, backupPath);
93
+ (0, node_assert_1.default)(fs_1.default.existsSync(backupPath), 'Backup file should be created');
94
+ const backupConfig = JSON.parse(fs_1.default.readFileSync(backupPath, 'utf8'));
95
+ node_assert_1.default.deepStrictEqual(backupConfig, originalConfig, 'Backup should match original');
96
+ // Cleanup
97
+ fs_1.default.rmSync(testDir, { recursive: true, force: true });
98
+ });
@@ -9,7 +9,7 @@ const db_service_js_1 = require("../src/fraim/db-service.js");
9
9
  const test_utils_1 = require("./test-utils");
10
10
  const node_assert_1 = __importDefault(require("node:assert"));
11
11
  const tree_kill_1 = __importDefault(require("tree-kill"));
12
- const path_1 = __importDefault(require("path"));
12
+ const test_server_utils_1 = require("./test-server-utils");
13
13
  async function testServerStartsAndResponds() {
14
14
  console.log(' 🚀 Testing Fraim Standalone Server...');
15
15
  let fraimProcess;
@@ -35,7 +35,7 @@ async function testServerStartsAndResponds() {
35
35
  // 2. Start server in standalone mode
36
36
  console.log(` Starting server on port ${PORT}...`);
37
37
  const npxCommand = process.platform === 'win32' ? 'npx.cmd' : 'npx';
38
- const serverScript = path_1.default.resolve(__dirname, '../dist/src/fraim-mcp-server.js');
38
+ const serverScript = (0, test_server_utils_1.getServerScriptPath)();
39
39
  fraimProcess = (0, node_child_process_1.spawn)(npxCommand, ['node', `"${serverScript}"`], {
40
40
  env: {
41
41
  ...process.env,
@@ -13,9 +13,21 @@ const build_stub_registry_1 = require("../scripts/build-stub-registry");
13
13
  * Tests for stub registry build process
14
14
  * Ensures that the npm package contains only stubs and scripts, not full workflows
15
15
  */
16
+ // Find project root by looking for package.json
17
+ function findProjectRoot() {
18
+ let currentDir = __dirname;
19
+ while (currentDir !== path_1.default.dirname(currentDir)) {
20
+ if (fs_1.default.existsSync(path_1.default.join(currentDir, 'package.json'))) {
21
+ return currentDir;
22
+ }
23
+ currentDir = path_1.default.dirname(currentDir);
24
+ }
25
+ throw new Error('Could not find project root (package.json not found)');
26
+ }
16
27
  (0, node_test_1.test)('Stub registry build generates correct structure', async () => {
17
28
  // Clean up any existing stubs
18
- const stubsPath = path_1.default.join(__dirname, '..', 'registry', 'stubs');
29
+ const projectRoot = findProjectRoot();
30
+ const stubsPath = path_1.default.join(projectRoot, 'registry', 'stubs');
19
31
  if (fs_1.default.existsSync(stubsPath)) {
20
32
  fs_1.default.rmSync(stubsPath, { recursive: true, force: true });
21
33
  }
@@ -32,7 +44,8 @@ const build_stub_registry_1 = require("../scripts/build-stub-registry");
32
44
  console.log(`✅ Generated ${stubFiles.length} workflow stubs`);
33
45
  });
34
46
  (0, node_test_1.test)('Generated stubs are lightweight and contain correct format', async () => {
35
- const stubsPath = path_1.default.join(__dirname, '..', 'registry', 'stubs', 'workflows');
47
+ const projectRoot = findProjectRoot();
48
+ const stubsPath = path_1.default.join(projectRoot, 'registry', 'stubs', 'workflows');
36
49
  // Find a stub file to test
37
50
  const stubFiles = getAllFiles(stubsPath, '.md');
38
51
  (0, node_assert_1.default)(stubFiles.length > 0, 'Should have stub files to test');
@@ -50,8 +63,9 @@ const build_stub_registry_1 = require("../scripts/build-stub-registry");
50
63
  console.log(`✅ Stub format verified (${stubLines} lines)`);
51
64
  });
52
65
  (0, node_test_1.test)('Stubs do not contain full workflow content', async () => {
53
- const stubsPath = path_1.default.join(__dirname, '..', 'registry', 'stubs', 'workflows');
54
- const fullWorkflowsPath = path_1.default.join(__dirname, '..', 'registry', 'workflows');
66
+ const projectRoot = findProjectRoot();
67
+ const stubsPath = path_1.default.join(projectRoot, 'registry', 'stubs', 'workflows');
68
+ const fullWorkflowsPath = path_1.default.join(projectRoot, 'registry', 'workflows');
55
69
  const stubFiles = getAllFiles(stubsPath, '.md');
56
70
  (0, node_assert_1.default)(stubFiles.length > 0, 'Should have stub files to test');
57
71
  for (const stubFile of stubFiles.slice(0, 3)) { // Test first 3 stubs
@@ -72,7 +86,8 @@ const build_stub_registry_1 = require("../scripts/build-stub-registry");
72
86
  console.log('✅ Stubs are properly lightweight');
73
87
  });
74
88
  (0, node_test_1.test)('Package.json files array excludes full workflows', () => {
75
- const packageJsonPath = path_1.default.join(__dirname, '..', 'package.json');
89
+ const projectRoot = findProjectRoot();
90
+ const packageJsonPath = path_1.default.join(projectRoot, 'package.json');
76
91
  const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
77
92
  const files = packageJson.files || [];
78
93
  // Should include stubs
@@ -87,12 +102,13 @@ const build_stub_registry_1 = require("../scripts/build-stub-registry");
87
102
  console.log('✅ Package.json correctly excludes full workflows');
88
103
  });
89
104
  (0, node_test_1.test)('Scripts are still included in package', () => {
90
- const scriptsPath = path_1.default.join(__dirname, '..', 'registry', 'scripts');
105
+ const projectRoot = findProjectRoot();
106
+ const scriptsPath = path_1.default.join(projectRoot, 'registry', 'scripts');
91
107
  (0, node_assert_1.default)(fs_1.default.existsSync(scriptsPath), 'Scripts directory should exist');
92
108
  const scriptFiles = getAllFiles(scriptsPath);
93
109
  (0, node_assert_1.default)(scriptFiles.length > 0, 'Should have script files');
94
110
  // Verify package.json includes scripts
95
- const packageJsonPath = path_1.default.join(__dirname, '..', 'package.json');
111
+ const packageJsonPath = path_1.default.join(projectRoot, 'package.json');
96
112
  const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
97
113
  const files = packageJson.files || [];
98
114
  (0, node_assert_1.default)(files.includes('registry/scripts/'), 'Package should include scripts');
@@ -9,7 +9,7 @@ const db_service_js_1 = require("../src/fraim/db-service.js");
9
9
  const test_utils_1 = require("./test-utils");
10
10
  const node_assert_1 = __importDefault(require("node:assert"));
11
11
  const tree_kill_1 = __importDefault(require("tree-kill"));
12
- const path_1 = __importDefault(require("path"));
12
+ const test_server_utils_1 = require("./test-server-utils");
13
13
  async function testTelemetryFlow() {
14
14
  console.log(' 🚀 Testing Fraim Telemetry System...');
15
15
  let fraimProcess;
@@ -39,7 +39,7 @@ async function testTelemetryFlow() {
39
39
  // 2. Start Server
40
40
  console.log(` Starting server on port ${PORT}...`);
41
41
  const npxCommand = process.platform === 'win32' ? 'npx.cmd' : 'npx';
42
- const serverScript = path_1.default.resolve(__dirname, '../dist/src/fraim-mcp-server.js');
42
+ const serverScript = (0, test_server_utils_1.getServerScriptPath)();
43
43
  fraimProcess = (0, node_child_process_1.spawn)(npxCommand, ['node', `"${serverScript}"`], {
44
44
  env: {
45
45
  ...process.env,
@@ -0,0 +1,30 @@
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_test_1 = require("node:test");
7
+ const node_assert_1 = __importDefault(require("node:assert"));
8
+ const token_validator_1 = require("../src/cli/setup/token-validator");
9
+ (0, node_test_1.test)('isValidTokenFormat should validate FRAIM keys correctly', () => {
10
+ (0, node_assert_1.default)((0, token_validator_1.isValidTokenFormat)('fraim_abc123456789012', 'fraim'), 'Valid FRAIM key should pass');
11
+ (0, node_assert_1.default)(!(0, token_validator_1.isValidTokenFormat)('invalid_key', 'fraim'), 'Invalid FRAIM key should fail');
12
+ (0, node_assert_1.default)(!(0, token_validator_1.isValidTokenFormat)('fraim_short', 'fraim'), 'Short FRAIM key should fail');
13
+ (0, node_assert_1.default)(!(0, token_validator_1.isValidTokenFormat)('', 'fraim'), 'Empty string should fail');
14
+ });
15
+ (0, node_test_1.test)('isValidTokenFormat should validate GitHub tokens correctly', () => {
16
+ (0, node_assert_1.default)((0, token_validator_1.isValidTokenFormat)('ghp_abc123456789', 'github'), 'Valid GitHub classic token should pass');
17
+ (0, node_assert_1.default)((0, token_validator_1.isValidTokenFormat)('github_pat_abc123456789', 'github'), 'Valid GitHub PAT should pass');
18
+ (0, node_assert_1.default)(!(0, token_validator_1.isValidTokenFormat)('invalid_token', 'github'), 'Invalid GitHub token should fail');
19
+ (0, node_assert_1.default)(!(0, token_validator_1.isValidTokenFormat)('gho_abc123456789', 'github'), 'OAuth token should fail');
20
+ (0, node_assert_1.default)(!(0, token_validator_1.isValidTokenFormat)('', 'github'), 'Empty string should fail');
21
+ });
22
+ (0, node_test_1.test)('validateFraimKey should validate format', async () => {
23
+ const validKey = 'fraim_abc123456789012';
24
+ const invalidKey = 'invalid_key';
25
+ const shortKey = 'fraim_short';
26
+ (0, node_assert_1.default)(await (0, token_validator_1.validateFraimKey)(validKey), 'Valid FRAIM key should pass');
27
+ (0, node_assert_1.default)(!await (0, token_validator_1.validateFraimKey)(invalidKey), 'Invalid FRAIM key should fail');
28
+ (0, node_assert_1.default)(!await (0, token_validator_1.validateFraimKey)(shortKey), 'Short FRAIM key should fail');
29
+ (0, node_assert_1.default)(!await (0, token_validator_1.validateFraimKey)(''), 'Empty key should fail');
30
+ });
@@ -12,6 +12,7 @@ const tree_kill_1 = __importDefault(require("tree-kill"));
12
12
  const fs_1 = __importDefault(require("fs"));
13
13
  const path_1 = __importDefault(require("path"));
14
14
  const os_1 = __importDefault(require("os"));
15
+ const test_server_utils_1 = require("./test-server-utils");
15
16
  async function testDualDiscoveryJourney() {
16
17
  console.log(' 🚀 Starting Dual Discovery User Journey Test...');
17
18
  // Setup
@@ -156,7 +157,7 @@ async function testDualDiscoveryJourney() {
156
157
  fs_1.default.writeFileSync(path_1.default.join(tempDir, 'package.json'), JSON.stringify({
157
158
  dependencies: { "@fraim/framework": "1.0.0" }
158
159
  }));
159
- const serverScript = path_1.default.resolve(__dirname, '../dist/src/fraim-mcp-server.js');
160
+ const serverScript = (0, test_server_utils_1.getServerScriptPath)();
160
161
  fraimProcess = (0, node_child_process_1.spawn)(process.execPath, [tsxCli, serverScript], {
161
162
  cwd: tempDir,
162
163
  env: { ...process.env, FRAIM_MCP_PORT: PORT.toString(), FRAIM_SKIP_INDEX_ON_START: 'true' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim-framework",
3
- "version": "2.0.37",
3
+ "version": "2.0.41",
4
4
  "description": "FRAIM v2: Framework for Rigor-based AI Management - Transform from solo developer to AI manager orchestrating production-ready code with enterprise-grade discipline",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -12,7 +12,7 @@
12
12
  "dev": "tsx --watch src/fraim-mcp-server.ts",
13
13
  "build": "tsc && npm run build:stubs && npm run validate:registry",
14
14
  "build:stubs": "tsx scripts/build-stub-registry.ts",
15
- "test": "tsx --test > test.log 2>&1",
15
+ "test": "node scripts/test-with-server.js",
16
16
  "start:fraim": "tsx src/fraim-mcp-server.ts",
17
17
  "dev:fraim": "tsx --watch src/fraim-mcp-server.ts",
18
18
  "watch:fraimlogs": "tsx scripts/watch-fraim-logs.ts",
@@ -86,6 +86,7 @@
86
86
  "cors": "^2.8.5",
87
87
  "dotenv": "^16.4.7",
88
88
  "express": "^5.2.1",
89
+ "fraim-framework": "^2.0.38",
89
90
  "markdown-it": "^14.1.0",
90
91
  "markdown-it-highlightjs": "^4.2.0",
91
92
  "mongodb": "^7.0.0",