fraim-framework 2.0.55 → 2.0.57
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/CHANGELOG.md +10 -0
- package/dist/src/cli/commands/init-project.js +10 -4
- package/dist/src/cli/setup/mcp-config-generator.js +23 -15
- package/dist/src/local-mcp-server/stdio-server.js +207 -0
- package/dist/src/utils/validate-workflows.js +101 -0
- package/dist/src/utils/workflow-parser.js +81 -0
- package/package.json +16 -11
- package/registry/scripts/pdf-styles.css +172 -0
- package/registry/scripts/prep-issue.sh +46 -4
- package/registry/scripts/profile-server.ts +131 -130
- package/registry/stubs/workflows/customer-development/user-survey-dispatch.md +1 -1
- package/registry/stubs/workflows/customer-development/users-to-target.md +1 -1
- package/registry/stubs/workflows/product-building/design.md +1 -1
- package/registry/stubs/workflows/product-building/implement.md +1 -1
- package/Claude.md +0 -1
- package/dist/registry/ai-manager-rules/design-phases/design-completeness-review.md +0 -73
- package/dist/registry/ai-manager-rules/design-phases/design-design.md +0 -145
- package/dist/registry/ai-manager-rules/implement-phases/implement-code.md +0 -283
- package/dist/registry/ai-manager-rules/implement-phases/implement-completeness-review.md +0 -120
- package/dist/registry/ai-manager-rules/implement-phases/implement-regression.md +0 -173
- package/dist/registry/ai-manager-rules/implement-phases/implement-repro.md +0 -104
- package/dist/registry/ai-manager-rules/implement-phases/implement-scoping.md +0 -100
- package/dist/registry/ai-manager-rules/implement-phases/implement-smoke.md +0 -237
- package/dist/registry/ai-manager-rules/implement-phases/implement-spike.md +0 -121
- package/dist/registry/ai-manager-rules/implement-phases/implement-validate.md +0 -375
- package/dist/registry/ai-manager-rules/retrospective.md +0 -116
- package/dist/registry/ai-manager-rules/shared-phases/address-pr-feedback.md +0 -188
- package/dist/registry/ai-manager-rules/shared-phases/submit-pr.md +0 -202
- package/dist/registry/ai-manager-rules/shared-phases/wait-for-pr-review.md +0 -170
- package/dist/registry/ai-manager-rules/spec-phases/spec-competitor-analysis.md +0 -105
- package/dist/registry/ai-manager-rules/spec-phases/spec-completeness-review.md +0 -66
- package/dist/registry/ai-manager-rules/spec-phases/spec-spec.md +0 -139
- package/dist/registry/providers/ado.json +0 -19
- package/dist/registry/providers/github.json +0 -19
- package/dist/registry/scripts/cleanup-branch.js +0 -287
- package/dist/registry/scripts/evaluate-code-quality.js +0 -66
- package/dist/registry/scripts/exec-with-timeout.js +0 -142
- package/dist/registry/scripts/generate-engagement-emails.js +0 -705
- package/dist/registry/scripts/newsletter-helpers.js +0 -671
- package/dist/registry/scripts/profile-server.js +0 -388
- package/dist/registry/scripts/run-thank-you-workflow.js +0 -92
- package/dist/registry/scripts/send-newsletter-simple.js +0 -85
- package/dist/registry/scripts/send-thank-you-emails.js +0 -54
- package/dist/registry/scripts/validate-openapi-limits.js +0 -311
- package/dist/registry/scripts/validate-test-coverage.js +0 -262
- package/dist/registry/scripts/verify-test-coverage.js +0 -66
- package/dist/scripts/build-stub-registry.js +0 -108
- package/dist/src/ai-manager/ai-manager.js +0 -482
- package/dist/src/ai-manager/phase-flow.js +0 -357
- package/dist/src/ai-manager/types.js +0 -5
- package/dist/src/fraim-mcp-server.js +0 -1885
- package/dist/tests/debug-tools.js +0 -80
- package/dist/tests/shared-server-utils.js +0 -57
- package/dist/tests/test-add-ide.js +0 -283
- package/dist/tests/test-ai-coach-edge-cases.js +0 -420
- package/dist/tests/test-ai-coach-mcp-integration.js +0 -450
- package/dist/tests/test-ai-coach-performance.js +0 -328
- package/dist/tests/test-ai-coach-phase-content.js +0 -264
- package/dist/tests/test-ai-coach-workflows.js +0 -514
- package/dist/tests/test-cli.js +0 -228
- package/dist/tests/test-client-scripts-validation.js +0 -167
- package/dist/tests/test-complete-setup-flow.js +0 -110
- package/dist/tests/test-config-system.js +0 -279
- package/dist/tests/test-debug-session.js +0 -134
- package/dist/tests/test-end-to-end-hybrid-validation.js +0 -328
- package/dist/tests/test-enhanced-session-init.js +0 -188
- package/dist/tests/test-first-run-journey.js +0 -368
- package/dist/tests/test-fraim-issues.js +0 -59
- package/dist/tests/test-genericization.js +0 -44
- package/dist/tests/test-hybrid-script-execution.js +0 -340
- package/dist/tests/test-ide-detector.js +0 -46
- package/dist/tests/test-improved-setup.js +0 -121
- package/dist/tests/test-mcp-config-generator.js +0 -99
- package/dist/tests/test-mcp-connection.js +0 -107
- package/dist/tests/test-mcp-issue-integration.js +0 -156
- package/dist/tests/test-mcp-lifecycle-methods.js +0 -240
- package/dist/tests/test-mcp-shared-server.js +0 -308
- package/dist/tests/test-mcp-template-processing.js +0 -160
- package/dist/tests/test-modular-issue-tracking.js +0 -165
- package/dist/tests/test-node-compatibility.js +0 -95
- package/dist/tests/test-npm-install.js +0 -68
- package/dist/tests/test-package-size.js +0 -108
- package/dist/tests/test-pr-review-workflow.js +0 -307
- package/dist/tests/test-prep-issue.js +0 -129
- package/dist/tests/test-productivity-integration.js +0 -157
- package/dist/tests/test-script-location-independence.js +0 -198
- package/dist/tests/test-script-sync.js +0 -557
- package/dist/tests/test-server-utils.js +0 -32
- package/dist/tests/test-session-rehydration.js +0 -148
- package/dist/tests/test-setup-integration.js +0 -98
- package/dist/tests/test-setup-scenarios.js +0 -322
- package/dist/tests/test-standalone.js +0 -143
- package/dist/tests/test-stub-registry.js +0 -136
- package/dist/tests/test-sync-stubs.js +0 -143
- package/dist/tests/test-sync-version-update.js +0 -93
- package/dist/tests/test-telemetry.js +0 -193
- package/dist/tests/test-token-validator.js +0 -30
- package/dist/tests/test-user-journey.js +0 -236
- package/dist/tests/test-users-to-target-workflow.js +0 -253
- package/dist/tests/test-utils.js +0 -109
- package/dist/tests/test-wizard.js +0 -71
- package/dist/tests/test-workflow-discovery.js +0 -242
- package/labels.json +0 -52
- package/registry/agent-guardrails.md +0 -63
- package/registry/fraim.md +0 -48
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase1-customer-profiling.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase1-survey-scoping.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase2-platform-discovery.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase2-survey-build-linkedin.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase3-prospect-qualification.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase3-survey-build-reddit.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase4-inventory-compilation.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase4-survey-build-x.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase5-survey-build-facebook.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase6-survey-build-custom.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase7-survey-dispatch.md +0 -11
- package/registry/stubs/workflows/customer-development/templates/customer-persona-template.md +0 -11
- package/registry/stubs/workflows/customer-development/templates/search-strategy-template.md +0 -11
- package/setup.js +0 -171
- package/tsconfig.json +0 -23
package/dist/tests/test-cli.js
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
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
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
const node_child_process_1 = require("node:child_process");
|
|
40
|
-
const test_utils_1 = require("./test-utils");
|
|
41
|
-
const node_assert_1 = __importDefault(require("node:assert"));
|
|
42
|
-
const fs_1 = __importDefault(require("fs"));
|
|
43
|
-
const path_1 = __importDefault(require("path"));
|
|
44
|
-
const os_1 = __importDefault(require("os"));
|
|
45
|
-
async function testRepoNameRequirement() {
|
|
46
|
-
console.log(' 🚀 Testing Repo Name Requirement...');
|
|
47
|
-
// Create a temp directory for the test project
|
|
48
|
-
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'fraim-repo-test-'));
|
|
49
|
-
console.log(` 📂 Created temp dir: ${tempDir}`);
|
|
50
|
-
try {
|
|
51
|
-
const platform = process.platform;
|
|
52
|
-
const npx = platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
53
|
-
const cliScript = (0, test_utils_1.resolveProjectPath)('dist/src/cli/fraim.js');
|
|
54
|
-
// Helper to run CLI commands
|
|
55
|
-
const runFraim = (args, cwdOverride) => {
|
|
56
|
-
return new Promise((resolve) => {
|
|
57
|
-
const ps = (0, node_child_process_1.spawn)(npx, ['node', `"${cliScript}"`, ...args], {
|
|
58
|
-
cwd: cwdOverride || tempDir,
|
|
59
|
-
env: { ...process.env, TEST_MODE: 'true' },
|
|
60
|
-
shell: true
|
|
61
|
-
});
|
|
62
|
-
let stdout = '';
|
|
63
|
-
let stderr = '';
|
|
64
|
-
ps.stdout.on('data', d => stdout += d.toString());
|
|
65
|
-
ps.stderr.on('data', d => stderr += d.toString());
|
|
66
|
-
ps.on('close', (code) => {
|
|
67
|
-
resolve({ stdout, stderr, code });
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
};
|
|
71
|
-
// Test 1: Init without git should fail
|
|
72
|
-
console.log(' Testing init without git remote (should fail)...');
|
|
73
|
-
const initWithoutGit = await runFraim(['init'], tempDir);
|
|
74
|
-
node_assert_1.default.strictEqual(initWithoutGit.code, 1, 'Init without git remote should fail');
|
|
75
|
-
node_assert_1.default.ok(initWithoutGit.stdout.includes('No git remote found') || initWithoutGit.stderr.includes('No git remote found'), 'Should show git remote error');
|
|
76
|
-
// Test 2: Init with git remote should succeed and use repo name
|
|
77
|
-
console.log(' Testing init with git remote (should succeed)...');
|
|
78
|
-
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
79
|
-
execSync('git init', { cwd: tempDir });
|
|
80
|
-
execSync('git remote add origin https://github.com/test-owner/my-awesome-project.git', { cwd: tempDir });
|
|
81
|
-
const initWithGit = await runFraim(['init'], tempDir);
|
|
82
|
-
node_assert_1.default.strictEqual(initWithGit.code, 0, 'Init with git remote should succeed');
|
|
83
|
-
const configPath = path_1.default.join(tempDir, '.fraim', 'config.json');
|
|
84
|
-
node_assert_1.default.ok(fs_1.default.existsSync(configPath), 'config.json should exist');
|
|
85
|
-
const config = JSON.parse(fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
86
|
-
node_assert_1.default.strictEqual(config.project.name, 'my-awesome-project', 'project.name should use repo name');
|
|
87
|
-
node_assert_1.default.strictEqual(config.repository.name, 'my-awesome-project', 'repository.name should match');
|
|
88
|
-
node_assert_1.default.strictEqual(config.repository.owner, 'test-owner', 'repository.owner should be detected');
|
|
89
|
-
console.log(' ✅ Repo name requirement verified!');
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
console.error(' ❌ Repo name test failed:', error);
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
finally {
|
|
97
|
-
try {
|
|
98
|
-
fs_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
99
|
-
}
|
|
100
|
-
catch (e) { }
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
async function testCliLifecycle() {
|
|
104
|
-
console.log(' 🚀 Testing Fraim CLI Lifecycle...');
|
|
105
|
-
// Create a temp directory for the test project
|
|
106
|
-
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'fraim-test-'));
|
|
107
|
-
console.log(` 📂 Created temp dir: ${tempDir}`);
|
|
108
|
-
try {
|
|
109
|
-
const platform = process.platform;
|
|
110
|
-
const npx = platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
111
|
-
const cliScript = (0, test_utils_1.resolveProjectPath)('dist/src/cli/fraim.js');
|
|
112
|
-
// Helper to run CLI commands
|
|
113
|
-
const runFraim = (args, cwdOverride) => {
|
|
114
|
-
return new Promise((resolve) => {
|
|
115
|
-
const ps = (0, node_child_process_1.spawn)(npx, ['node', `"${cliScript}"`, ...args], {
|
|
116
|
-
cwd: cwdOverride || tempDir,
|
|
117
|
-
env: { ...process.env, TEST_MODE: 'true' }, // Disable interactive first-run
|
|
118
|
-
shell: true
|
|
119
|
-
});
|
|
120
|
-
let stdout = '';
|
|
121
|
-
let stderr = '';
|
|
122
|
-
ps.stdout.on('data', d => stdout += d.toString());
|
|
123
|
-
ps.stderr.on('data', d => stderr += d.toString());
|
|
124
|
-
ps.on('close', (code) => {
|
|
125
|
-
resolve({ stdout, stderr, code });
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
};
|
|
129
|
-
// 1. Test `fraim init`
|
|
130
|
-
console.log(' Testing "fraim init"...');
|
|
131
|
-
// Create a fake git repo to test population
|
|
132
|
-
try {
|
|
133
|
-
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
134
|
-
execSync('git init', { cwd: tempDir });
|
|
135
|
-
execSync('git remote add origin https://github.com/test-owner/test-repo.git', { cwd: tempDir });
|
|
136
|
-
}
|
|
137
|
-
catch (e) {
|
|
138
|
-
console.warn(' ⚠️ Could not initialize git in temp dir, skipping population test');
|
|
139
|
-
}
|
|
140
|
-
const initRes = await runFraim(['init'], tempDir);
|
|
141
|
-
if (initRes.code !== 0) {
|
|
142
|
-
console.error('Init failed:', initRes.stderr);
|
|
143
|
-
console.error('STDOUT:', initRes.stdout); // Keep this line for better debugging
|
|
144
|
-
}
|
|
145
|
-
node_assert_1.default.strictEqual(initRes.code, 0, 'Init should exit with 0');
|
|
146
|
-
node_assert_1.default.ok(fs_1.default.existsSync(path_1.default.join(tempDir, '.fraim')), '.fraim dir should exist');
|
|
147
|
-
const configPath = path_1.default.join(tempDir, '.fraim', 'config.json');
|
|
148
|
-
node_assert_1.default.ok(fs_1.default.existsSync(configPath), 'config.json should exist');
|
|
149
|
-
const config = JSON.parse(fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
150
|
-
// Deep structure validation
|
|
151
|
-
node_assert_1.default.ok(config.project, 'config.project should exist');
|
|
152
|
-
node_assert_1.default.strictEqual(typeof config.project.name, 'string', 'project.name should be a string');
|
|
153
|
-
node_assert_1.default.ok(config.repository, 'config.repository should exist');
|
|
154
|
-
node_assert_1.default.strictEqual(config.repository.provider, 'github', 'Default provider should be github');
|
|
155
|
-
// If git was initialized successfully, these should NOT be empty
|
|
156
|
-
const hasGit = fs_1.default.existsSync(path_1.default.join(tempDir, '.git'));
|
|
157
|
-
if (hasGit) {
|
|
158
|
-
node_assert_1.default.strictEqual(config.repository.owner, 'test-owner', 'repository.owner should be detected from git');
|
|
159
|
-
node_assert_1.default.strictEqual(config.repository.name, 'test-repo', 'repository.name should be detected from git');
|
|
160
|
-
node_assert_1.default.strictEqual(config.project.name, 'test-repo', 'project.name should use repo name, not folder name');
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
console.warn(' ⚠️ No .git found in tempDir, owner/repo detection skipped');
|
|
164
|
-
// Without git remote, init should fail with our new logic
|
|
165
|
-
console.warn(' ⚠️ Init should have failed without git remote in new logic');
|
|
166
|
-
}
|
|
167
|
-
node_assert_1.default.ok(config.customizations, 'config.customizations should exist');
|
|
168
|
-
node_assert_1.default.strictEqual(config.customizations.workflowsPath, '.fraim/workflows');
|
|
169
|
-
// 2. Test `fraim sync`
|
|
170
|
-
// We need a fake registry for sync to work against
|
|
171
|
-
const registryDir = path_1.default.join(tempDir, 'registry', 'workflows');
|
|
172
|
-
fs_1.default.mkdirSync(registryDir, { recursive: true });
|
|
173
|
-
const dummyWorkflow = `---
|
|
174
|
-
intent: Test Intent
|
|
175
|
-
principles:
|
|
176
|
-
- Test Principle
|
|
177
|
-
---
|
|
178
|
-
# Test Workflow`;
|
|
179
|
-
fs_1.default.writeFileSync(path_1.default.join(registryDir, 'test-workflow.md'), dummyWorkflow);
|
|
180
|
-
console.log(' Testing "fraim sync"...');
|
|
181
|
-
const syncRes = await runFraim(['sync']);
|
|
182
|
-
if (syncRes.code === 0) {
|
|
183
|
-
console.log(' ✅ Sync successful');
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
console.log(' ⚠️ Sync warning (env dependent):', syncRes.stderr);
|
|
187
|
-
}
|
|
188
|
-
// 3. Test `fraim list`
|
|
189
|
-
console.log(' Testing "fraim list"...');
|
|
190
|
-
const listRes = await runFraim(['list']);
|
|
191
|
-
node_assert_1.default.strictEqual(listRes.code, 0);
|
|
192
|
-
// 4. Test `fraim doctor`
|
|
193
|
-
console.log(' Testing "fraim doctor"...');
|
|
194
|
-
const doctorRes = await runFraim(['doctor']);
|
|
195
|
-
node_assert_1.default.strictEqual(doctorRes.code, 0);
|
|
196
|
-
node_assert_1.default.ok(doctorRes.stdout.includes('Everything looks great'), 'Doctor should be happy');
|
|
197
|
-
console.log(' ✅ CLI Lifecycle verified!');
|
|
198
|
-
return true;
|
|
199
|
-
}
|
|
200
|
-
catch (error) {
|
|
201
|
-
console.error(' ❌ CLI Test failed:', error);
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
204
|
-
finally {
|
|
205
|
-
try {
|
|
206
|
-
fs_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
207
|
-
}
|
|
208
|
-
catch (e) { }
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
async function runCliTest(testCase) {
|
|
212
|
-
return await testCase.testFunction();
|
|
213
|
-
}
|
|
214
|
-
const testCases = [
|
|
215
|
-
{
|
|
216
|
-
name: 'Repo Name Requirement',
|
|
217
|
-
description: 'Tests that init requires git remote and uses repo name for project.name',
|
|
218
|
-
testFunction: testRepoNameRequirement,
|
|
219
|
-
tags: ['cli', 'fraim', 'git']
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
name: 'Fraim CLI Lifecycle',
|
|
223
|
-
description: 'Tests init, sync, list, and doctor commands',
|
|
224
|
-
testFunction: testCliLifecycle,
|
|
225
|
-
tags: ['cli', 'fraim']
|
|
226
|
-
},
|
|
227
|
-
];
|
|
228
|
-
(0, test_utils_1.runTests)(testCases, runCliTest, 'Fraim CLI Tests');
|
|
@@ -1,167 +0,0 @@
|
|
|
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 test_utils_1 = require("./test-utils");
|
|
7
|
-
const child_process_1 = require("child_process");
|
|
8
|
-
const fs_1 = require("fs");
|
|
9
|
-
const path_1 = require("path");
|
|
10
|
-
const node_assert_1 = __importDefault(require("node:assert"));
|
|
11
|
-
const path_2 = __importDefault(require("path"));
|
|
12
|
-
/**
|
|
13
|
-
* Client-side script validation tests
|
|
14
|
-
* Ensures scripts can run without FRAIM internal imports
|
|
15
|
-
* Following the project's standard test structure from test-utils.ts
|
|
16
|
-
*/
|
|
17
|
-
// Find project root by looking for package.json
|
|
18
|
-
function findProjectRoot() {
|
|
19
|
-
let currentDir = __dirname;
|
|
20
|
-
while (currentDir !== path_2.default.dirname(currentDir)) {
|
|
21
|
-
if ((0, fs_1.existsSync)(path_2.default.join(currentDir, 'package.json'))) {
|
|
22
|
-
return currentDir;
|
|
23
|
-
}
|
|
24
|
-
currentDir = path_2.default.dirname(currentDir);
|
|
25
|
-
}
|
|
26
|
-
throw new Error('Could not find project root (package.json not found)');
|
|
27
|
-
}
|
|
28
|
-
async function testNoFraimInternalImports() {
|
|
29
|
-
console.log(' 🧪 Testing registry scripts do not import FRAIM internals...');
|
|
30
|
-
try {
|
|
31
|
-
const projectRoot = findProjectRoot();
|
|
32
|
-
const scriptsDir = (0, path_1.join)(projectRoot, 'registry', 'scripts');
|
|
33
|
-
const scriptFiles = ['cleanup-branch.ts', 'generate-engagement-emails.ts', 'newsletter-helpers.ts'];
|
|
34
|
-
for (const scriptFile of scriptFiles) {
|
|
35
|
-
const scriptPath = (0, path_1.join)(scriptsDir, scriptFile);
|
|
36
|
-
if ((0, fs_1.existsSync)(scriptPath)) {
|
|
37
|
-
const content = (0, fs_1.readFileSync)(scriptPath, 'utf-8');
|
|
38
|
-
// Should not import from ../../src/
|
|
39
|
-
(0, node_assert_1.default)(!content.includes('../../src/'), `${scriptFile} should not import from ../../src/`);
|
|
40
|
-
// Should not import fraim-config
|
|
41
|
-
(0, node_assert_1.default)(!content.includes('./fraim-config'), `${scriptFile} should not import fraim-config`);
|
|
42
|
-
// Should have self-contained config loading
|
|
43
|
-
(0, node_assert_1.default)(content.includes('.fraim/config.json') || content.includes('loadClientConfig'), `${scriptFile} should read config directly`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
catch (e) {
|
|
49
|
-
console.error(' ❌ FRAIM internal imports check failed:', e);
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
async function testInlineUtilityFunctions() {
|
|
54
|
-
console.log(' 🧪 Testing scripts have inline utility functions...');
|
|
55
|
-
try {
|
|
56
|
-
const projectRoot = findProjectRoot();
|
|
57
|
-
const scriptsDir = (0, path_1.join)(projectRoot, 'registry', 'scripts');
|
|
58
|
-
const scriptPath = (0, path_1.join)(scriptsDir, 'cleanup-branch.ts');
|
|
59
|
-
if ((0, fs_1.existsSync)(scriptPath)) {
|
|
60
|
-
const content = (0, fs_1.readFileSync)(scriptPath, 'utf-8');
|
|
61
|
-
// Should have inline extractIssueNumber function
|
|
62
|
-
(0, node_assert_1.default)(content.includes('function extractIssueNumber'), 'cleanup-branch.ts should have inline extractIssueNumber function');
|
|
63
|
-
// Should have inline getCurrentGitBranch function
|
|
64
|
-
(0, node_assert_1.default)(content.includes('function getCurrentGitBranch'), 'cleanup-branch.ts should have inline getCurrentGitBranch function');
|
|
65
|
-
}
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
catch (e) {
|
|
69
|
-
console.error(' ❌ Inline utility functions check failed:', e);
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
async function testConfigLoadingFromJson() {
|
|
74
|
-
console.log(' 🧪 Testing scripts load config from .fraim/config.json...');
|
|
75
|
-
try {
|
|
76
|
-
const projectRoot = findProjectRoot();
|
|
77
|
-
const scriptsDir = (0, path_1.join)(projectRoot, 'registry', 'scripts');
|
|
78
|
-
const scriptPath = (0, path_1.join)(scriptsDir, 'generate-engagement-emails.ts');
|
|
79
|
-
if ((0, fs_1.existsSync)(scriptPath)) {
|
|
80
|
-
const content = (0, fs_1.readFileSync)(scriptPath, 'utf-8');
|
|
81
|
-
// Should have loadClientConfig function
|
|
82
|
-
(0, node_assert_1.default)(content.includes('function loadClientConfig'), 'generate-engagement-emails.ts should have loadClientConfig function');
|
|
83
|
-
// Should read .fraim/config.json
|
|
84
|
-
(0, node_assert_1.default)(content.includes('.fraim/config.json'), 'generate-engagement-emails.ts should read .fraim/config.json');
|
|
85
|
-
}
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
catch (e) {
|
|
89
|
-
console.error(' ❌ Config loading check failed:', e);
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
async function testRegistryPathValidator() {
|
|
94
|
-
console.log(' 🧪 Testing registry path validator passes...');
|
|
95
|
-
try {
|
|
96
|
-
const projectRoot = findProjectRoot();
|
|
97
|
-
(0, child_process_1.execSync)('npm run validate:registry', { stdio: 'pipe', cwd: projectRoot });
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
console.error(' ❌ Registry validation failed:', error.message);
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
async function testScriptExecutability() {
|
|
106
|
-
console.log(' 🧪 Testing scripts are executable without import errors...');
|
|
107
|
-
try {
|
|
108
|
-
const projectRoot = findProjectRoot();
|
|
109
|
-
const scriptsDir = (0, path_1.join)(projectRoot, 'registry', 'scripts');
|
|
110
|
-
const scriptPath = (0, path_1.join)(scriptsDir, 'cleanup-branch.ts');
|
|
111
|
-
if ((0, fs_1.existsSync)(scriptPath)) {
|
|
112
|
-
// Test that the script can be compiled/parsed without import errors
|
|
113
|
-
// Use tsx --check to validate syntax without executing
|
|
114
|
-
const result = (0, child_process_1.execSync)(`npx tsx --check ${scriptPath}`, {
|
|
115
|
-
stdio: 'pipe',
|
|
116
|
-
encoding: 'utf-8',
|
|
117
|
-
timeout: 5000 // 5 second timeout
|
|
118
|
-
});
|
|
119
|
-
console.log(' ✅ Script compiles without import errors');
|
|
120
|
-
}
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
catch (error) {
|
|
124
|
-
// Check if it's an import error
|
|
125
|
-
if (error.message.includes('Cannot find module') || error.message.includes('../../src/')) {
|
|
126
|
-
console.error(' ❌ Script has import errors:', error.message);
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
// Timeout errors indicate the script is hanging
|
|
130
|
-
if (error.message.includes('ETIMEDOUT') || error.message.includes('timeout')) {
|
|
131
|
-
console.error(' ❌ Script compilation timed out:', error.message);
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
// TypeScript compilation errors
|
|
135
|
-
if (error.message.includes('error TS')) {
|
|
136
|
-
console.error(' ❌ Script has TypeScript compilation errors:', error.message);
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
// Other errors might be acceptable
|
|
140
|
-
console.log(' ℹ️ Script compilation had non-critical error (acceptable):', error.message);
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
const testCases = [
|
|
145
|
-
{
|
|
146
|
-
name: 'Registry scripts should not import FRAIM internals',
|
|
147
|
-
testFunction: testNoFraimInternalImports
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
name: 'Scripts should have inline utility functions',
|
|
151
|
-
testFunction: testInlineUtilityFunctions
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
name: 'Scripts should load config from .fraim/config.json',
|
|
155
|
-
testFunction: testConfigLoadingFromJson
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
name: 'Registry path validator should pass',
|
|
159
|
-
testFunction: testRegistryPathValidator
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
name: 'Scripts should be executable without import errors',
|
|
163
|
-
testFunction: testScriptExecutability
|
|
164
|
-
}
|
|
165
|
-
];
|
|
166
|
-
// Run the tests using the standard test runner
|
|
167
|
-
(0, test_utils_1.runTests)(testCases, async (testCase) => testCase.testFunction(), 'Client-Side Script Validation');
|
|
@@ -1,110 +0,0 @@
|
|
|
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)('Complete setup flow - end to end simulation', async () => {
|
|
15
|
-
const testFraimKey = 'fraim_test123456789012345';
|
|
16
|
-
const testGithubToken = 'ghp_test123456789012345';
|
|
17
|
-
// Step 1: Validate tokens (as setup command would)
|
|
18
|
-
(0, node_assert_1.default)((0, token_validator_1.isValidTokenFormat)(testFraimKey, 'fraim'), 'FRAIM key should be valid format');
|
|
19
|
-
(0, node_assert_1.default)((0, token_validator_1.isValidTokenFormat)(testGithubToken, 'github'), 'GitHub token should be valid format');
|
|
20
|
-
(0, node_assert_1.default)(await (0, token_validator_1.validateFraimKey)(testFraimKey), 'FRAIM key should validate');
|
|
21
|
-
// Step 2: Detect IDEs (as setup command would)
|
|
22
|
-
const detectedIDEs = (0, ide_detector_1.detectInstalledIDEs)();
|
|
23
|
-
console.log(`Detected ${detectedIDEs.length} IDEs in test environment`);
|
|
24
|
-
// Step 3: Generate configs for each detected IDE type
|
|
25
|
-
const configTypes = ['standard', 'kiro', 'codex', 'windsurf'];
|
|
26
|
-
for (const configType of configTypes) {
|
|
27
|
-
const config = (0, mcp_config_generator_1.generateMCPConfig)(configType, testFraimKey, testGithubToken);
|
|
28
|
-
if (configType === 'codex') {
|
|
29
|
-
// TOML config
|
|
30
|
-
(0, node_assert_1.default)(typeof config === 'string', `${configType} should return string`);
|
|
31
|
-
(0, node_assert_1.default)(config.includes(testFraimKey), `${configType} should contain FRAIM key`);
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
// JSON configs
|
|
35
|
-
(0, node_assert_1.default)(typeof config === 'object', `${configType} should return object`);
|
|
36
|
-
(0, node_assert_1.default)(config.mcpServers, `${configType} should have mcpServers`);
|
|
37
|
-
(0, node_assert_1.default)(config.mcpServers.fraim, `${configType} should have fraim server`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
// Step 4: Simulate global config creation
|
|
41
|
-
const testGlobalDir = path_1.default.join(os_1.default.tmpdir(), 'fraim-global-test-' + Date.now());
|
|
42
|
-
fs_1.default.mkdirSync(testGlobalDir, { recursive: true });
|
|
43
|
-
const globalConfig = {
|
|
44
|
-
version: '2.0.37',
|
|
45
|
-
apiKey: testFraimKey,
|
|
46
|
-
configuredAt: new Date().toISOString(),
|
|
47
|
-
userPreferences: {
|
|
48
|
-
autoSync: true,
|
|
49
|
-
backupConfigs: true
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
const globalConfigPath = path_1.default.join(testGlobalDir, 'config.json');
|
|
53
|
-
fs_1.default.writeFileSync(globalConfigPath, JSON.stringify(globalConfig, null, 2));
|
|
54
|
-
(0, node_assert_1.default)(fs_1.default.existsSync(globalConfigPath), 'Global config should be created');
|
|
55
|
-
// Step 5: Simulate project config creation (as init-project would)
|
|
56
|
-
const testProjectDir = path_1.default.join(os_1.default.tmpdir(), 'fraim-project-test-' + Date.now());
|
|
57
|
-
fs_1.default.mkdirSync(testProjectDir, { recursive: true });
|
|
58
|
-
const fraimProjectDir = path_1.default.join(testProjectDir, '.fraim');
|
|
59
|
-
fs_1.default.mkdirSync(fraimProjectDir, { recursive: true });
|
|
60
|
-
const projectConfig = {
|
|
61
|
-
version: '2.0.37',
|
|
62
|
-
project: {
|
|
63
|
-
name: 'test-project'
|
|
64
|
-
},
|
|
65
|
-
git: {
|
|
66
|
-
defaultBranch: 'master',
|
|
67
|
-
repoOwner: 'test-owner',
|
|
68
|
-
repoName: 'test-project'
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
const projectConfigPath = path_1.default.join(fraimProjectDir, 'config.json');
|
|
72
|
-
fs_1.default.writeFileSync(projectConfigPath, JSON.stringify(projectConfig, null, 2));
|
|
73
|
-
(0, node_assert_1.default)(fs_1.default.existsSync(projectConfigPath), 'Project config should be created');
|
|
74
|
-
// Step 6: Verify configs can be read back correctly
|
|
75
|
-
const savedGlobalConfig = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
|
|
76
|
-
const savedProjectConfig = JSON.parse(fs_1.default.readFileSync(projectConfigPath, 'utf8'));
|
|
77
|
-
(0, node_assert_1.default)(savedGlobalConfig.apiKey === testFraimKey, 'Global config should contain API key');
|
|
78
|
-
(0, node_assert_1.default)(savedProjectConfig.project.name === 'test-project', 'Project config should contain project name');
|
|
79
|
-
// Cleanup
|
|
80
|
-
fs_1.default.rmSync(testGlobalDir, { recursive: true, force: true });
|
|
81
|
-
fs_1.default.rmSync(testProjectDir, { recursive: true, force: true });
|
|
82
|
-
console.log('✅ Complete setup flow simulation passed');
|
|
83
|
-
});
|
|
84
|
-
(0, node_test_1.test)('Error handling - invalid tokens should be rejected', async () => {
|
|
85
|
-
const invalidFraimKey = 'invalid_key';
|
|
86
|
-
const invalidGithubToken = 'invalid_token';
|
|
87
|
-
const shortFraimKey = 'fraim_short';
|
|
88
|
-
(0, node_assert_1.default)(!(0, token_validator_1.isValidTokenFormat)(invalidFraimKey, 'fraim'), 'Invalid FRAIM key should be rejected');
|
|
89
|
-
(0, node_assert_1.default)(!(0, token_validator_1.isValidTokenFormat)(invalidGithubToken, 'github'), 'Invalid GitHub token should be rejected');
|
|
90
|
-
(0, node_assert_1.default)(!(0, token_validator_1.isValidTokenFormat)(shortFraimKey, 'fraim'), 'Short FRAIM key should be rejected');
|
|
91
|
-
(0, node_assert_1.default)(!await (0, token_validator_1.validateFraimKey)(invalidFraimKey), 'Invalid FRAIM key should not validate');
|
|
92
|
-
(0, node_assert_1.default)(!await (0, token_validator_1.validateFraimKey)(shortFraimKey), 'Short FRAIM key should not validate');
|
|
93
|
-
console.log('✅ Error handling tests passed');
|
|
94
|
-
});
|
|
95
|
-
(0, node_test_1.test)('Path expansion works correctly across platforms', () => {
|
|
96
|
-
const testPaths = [
|
|
97
|
-
'~/.fraim/config.json',
|
|
98
|
-
'~/.claude.json',
|
|
99
|
-
'~/.kiro/settings/mcp.json'
|
|
100
|
-
];
|
|
101
|
-
testPaths.forEach(testPath => {
|
|
102
|
-
const expanded = (0, ide_detector_1.expandPath)(testPath);
|
|
103
|
-
(0, node_assert_1.default)(expanded.includes(os_1.default.homedir()), `${testPath} should expand to include home directory`);
|
|
104
|
-
(0, node_assert_1.default)(!expanded.includes('~'), `${testPath} should not contain tilde after expansion`);
|
|
105
|
-
});
|
|
106
|
-
// Test absolute paths remain unchanged
|
|
107
|
-
const absolutePath = '/usr/local/bin/fraim';
|
|
108
|
-
(0, node_assert_1.default)((0, ide_detector_1.expandPath)(absolutePath) === absolutePath, 'Absolute paths should remain unchanged');
|
|
109
|
-
console.log('✅ Path expansion tests passed');
|
|
110
|
-
});
|