fraim-framework 2.0.27 → 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.
- package/.github/workflows/deploy-fraim.yml +1 -1
- package/dist/registry/scripts/build-scripts-generator.js +205 -0
- package/dist/registry/scripts/cleanup-branch.js +258 -0
- package/dist/registry/scripts/evaluate-code-quality.js +66 -0
- package/dist/registry/scripts/exec-with-timeout.js +142 -0
- package/dist/registry/scripts/fraim-config.js +61 -0
- package/dist/registry/scripts/generate-engagement-emails.js +630 -0
- package/dist/registry/scripts/generic-issues-api.js +100 -0
- package/dist/registry/scripts/newsletter-helpers.js +731 -0
- package/dist/registry/scripts/openapi-generator.js +664 -0
- package/dist/registry/scripts/performance/profile-server.js +390 -0
- package/dist/registry/scripts/run-thank-you-workflow.js +92 -0
- package/dist/registry/scripts/send-newsletter-simple.js +85 -0
- package/dist/registry/scripts/send-thank-you-emails.js +54 -0
- package/dist/registry/scripts/validate-openapi-limits.js +311 -0
- package/dist/registry/scripts/validate-test-coverage.js +262 -0
- package/dist/registry/scripts/verify-test-coverage.js +66 -0
- package/dist/src/cli/commands/init.js +14 -14
- package/dist/src/cli/commands/sync.js +4 -4
- package/dist/src/cli/fraim.js +24 -22
- package/dist/src/cli/setup/first-run.js +13 -6
- package/dist/src/fraim/db-service.js +26 -15
- package/dist/src/fraim/issues.js +67 -0
- package/dist/src/fraim-mcp-server.js +272 -18
- package/dist/src/utils/git-utils.js +1 -1
- package/dist/tests/debug-tools.js +79 -0
- package/dist/tests/esm-compat.js +11 -0
- package/dist/tests/test-chalk-esm-issue.js +159 -0
- package/dist/tests/test-chalk-real-world.js +265 -0
- package/dist/tests/test-chalk-regression.js +327 -0
- package/dist/tests/test-chalk-resolution-issue.js +304 -0
- package/dist/tests/test-fraim-install-chalk-issue.js +254 -0
- package/dist/tests/test-fraim-issues.js +59 -0
- package/dist/tests/test-genericization.js +1 -1
- package/dist/tests/test-mcp-connection.js +166 -0
- package/dist/tests/test-mcp-issue-integration.js +144 -0
- package/dist/tests/test-mcp-lifecycle-methods.js +312 -0
- package/dist/tests/test-node-compatibility.js +71 -0
- package/dist/tests/test-npm-install.js +66 -0
- package/dist/tests/test-npm-resolution-diagnostic.js +140 -0
- package/dist/tests/test-session-rehydration.js +145 -0
- package/dist/tests/test-standalone.js +2 -8
- package/dist/tests/test-telemetry.js +190 -0
- package/package.json +10 -8
- package/registry/agent-guardrails.md +62 -54
- package/registry/rules/agent-success-criteria.md +52 -0
- package/registry/rules/agent-testing-guidelines.md +502 -502
- package/registry/rules/communication.md +121 -121
- package/registry/rules/continuous-learning.md +54 -54
- package/registry/rules/ephemeral-execution.md +10 -5
- package/registry/rules/hitl-ppe-record-analysis.md +302 -302
- package/registry/rules/local-development.md +251 -251
- package/registry/rules/software-development-lifecycle.md +104 -104
- package/registry/rules/successful-debugging-patterns.md +482 -478
- package/registry/rules/telemetry.md +67 -0
- package/registry/scripts/build-scripts-generator.ts +216 -215
- package/registry/scripts/cleanup-branch.ts +303 -284
- package/registry/scripts/code-quality-check.sh +559 -559
- package/registry/scripts/detect-tautological-tests.sh +38 -38
- package/registry/scripts/evaluate-code-quality.ts +1 -1
- package/registry/scripts/generate-engagement-emails.ts +744 -744
- package/registry/scripts/generic-issues-api.ts +110 -150
- package/registry/scripts/newsletter-helpers.ts +874 -874
- package/registry/scripts/openapi-generator.ts +695 -693
- package/registry/scripts/performance/profile-server.ts +5 -3
- package/registry/scripts/prep-issue.sh +468 -455
- package/registry/scripts/validate-openapi-limits.ts +366 -365
- package/registry/scripts/validate-test-coverage.ts +280 -280
- package/registry/scripts/verify-pr-comments.sh +70 -70
- package/registry/scripts/verify-test-coverage.ts +1 -1
- package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -53
- package/registry/templates/evidence/Implementation-BugEvidence.md +85 -85
- package/registry/templates/evidence/Implementation-FeatureEvidence.md +120 -120
- package/registry/templates/marketing/HBR-ARTICLE-TEMPLATE.md +66 -0
- package/registry/workflows/bootstrap/create-architecture.md +2 -2
- package/registry/workflows/bootstrap/evaluate-code-quality.md +3 -3
- package/registry/workflows/bootstrap/verify-test-coverage.md +2 -2
- package/registry/workflows/customer-development/insight-analysis.md +156 -156
- package/registry/workflows/customer-development/interview-preparation.md +421 -421
- package/registry/workflows/customer-development/strategic-brainstorming.md +146 -146
- package/registry/workflows/customer-development/thank-customers.md +193 -191
- package/registry/workflows/customer-development/weekly-newsletter.md +362 -352
- package/registry/workflows/improve-fraim/contribute.md +32 -0
- package/registry/workflows/improve-fraim/file-issue.md +32 -0
- package/registry/workflows/marketing/hbr-article.md +73 -0
- package/registry/workflows/performance/analyze-performance.md +63 -59
- package/registry/workflows/product-building/design.md +3 -2
- package/registry/workflows/product-building/implement.md +4 -3
- package/registry/workflows/product-building/prep-issue.md +28 -17
- package/registry/workflows/product-building/resolve.md +3 -2
- package/registry/workflows/product-building/retrospect.md +3 -2
- package/registry/workflows/product-building/spec.md +5 -4
- package/registry/workflows/product-building/test.md +3 -2
- package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +562 -562
- package/registry/workflows/replicate/website-discovery-analysis.md +3 -3
- package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +632 -632
- package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +669 -669
- package/tsconfig.json +2 -1
|
@@ -8,11 +8,11 @@ const commander_1 = require("commander");
|
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
11
|
+
const first_run_1 = require("../setup/first-run");
|
|
12
|
+
const sync_1 = require("./sync");
|
|
13
|
+
const types_1 = require("../../fraim/types");
|
|
14
|
+
const git_utils_1 = require("../../utils/git-utils");
|
|
15
|
+
const version_utils_1 = require("../../utils/version-utils");
|
|
16
16
|
const runInit = async () => {
|
|
17
17
|
const projectRoot = process.cwd();
|
|
18
18
|
const fraimDir = path_1.default.join(projectRoot, '.fraim');
|
|
@@ -26,18 +26,18 @@ const runInit = async () => {
|
|
|
26
26
|
console.log(chalk_1.default.yellow('ℹ️ .fraim directory already exists'));
|
|
27
27
|
}
|
|
28
28
|
if (!fs_1.default.existsSync(configPath)) {
|
|
29
|
-
const remoteInfo = (0,
|
|
29
|
+
const remoteInfo = (0, git_utils_1.getGitRemoteInfo)();
|
|
30
30
|
const config = {
|
|
31
|
-
...
|
|
32
|
-
version: (0,
|
|
31
|
+
...types_1.DEFAULT_FRAIM_CONFIG,
|
|
32
|
+
version: (0, version_utils_1.getFraimVersion)(),
|
|
33
33
|
project: {
|
|
34
|
-
...
|
|
34
|
+
...types_1.DEFAULT_FRAIM_CONFIG.project,
|
|
35
35
|
name: path_1.default.basename(projectRoot)
|
|
36
36
|
},
|
|
37
37
|
git: {
|
|
38
|
-
...
|
|
39
|
-
repoOwner: remoteInfo.owner ||
|
|
40
|
-
repoName: remoteInfo.repo ||
|
|
38
|
+
...types_1.DEFAULT_FRAIM_CONFIG.git,
|
|
39
|
+
repoOwner: remoteInfo.owner || types_1.DEFAULT_FRAIM_CONFIG.git.repoOwner,
|
|
40
|
+
repoName: remoteInfo.repo || types_1.DEFAULT_FRAIM_CONFIG.git.repoName
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
43
|
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
@@ -53,9 +53,9 @@ const runInit = async () => {
|
|
|
53
53
|
});
|
|
54
54
|
console.log(chalk_1.default.blue('\n🎉 FRAIM initialized successfully!'));
|
|
55
55
|
// Sync workflows from registry
|
|
56
|
-
await (0,
|
|
56
|
+
await (0, sync_1.runSync)({});
|
|
57
57
|
// Trigger First Run Experience
|
|
58
|
-
await (0,
|
|
58
|
+
await (0, first_run_1.runFirstRunExperience)();
|
|
59
59
|
process.exit(0);
|
|
60
60
|
};
|
|
61
61
|
exports.runInit = runInit;
|
|
@@ -10,11 +10,11 @@ const path_1 = __importDefault(require("path"));
|
|
|
10
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
11
|
const digest_utils_1 = require("../../utils/digest-utils");
|
|
12
12
|
const stub_generator_1 = require("../../utils/stub-generator");
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
const config_loader_1 = require("../../fraim/config-loader");
|
|
14
|
+
const version_utils_1 = require("../../utils/version-utils");
|
|
15
15
|
const runSync = async (options) => {
|
|
16
16
|
const projectRoot = process.cwd();
|
|
17
|
-
const config = (0,
|
|
17
|
+
const config = (0, config_loader_1.loadFraimConfig)();
|
|
18
18
|
const fraimDir = path_1.default.join(projectRoot, '.fraim');
|
|
19
19
|
const workflowsRelativePath = config.customizations?.workflowsPath || '.fraim/workflows';
|
|
20
20
|
const workflowsDir = path_1.default.resolve(projectRoot, workflowsRelativePath);
|
|
@@ -43,7 +43,7 @@ const runSync = async (options) => {
|
|
|
43
43
|
if (fs_1.default.existsSync(configPath)) {
|
|
44
44
|
try {
|
|
45
45
|
const currentConfig = JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
|
|
46
|
-
const newVersion = (0,
|
|
46
|
+
const newVersion = (0, version_utils_1.getFraimVersion)();
|
|
47
47
|
if (currentConfig.version !== newVersion) {
|
|
48
48
|
currentConfig.version = newVersion;
|
|
49
49
|
fs_1.default.writeFileSync(configPath, JSON.stringify(currentConfig, null, 2));
|
package/dist/src/cli/fraim.js
CHANGED
|
@@ -5,39 +5,41 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
8
|
+
const init_1 = require("./commands/init");
|
|
9
|
+
const sync_1 = require("./commands/sync");
|
|
10
|
+
const doctor_1 = require("./commands/doctor");
|
|
11
|
+
const list_1 = require("./commands/list");
|
|
12
|
+
const wizard_1 = require("./commands/wizard");
|
|
13
13
|
const fs_1 = __importDefault(require("fs"));
|
|
14
14
|
const path_1 = __importDefault(require("path"));
|
|
15
15
|
const program = new commander_1.Command();
|
|
16
16
|
// Load version from package.json
|
|
17
17
|
// Handle both src/ (dev) and dist/ (prod) execution paths
|
|
18
|
-
let packageJson;
|
|
18
|
+
let packageJson = { version: 'unknown' };
|
|
19
19
|
try {
|
|
20
|
-
//
|
|
21
|
-
|
|
20
|
+
// Robustly find package.json by searching upwards from this file's directory
|
|
21
|
+
let currentDir = __dirname;
|
|
22
|
+
let found = false;
|
|
23
|
+
for (let i = 0; i < 5; i++) { // Max search depth 5
|
|
24
|
+
const pkgPath = path_1.default.join(currentDir, 'package.json');
|
|
25
|
+
if (fs_1.default.existsSync(pkgPath)) {
|
|
26
|
+
packageJson = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8'));
|
|
27
|
+
found = true;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
currentDir = path_1.default.dirname(currentDir);
|
|
31
|
+
}
|
|
22
32
|
}
|
|
23
33
|
catch (e) {
|
|
24
|
-
|
|
25
|
-
// Try 3 levels up (dist/src/cli -> root) - for compiled js
|
|
26
|
-
packageJson = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '..', '..', '..', 'package.json'), 'utf8'));
|
|
27
|
-
}
|
|
28
|
-
catch (e2) {
|
|
29
|
-
// Fallback or throw
|
|
30
|
-
console.error('Could not locate package.json');
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
34
|
+
console.warn('⚠️ Could not fully resolve package.json version.');
|
|
33
35
|
}
|
|
34
36
|
program
|
|
35
37
|
.name('fraim')
|
|
36
38
|
.description('FRAIM Framework CLI - Manage your AI workflows and rules')
|
|
37
39
|
.version(packageJson.version);
|
|
38
|
-
program.addCommand(
|
|
39
|
-
program.addCommand(
|
|
40
|
-
program.addCommand(
|
|
41
|
-
program.addCommand(
|
|
42
|
-
program.addCommand(
|
|
40
|
+
program.addCommand(init_1.initCommand);
|
|
41
|
+
program.addCommand(sync_1.syncCommand);
|
|
42
|
+
program.addCommand(doctor_1.doctorCommand);
|
|
43
|
+
program.addCommand(list_1.listCommand);
|
|
44
|
+
program.addCommand(wizard_1.wizardCommand);
|
|
43
45
|
program.parse(process.argv);
|
|
@@ -47,12 +47,19 @@ const runFirstRunExperience = async () => {
|
|
|
47
47
|
}
|
|
48
48
|
console.log(chalk_1.default.blue('\n👋 Welcome to FRAIM! Let\'s get you set up.\n'));
|
|
49
49
|
// 1. Ask for FRAIM key
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
let response;
|
|
51
|
+
try {
|
|
52
|
+
response = await (0, prompts_1.default)({
|
|
53
|
+
type: 'text',
|
|
54
|
+
name: 'fraimKey',
|
|
55
|
+
message: 'Do you have a FRAIM key? (Input key or press Enter to skip)',
|
|
56
|
+
validate: (value) => true // Optional input
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
console.warn(chalk_1.default.yellow('\n⚠️ Interactive prompts experienced an issue. Skipping setup.\n'));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
56
63
|
if (response.fraimKey && response.fraimKey.trim().length > 0) {
|
|
57
64
|
const key = response.fraimKey.trim();
|
|
58
65
|
console.log(chalk_1.default.green('\n✅ Key received.'));
|
|
@@ -17,32 +17,43 @@ class FraimDbService {
|
|
|
17
17
|
this.db = this.client.db(dbName);
|
|
18
18
|
// Use fraim_ prefix for standalone server collections
|
|
19
19
|
this.keysCollection = this.db.collection('fraim_api_keys');
|
|
20
|
-
this.
|
|
20
|
+
this.sessionsCollection = this.db.collection('fraim_telemetry_sessions');
|
|
21
21
|
// Create indexes
|
|
22
22
|
await this.keysCollection.createIndex({ key: 1 }, { unique: true });
|
|
23
|
-
await this.
|
|
24
|
-
await this.
|
|
23
|
+
await this.sessionsCollection.createIndex({ sessionId: 1 }, { unique: true });
|
|
24
|
+
await this.sessionsCollection.createIndex({ userId: 1 });
|
|
25
|
+
await this.sessionsCollection.createIndex({ lastActive: -1 });
|
|
25
26
|
console.log(`✅ Connected to Fraim DB: ${dbName}`);
|
|
26
27
|
}
|
|
28
|
+
async createSession(session) {
|
|
29
|
+
if (!this.sessionsCollection)
|
|
30
|
+
throw new Error('DB not connected');
|
|
31
|
+
await this.sessionsCollection.insertOne(session);
|
|
32
|
+
}
|
|
33
|
+
async updateSessionActivity(sessionId, lastActive) {
|
|
34
|
+
if (!this.sessionsCollection)
|
|
35
|
+
return; // Fail silently
|
|
36
|
+
await this.sessionsCollection.updateOne({ sessionId }, { $set: { lastActive } });
|
|
37
|
+
}
|
|
27
38
|
async verifyApiKey(key) {
|
|
28
39
|
if (!this.keysCollection)
|
|
29
40
|
throw new Error('DB not connected');
|
|
30
41
|
const apiKey = await this.keysCollection.findOne({ key, isActive: true });
|
|
31
42
|
return apiKey;
|
|
32
43
|
}
|
|
33
|
-
async
|
|
34
|
-
if (!this.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
44
|
+
async getActiveSessionByApiKey(key) {
|
|
45
|
+
if (!this.keysCollection || !this.sessionsCollection)
|
|
46
|
+
throw new Error('DB not connected');
|
|
47
|
+
// 1. Get user for this key
|
|
48
|
+
const apiKeyData = await this.verifyApiKey(key);
|
|
49
|
+
if (!apiKeyData)
|
|
50
|
+
return null;
|
|
51
|
+
// 2. Get latest session for this user (within last 24h)
|
|
52
|
+
const dayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
53
|
+
const session = await this.sessionsCollection.findOne({ userId: apiKeyData.userId, lastActive: { $gt: dayAgo } }, { sort: { lastActive: -1 } });
|
|
54
|
+
return session;
|
|
45
55
|
}
|
|
56
|
+
// ... (rest of methods)
|
|
46
57
|
async listApiKeys() {
|
|
47
58
|
if (!this.keysCollection)
|
|
48
59
|
throw new Error('DB not connected');
|
|
@@ -0,0 +1,67 @@
|
|
|
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.fileFraimIssue = fileFraimIssue;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const config_loader_1 = require("./config-loader");
|
|
9
|
+
/**
|
|
10
|
+
* File a GitHub issue in the configured FRAIM repository
|
|
11
|
+
*/
|
|
12
|
+
async function fileFraimIssue(params) {
|
|
13
|
+
const { title, body, labels, dryRun } = params;
|
|
14
|
+
// Load config
|
|
15
|
+
const config = (0, config_loader_1.loadFraimConfig)();
|
|
16
|
+
const owner = config.git?.repoOwner || 'mathursrus';
|
|
17
|
+
const repo = config.git?.repoName || 'FRAIM';
|
|
18
|
+
const url = `https://api.github.com/repos/${owner}/${repo}/issues`;
|
|
19
|
+
const payload = {
|
|
20
|
+
title,
|
|
21
|
+
body
|
|
22
|
+
};
|
|
23
|
+
if (labels && labels.length > 0) {
|
|
24
|
+
payload.labels = labels;
|
|
25
|
+
}
|
|
26
|
+
if (dryRun) {
|
|
27
|
+
return {
|
|
28
|
+
success: true,
|
|
29
|
+
dryRun: true,
|
|
30
|
+
message: `[DRY RUN] Would POST to ${url} with title: "${title}"`
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const token = process.env.GITHUB_TOKEN;
|
|
34
|
+
if (!token) {
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
message: 'GITHUB_TOKEN environment variable is required'
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const response = await axios_1.default.post(url, payload, {
|
|
42
|
+
headers: {
|
|
43
|
+
'Authorization': `Bearer ${token}`,
|
|
44
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
success: true,
|
|
50
|
+
issueNumber: response.data.number,
|
|
51
|
+
htmlUrl: response.data.html_url
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
let errorMessage = 'Unknown error';
|
|
56
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
57
|
+
errorMessage = `Status: ${error.response?.status} - ${JSON.stringify(error.response?.data)}`;
|
|
58
|
+
}
|
|
59
|
+
else if (error instanceof Error) {
|
|
60
|
+
errorMessage = error.message;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
message: errorMessage
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|