fraim-framework 2.0.36 ā 2.0.38
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.js +5 -52
- package/dist/registry/scripts/build-scripts-generator.js +205 -0
- package/dist/registry/scripts/fraim-config.js +61 -0
- package/dist/registry/scripts/generic-issues-api.js +100 -0
- package/dist/registry/scripts/openapi-generator.js +664 -0
- package/dist/registry/scripts/performance/profile-server.js +390 -0
- package/dist/scripts/build-stub-registry.js +108 -0
- package/dist/src/cli/commands/doctor.js +5 -5
- package/dist/src/cli/commands/init-project.js +74 -0
- package/dist/src/cli/commands/setup.js +176 -0
- package/dist/src/cli/commands/sync.js +33 -19
- package/dist/src/cli/commands/test-mcp.js +135 -0
- package/dist/src/cli/fraim.js +6 -0
- package/dist/src/cli/setup/auto-mcp-setup.js +367 -0
- package/dist/src/cli/setup/ide-detector.js +163 -0
- package/dist/src/cli/setup/mcp-config-generator.js +115 -0
- package/dist/src/cli/setup/token-validator.js +49 -0
- package/dist/test-utils.js +96 -0
- package/dist/tests/debug-tools.js +2 -2
- package/dist/tests/esm-compat.js +11 -0
- package/dist/tests/shared-server-utils.js +57 -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 +2 -18
- package/dist/tests/test-chalk-resolution-issue.js +304 -0
- package/dist/tests/test-client-scripts-validation.js +27 -5
- package/dist/tests/test-complete-setup-flow.js +110 -0
- package/dist/tests/test-fraim-install-chalk-issue.js +254 -0
- package/dist/tests/test-ide-detector.js +46 -0
- package/dist/tests/test-improved-setup.js +121 -0
- package/dist/tests/test-mcp-config-generator.js +70 -0
- package/dist/tests/test-mcp-connection.js +58 -117
- package/dist/tests/test-mcp-issue-integration.js +2 -2
- package/dist/tests/test-mcp-lifecycle-methods.js +34 -100
- package/dist/tests/test-mcp-shared-server.js +308 -0
- package/dist/tests/test-npm-resolution-diagnostic.js +140 -0
- package/dist/tests/test-package-size.js +101 -0
- package/dist/tests/test-prep-issue.js +34 -1
- package/dist/tests/test-script-location-independence.js +39 -62
- package/dist/tests/test-server-utils.js +32 -0
- package/dist/tests/test-session-rehydration.js +2 -2
- package/dist/tests/test-setup-integration.js +98 -0
- package/dist/tests/test-standalone.js +2 -2
- package/dist/tests/test-stub-registry.js +136 -0
- package/dist/tests/test-sync-stubs.js +143 -0
- package/dist/tests/test-telemetry.js +2 -2
- package/dist/tests/test-token-validator.js +30 -0
- package/dist/tests/test-user-journey.js +2 -1
- package/package.json +7 -9
- package/registry/agent-guardrails.md +62 -62
- package/registry/scripts/code-quality-check.sh +559 -559
- package/registry/scripts/detect-tautological-tests.sh +38 -38
- package/registry/scripts/prep-issue.sh +61 -30
- package/registry/scripts/validate-openapi-limits.ts +366 -366
- package/registry/scripts/validate-test-coverage.ts +280 -280
- package/registry/scripts/verify-pr-comments.sh +70 -70
- package/registry/stubs/workflows/bootstrap/create-architecture.md +11 -0
- package/registry/stubs/workflows/bootstrap/detect-broken-windows.md +11 -0
- package/registry/stubs/workflows/bootstrap/evaluate-code-quality.md +11 -0
- package/registry/stubs/workflows/bootstrap/verify-test-coverage.md +11 -0
- package/registry/stubs/workflows/business-development/create-business-plan.md +11 -0
- package/registry/stubs/workflows/business-development/ideate-business-opportunity.md +11 -0
- package/registry/stubs/workflows/business-development/price-product.md +18 -0
- package/registry/stubs/workflows/convert-to-pdf.md +11 -0
- package/registry/stubs/workflows/customer-development/insight-analysis.md +11 -0
- package/registry/stubs/workflows/customer-development/insight-triage.md +11 -0
- package/registry/stubs/workflows/customer-development/interview-preparation.md +11 -0
- package/registry/stubs/workflows/customer-development/linkedin-outreach.md +11 -0
- package/registry/stubs/workflows/customer-development/strategic-brainstorming.md +11 -0
- package/registry/stubs/workflows/customer-development/thank-customers.md +11 -0
- package/registry/stubs/workflows/customer-development/weekly-newsletter.md +11 -0
- package/registry/stubs/workflows/deploy/cloud-deployment.md +11 -0
- package/registry/stubs/workflows/improve-fraim/contribute.md +11 -0
- package/registry/stubs/workflows/improve-fraim/file-issue.md +11 -0
- package/registry/stubs/workflows/marketing/content-creation.md +11 -0
- package/registry/stubs/workflows/marketing/hbr-article.md +11 -0
- package/registry/stubs/workflows/marketing/launch-checklist.md +11 -0
- package/registry/stubs/workflows/marketing/marketing-strategy.md +11 -0
- package/registry/stubs/workflows/marketing/storytelling.md +11 -0
- package/registry/stubs/workflows/performance/analyze-performance.md +11 -0
- package/registry/stubs/workflows/product-building/design.md +11 -0
- package/registry/stubs/workflows/product-building/implement.md +12 -0
- package/registry/stubs/workflows/product-building/iterate-on-pr-comments.md +11 -0
- package/registry/stubs/workflows/product-building/prep-issue.md +11 -0
- package/registry/stubs/workflows/product-building/prototype.md +11 -0
- package/registry/stubs/workflows/product-building/resolve.md +11 -0
- package/registry/stubs/workflows/product-building/retrospect.md +11 -0
- package/registry/stubs/workflows/product-building/spec.md +11 -0
- package/registry/stubs/workflows/product-building/test.md +11 -0
- package/registry/stubs/workflows/quality-assurance/browser-validation.md +11 -0
- package/registry/stubs/workflows/quality-assurance/iterative-improvement-cycle.md +11 -0
- package/registry/stubs/workflows/replicate/replicate-discovery.md +11 -0
- package/registry/stubs/workflows/replicate/replicate-to-issues.md +11 -0
- package/registry/stubs/workflows/reviewer/review-implementation-vs-design-spec.md +11 -0
- package/registry/stubs/workflows/reviewer/review-implementation-vs-feature-spec.md +11 -0
- package/registry/stubs/workflows/startup-credits/aws-activate-application.md +11 -0
- package/registry/stubs/workflows/startup-credits/google-cloud-application.md +11 -0
- package/registry/stubs/workflows/startup-credits/microsoft-azure-application.md +11 -0
- package/.github/workflows/ci.yml +0 -65
- package/.github/workflows/deploy-fraim.yml +0 -87
- package/.github/workflows/phase-change.yml +0 -251
- package/.github/workflows/status-change.yml +0 -68
- package/.github/workflows/sync-on-pr-review.yml +0 -66
- package/examples/simple-webapp/TESTING.md +0 -62
- package/examples/simple-webapp/example-test.ts +0 -186
- package/registry/github/workflows/ci.yml +0 -51
- package/registry/github/workflows/phase-change.yml +0 -251
- package/registry/github/workflows/status-change.yml +0 -68
- package/registry/github/workflows/sync-on-pr-review.yml +0 -66
- package/registry/mcp-template.jsonc +0 -29
- package/registry/rules/agent-success-criteria.md +0 -52
- package/registry/rules/agent-testing-guidelines.md +0 -502
- package/registry/rules/architecture.md +0 -52
- package/registry/rules/communication.md +0 -122
- package/registry/rules/continuous-learning.md +0 -55
- package/registry/rules/debugging-multitenancy-issues.md +0 -85
- package/registry/rules/ephemeral-execution.md +0 -57
- package/registry/rules/git-safe-commands.md +0 -34
- package/registry/rules/hitl-ppe-record-analysis.md +0 -302
- package/registry/rules/integrity-and-test-ethics.md +0 -275
- package/registry/rules/local-development.md +0 -254
- package/registry/rules/merge-requirements.md +0 -231
- package/registry/rules/simplicity.md +0 -118
- package/registry/rules/software-development-lifecycle.md +0 -105
- package/registry/rules/spike-first-development.md +0 -205
- package/registry/rules/successful-debugging-patterns.md +0 -491
- package/registry/rules/telemetry.md +0 -67
- package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +0 -53
- package/registry/templates/bootstrap/CODE-QUALITY-REPORT-TEMPLATE.md +0 -37
- package/registry/templates/bootstrap/TEST-COVERAGE-REPORT-TEMPLATE.md +0 -35
- package/registry/templates/business-development/IDEATION-REPORT-TEMPLATE.md +0 -29
- package/registry/templates/business-development/PRICING-STRATEGY-TEMPLATE.md +0 -126
- package/registry/templates/customer-development/customer-interview-template.md +0 -99
- package/registry/templates/customer-development/follow-up-email-templates.md +0 -132
- package/registry/templates/customer-development/insight-analysis-template.md +0 -74
- package/registry/templates/customer-development/strategic-recommendations-template.md +0 -53
- package/registry/templates/customer-development/thank-you-email-template.html +0 -124
- package/registry/templates/customer-development/thank-you-note-template.md +0 -16
- package/registry/templates/customer-development/triage-log-template.md +0 -278
- package/registry/templates/customer-development/weekly-newsletter-template.html +0 -204
- package/registry/templates/evidence/Design-Evidence.md +0 -30
- package/registry/templates/evidence/Implementation-BugEvidence.md +0 -86
- package/registry/templates/evidence/Implementation-FeatureEvidence.md +0 -121
- package/registry/templates/evidence/Spec-Evidence.md +0 -19
- package/registry/templates/help/HelpNeeded.md +0 -14
- package/registry/templates/marketing/HBR-ARTICLE-TEMPLATE.md +0 -66
- package/registry/templates/replicate/implementation-checklist.md +0 -39
- package/registry/templates/replicate/use-cases-template.md +0 -88
- package/registry/templates/retrospective/RETROSPECTIVE-TEMPLATE.md +0 -55
- package/registry/templates/specs/BUGSPEC-TEMPLATE.md +0 -37
- package/registry/templates/specs/FEATURESPEC-TEMPLATE.md +0 -29
- package/registry/templates/specs/TECHSPEC-TEMPLATE.md +0 -39
- package/registry/workflows/bootstrap/create-architecture.md +0 -38
- package/registry/workflows/bootstrap/evaluate-code-quality.md +0 -36
- package/registry/workflows/bootstrap/verify-test-coverage.md +0 -37
- package/registry/workflows/business-development/create-business-plan.md +0 -737
- package/registry/workflows/business-development/ideate-business-opportunity.md +0 -55
- package/registry/workflows/business-development/price-product.md +0 -325
- package/registry/workflows/convert-to-pdf.md +0 -235
- package/registry/workflows/customer-development/insight-analysis.md +0 -156
- package/registry/workflows/customer-development/insight-triage.md +0 -933
- package/registry/workflows/customer-development/interview-preparation.md +0 -421
- package/registry/workflows/customer-development/linkedin-outreach.md +0 -593
- package/registry/workflows/customer-development/strategic-brainstorming.md +0 -146
- package/registry/workflows/customer-development/thank-customers.md +0 -203
- package/registry/workflows/customer-development/weekly-newsletter.md +0 -366
- package/registry/workflows/deploy/cloud-deployment.md +0 -310
- package/registry/workflows/improve-fraim/contribute.md +0 -32
- package/registry/workflows/improve-fraim/file-issue.md +0 -32
- package/registry/workflows/marketing/content-creation.md +0 -37
- package/registry/workflows/marketing/hbr-article.md +0 -73
- package/registry/workflows/marketing/launch-checklist.md +0 -37
- package/registry/workflows/marketing/marketing-strategy.md +0 -45
- package/registry/workflows/performance/analyze-performance.md +0 -65
- package/registry/workflows/product-building/design.md +0 -130
- package/registry/workflows/product-building/implement.md +0 -315
- package/registry/workflows/product-building/iterate-on-pr-comments.md +0 -70
- package/registry/workflows/product-building/prep-issue.md +0 -43
- package/registry/workflows/product-building/prototype.md +0 -60
- package/registry/workflows/product-building/resolve.md +0 -164
- package/registry/workflows/product-building/retrospect.md +0 -86
- package/registry/workflows/product-building/spec.md +0 -117
- package/registry/workflows/product-building/test.md +0 -120
- package/registry/workflows/quality-assurance/browser-validation.md +0 -221
- package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +0 -562
- package/registry/workflows/replicate/replicate-discovery.md +0 -336
- package/registry/workflows/replicate/replicate-to-issues.md +0 -319
- package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +0 -632
- package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +0 -669
- package/registry/workflows/startup-credits/aws-activate-application.md +0 -535
- package/registry/workflows/startup-credits/google-cloud-application.md +0 -647
- package/registry/workflows/startup-credits/microsoft-azure-application.md +0 -538
|
@@ -0,0 +1,176 @@
|
|
|
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.setupCommand = exports.runSetup = void 0;
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const os_1 = __importDefault(require("os"));
|
|
13
|
+
const token_validator_1 = require("../setup/token-validator");
|
|
14
|
+
const auto_mcp_setup_1 = require("../setup/auto-mcp-setup");
|
|
15
|
+
const script_sync_utils_1 = require("../../utils/script-sync-utils");
|
|
16
|
+
const version_utils_1 = require("../../utils/version-utils");
|
|
17
|
+
const promptForFraimKey = async () => {
|
|
18
|
+
console.log(chalk_1.default.blue('š FRAIM Key Setup'));
|
|
19
|
+
console.log('FRAIM requires a valid API key to access workflows and features.\n');
|
|
20
|
+
let key = null;
|
|
21
|
+
let attempts = 0;
|
|
22
|
+
const maxAttempts = 3;
|
|
23
|
+
while (!key && attempts < maxAttempts) {
|
|
24
|
+
const keyResponse = await (0, prompts_1.default)({
|
|
25
|
+
type: 'password',
|
|
26
|
+
name: 'key',
|
|
27
|
+
message: attempts === 0
|
|
28
|
+
? 'Enter your FRAIM key (required)'
|
|
29
|
+
: `Enter your FRAIM key (attempt ${attempts + 1}/${maxAttempts})`,
|
|
30
|
+
validate: (value) => {
|
|
31
|
+
if (!value)
|
|
32
|
+
return 'FRAIM key is required';
|
|
33
|
+
if ((0, token_validator_1.isValidTokenFormat)(value, 'fraim'))
|
|
34
|
+
return true;
|
|
35
|
+
return 'Please enter a valid FRAIM key (starts with fraim_)';
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
if (!keyResponse.key) {
|
|
39
|
+
console.log(chalk_1.default.red('\nā FRAIM key is required to proceed.'));
|
|
40
|
+
console.log(chalk_1.default.gray('If you need a key, please email sid.mathur@gmail.com to request one.\n'));
|
|
41
|
+
const retry = await (0, prompts_1.default)({
|
|
42
|
+
type: 'confirm',
|
|
43
|
+
name: 'retry',
|
|
44
|
+
message: 'Would you like to try entering the FRAIM key again?',
|
|
45
|
+
initial: true
|
|
46
|
+
});
|
|
47
|
+
if (!retry.retry) {
|
|
48
|
+
console.log(chalk_1.default.red('Setup cancelled. Please obtain a FRAIM key and try again.'));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
attempts++;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
// Validate key
|
|
55
|
+
const isValid = await (0, token_validator_1.validateFraimKey)(keyResponse.key);
|
|
56
|
+
if (isValid) {
|
|
57
|
+
console.log(chalk_1.default.green('ā
FRAIM key validated\n'));
|
|
58
|
+
return keyResponse.key;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.log(chalk_1.default.red('ā Invalid FRAIM key\n'));
|
|
62
|
+
attempts++;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
console.log(chalk_1.default.red('\nā Maximum attempts reached. Setup cancelled.'));
|
|
66
|
+
console.log(chalk_1.default.gray('Please ensure you have a valid FRAIM key and try again.'));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
};
|
|
69
|
+
const saveGlobalConfig = (fraimKey) => {
|
|
70
|
+
const globalConfigDir = path_1.default.join(os_1.default.homedir(), '.fraim');
|
|
71
|
+
const globalConfigPath = path_1.default.join(globalConfigDir, 'config.json');
|
|
72
|
+
if (!fs_1.default.existsSync(globalConfigDir)) {
|
|
73
|
+
fs_1.default.mkdirSync(globalConfigDir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
const config = {
|
|
76
|
+
version: (0, version_utils_1.getFraimVersion)(),
|
|
77
|
+
apiKey: fraimKey,
|
|
78
|
+
configuredAt: new Date().toISOString(),
|
|
79
|
+
userPreferences: {
|
|
80
|
+
autoSync: true,
|
|
81
|
+
backupConfigs: true
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
fs_1.default.writeFileSync(globalConfigPath, JSON.stringify(config, null, 2));
|
|
85
|
+
console.log(chalk_1.default.green('ā
Global FRAIM configuration saved'));
|
|
86
|
+
};
|
|
87
|
+
const syncGlobalScripts = () => {
|
|
88
|
+
console.log(chalk_1.default.blue('š Syncing FRAIM scripts to user directory...'));
|
|
89
|
+
// Find registry path
|
|
90
|
+
let registryPath = path_1.default.join(__dirname, '../../../../registry');
|
|
91
|
+
if (!fs_1.default.existsSync(registryPath)) {
|
|
92
|
+
registryPath = path_1.default.join(__dirname, '../../../registry');
|
|
93
|
+
}
|
|
94
|
+
if (fs_1.default.existsSync(registryPath)) {
|
|
95
|
+
const syncResult = (0, script_sync_utils_1.syncScriptsToUserDirectory)(registryPath);
|
|
96
|
+
console.log(chalk_1.default.green(`ā
Synced ${syncResult.synced} scripts to user directory.`));
|
|
97
|
+
if (syncResult.ephemeral > 0) {
|
|
98
|
+
console.log(chalk_1.default.gray(` ${syncResult.ephemeral} dependent scripts will use ephemeral execution.`));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
console.log(chalk_1.default.yellow('ā ļø Registry not found, skipping script sync.'));
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const runSetup = async (options) => {
|
|
106
|
+
console.log(chalk_1.default.blue('š Welcome to FRAIM! Let\'s get you set up.\n'));
|
|
107
|
+
// Show what we're about to do
|
|
108
|
+
console.log(chalk_1.default.yellow('š This setup will:'));
|
|
109
|
+
console.log(chalk_1.default.gray(' ⢠Validate your FRAIM and GitHub tokens'));
|
|
110
|
+
console.log(chalk_1.default.gray(' ⢠Create global FRAIM configuration'));
|
|
111
|
+
console.log(chalk_1.default.gray(' ⢠Detect and configure supported IDEs'));
|
|
112
|
+
console.log(chalk_1.default.gray(' ⢠Sync FRAIM scripts to your system'));
|
|
113
|
+
console.log(chalk_1.default.gray(' ⢠Set up MCP servers for AI integration\n'));
|
|
114
|
+
// Get FRAIM key
|
|
115
|
+
let fraimKey = options.key;
|
|
116
|
+
if (!fraimKey) {
|
|
117
|
+
fraimKey = await promptForFraimKey();
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
if (!(0, token_validator_1.isValidTokenFormat)(fraimKey, 'fraim')) {
|
|
121
|
+
console.log(chalk_1.default.red('ā Invalid FRAIM key format. Key must start with fraim_'));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
console.log(chalk_1.default.blue('š Validating FRAIM key...'));
|
|
125
|
+
const isValid = await (0, token_validator_1.validateFraimKey)(fraimKey);
|
|
126
|
+
if (!isValid) {
|
|
127
|
+
console.log(chalk_1.default.red('ā Invalid FRAIM key'));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
console.log(chalk_1.default.green('ā
FRAIM key validated\n'));
|
|
131
|
+
}
|
|
132
|
+
// Get GitHub token
|
|
133
|
+
let githubToken = options.githubToken;
|
|
134
|
+
if (!githubToken) {
|
|
135
|
+
githubToken = await (0, auto_mcp_setup_1.promptForGitHubToken)();
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
if (!(0, token_validator_1.isValidTokenFormat)(githubToken, 'github')) {
|
|
139
|
+
console.log(chalk_1.default.red('ā Invalid GitHub token format. Token must start with ghp_ or github_pat_'));
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
console.log(chalk_1.default.blue('š Validating GitHub token...'));
|
|
143
|
+
const isValid = await (0, token_validator_1.validateGitHubToken)(githubToken);
|
|
144
|
+
if (!isValid) {
|
|
145
|
+
console.log(chalk_1.default.red('ā Invalid GitHub token'));
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
console.log(chalk_1.default.green('ā
GitHub token validated\n'));
|
|
149
|
+
}
|
|
150
|
+
// Save global configuration
|
|
151
|
+
console.log(chalk_1.default.blue('š¾ Saving global configuration...'));
|
|
152
|
+
saveGlobalConfig(fraimKey);
|
|
153
|
+
// Sync global scripts
|
|
154
|
+
syncGlobalScripts();
|
|
155
|
+
// Configure IDEs
|
|
156
|
+
const selectedIDEs = options.ide ? options.ide.split(',') : undefined;
|
|
157
|
+
const autoAll = options.all;
|
|
158
|
+
if (autoAll && selectedIDEs) {
|
|
159
|
+
console.log(chalk_1.default.yellow('ā ļø Both --all and --ide specified. Using --ide selection.'));
|
|
160
|
+
}
|
|
161
|
+
await (0, auto_mcp_setup_1.autoConfigureMCP)(fraimKey, githubToken, selectedIDEs);
|
|
162
|
+
console.log(chalk_1.default.green('\nšÆ Setup complete! Next steps:'));
|
|
163
|
+
console.log(chalk_1.default.cyan(' 1. Restart your configured IDEs'));
|
|
164
|
+
console.log(chalk_1.default.cyan(' 2. Go to any project directory'));
|
|
165
|
+
console.log(chalk_1.default.cyan(' 3. Run: fraim init-project'));
|
|
166
|
+
console.log(chalk_1.default.cyan(' 4. Ask your AI agent: "list fraim workflows"'));
|
|
167
|
+
console.log(chalk_1.default.blue('\nš” Use "fraim test-mcp" to verify your setup anytime.'));
|
|
168
|
+
};
|
|
169
|
+
exports.runSetup = runSetup;
|
|
170
|
+
exports.setupCommand = new commander_1.Command('setup')
|
|
171
|
+
.description('Complete global FRAIM setup with IDE configuration')
|
|
172
|
+
.option('--key <key>', 'FRAIM API key')
|
|
173
|
+
.option('--github-token <token>', 'GitHub Personal Access Token')
|
|
174
|
+
.option('--all', 'Auto-configure all detected IDEs')
|
|
175
|
+
.option('--ide <ides>', 'Configure specific IDEs (comma-separated: claude,cursor,kiro)')
|
|
176
|
+
.action(exports.runSetup);
|
|
@@ -9,7 +9,6 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
9
9
|
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
|
-
const stub_generator_1 = require("../../utils/stub-generator");
|
|
13
12
|
const config_loader_1 = require("../../fraim/config-loader");
|
|
14
13
|
const version_utils_1 = require("../../utils/version-utils");
|
|
15
14
|
const script_sync_utils_1 = require("../../utils/script-sync-utils");
|
|
@@ -20,20 +19,20 @@ const runSync = async (options) => {
|
|
|
20
19
|
const workflowsRelativePath = config.customizations?.workflowsPath || '.fraim/workflows';
|
|
21
20
|
const workflowsDir = path_1.default.resolve(projectRoot, workflowsRelativePath);
|
|
22
21
|
const digestPath = path_1.default.join(fraimDir, '.digest');
|
|
23
|
-
// In
|
|
22
|
+
// In npm package, stubs are in node_modules/@fraim/framework/registry/stubs/workflows
|
|
24
23
|
// We need to handle both "running from source" (src/cli/commands) and "running from dist" (dist/src/cli/commands)
|
|
25
24
|
// Try 4 levels up (dist/src/cli/commands -> root)
|
|
26
|
-
let registryPath = path_1.default.join(__dirname, '../../../../registry');
|
|
25
|
+
let registryPath = path_1.default.join(__dirname, '../../../../registry/stubs');
|
|
27
26
|
if (!fs_1.default.existsSync(registryPath)) {
|
|
28
27
|
// Try 3 levels up (src/cli/commands -> root)
|
|
29
|
-
registryPath = path_1.default.join(__dirname, '../../../registry');
|
|
28
|
+
registryPath = path_1.default.join(__dirname, '../../../registry/stubs');
|
|
30
29
|
}
|
|
31
30
|
// Fallback for local development if running from within the framework repo itself
|
|
32
31
|
if (!fs_1.default.existsSync(registryPath)) {
|
|
33
|
-
registryPath = path_1.default.join(projectRoot, 'registry');
|
|
32
|
+
registryPath = path_1.default.join(projectRoot, 'registry/stubs');
|
|
34
33
|
}
|
|
35
34
|
if (!fs_1.default.existsSync(registryPath)) {
|
|
36
|
-
console.error(chalk_1.default.red('ā
|
|
35
|
+
console.error(chalk_1.default.red('ā Stub registry not found. Ensure @fraim/framework is installed and built.'));
|
|
37
36
|
process.exit(1);
|
|
38
37
|
}
|
|
39
38
|
if (!fs_1.default.existsSync(workflowsDir)) {
|
|
@@ -64,10 +63,10 @@ const runSync = async (options) => {
|
|
|
64
63
|
else {
|
|
65
64
|
const registryWorkflowsPath = path_1.default.join(registryPath, 'workflows');
|
|
66
65
|
if (!fs_1.default.existsSync(registryWorkflowsPath)) {
|
|
67
|
-
console.log(chalk_1.default.yellow('ā ļø No
|
|
66
|
+
console.log(chalk_1.default.yellow('ā ļø No workflow stubs found in registry.'));
|
|
68
67
|
}
|
|
69
68
|
else {
|
|
70
|
-
// Get all
|
|
69
|
+
// Get all workflow stubs from registry (recursive)
|
|
71
70
|
const getFiles = (dir) => {
|
|
72
71
|
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
73
72
|
const files = entries
|
|
@@ -80,13 +79,12 @@ const runSync = async (options) => {
|
|
|
80
79
|
return files;
|
|
81
80
|
};
|
|
82
81
|
const registryFiles = getFiles(registryWorkflowsPath);
|
|
83
|
-
const
|
|
82
|
+
const copiedStubs = [];
|
|
84
83
|
for (const file of registryFiles) {
|
|
85
|
-
|
|
86
|
-
const { intent, principles } = (0, stub_generator_1.parseRegistryWorkflow)(content);
|
|
84
|
+
// These are already stubs, just copy them directly
|
|
87
85
|
const fileName = path_1.default.basename(file);
|
|
88
86
|
const workflowName = fileName.replace('.md', '');
|
|
89
|
-
// Calculate relative path from registry/workflows to preserve structure
|
|
87
|
+
// Calculate relative path from registry/stubs/workflows to preserve structure
|
|
90
88
|
const relativePath = path_1.default.relative(registryWorkflowsPath, file);
|
|
91
89
|
const relativeDir = path_1.default.dirname(relativePath);
|
|
92
90
|
// Ensure target directory exists
|
|
@@ -94,10 +92,11 @@ const runSync = async (options) => {
|
|
|
94
92
|
if (!fs_1.default.existsSync(targetDir)) {
|
|
95
93
|
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
96
94
|
}
|
|
97
|
-
|
|
95
|
+
// Copy stub file directly
|
|
96
|
+
const stubContent = fs_1.default.readFileSync(file, 'utf8');
|
|
98
97
|
const stubPath = path_1.default.join(targetDir, fileName);
|
|
99
98
|
fs_1.default.writeFileSync(stubPath, stubContent);
|
|
100
|
-
|
|
99
|
+
copiedStubs.push(relativePath); // Store relative path for cleanup tracking
|
|
101
100
|
console.log(chalk_1.default.gray(` + ${workflowName} (${relativeDir === '.' ? 'root' : relativeDir})`));
|
|
102
101
|
}
|
|
103
102
|
// Cleanup stubs that no longer exist in registry
|
|
@@ -119,8 +118,8 @@ const runSync = async (options) => {
|
|
|
119
118
|
for (const stub of localStubs) {
|
|
120
119
|
// standardise path separators for comparison
|
|
121
120
|
const normalizedStub = stub.replace(/\\/g, '/');
|
|
122
|
-
const
|
|
123
|
-
if (!
|
|
121
|
+
const normalizedCopied = copiedStubs.map(s => s.replace(/\\/g, '/'));
|
|
122
|
+
if (!normalizedCopied.includes(normalizedStub)) {
|
|
124
123
|
fs_1.default.unlinkSync(path_1.default.join(workflowsDir, stub));
|
|
125
124
|
console.log(chalk_1.default.yellow(` - ${stub} (removed from registry)`));
|
|
126
125
|
// Cleanup empty directories
|
|
@@ -134,13 +133,28 @@ const runSync = async (options) => {
|
|
|
134
133
|
}
|
|
135
134
|
}
|
|
136
135
|
fs_1.default.writeFileSync(digestPath, currentDigest);
|
|
137
|
-
console.log(chalk_1.default.green(`\nā
Workflow sync complete.
|
|
136
|
+
console.log(chalk_1.default.green(`\nā
Workflow sync complete. Copied ${copiedStubs.length} stubs.`));
|
|
138
137
|
}
|
|
139
138
|
}
|
|
140
139
|
// Always sync scripts, regardless of workflow sync status
|
|
141
140
|
console.log(chalk_1.default.blue('\nš Syncing FRAIM scripts to user directory...'));
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
// Scripts are packaged separately from stubs, find the correct registry path
|
|
142
|
+
// Try 4 levels up (dist/src/cli/commands -> root)
|
|
143
|
+
let scriptsRegistryPath = path_1.default.join(__dirname, '../../../../registry');
|
|
144
|
+
if (!fs_1.default.existsSync(path_1.default.join(scriptsRegistryPath, 'scripts'))) {
|
|
145
|
+
// Try 3 levels up (src/cli/commands -> root)
|
|
146
|
+
scriptsRegistryPath = path_1.default.join(__dirname, '../../../registry');
|
|
147
|
+
}
|
|
148
|
+
// Fallback for local development if running from within the framework repo itself
|
|
149
|
+
if (!fs_1.default.existsSync(path_1.default.join(scriptsRegistryPath, 'scripts'))) {
|
|
150
|
+
scriptsRegistryPath = path_1.default.join(projectRoot, 'registry');
|
|
151
|
+
}
|
|
152
|
+
if (!fs_1.default.existsSync(path_1.default.join(scriptsRegistryPath, 'scripts'))) {
|
|
153
|
+
console.error(chalk_1.default.red('ā Scripts registry not found. Ensure @fraim/framework is installed and built.'));
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
const syncResult = (0, script_sync_utils_1.syncScriptsToUserDirectory)(scriptsRegistryPath);
|
|
157
|
+
const cleanedScriptCount = (0, script_sync_utils_1.cleanupObsoleteUserScripts)(scriptsRegistryPath);
|
|
144
158
|
if (syncResult.synced > 0 || cleanedScriptCount > 0) {
|
|
145
159
|
console.log(chalk_1.default.green(`ā
Script sync complete. Updated ${syncResult.synced} scripts, removed ${cleanedScriptCount} obsolete scripts.`));
|
|
146
160
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
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.testMCPCommand = exports.runTestMCP = void 0;
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const ide_detector_1 = require("../setup/ide-detector");
|
|
13
|
+
const testIDEConfig = async (ide) => {
|
|
14
|
+
const result = {
|
|
15
|
+
ide: ide.name,
|
|
16
|
+
configExists: false,
|
|
17
|
+
configValid: false,
|
|
18
|
+
mcpServers: [],
|
|
19
|
+
errors: []
|
|
20
|
+
};
|
|
21
|
+
const configPath = (0, ide_detector_1.expandPath)(ide.configPath);
|
|
22
|
+
if (!fs_1.default.existsSync(configPath)) {
|
|
23
|
+
result.errors.push('Config file does not exist');
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
result.configExists = true;
|
|
27
|
+
try {
|
|
28
|
+
if (ide.configFormat === 'json') {
|
|
29
|
+
const configContent = fs_1.default.readFileSync(configPath, 'utf8');
|
|
30
|
+
const config = JSON.parse(configContent);
|
|
31
|
+
if (config.mcpServers) {
|
|
32
|
+
result.configValid = true;
|
|
33
|
+
result.mcpServers = Object.keys(config.mcpServers);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
result.errors.push('No mcpServers section found');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else if (ide.configFormat === 'toml') {
|
|
40
|
+
const configContent = fs_1.default.readFileSync(configPath, 'utf8');
|
|
41
|
+
// Simple TOML parsing for MCP servers
|
|
42
|
+
const serverMatches = configContent.match(/\[mcp_servers\.(\w+)\]/g);
|
|
43
|
+
if (serverMatches) {
|
|
44
|
+
result.configValid = true;
|
|
45
|
+
result.mcpServers = serverMatches.map(match => match.replace(/\[mcp_servers\.(\w+)\]/, '$1'));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
result.errors.push('No mcp_servers sections found');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
result.errors.push(`Failed to parse config: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
};
|
|
57
|
+
const checkGlobalSetup = () => {
|
|
58
|
+
const globalConfigPath = path_1.default.join(os_1.default.homedir(), '.fraim', 'config.json');
|
|
59
|
+
return fs_1.default.existsSync(globalConfigPath);
|
|
60
|
+
};
|
|
61
|
+
const runTestMCP = async () => {
|
|
62
|
+
console.log(chalk_1.default.blue('š Testing MCP configuration...\n'));
|
|
63
|
+
// Check global setup
|
|
64
|
+
if (!checkGlobalSetup()) {
|
|
65
|
+
console.log(chalk_1.default.red('ā Global FRAIM setup not found.'));
|
|
66
|
+
console.log(chalk_1.default.yellow('Please run: fraim setup --key=<your-fraim-key>'));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
console.log(chalk_1.default.green('ā
Global FRAIM setup found'));
|
|
70
|
+
// Detect IDEs
|
|
71
|
+
const detectedIDEs = (0, ide_detector_1.detectInstalledIDEs)();
|
|
72
|
+
if (detectedIDEs.length === 0) {
|
|
73
|
+
console.log(chalk_1.default.yellow('ā ļø No supported IDEs detected.'));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
console.log(chalk_1.default.blue(`\nš Testing ${detectedIDEs.length} detected IDEs...\n`));
|
|
77
|
+
const results = await Promise.all(detectedIDEs.map(ide => testIDEConfig(ide)));
|
|
78
|
+
let totalConfigured = 0;
|
|
79
|
+
let totalWithFRAIM = 0;
|
|
80
|
+
for (const result of results) {
|
|
81
|
+
console.log(chalk_1.default.white(`š± ${result.ide}`));
|
|
82
|
+
if (!result.configExists) {
|
|
83
|
+
console.log(chalk_1.default.red(' ā No MCP config found'));
|
|
84
|
+
console.log(chalk_1.default.gray(` š” Run: fraim setup --ide=${result.ide.toLowerCase()}`));
|
|
85
|
+
}
|
|
86
|
+
else if (!result.configValid) {
|
|
87
|
+
console.log(chalk_1.default.yellow(' ā ļø Config exists but invalid'));
|
|
88
|
+
result.errors.forEach(error => {
|
|
89
|
+
console.log(chalk_1.default.red(` ā ${error}`));
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
totalConfigured++;
|
|
94
|
+
console.log(chalk_1.default.green(` ā
MCP config valid (${result.mcpServers.length} servers)`));
|
|
95
|
+
// Check for essential servers
|
|
96
|
+
const essentialServers = ['fraim', 'git', 'github', 'playwright'];
|
|
97
|
+
const hasEssential = essentialServers.filter(server => result.mcpServers.includes(server));
|
|
98
|
+
if (hasEssential.includes('fraim')) {
|
|
99
|
+
totalWithFRAIM++;
|
|
100
|
+
console.log(chalk_1.default.green(' ā
FRAIM server configured'));
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
console.log(chalk_1.default.yellow(' ā ļø FRAIM server missing'));
|
|
104
|
+
}
|
|
105
|
+
if (hasEssential.length > 1) {
|
|
106
|
+
console.log(chalk_1.default.green(` ā
${hasEssential.length - 1} additional servers: ${hasEssential.filter(s => s !== 'fraim').join(', ')}`));
|
|
107
|
+
}
|
|
108
|
+
const missingEssential = essentialServers.filter(server => !result.mcpServers.includes(server));
|
|
109
|
+
if (missingEssential.length > 0) {
|
|
110
|
+
console.log(chalk_1.default.yellow(` ā ļø Missing servers: ${missingEssential.join(', ')}`));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
console.log(); // Empty line
|
|
114
|
+
}
|
|
115
|
+
// Summary
|
|
116
|
+
console.log(chalk_1.default.blue('š Summary:'));
|
|
117
|
+
console.log(chalk_1.default.green(` ā
${totalConfigured}/${detectedIDEs.length} IDEs have valid MCP configs`));
|
|
118
|
+
console.log(chalk_1.default.green(` ā
${totalWithFRAIM}/${detectedIDEs.length} IDEs have FRAIM configured`));
|
|
119
|
+
if (totalWithFRAIM === 0) {
|
|
120
|
+
console.log(chalk_1.default.red('\nā No IDEs have FRAIM configured!'));
|
|
121
|
+
console.log(chalk_1.default.yellow('š” Run: fraim setup --key=<your-fraim-key>'));
|
|
122
|
+
}
|
|
123
|
+
else if (totalWithFRAIM < detectedIDEs.length) {
|
|
124
|
+
console.log(chalk_1.default.yellow(`\nā ļø ${detectedIDEs.length - totalWithFRAIM} IDEs missing FRAIM configuration`));
|
|
125
|
+
console.log(chalk_1.default.yellow('š” Run: fraim setup to configure remaining IDEs'));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
console.log(chalk_1.default.green('\nš All detected IDEs have FRAIM configured!'));
|
|
129
|
+
console.log(chalk_1.default.blue('š” Try running: fraim init-project in any project'));
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
exports.runTestMCP = runTestMCP;
|
|
133
|
+
exports.testMCPCommand = new commander_1.Command('test-mcp')
|
|
134
|
+
.description('Test MCP server configurations for all detected IDEs')
|
|
135
|
+
.action(exports.runTestMCP);
|
package/dist/src/cli/fraim.js
CHANGED
|
@@ -10,6 +10,9 @@ const sync_1 = require("./commands/sync");
|
|
|
10
10
|
const doctor_1 = require("./commands/doctor");
|
|
11
11
|
const list_1 = require("./commands/list");
|
|
12
12
|
const wizard_1 = require("./commands/wizard");
|
|
13
|
+
const setup_1 = require("./commands/setup");
|
|
14
|
+
const init_project_1 = require("./commands/init-project");
|
|
15
|
+
const test_mcp_1 = require("./commands/test-mcp");
|
|
13
16
|
const fs_1 = __importDefault(require("fs"));
|
|
14
17
|
const path_1 = __importDefault(require("path"));
|
|
15
18
|
const program = new commander_1.Command();
|
|
@@ -42,4 +45,7 @@ program.addCommand(sync_1.syncCommand);
|
|
|
42
45
|
program.addCommand(doctor_1.doctorCommand);
|
|
43
46
|
program.addCommand(list_1.listCommand);
|
|
44
47
|
program.addCommand(wizard_1.wizardCommand);
|
|
48
|
+
program.addCommand(setup_1.setupCommand);
|
|
49
|
+
program.addCommand(init_project_1.initProjectCommand);
|
|
50
|
+
program.addCommand(test_mcp_1.testMCPCommand);
|
|
45
51
|
program.parse(process.argv);
|