fraim-framework 2.0.64 → 2.0.65
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/bin/fraim-mcp.js +52 -19
- package/bin/fraim.js +23 -0
- package/dist/src/cli/commands/add-ide.js +53 -14
- package/dist/src/cli/commands/doctor.js +12 -24
- package/dist/src/cli/commands/init-project.js +0 -3
- package/dist/src/cli/commands/init.js +0 -2
- package/dist/src/cli/commands/mcp.js +65 -0
- package/dist/src/cli/commands/setup.js +17 -1
- package/dist/src/cli/commands/sync.js +173 -104
- package/dist/src/cli/setup/auto-mcp-setup.js +6 -4
- package/dist/src/cli/setup/mcp-config-generator.js +65 -41
- package/dist/src/fraim/issue-tracking/ado-provider.js +304 -0
- package/dist/src/fraim/issue-tracking/factory.js +63 -0
- package/dist/src/fraim/issue-tracking/github-provider.js +200 -0
- package/dist/src/fraim/issue-tracking/types.js +7 -0
- package/dist/src/fraim/issue-tracking-config.js +83 -0
- package/dist/src/local-mcp-server/stdio-server.js +23 -3
- package/dist/src/utils/remote-sync.js +130 -0
- package/package.json +2 -3
- package/dist/src/utils/enforcement-utils.js +0 -239
- package/dist/src/utils/validate-workflows.js +0 -101
- package/registry/scripts/cleanup-branch.ts +0 -341
- package/registry/scripts/code-quality-check.sh +0 -566
- package/registry/scripts/comprehensive-explorer.py +0 -297
- package/registry/scripts/create-git-labels.sh +0 -49
- package/registry/scripts/create-website-structure.js +0 -562
- package/registry/scripts/detect-tautological-tests.sh +0 -38
- package/registry/scripts/evaluate-code-quality.ts +0 -36
- package/registry/scripts/exec-with-timeout.ts +0 -122
- package/registry/scripts/generate-engagement-emails.ts +0 -830
- package/registry/scripts/interactive-explorer.py +0 -270
- package/registry/scripts/markdown-to-pdf.js +0 -395
- package/registry/scripts/newsletter-helpers.ts +0 -777
- package/registry/scripts/pdf-styles.css +0 -172
- package/registry/scripts/prep-issue.sh +0 -548
- package/registry/scripts/productivity/build-productivity-csv.mjs +0 -242
- package/registry/scripts/productivity/fetch-pr-details.mjs +0 -144
- package/registry/scripts/productivity/productivity-report.sh +0 -147
- package/registry/scripts/profile-server.ts +0 -426
- package/registry/scripts/run-thank-you-workflow.ts +0 -122
- package/registry/scripts/scrape-site.py +0 -302
- package/registry/scripts/send-newsletter-simple.ts +0 -102
- package/registry/scripts/send-thank-you-emails.ts +0 -57
- package/registry/scripts/validate-openapi-limits.ts +0 -366
- package/registry/scripts/validate-test-coverage.ts +0 -280
- package/registry/scripts/verify-pr-comments.sh +0 -74
- package/registry/scripts/verify-test-coverage.ts +0 -36
- package/registry/stubs/workflows/azure/cost-optimization.md +0 -11
- package/registry/stubs/workflows/bootstrap/create-architecture.md +0 -11
- package/registry/stubs/workflows/bootstrap/detect-broken-windows.md +0 -11
- package/registry/stubs/workflows/bootstrap/evaluate-code-quality.md +0 -11
- package/registry/stubs/workflows/bootstrap/verify-test-coverage.md +0 -11
- package/registry/stubs/workflows/brainstorming/blue-sky-brainstorming.md +0 -11
- package/registry/stubs/workflows/brainstorming/codebase-brainstorming.md +0 -11
- package/registry/stubs/workflows/business-development/create-business-plan.md +0 -11
- package/registry/stubs/workflows/business-development/ideate-business-opportunity.md +0 -11
- package/registry/stubs/workflows/business-development/price-product.md +0 -18
- package/registry/stubs/workflows/compliance/detect-compliance-requirements.md +0 -11
- package/registry/stubs/workflows/compliance/generate-audit-evidence.md +0 -11
- package/registry/stubs/workflows/compliance/soc2-evidence-generator.md +0 -11
- package/registry/stubs/workflows/customer-development/insight-analysis.md +0 -11
- package/registry/stubs/workflows/customer-development/insight-triage.md +0 -11
- package/registry/stubs/workflows/customer-development/interview-preparation.md +0 -11
- package/registry/stubs/workflows/customer-development/linkedin-outreach.md +0 -11
- package/registry/stubs/workflows/customer-development/strategic-brainstorming.md +0 -11
- package/registry/stubs/workflows/customer-development/thank-customers.md +0 -11
- package/registry/stubs/workflows/customer-development/user-survey-dispatch.md +0 -11
- package/registry/stubs/workflows/customer-development/users-to-target.md +0 -11
- package/registry/stubs/workflows/customer-development/weekly-newsletter.md +0 -11
- package/registry/stubs/workflows/deploy/cloud-deployment.md +0 -11
- package/registry/stubs/workflows/improve-fraim/contribute.md +0 -11
- package/registry/stubs/workflows/improve-fraim/file-issue.md +0 -11
- package/registry/stubs/workflows/learning/build-skillset.md +0 -11
- package/registry/stubs/workflows/learning/synthesize-learnings.md +0 -11
- package/registry/stubs/workflows/legal/contract-review-analysis.md +0 -11
- package/registry/stubs/workflows/legal/nda.md +0 -11
- package/registry/stubs/workflows/legal/patent-filing.md +0 -11
- package/registry/stubs/workflows/legal/saas-contract-development.md +0 -11
- package/registry/stubs/workflows/legal/trademark-filing.md +0 -11
- package/registry/stubs/workflows/marketing/content-creation.md +0 -11
- package/registry/stubs/workflows/marketing/convert-to-pdf.md +0 -11
- package/registry/stubs/workflows/marketing/create-modern-website.md +0 -11
- package/registry/stubs/workflows/marketing/domain-registration.md +0 -11
- package/registry/stubs/workflows/marketing/hbr-article.md +0 -11
- package/registry/stubs/workflows/marketing/launch-checklist.md +0 -11
- package/registry/stubs/workflows/marketing/marketing-strategy.md +0 -11
- package/registry/stubs/workflows/marketing/storytelling.md +0 -11
- package/registry/stubs/workflows/performance/analyze-performance.md +0 -11
- package/registry/stubs/workflows/product-building/design.md +0 -11
- package/registry/stubs/workflows/product-building/implement.md +0 -11
- package/registry/stubs/workflows/product-building/iterate-on-pr-comments.md +0 -11
- package/registry/stubs/workflows/product-building/prep-issue.md +0 -11
- package/registry/stubs/workflows/product-building/prototype.md +0 -11
- package/registry/stubs/workflows/product-building/resolve.md +0 -11
- package/registry/stubs/workflows/product-building/retrospect.md +0 -11
- package/registry/stubs/workflows/product-building/spec.md +0 -11
- package/registry/stubs/workflows/product-building/test.md +0 -11
- package/registry/stubs/workflows/productivity-report/productivity-report.md +0 -11
- package/registry/stubs/workflows/quality-assurance/browser-validation.md +0 -11
- package/registry/stubs/workflows/quality-assurance/iterative-improvement-cycle.md +0 -11
- package/registry/stubs/workflows/replicate/replicate-discovery.md +0 -11
- package/registry/stubs/workflows/replicate/replicate-to-issues.md +0 -11
- package/registry/stubs/workflows/reviewer/review-implementation-vs-design-spec.md +0 -11
- package/registry/stubs/workflows/reviewer/review-implementation-vs-feature-spec.md +0 -11
- package/registry/stubs/workflows/startup-credits/aws-activate-application.md +0 -11
- package/registry/stubs/workflows/startup-credits/google-cloud-application.md +0 -11
- package/registry/stubs/workflows/startup-credits/microsoft-azure-application.md +0 -11
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -9,17 +42,32 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
9
42
|
const path_1 = __importDefault(require("path"));
|
|
10
43
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
44
|
const child_process_1 = require("child_process");
|
|
12
|
-
const digest_utils_1 = require("../../utils/digest-utils");
|
|
13
45
|
const config_loader_1 = require("../../fraim/config-loader");
|
|
14
46
|
const version_utils_1 = require("../../utils/version-utils");
|
|
15
47
|
const script_sync_utils_1 = require("../../utils/script-sync-utils");
|
|
48
|
+
/**
|
|
49
|
+
* Load API key from user-level config (~/.fraim/config.json)
|
|
50
|
+
*/
|
|
51
|
+
function loadUserApiKey() {
|
|
52
|
+
const userConfigPath = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
|
|
53
|
+
if (!fs_1.default.existsSync(userConfigPath)) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const userConfig = JSON.parse(fs_1.default.readFileSync(userConfigPath, 'utf8'));
|
|
58
|
+
return userConfig.apiKey;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.warn(chalk_1.default.yellow('⚠️ Failed to read user-level config for API key'));
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
16
65
|
const runSync = async (options) => {
|
|
17
66
|
const projectRoot = process.cwd();
|
|
18
67
|
const config = (0, config_loader_1.loadFraimConfig)();
|
|
19
68
|
const fraimDir = path_1.default.join(projectRoot, '.fraim');
|
|
20
69
|
const workflowsRelativePath = config.customizations?.workflowsPath || '.fraim/workflows';
|
|
21
70
|
const workflowsDir = path_1.default.resolve(projectRoot, workflowsRelativePath);
|
|
22
|
-
const digestPath = path_1.default.join(fraimDir, '.digest');
|
|
23
71
|
// Check for CLI updates first (but skip during installation to prevent loops)
|
|
24
72
|
const isPostInstall = process.env.npm_lifecycle_event === 'postinstall' ||
|
|
25
73
|
process.env.npm_lifecycle_script === 'postinstall';
|
|
@@ -29,138 +77,143 @@ const runSync = async (options) => {
|
|
|
29
77
|
else if (isPostInstall) {
|
|
30
78
|
console.log(chalk_1.default.gray('⏭️ Skipping auto-update check during installation to prevent loops.'));
|
|
31
79
|
}
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
80
|
+
// Try remote sync first if API key is available (from user config, project config, or env)
|
|
81
|
+
const apiKey = loadUserApiKey() || config.apiKey || process.env.FRAIM_API_KEY;
|
|
82
|
+
if (apiKey) {
|
|
83
|
+
console.log(chalk_1.default.blue('🔄 Syncing FRAIM workflows from remote server...'));
|
|
84
|
+
try {
|
|
85
|
+
const { syncFromRemote } = await Promise.resolve().then(() => __importStar(require('../../utils/remote-sync.js')));
|
|
86
|
+
const result = await syncFromRemote({
|
|
87
|
+
remoteUrl: config.remoteUrl, // Will use default if not set
|
|
88
|
+
apiKey: apiKey,
|
|
89
|
+
projectRoot,
|
|
90
|
+
skipUpdates: options.skipUpdates || false
|
|
91
|
+
});
|
|
92
|
+
if (result.success) {
|
|
93
|
+
console.log(chalk_1.default.green(`✅ Successfully synced ${result.workflowsSynced} workflows and ${result.scriptsSynced} scripts from remote`));
|
|
94
|
+
// Update version in config.json
|
|
95
|
+
const configPath = path_1.default.join(fraimDir, 'config.json');
|
|
96
|
+
if (fs_1.default.existsSync(configPath)) {
|
|
97
|
+
try {
|
|
98
|
+
const currentConfig = JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
|
|
99
|
+
const newVersion = (0, version_utils_1.getFraimVersion)();
|
|
100
|
+
if (currentConfig.version !== newVersion) {
|
|
101
|
+
currentConfig.version = newVersion;
|
|
102
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(currentConfig, null, 2));
|
|
103
|
+
console.log(chalk_1.default.green(`✅ Updated FRAIM version to ${newVersion} in config.`));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
console.warn(chalk_1.default.yellow('⚠️ Could not update version in config.json.'));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.warn(chalk_1.default.yellow(`⚠️ Remote sync failed: ${result.error}, falling back to local sync`));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.warn(chalk_1.default.yellow(`⚠️ Remote sync failed: ${error.message}, falling back to local sync`));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Fallback to local sync (for development/testing or when remote is not configured)
|
|
121
|
+
console.log(chalk_1.default.blue('🔄 Syncing FRAIM workflows from local package...'));
|
|
122
|
+
// Find local stubs directory
|
|
35
123
|
let registryPath = path_1.default.join(__dirname, '../../../../registry/stubs');
|
|
36
124
|
if (!fs_1.default.existsSync(registryPath)) {
|
|
37
|
-
// Try 3 levels up (src/cli/commands -> root)
|
|
38
125
|
registryPath = path_1.default.join(__dirname, '../../../registry/stubs');
|
|
39
126
|
}
|
|
40
|
-
// Fallback for local development if running from within the framework repo itself
|
|
41
127
|
if (!fs_1.default.existsSync(registryPath)) {
|
|
42
128
|
registryPath = path_1.default.join(projectRoot, 'registry/stubs');
|
|
43
129
|
}
|
|
44
130
|
if (!fs_1.default.existsSync(registryPath)) {
|
|
45
|
-
console.error(chalk_1.default.red('❌ Stub registry not found
|
|
131
|
+
console.error(chalk_1.default.red('❌ Stub registry not found locally and no remote server configured.'));
|
|
132
|
+
console.error(chalk_1.default.yellow('💡 Configure remoteUrl and apiKey in .fraim/config.json to sync from remote server.'));
|
|
46
133
|
process.exit(1);
|
|
47
134
|
}
|
|
48
135
|
if (!fs_1.default.existsSync(workflowsDir)) {
|
|
49
136
|
fs_1.default.mkdirSync(workflowsDir, { recursive: true });
|
|
50
137
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
138
|
+
const registryWorkflowsPath = path_1.default.join(registryPath, 'workflows');
|
|
139
|
+
if (!fs_1.default.existsSync(registryWorkflowsPath)) {
|
|
140
|
+
console.log(chalk_1.default.yellow('⚠️ No workflow stubs found in local registry.'));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
// Get all workflow stubs from registry (recursive)
|
|
144
|
+
const getFiles = (dir) => {
|
|
145
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
146
|
+
const files = entries
|
|
147
|
+
.filter(e => !e.isDirectory() && e.name.endsWith('.md'))
|
|
148
|
+
.map(e => path_1.default.join(dir, e.name));
|
|
149
|
+
const folders = entries.filter(e => e.isDirectory());
|
|
150
|
+
for (const folder of folders) {
|
|
151
|
+
files.push(...getFiles(path_1.default.join(dir, folder.name)));
|
|
62
152
|
}
|
|
63
|
-
|
|
64
|
-
|
|
153
|
+
return files;
|
|
154
|
+
};
|
|
155
|
+
const registryFiles = getFiles(registryWorkflowsPath);
|
|
156
|
+
const copiedStubs = [];
|
|
157
|
+
for (const file of registryFiles) {
|
|
158
|
+
const fileName = path_1.default.basename(file);
|
|
159
|
+
const workflowName = fileName.replace('.md', '');
|
|
160
|
+
// Calculate relative path from registry/stubs/workflows to preserve structure
|
|
161
|
+
const relativePath = path_1.default.relative(registryWorkflowsPath, file);
|
|
162
|
+
const relativeDir = path_1.default.dirname(relativePath);
|
|
163
|
+
// Ensure target directory exists
|
|
164
|
+
const targetDir = path_1.default.join(workflowsDir, relativeDir);
|
|
165
|
+
if (!fs_1.default.existsSync(targetDir)) {
|
|
166
|
+
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
65
167
|
}
|
|
168
|
+
// Copy stub file directly
|
|
169
|
+
const stubContent = fs_1.default.readFileSync(file, 'utf8');
|
|
170
|
+
const stubPath = path_1.default.join(targetDir, fileName);
|
|
171
|
+
fs_1.default.writeFileSync(stubPath, stubContent);
|
|
172
|
+
copiedStubs.push(relativePath);
|
|
173
|
+
console.log(chalk_1.default.gray(` + ${workflowName} (${relativeDir === '.' ? 'root' : relativeDir})`));
|
|
66
174
|
}
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
175
|
+
// Cleanup stubs that no longer exist in registry
|
|
176
|
+
const getLocalStubs = (dir) => {
|
|
177
|
+
if (!fs_1.default.existsSync(dir))
|
|
178
|
+
return [];
|
|
179
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
180
|
+
const files = entries
|
|
181
|
+
.filter(e => !e.isDirectory() && e.name.endsWith('.md'))
|
|
182
|
+
.map(e => path_1.default.relative(workflowsDir, path_1.default.join(dir, e.name)));
|
|
183
|
+
const folders = entries.filter(e => e.isDirectory());
|
|
184
|
+
for (const folder of folders) {
|
|
185
|
+
files.push(...getLocalStubs(path_1.default.join(dir, folder.name)));
|
|
77
186
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const registryFiles = getFiles(registryWorkflowsPath);
|
|
92
|
-
const copiedStubs = [];
|
|
93
|
-
for (const file of registryFiles) {
|
|
94
|
-
// These are already stubs, just copy them directly
|
|
95
|
-
const fileName = path_1.default.basename(file);
|
|
96
|
-
const workflowName = fileName.replace('.md', '');
|
|
97
|
-
// Calculate relative path from registry/stubs/workflows to preserve structure
|
|
98
|
-
const relativePath = path_1.default.relative(registryWorkflowsPath, file);
|
|
99
|
-
const relativeDir = path_1.default.dirname(relativePath);
|
|
100
|
-
// Ensure target directory exists
|
|
101
|
-
const targetDir = path_1.default.join(workflowsDir, relativeDir);
|
|
102
|
-
if (!fs_1.default.existsSync(targetDir)) {
|
|
103
|
-
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
187
|
+
return files;
|
|
188
|
+
};
|
|
189
|
+
const localStubs = getLocalStubs(workflowsDir);
|
|
190
|
+
for (const stub of localStubs) {
|
|
191
|
+
const normalizedStub = stub.replace(/\\/g, '/');
|
|
192
|
+
const normalizedCopied = copiedStubs.map(s => s.replace(/\\/g, '/'));
|
|
193
|
+
if (!normalizedCopied.includes(normalizedStub)) {
|
|
194
|
+
fs_1.default.unlinkSync(path_1.default.join(workflowsDir, stub));
|
|
195
|
+
console.log(chalk_1.default.yellow(` - ${stub} (removed from registry)`));
|
|
196
|
+
try {
|
|
197
|
+
const dir = path_1.default.dirname(path_1.default.join(workflowsDir, stub));
|
|
198
|
+
if (fs_1.default.readdirSync(dir).length === 0) {
|
|
199
|
+
fs_1.default.rmdirSync(dir);
|
|
104
200
|
}
|
|
105
|
-
// Copy stub file directly
|
|
106
|
-
const stubContent = fs_1.default.readFileSync(file, 'utf8');
|
|
107
|
-
const stubPath = path_1.default.join(targetDir, fileName);
|
|
108
|
-
fs_1.default.writeFileSync(stubPath, stubContent);
|
|
109
|
-
copiedStubs.push(relativePath); // Store relative path for cleanup tracking
|
|
110
|
-
console.log(chalk_1.default.gray(` + ${workflowName} (${relativeDir === '.' ? 'root' : relativeDir})`));
|
|
111
201
|
}
|
|
112
|
-
|
|
113
|
-
// Helper to get all local stubs recursively
|
|
114
|
-
const getLocalStubs = (dir) => {
|
|
115
|
-
if (!fs_1.default.existsSync(dir))
|
|
116
|
-
return [];
|
|
117
|
-
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
118
|
-
const files = entries
|
|
119
|
-
.filter(e => !e.isDirectory() && e.name.endsWith('.md'))
|
|
120
|
-
.map(e => path_1.default.relative(workflowsDir, path_1.default.join(dir, e.name)));
|
|
121
|
-
const folders = entries.filter(e => e.isDirectory());
|
|
122
|
-
for (const folder of folders) {
|
|
123
|
-
files.push(...getLocalStubs(path_1.default.join(dir, folder.name)));
|
|
124
|
-
}
|
|
125
|
-
return files;
|
|
126
|
-
};
|
|
127
|
-
const localStubs = getLocalStubs(workflowsDir);
|
|
128
|
-
for (const stub of localStubs) {
|
|
129
|
-
// standardise path separators for comparison
|
|
130
|
-
const normalizedStub = stub.replace(/\\/g, '/');
|
|
131
|
-
const normalizedCopied = copiedStubs.map(s => s.replace(/\\/g, '/'));
|
|
132
|
-
if (!normalizedCopied.includes(normalizedStub)) {
|
|
133
|
-
fs_1.default.unlinkSync(path_1.default.join(workflowsDir, stub));
|
|
134
|
-
console.log(chalk_1.default.yellow(` - ${stub} (removed from registry)`));
|
|
135
|
-
// Cleanup empty directories
|
|
136
|
-
try {
|
|
137
|
-
const dir = path_1.default.dirname(path_1.default.join(workflowsDir, stub));
|
|
138
|
-
if (fs_1.default.readdirSync(dir).length === 0) {
|
|
139
|
-
fs_1.default.rmdirSync(dir);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
catch (e) { }
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
fs_1.default.writeFileSync(digestPath, currentDigest);
|
|
146
|
-
console.log(chalk_1.default.green(`\n✅ Workflow sync complete. Copied ${copiedStubs.length} stubs.`));
|
|
202
|
+
catch (e) { }
|
|
147
203
|
}
|
|
148
204
|
}
|
|
149
|
-
|
|
205
|
+
console.log(chalk_1.default.green(`\n✅ Workflow sync complete. Copied ${copiedStubs.length} stubs.`));
|
|
206
|
+
// Sync scripts
|
|
150
207
|
console.log(chalk_1.default.blue('\n🔄 Syncing FRAIM scripts to user directory...'));
|
|
151
|
-
// Scripts are packaged separately from stubs, find the correct registry path
|
|
152
|
-
// Try 4 levels up (dist/src/cli/commands -> root)
|
|
153
208
|
let scriptsRegistryPath = path_1.default.join(__dirname, '../../../../registry');
|
|
154
209
|
if (!fs_1.default.existsSync(path_1.default.join(scriptsRegistryPath, 'scripts'))) {
|
|
155
|
-
// Try 3 levels up (src/cli/commands -> root)
|
|
156
210
|
scriptsRegistryPath = path_1.default.join(__dirname, '../../../registry');
|
|
157
211
|
}
|
|
158
|
-
// Fallback for local development if running from within the framework repo itself
|
|
159
212
|
if (!fs_1.default.existsSync(path_1.default.join(scriptsRegistryPath, 'scripts'))) {
|
|
160
213
|
scriptsRegistryPath = path_1.default.join(projectRoot, 'registry');
|
|
161
214
|
}
|
|
162
215
|
if (!fs_1.default.existsSync(path_1.default.join(scriptsRegistryPath, 'scripts'))) {
|
|
163
|
-
console.error(chalk_1.default.red('❌ Scripts registry not found.
|
|
216
|
+
console.error(chalk_1.default.red('❌ Scripts registry not found.'));
|
|
164
217
|
process.exit(1);
|
|
165
218
|
}
|
|
166
219
|
const syncResult = (0, script_sync_utils_1.syncScriptsToUserDirectory)(scriptsRegistryPath);
|
|
@@ -171,6 +224,22 @@ const runSync = async (options) => {
|
|
|
171
224
|
else {
|
|
172
225
|
console.log(chalk_1.default.green('✅ Scripts are already in sync.'));
|
|
173
226
|
}
|
|
227
|
+
// Update version in config.json
|
|
228
|
+
const configPath = path_1.default.join(fraimDir, 'config.json');
|
|
229
|
+
if (fs_1.default.existsSync(configPath)) {
|
|
230
|
+
try {
|
|
231
|
+
const currentConfig = JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
|
|
232
|
+
const newVersion = (0, version_utils_1.getFraimVersion)();
|
|
233
|
+
if (currentConfig.version !== newVersion) {
|
|
234
|
+
currentConfig.version = newVersion;
|
|
235
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(currentConfig, null, 2));
|
|
236
|
+
console.log(chalk_1.default.green(`✅ Updated FRAIM version to ${newVersion} in config.`));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch (e) {
|
|
240
|
+
console.warn(chalk_1.default.yellow('⚠️ Could not update version in config.json.'));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
174
243
|
};
|
|
175
244
|
exports.runSync = runSync;
|
|
176
245
|
async function checkAndUpdateCLI() {
|
|
@@ -11,7 +11,7 @@ const prompts_1 = __importDefault(require("prompts"));
|
|
|
11
11
|
const ide_detector_1 = require("./ide-detector");
|
|
12
12
|
const mcp_config_generator_1 = require("./mcp-config-generator");
|
|
13
13
|
const token_validator_1 = require("./token-validator");
|
|
14
|
-
const promptForIDESelection = async (detectedIDEs) => {
|
|
14
|
+
const promptForIDESelection = async (detectedIDEs, githubToken) => {
|
|
15
15
|
console.log(chalk_1.default.green(`✅ Found ${detectedIDEs.length} IDEs that can be configured with FRAIM:\n`));
|
|
16
16
|
detectedIDEs.forEach((ide, index) => {
|
|
17
17
|
const configExists = fs_1.default.existsSync((0, ide_detector_1.expandPath)(ide.configPath));
|
|
@@ -20,10 +20,12 @@ const promptForIDESelection = async (detectedIDEs) => {
|
|
|
20
20
|
console.log(chalk_1.default.white(` ${index + 1}. ${ide.name} ${statusIcon} (${statusText})`));
|
|
21
21
|
console.log(chalk_1.default.gray(` Config: ${ide.configPath}`));
|
|
22
22
|
});
|
|
23
|
-
console.log(chalk_1.default.blue('
|
|
23
|
+
console.log(chalk_1.default.blue('FRAIM will add these MCP servers to selected IDEs:'));
|
|
24
24
|
console.log(chalk_1.default.gray(' • fraim (required for FRAIM workflows)'));
|
|
25
25
|
console.log(chalk_1.default.gray(' • git (version control integration)'));
|
|
26
|
-
|
|
26
|
+
if (githubToken) {
|
|
27
|
+
console.log(chalk_1.default.gray(' • github (GitHub API access)'));
|
|
28
|
+
}
|
|
27
29
|
console.log(chalk_1.default.gray(' • playwright (browser automation)'));
|
|
28
30
|
console.log(chalk_1.default.yellow('\n💡 Existing MCP servers will be preserved - only missing servers will be added.'));
|
|
29
31
|
const response = await (0, prompts_1.default)({
|
|
@@ -314,7 +316,7 @@ const autoConfigureMCP = async (fraimKey, githubToken, selectedIDEs) => {
|
|
|
314
316
|
}
|
|
315
317
|
else {
|
|
316
318
|
// Interactive selection
|
|
317
|
-
idesToConfigure = await (0, exports.promptForIDESelection)(detectedIDEs);
|
|
319
|
+
idesToConfigure = await (0, exports.promptForIDESelection)(detectedIDEs, githubToken);
|
|
318
320
|
}
|
|
319
321
|
if (idesToConfigure.length === 0) {
|
|
320
322
|
console.log(chalk_1.default.yellow('⚠️ No IDEs selected for configuration.'));
|
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateMCPConfig = exports.generateWindsurfMCPServers = exports.generateCodexMCPServers = exports.generateKiroMCPServers = exports.generateClaudeMCPServers = exports.generateStandardMCPServers = void 0;
|
|
4
|
-
const generateStandardMCPServers = (fraimKey, githubToken) =>
|
|
5
|
-
|
|
4
|
+
const generateStandardMCPServers = (fraimKey, githubToken) => {
|
|
5
|
+
const servers = {
|
|
6
6
|
git: {
|
|
7
7
|
command: "npx",
|
|
8
8
|
args: ["-y", "@cyanheads/git-mcp-server"]
|
|
9
9
|
},
|
|
10
|
-
github: {
|
|
11
|
-
serverUrl: "https://api.githubcopilot.com/mcp/",
|
|
12
|
-
headers: {
|
|
13
|
-
Authorization: `Bearer ${githubToken}`
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
10
|
playwright: {
|
|
17
11
|
command: "npx",
|
|
18
12
|
args: ["-y", "@playwright/mcp"]
|
|
@@ -24,22 +18,25 @@ const generateStandardMCPServers = (fraimKey, githubToken) => ({
|
|
|
24
18
|
FRAIM_REMOTE_URL: "https://fraim.wellnessatwork.me"
|
|
25
19
|
}
|
|
26
20
|
}
|
|
21
|
+
};
|
|
22
|
+
// Only add GitHub server if token is provided
|
|
23
|
+
if (githubToken) {
|
|
24
|
+
servers.github = {
|
|
25
|
+
serverUrl: "https://api.githubcopilot.com/mcp/",
|
|
26
|
+
headers: {
|
|
27
|
+
Authorization: `Bearer ${githubToken}`
|
|
28
|
+
}
|
|
29
|
+
};
|
|
27
30
|
}
|
|
28
|
-
}
|
|
31
|
+
return { mcpServers: servers };
|
|
32
|
+
};
|
|
29
33
|
exports.generateStandardMCPServers = generateStandardMCPServers;
|
|
30
|
-
const generateClaudeMCPServers = (fraimKey, githubToken) =>
|
|
31
|
-
|
|
34
|
+
const generateClaudeMCPServers = (fraimKey, githubToken) => {
|
|
35
|
+
const servers = {
|
|
32
36
|
git: {
|
|
33
37
|
command: "npx",
|
|
34
38
|
args: ["-y", "@cyanheads/git-mcp-server"]
|
|
35
39
|
},
|
|
36
|
-
github: {
|
|
37
|
-
type: "http",
|
|
38
|
-
url: "https://api.githubcopilot.com/mcp/",
|
|
39
|
-
headers: {
|
|
40
|
-
Authorization: `Bearer ${githubToken}`
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
40
|
playwright: {
|
|
44
41
|
command: "npx",
|
|
45
42
|
args: ["-y", "@playwright/mcp"]
|
|
@@ -51,21 +48,26 @@ const generateClaudeMCPServers = (fraimKey, githubToken) => ({
|
|
|
51
48
|
FRAIM_REMOTE_URL: "https://fraim.wellnessatwork.me"
|
|
52
49
|
}
|
|
53
50
|
}
|
|
51
|
+
};
|
|
52
|
+
// Only add GitHub server if token is provided
|
|
53
|
+
if (githubToken) {
|
|
54
|
+
servers.github = {
|
|
55
|
+
type: "http",
|
|
56
|
+
url: "https://api.githubcopilot.com/mcp/",
|
|
57
|
+
headers: {
|
|
58
|
+
Authorization: `Bearer ${githubToken}`
|
|
59
|
+
}
|
|
60
|
+
};
|
|
54
61
|
}
|
|
55
|
-
}
|
|
62
|
+
return { mcpServers: servers };
|
|
63
|
+
};
|
|
56
64
|
exports.generateClaudeMCPServers = generateClaudeMCPServers;
|
|
57
|
-
const generateKiroMCPServers = (fraimKey, githubToken) =>
|
|
58
|
-
|
|
65
|
+
const generateKiroMCPServers = (fraimKey, githubToken) => {
|
|
66
|
+
const servers = {
|
|
59
67
|
git: {
|
|
60
68
|
command: "npx",
|
|
61
69
|
args: ["-y", "@cyanheads/git-mcp-server"]
|
|
62
70
|
},
|
|
63
|
-
github: {
|
|
64
|
-
url: "https://api.githubcopilot.com/mcp/",
|
|
65
|
-
headers: {
|
|
66
|
-
Authorization: `Bearer ${githubToken}`
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
71
|
playwright: {
|
|
70
72
|
command: "npx",
|
|
71
73
|
args: ["-y", "@playwright/mcp"]
|
|
@@ -77,18 +79,34 @@ const generateKiroMCPServers = (fraimKey, githubToken) => ({
|
|
|
77
79
|
FRAIM_REMOTE_URL: "https://fraim.wellnessatwork.me"
|
|
78
80
|
}
|
|
79
81
|
}
|
|
82
|
+
};
|
|
83
|
+
// Only add GitHub server if token is provided
|
|
84
|
+
if (githubToken) {
|
|
85
|
+
servers.github = {
|
|
86
|
+
url: "https://api.githubcopilot.com/mcp/",
|
|
87
|
+
headers: {
|
|
88
|
+
Authorization: `Bearer ${githubToken}`
|
|
89
|
+
}
|
|
90
|
+
};
|
|
80
91
|
}
|
|
81
|
-
}
|
|
92
|
+
return { mcpServers: servers };
|
|
93
|
+
};
|
|
82
94
|
exports.generateKiroMCPServers = generateKiroMCPServers;
|
|
83
|
-
const generateCodexMCPServers = (fraimKey, githubToken) =>
|
|
95
|
+
const generateCodexMCPServers = (fraimKey, githubToken) => {
|
|
96
|
+
let config = `
|
|
84
97
|
[mcp_servers.git]
|
|
85
98
|
command = "npx"
|
|
86
99
|
args = ["-y", "@cyanheads/git-mcp-server"]
|
|
87
|
-
|
|
100
|
+
`;
|
|
101
|
+
// Only add GitHub server if token is provided
|
|
102
|
+
if (githubToken) {
|
|
103
|
+
config += `
|
|
88
104
|
[mcp_servers.github]
|
|
89
105
|
url = "https://api.githubcopilot.com/mcp/"
|
|
90
106
|
bearer_token_env_var = "GIT_TOKEN"
|
|
91
|
-
|
|
107
|
+
`;
|
|
108
|
+
}
|
|
109
|
+
config += `
|
|
92
110
|
[mcp_servers.playwright]
|
|
93
111
|
command = "npx"
|
|
94
112
|
args = ["-y", "@playwright/mcp"]
|
|
@@ -100,20 +118,15 @@ command = "fraim-mcp"
|
|
|
100
118
|
FRAIM_API_KEY = "${fraimKey}"
|
|
101
119
|
FRAIM_REMOTE_URL = "https://fraim.wellnessatwork.me"
|
|
102
120
|
`;
|
|
121
|
+
return config;
|
|
122
|
+
};
|
|
103
123
|
exports.generateCodexMCPServers = generateCodexMCPServers;
|
|
104
|
-
const generateWindsurfMCPServers = (fraimKey, githubToken) =>
|
|
105
|
-
|
|
124
|
+
const generateWindsurfMCPServers = (fraimKey, githubToken) => {
|
|
125
|
+
const servers = {
|
|
106
126
|
git: {
|
|
107
127
|
command: "npx",
|
|
108
128
|
args: ["-y", "@cyanheads/git-mcp-server"]
|
|
109
129
|
},
|
|
110
|
-
github: {
|
|
111
|
-
command: "npx",
|
|
112
|
-
args: ["-y", "@modelcontextprotocol/server-fetch", "https://api.githubcopilot.com/mcp/"],
|
|
113
|
-
env: {
|
|
114
|
-
GITHUB_TOKEN: githubToken
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
130
|
playwright: {
|
|
118
131
|
command: "npx",
|
|
119
132
|
args: ["-y", "@playwright/mcp"]
|
|
@@ -125,8 +138,19 @@ const generateWindsurfMCPServers = (fraimKey, githubToken) => ({
|
|
|
125
138
|
FRAIM_REMOTE_URL: "https://fraim.wellnessatwork.me"
|
|
126
139
|
}
|
|
127
140
|
}
|
|
141
|
+
};
|
|
142
|
+
// Only add GitHub server if token is provided
|
|
143
|
+
if (githubToken) {
|
|
144
|
+
servers.github = {
|
|
145
|
+
command: "npx",
|
|
146
|
+
args: ["-y", "@modelcontextprotocol/server-fetch", "https://api.githubcopilot.com/mcp/"],
|
|
147
|
+
env: {
|
|
148
|
+
GITHUB_TOKEN: githubToken
|
|
149
|
+
}
|
|
150
|
+
};
|
|
128
151
|
}
|
|
129
|
-
}
|
|
152
|
+
return { mcpServers: servers };
|
|
153
|
+
};
|
|
130
154
|
exports.generateWindsurfMCPServers = generateWindsurfMCPServers;
|
|
131
155
|
const generateMCPConfig = (configType, fraimKey, githubToken) => {
|
|
132
156
|
switch (configType) {
|