fraim-framework 2.0.22 → 2.0.26
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 +3 -1
- package/dist/src/cli/commands/init.js +14 -6
- package/dist/src/cli/commands/sync.js +4 -1
- package/dist/src/fraim/config-loader.js +30 -18
- package/dist/src/fraim/setup-wizard.js +13 -50
- package/dist/src/fraim/template-processor.js +1 -1
- package/dist/src/fraim/types.js +21 -25
- package/dist/src/fraim-mcp-server.js +37 -33
- package/dist/src/utils/git-utils.js +24 -1
- package/dist/tests/test-cli.js +169 -0
- package/dist/tests/test-first-run-journey.js +108 -0
- package/dist/tests/test-genericization.js +66 -0
- package/{test-prep-issue.ts → dist/tests/test-prep-issue.js} +93 -101
- package/{test-standalone.ts → dist/tests/test-standalone.js} +149 -161
- package/dist/tests/test-user-journey.js +231 -0
- package/dist/tests/test-utils.js +96 -0
- package/{test-wizard.ts → dist/tests/test-wizard.js} +71 -81
- package/package.json +11 -5
- package/registry/rules/architecture.md +1 -1
- package/registry/scripts/code-quality-check.sh +5 -4
- package/registry/scripts/evaluate-code-quality.ts +36 -0
- package/registry/scripts/fraim-config.ts +2 -1
- package/registry/scripts/generate-engagement-emails.ts +3 -0
- package/registry/scripts/newsletter-helpers.ts +3 -0
- package/registry/scripts/{validate-coverage.ts → validate-test-coverage.ts} +39 -39
- package/registry/scripts/verify-test-coverage.ts +36 -0
- package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -0
- package/registry/templates/bootstrap/CODE-QUALITY-REPORT-TEMPLATE.md +37 -0
- package/registry/templates/bootstrap/TEST-COVERAGE-REPORT-TEMPLATE.md +35 -0
- package/registry/templates/business-development/PRICING-STRATEGY-TEMPLATE.md +126 -0
- package/registry/templates/customer-development/thank-you-email-template.html +76 -60
- package/registry/templates/customer-development/weekly-newsletter-template.html +5 -5
- package/registry/workflows/bootstrap/create-architecture.md +13 -12
- package/registry/workflows/bootstrap/evaluate-code-quality.md +30 -0
- package/registry/workflows/bootstrap/verify-test-coverage.md +31 -0
- package/registry/workflows/business-development/price-product.md +325 -0
- package/registry/workflows/customer-development/weekly-newsletter.md +16 -43
- package/tsconfig.json +4 -4
- package/test-cli.ts +0 -116
- package/test-first-run-journey.ts +0 -122
- package/test-user-journey.ts +0 -244
- package/test-utils.ts +0 -120
|
@@ -7,6 +7,7 @@ on:
|
|
|
7
7
|
- master
|
|
8
8
|
paths:
|
|
9
9
|
- 'src/fraim-mcp-server.ts'
|
|
10
|
+
- 'src/fraim/**'
|
|
10
11
|
- '.ai-agents/**'
|
|
11
12
|
- '.fraim/**'
|
|
12
13
|
- '.github/workflows/deploy-fraim.yml'
|
|
@@ -55,7 +56,8 @@ jobs:
|
|
|
55
56
|
# Copy necessary files
|
|
56
57
|
cp -r dist deploy-package/
|
|
57
58
|
cp -r node_modules deploy-package/
|
|
58
|
-
# Include .fraim directory for runtime
|
|
59
|
+
# Include registry and .fraim directory for runtime
|
|
60
|
+
cp -r registry deploy-package/
|
|
59
61
|
cp -r .fraim deploy-package/
|
|
60
62
|
|
|
61
63
|
cp package.json deploy-package/
|
|
@@ -10,6 +10,8 @@ const path_1 = __importDefault(require("path"));
|
|
|
10
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
11
|
const first_run_js_1 = require("../setup/first-run.js");
|
|
12
12
|
const sync_js_1 = require("./sync.js");
|
|
13
|
+
const types_js_1 = require("../../fraim/types.js");
|
|
14
|
+
const git_utils_js_1 = require("../../utils/git-utils.js");
|
|
13
15
|
const runInit = async () => {
|
|
14
16
|
const projectRoot = process.cwd();
|
|
15
17
|
const fraimDir = path_1.default.join(projectRoot, '.fraim');
|
|
@@ -23,14 +25,20 @@ const runInit = async () => {
|
|
|
23
25
|
console.log(chalk_1.default.yellow('ℹ️ .fraim directory already exists'));
|
|
24
26
|
}
|
|
25
27
|
if (!fs_1.default.existsSync(configPath)) {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const remoteInfo = (0, git_utils_js_1.getGitRemoteInfo)();
|
|
29
|
+
const config = {
|
|
30
|
+
...types_js_1.DEFAULT_FRAIM_CONFIG,
|
|
31
|
+
project: {
|
|
32
|
+
...types_js_1.DEFAULT_FRAIM_CONFIG.project,
|
|
33
|
+
name: path_1.default.basename(projectRoot)
|
|
34
|
+
},
|
|
35
|
+
git: {
|
|
36
|
+
...types_js_1.DEFAULT_FRAIM_CONFIG.git,
|
|
37
|
+
repoOwner: remoteInfo.owner || types_js_1.DEFAULT_FRAIM_CONFIG.git.repoOwner,
|
|
38
|
+
repoName: remoteInfo.repo || types_js_1.DEFAULT_FRAIM_CONFIG.git.repoName
|
|
31
39
|
}
|
|
32
40
|
};
|
|
33
|
-
fs_1.default.writeFileSync(configPath, JSON.stringify(
|
|
41
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
34
42
|
console.log(chalk_1.default.green('✅ Created .fraim/config.json'));
|
|
35
43
|
}
|
|
36
44
|
// Create subdirectories
|
|
@@ -10,10 +10,13 @@ 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 config_loader_js_1 = require("../../fraim/config-loader.js");
|
|
13
14
|
const runSync = async (options) => {
|
|
14
15
|
const projectRoot = process.cwd();
|
|
16
|
+
const config = (0, config_loader_js_1.loadFraimConfig)();
|
|
15
17
|
const fraimDir = path_1.default.join(projectRoot, '.fraim');
|
|
16
|
-
const
|
|
18
|
+
const workflowsRelativePath = config.customizations?.workflowsPath || '.fraim/workflows';
|
|
19
|
+
const workflowsDir = path_1.default.resolve(projectRoot, workflowsRelativePath);
|
|
17
20
|
const digestPath = path_1.default.join(fraimDir, '.digest');
|
|
18
21
|
// In a real npm package, registry would be in node_modules/@fraim/framework/registry
|
|
19
22
|
// We need to handle both "running from source" (src/cli/commands) and "running from dist" (dist/src/cli/commands)
|
|
@@ -8,7 +8,7 @@ exports.loadFraimConfig = loadFraimConfig;
|
|
|
8
8
|
exports.getConfigValue = getConfigValue;
|
|
9
9
|
const fs_1 = require("fs");
|
|
10
10
|
const path_1 = require("path");
|
|
11
|
-
const
|
|
11
|
+
const types_1 = require("./types");
|
|
12
12
|
/**
|
|
13
13
|
* Load FRAIM configuration from .fraim/config.json
|
|
14
14
|
* Falls back to defaults if file doesn't exist
|
|
@@ -17,39 +17,51 @@ function loadFraimConfig() {
|
|
|
17
17
|
const configPath = (0, path_1.join)(process.cwd(), '.fraim', 'config.json');
|
|
18
18
|
if (!(0, fs_1.existsSync)(configPath)) {
|
|
19
19
|
console.log('📋 No .fraim/config.json found, using defaults');
|
|
20
|
-
return { ...
|
|
20
|
+
return { ...types_1.DEFAULT_FRAIM_CONFIG };
|
|
21
21
|
}
|
|
22
22
|
try {
|
|
23
23
|
const configContent = (0, fs_1.readFileSync)(configPath, 'utf-8');
|
|
24
24
|
const config = JSON.parse(configContent);
|
|
25
25
|
// Merge with defaults to ensure all required fields exist
|
|
26
26
|
const mergedConfig = {
|
|
27
|
-
...
|
|
27
|
+
...types_1.DEFAULT_FRAIM_CONFIG,
|
|
28
28
|
...config,
|
|
29
29
|
project: {
|
|
30
|
-
...
|
|
31
|
-
...config.project
|
|
32
|
-
},
|
|
33
|
-
testing: {
|
|
34
|
-
...types_js_1.DEFAULT_FRAIM_CONFIG.testing,
|
|
35
|
-
...config.testing
|
|
30
|
+
...types_1.DEFAULT_FRAIM_CONFIG.project,
|
|
31
|
+
...(config.project || {})
|
|
36
32
|
},
|
|
37
33
|
git: {
|
|
38
|
-
...
|
|
39
|
-
...config.git
|
|
34
|
+
...types_1.DEFAULT_FRAIM_CONFIG.git,
|
|
35
|
+
...(config.git || {})
|
|
40
36
|
},
|
|
41
37
|
customizations: {
|
|
42
|
-
...
|
|
43
|
-
...config.customizations
|
|
38
|
+
...types_1.DEFAULT_FRAIM_CONFIG.customizations,
|
|
39
|
+
...(config.customizations || {})
|
|
40
|
+
},
|
|
41
|
+
architecture: {
|
|
42
|
+
...types_1.DEFAULT_FRAIM_CONFIG.architecture,
|
|
43
|
+
...(config.architecture || {})
|
|
44
|
+
},
|
|
45
|
+
testing: {
|
|
46
|
+
...types_1.DEFAULT_FRAIM_CONFIG.testing,
|
|
47
|
+
...(config.testing || {})
|
|
48
|
+
},
|
|
49
|
+
persona: {
|
|
50
|
+
...types_1.DEFAULT_FRAIM_CONFIG.persona,
|
|
51
|
+
...(config.persona || {})
|
|
52
|
+
},
|
|
53
|
+
marketing: {
|
|
54
|
+
...types_1.DEFAULT_FRAIM_CONFIG.marketing,
|
|
55
|
+
...(config.marketing || {})
|
|
44
56
|
},
|
|
45
|
-
|
|
46
|
-
...
|
|
47
|
-
...config.
|
|
57
|
+
database: {
|
|
58
|
+
...types_1.DEFAULT_FRAIM_CONFIG.database,
|
|
59
|
+
...(config.database || {})
|
|
48
60
|
}
|
|
49
61
|
};
|
|
50
62
|
// Validate version
|
|
51
63
|
if (!mergedConfig.version) {
|
|
52
|
-
mergedConfig.version =
|
|
64
|
+
mergedConfig.version = types_1.DEFAULT_FRAIM_CONFIG.version;
|
|
53
65
|
}
|
|
54
66
|
console.log(`📋 Loaded FRAIM config from .fraim/config.json (version ${mergedConfig.version})`);
|
|
55
67
|
return mergedConfig;
|
|
@@ -57,7 +69,7 @@ function loadFraimConfig() {
|
|
|
57
69
|
catch (error) {
|
|
58
70
|
console.warn(`⚠️ Failed to load .fraim/config.json: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
59
71
|
console.warn(' Using default configuration');
|
|
60
|
-
return { ...
|
|
72
|
+
return { ...types_1.DEFAULT_FRAIM_CONFIG };
|
|
61
73
|
}
|
|
62
74
|
}
|
|
63
75
|
/**
|
|
@@ -20,44 +20,14 @@ function generateConfigFromAnswers(answers) {
|
|
|
20
20
|
const config = {
|
|
21
21
|
...types_js_1.DEFAULT_FRAIM_CONFIG,
|
|
22
22
|
project: {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
primaryLanguage: answers.primaryLanguage || types_js_1.DEFAULT_FRAIM_CONFIG.project.primaryLanguage,
|
|
26
|
-
framework: answers.framework,
|
|
27
|
-
database: answers.database || types_js_1.DEFAULT_FRAIM_CONFIG.project.database,
|
|
28
|
-
orm: answers.orm || types_js_1.DEFAULT_FRAIM_CONFIG.project.orm
|
|
29
|
-
},
|
|
30
|
-
testing: {
|
|
31
|
-
framework: answers.testFramework || types_js_1.DEFAULT_FRAIM_CONFIG.testing.framework,
|
|
32
|
-
testLocation: answers.testLocation || types_js_1.DEFAULT_FRAIM_CONFIG.testing.testLocation,
|
|
33
|
-
testNaming: answers.testNaming || types_js_1.DEFAULT_FRAIM_CONFIG.testing.testNaming,
|
|
34
|
-
e2eFramework: answers.e2eFramework || types_js_1.DEFAULT_FRAIM_CONFIG.testing.e2eFramework,
|
|
35
|
-
coverageTool: types_js_1.DEFAULT_FRAIM_CONFIG.testing.coverageTool
|
|
23
|
+
...types_js_1.DEFAULT_FRAIM_CONFIG.project,
|
|
24
|
+
name: answers.projectName || types_js_1.DEFAULT_FRAIM_CONFIG.project.name
|
|
36
25
|
},
|
|
37
26
|
git: {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
commitStyle: answers.commitStyle || types_js_1.DEFAULT_FRAIM_CONFIG.git.commitStyle,
|
|
41
|
-
prWorkflow: answers.prWorkflow || types_js_1.DEFAULT_FRAIM_CONFIG.git.prWorkflow
|
|
27
|
+
...types_js_1.DEFAULT_FRAIM_CONFIG.git,
|
|
28
|
+
defaultBranch: answers.defaultBranch || types_js_1.DEFAULT_FRAIM_CONFIG.git.defaultBranch
|
|
42
29
|
}
|
|
43
30
|
};
|
|
44
|
-
// Add architecture config if LLM is used
|
|
45
|
-
if (answers.usesLLM) {
|
|
46
|
-
config.architecture = {
|
|
47
|
-
pattern: answers.architecturePattern || 'llm-deterministic-separation',
|
|
48
|
-
llmFramework: answers.llmFramework || null,
|
|
49
|
-
llmUsage: answers.llmUsage || [],
|
|
50
|
-
deterministicUsage: ['data-processing', 'api-calls', 'database-operations'],
|
|
51
|
-
pipeline: {
|
|
52
|
-
steps: ['parse', 'normalize', 'validate', 'decide', 'act'],
|
|
53
|
-
parse: 'llm',
|
|
54
|
-
normalize: 'deterministic',
|
|
55
|
-
validate: 'deterministic',
|
|
56
|
-
decide: 'llm',
|
|
57
|
-
act: 'deterministic'
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
31
|
return config;
|
|
62
32
|
}
|
|
63
33
|
/**
|
|
@@ -106,17 +76,17 @@ This document describes the architecture patterns and conventions for ${config.p
|
|
|
106
76
|
|
|
107
77
|
## Overview
|
|
108
78
|
|
|
109
|
-
- **Project
|
|
110
|
-
- **
|
|
111
|
-
- **
|
|
112
|
-
- **Database:** ${config.project.database}
|
|
113
|
-
- **ORM:** ${config.project.orm}
|
|
79
|
+
- **Project Name:** ${config.project.name}
|
|
80
|
+
- **Project Type:** ${config.project.type || 'Not specified'}
|
|
81
|
+
- **Primary Language:** ${config.project.primaryLanguage || 'Not specified'}
|
|
82
|
+
- **Database:** ${config.project.database || 'Not specified'}
|
|
83
|
+
- **ORM:** ${config.project.orm || 'Not specified'}
|
|
114
84
|
|
|
115
85
|
`;
|
|
116
86
|
if (arch) {
|
|
117
87
|
template += `## Architecture Pattern
|
|
118
88
|
|
|
119
|
-
**Pattern:** ${arch.pattern}
|
|
89
|
+
**Pattern:** ${arch.pattern || 'Not specified'}
|
|
120
90
|
|
|
121
91
|
`;
|
|
122
92
|
if (arch.llmFramework) {
|
|
@@ -126,25 +96,18 @@ This document describes the architecture patterns and conventions for ${config.p
|
|
|
126
96
|
- **Usage:** ${arch.llmUsage?.join(', ') || 'Not specified'}
|
|
127
97
|
- **Deterministic Usage:** ${arch.deterministicUsage?.join(', ') || 'Not specified'}
|
|
128
98
|
|
|
129
|
-
**Pipeline:**
|
|
130
|
-
${arch.pipeline?.steps.map(step => `- ${step}: ${arch.pipeline?.[step] || 'N/A'}`).join('\n') || 'Not specified'}
|
|
131
|
-
|
|
132
99
|
`;
|
|
133
100
|
}
|
|
134
101
|
}
|
|
135
102
|
template += `## Development Guidelines
|
|
136
103
|
|
|
137
104
|
### Testing
|
|
138
|
-
- **Framework:** ${config.testing
|
|
139
|
-
- **Test Location:** ${config.testing
|
|
140
|
-
- **Test Naming:** ${config.testing
|
|
141
|
-
- **E2E Framework:** ${config.testing.e2eFramework || 'None'}
|
|
105
|
+
- **Framework:** ${config.testing?.framework || 'Not specified'}
|
|
106
|
+
- **Test Location:** ${config.testing?.testLocation || 'Not specified'}
|
|
107
|
+
- **Test Naming:** ${config.testing?.testNaming || 'Not specified'}
|
|
142
108
|
|
|
143
109
|
### Git Workflow
|
|
144
110
|
- **Default Branch:** ${config.git.defaultBranch}
|
|
145
|
-
- **Branch Naming:** ${config.git.branchNaming}
|
|
146
|
-
- **Commit Style:** ${config.git.commitStyle}
|
|
147
|
-
- **PR Workflow:** ${config.git.prWorkflow}
|
|
148
111
|
|
|
149
112
|
## Customization
|
|
150
113
|
|
|
@@ -12,7 +12,7 @@ const config_loader_js_1 = require("./config-loader.js");
|
|
|
12
12
|
* Replaces {{config.path}} with actual config values
|
|
13
13
|
*
|
|
14
14
|
* Examples:
|
|
15
|
-
* - {{config.project.name}} -> "
|
|
15
|
+
* - {{config.project.name}} -> "My Project"
|
|
16
16
|
* - {{config.git.defaultBranch}} -> "master"
|
|
17
17
|
* - {{config.testing.framework}} -> "tsx-test"
|
|
18
18
|
*/
|
package/dist/src/fraim/types.js
CHANGED
|
@@ -11,47 +11,43 @@ exports.DEFAULT_FRAIM_CONFIG = void 0;
|
|
|
11
11
|
exports.DEFAULT_FRAIM_CONFIG = {
|
|
12
12
|
version: '1.0.0',
|
|
13
13
|
project: {
|
|
14
|
-
name: 'Untitled Project'
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
name: 'Untitled Project'
|
|
15
|
+
},
|
|
16
|
+
git: {
|
|
17
|
+
defaultBranch: 'master',
|
|
18
|
+
repoOwner: '',
|
|
19
|
+
repoName: ''
|
|
20
|
+
},
|
|
21
|
+
architecture: {
|
|
22
|
+
pattern: 'llm-deterministic-separation',
|
|
23
|
+
llmFramework: 'baml',
|
|
24
|
+
llmUsage: [],
|
|
25
|
+
deterministicUsage: ['data-processing', 'api-calls']
|
|
19
26
|
},
|
|
20
27
|
testing: {
|
|
21
28
|
framework: 'tsx-test',
|
|
22
29
|
testLocation: 'root',
|
|
23
|
-
testNaming: 'test-*.ts'
|
|
24
|
-
e2eFramework: 'playwright',
|
|
25
|
-
coverageTool: 'none'
|
|
26
|
-
},
|
|
27
|
-
git: {
|
|
28
|
-
defaultBranch: 'master',
|
|
29
|
-
branchNaming: 'feature/{issue}-{slug}',
|
|
30
|
-
commitStyle: 'imperative',
|
|
31
|
-
prWorkflow: 'draft-first'
|
|
30
|
+
testNaming: 'test-*.ts'
|
|
32
31
|
},
|
|
33
32
|
customizations: {
|
|
34
|
-
|
|
35
|
-
workflowsPath: '.fraim/workflows',
|
|
36
|
-
templatesPath: '.fraim/templates',
|
|
37
|
-
scriptsPath: '.fraim/scripts'
|
|
38
|
-
},
|
|
39
|
-
mcp: {
|
|
40
|
-
serverPort: 'auto',
|
|
41
|
-
protocolVersion: '2024-11-05'
|
|
33
|
+
workflowsPath: '.fraim/workflows'
|
|
42
34
|
},
|
|
43
35
|
persona: {
|
|
44
36
|
name: 'AI Agent',
|
|
45
37
|
voice: 'Helpful and precise.',
|
|
46
38
|
emailSignature: 'Powered by FRAIM',
|
|
47
|
-
displayNamePattern: '
|
|
39
|
+
displayNamePattern: '{executiveName}\'s Assistant'
|
|
48
40
|
},
|
|
49
41
|
marketing: {
|
|
50
42
|
newsletterTitle: 'Product Update',
|
|
51
|
-
newsletterCtaText: 'Learn More'
|
|
43
|
+
newsletterCtaText: 'Learn More',
|
|
44
|
+
newsletterUrl: '',
|
|
45
|
+
websiteUrl: '',
|
|
46
|
+
chatUrl: ''
|
|
52
47
|
},
|
|
53
48
|
database: {
|
|
54
49
|
identityCollection: 'Identity',
|
|
55
|
-
tokensCollection: 'Tokens'
|
|
50
|
+
tokensCollection: 'Tokens',
|
|
51
|
+
executiveCollection: 'Executive'
|
|
56
52
|
}
|
|
57
53
|
};
|
|
@@ -82,9 +82,19 @@ class FraimMCPServer {
|
|
|
82
82
|
this.app.use(express_1.default.json());
|
|
83
83
|
// Load server version
|
|
84
84
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
// Try process.cwd() first as it's the root in most deployments
|
|
86
|
+
let pkgPath = (0, path_1.join)(process.cwd(), 'package.json');
|
|
87
|
+
if (!(0, fs_1.existsSync)(pkgPath)) {
|
|
88
|
+
// Fallback to relative to __dirname (dist/src/ -> ../../package.json)
|
|
89
|
+
pkgPath = (0, path_1.join)(__dirname, '..', '..', 'package.json');
|
|
90
|
+
}
|
|
91
|
+
if ((0, fs_1.existsSync)(pkgPath)) {
|
|
92
|
+
const pkg = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf8'));
|
|
93
|
+
this.serverVersion = pkg.version;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.serverVersion = 'unknown';
|
|
97
|
+
}
|
|
88
98
|
}
|
|
89
99
|
catch (e) {
|
|
90
100
|
this.serverVersion = 'unknown';
|
|
@@ -217,8 +227,11 @@ class FraimMCPServer {
|
|
|
217
227
|
});
|
|
218
228
|
}
|
|
219
229
|
catch (error) {
|
|
220
|
-
|
|
221
|
-
|
|
230
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
231
|
+
console.error('❌ FRAIM AUTH: Error during verification:', msg);
|
|
232
|
+
if (error instanceof Error && error.stack)
|
|
233
|
+
console.error(error.stack);
|
|
234
|
+
res.status(500).json({ error: 'Internal Server Error', details: msg });
|
|
222
235
|
}
|
|
223
236
|
}
|
|
224
237
|
adminAuthMiddleware(req, res, next) {
|
|
@@ -284,32 +297,25 @@ class FraimMCPServer {
|
|
|
284
297
|
console.log(`🎨 Indexing project-specific .fraim customizations`);
|
|
285
298
|
const customizations = this.config.customizations || {};
|
|
286
299
|
// Index custom rules
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
this.indexDirectory(rulesPath, 'fraim/rules');
|
|
291
|
-
}
|
|
300
|
+
const rulesPath = (0, path_1.join)(process.cwd(), '.fraim/rules');
|
|
301
|
+
if ((0, fs_1.existsSync)(rulesPath)) {
|
|
302
|
+
this.indexDirectory(rulesPath, 'fraim/rules');
|
|
292
303
|
}
|
|
293
304
|
// Index custom workflows
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
305
|
+
const workflowsPathStr = customizations.workflowsPath || '.fraim/workflows';
|
|
306
|
+
const workflowsPath = (0, path_1.join)(process.cwd(), workflowsPathStr);
|
|
307
|
+
if ((0, fs_1.existsSync)(workflowsPath)) {
|
|
308
|
+
this.indexDirectory(workflowsPath, 'fraim/workflows');
|
|
299
309
|
}
|
|
300
310
|
// Index custom templates
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
this.indexDirectory(templatesPath, 'fraim/templates');
|
|
305
|
-
}
|
|
311
|
+
const templatesPath = (0, path_1.join)(process.cwd(), '.fraim/templates');
|
|
312
|
+
if ((0, fs_1.existsSync)(templatesPath)) {
|
|
313
|
+
this.indexDirectory(templatesPath, 'fraim/templates');
|
|
306
314
|
}
|
|
307
315
|
// Index custom scripts
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
this.indexDirectory(scriptsPath, 'fraim/scripts');
|
|
312
|
-
}
|
|
316
|
+
const scriptsPath = (0, path_1.join)(process.cwd(), '.fraim/scripts');
|
|
317
|
+
if ((0, fs_1.existsSync)(scriptsPath)) {
|
|
318
|
+
this.indexDirectory(scriptsPath, 'fraim/scripts');
|
|
313
319
|
}
|
|
314
320
|
}
|
|
315
321
|
console.log(`📚 Indexed ${this.fileIndex.size} files total (Global + Local)`);
|
|
@@ -963,12 +969,10 @@ Use this to discover which workflow to call with get_fraim_workflow.`,
|
|
|
963
969
|
const workflowPath = `workflows/${normalizedName}.md`;
|
|
964
970
|
const metadata = this.fileIndex.get(workflowPath);
|
|
965
971
|
if (!metadata) {
|
|
966
|
-
// Try alternative paths
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
`workflows
|
|
970
|
-
];
|
|
971
|
-
for (const altPath of alternativePaths) {
|
|
972
|
+
// Try alternative category paths
|
|
973
|
+
const categories = ['product-building', 'customer-development', 'business-development', 'marketing', 'performance', 'quality-assurance', 'reviewer', 'startup-credits', 'bootstrap', 'deploy'];
|
|
974
|
+
for (const cat of categories) {
|
|
975
|
+
const altPath = `workflows/${cat}/${normalizedName}.md`;
|
|
972
976
|
const altMetadata = this.fileIndex.get(altPath);
|
|
973
977
|
if (altMetadata) {
|
|
974
978
|
return this.returnWorkflowFile(altMetadata);
|
|
@@ -1213,8 +1217,8 @@ If \`.fraim/config.json\` doesn't exist:
|
|
|
1213
1217
|
exports.FraimMCPServer = FraimMCPServer;
|
|
1214
1218
|
// Start the server if this file is run directly
|
|
1215
1219
|
// In Azure App Service, the port is provided via process.env.PORT
|
|
1216
|
-
const
|
|
1217
|
-
const defaultFraimPort = process.env.FRAIM_MCP_PORT ? parseInt(process.env.FRAIM_MCP_PORT) :
|
|
1220
|
+
const serverPort = (0, git_utils_1.getPort)();
|
|
1221
|
+
const defaultFraimPort = process.env.FRAIM_MCP_PORT ? parseInt(process.env.FRAIM_MCP_PORT) : serverPort + 2;
|
|
1218
1222
|
const port = process.env.PORT ? parseInt(process.env.PORT) : defaultFraimPort;
|
|
1219
1223
|
const server = new FraimMCPServer();
|
|
1220
1224
|
server.start(port).catch((error) => {
|
|
@@ -4,6 +4,7 @@ exports.getPort = getPort;
|
|
|
4
4
|
exports.determineDatabaseName = determineDatabaseName;
|
|
5
5
|
exports.getCurrentGitBranch = getCurrentGitBranch;
|
|
6
6
|
exports.determineSchema = determineSchema;
|
|
7
|
+
exports.getGitRemoteInfo = getGitRemoteInfo;
|
|
7
8
|
const child_process_1 = require("child_process");
|
|
8
9
|
/**
|
|
9
10
|
* Gets a unique port based on the current git branch name (if it's an issue branch)
|
|
@@ -23,7 +24,7 @@ function getPort() {
|
|
|
23
24
|
catch (e) {
|
|
24
25
|
// Silently fail and use default
|
|
25
26
|
}
|
|
26
|
-
return Number(process.env.FRAIM_MCP_PORT) || 15300;
|
|
27
|
+
return Number(process.env.PORT) || Number(process.env.WEBSITES_PORT) || Number(process.env.FRAIM_MCP_PORT) || 15300;
|
|
27
28
|
}
|
|
28
29
|
/**
|
|
29
30
|
* Determines the database name based on the git branch
|
|
@@ -62,3 +63,25 @@ function determineSchema(branchName) {
|
|
|
62
63
|
}
|
|
63
64
|
return 'prod';
|
|
64
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Gets the GitHub remote info (owner and repo name)
|
|
68
|
+
*/
|
|
69
|
+
function getGitRemoteInfo() {
|
|
70
|
+
try {
|
|
71
|
+
const remoteUrl = (0, child_process_1.execSync)('git remote get-url origin').toString().trim();
|
|
72
|
+
// Match both HTTPS and SSH formats
|
|
73
|
+
// HTTPS: https://github.com/owner/repo.git OR https://github.com/owner/repo
|
|
74
|
+
// SSH: git@github.com:owner/repo.git OR git@github.com:owner/repo
|
|
75
|
+
const match = remoteUrl.match(/github\.com[/:]([^/]+)\/([^/.]+?)(?:\.git)?$/);
|
|
76
|
+
if (match) {
|
|
77
|
+
return {
|
|
78
|
+
owner: match[1],
|
|
79
|
+
repo: match[2]
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
// Silently fail if not a git repo or no origin
|
|
85
|
+
}
|
|
86
|
+
return { owner: null, repo: null };
|
|
87
|
+
}
|