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.
Files changed (42) hide show
  1. package/.github/workflows/deploy-fraim.yml +3 -1
  2. package/dist/src/cli/commands/init.js +14 -6
  3. package/dist/src/cli/commands/sync.js +4 -1
  4. package/dist/src/fraim/config-loader.js +30 -18
  5. package/dist/src/fraim/setup-wizard.js +13 -50
  6. package/dist/src/fraim/template-processor.js +1 -1
  7. package/dist/src/fraim/types.js +21 -25
  8. package/dist/src/fraim-mcp-server.js +37 -33
  9. package/dist/src/utils/git-utils.js +24 -1
  10. package/dist/tests/test-cli.js +169 -0
  11. package/dist/tests/test-first-run-journey.js +108 -0
  12. package/dist/tests/test-genericization.js +66 -0
  13. package/{test-prep-issue.ts → dist/tests/test-prep-issue.js} +93 -101
  14. package/{test-standalone.ts → dist/tests/test-standalone.js} +149 -161
  15. package/dist/tests/test-user-journey.js +231 -0
  16. package/dist/tests/test-utils.js +96 -0
  17. package/{test-wizard.ts → dist/tests/test-wizard.js} +71 -81
  18. package/package.json +11 -5
  19. package/registry/rules/architecture.md +1 -1
  20. package/registry/scripts/code-quality-check.sh +5 -4
  21. package/registry/scripts/evaluate-code-quality.ts +36 -0
  22. package/registry/scripts/fraim-config.ts +2 -1
  23. package/registry/scripts/generate-engagement-emails.ts +3 -0
  24. package/registry/scripts/newsletter-helpers.ts +3 -0
  25. package/registry/scripts/{validate-coverage.ts → validate-test-coverage.ts} +39 -39
  26. package/registry/scripts/verify-test-coverage.ts +36 -0
  27. package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -0
  28. package/registry/templates/bootstrap/CODE-QUALITY-REPORT-TEMPLATE.md +37 -0
  29. package/registry/templates/bootstrap/TEST-COVERAGE-REPORT-TEMPLATE.md +35 -0
  30. package/registry/templates/business-development/PRICING-STRATEGY-TEMPLATE.md +126 -0
  31. package/registry/templates/customer-development/thank-you-email-template.html +76 -60
  32. package/registry/templates/customer-development/weekly-newsletter-template.html +5 -5
  33. package/registry/workflows/bootstrap/create-architecture.md +13 -12
  34. package/registry/workflows/bootstrap/evaluate-code-quality.md +30 -0
  35. package/registry/workflows/bootstrap/verify-test-coverage.md +31 -0
  36. package/registry/workflows/business-development/price-product.md +325 -0
  37. package/registry/workflows/customer-development/weekly-newsletter.md +16 -43
  38. package/tsconfig.json +4 -4
  39. package/test-cli.ts +0 -116
  40. package/test-first-run-journey.ts +0 -122
  41. package/test-user-journey.ts +0 -244
  42. 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 configuration
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 defaultConfig = {
27
- name: path_1.default.basename(projectRoot),
28
- version: '1.0.0',
29
- customizations: {
30
- workflowsPath: '.fraim/workflows'
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(defaultConfig, null, 2));
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 workflowsDir = path_1.default.join(fraimDir, 'workflows');
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 types_js_1 = require("./types.js");
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 { ...types_js_1.DEFAULT_FRAIM_CONFIG };
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
- ...types_js_1.DEFAULT_FRAIM_CONFIG,
27
+ ...types_1.DEFAULT_FRAIM_CONFIG,
28
28
  ...config,
29
29
  project: {
30
- ...types_js_1.DEFAULT_FRAIM_CONFIG.project,
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
- ...types_js_1.DEFAULT_FRAIM_CONFIG.git,
39
- ...config.git
34
+ ...types_1.DEFAULT_FRAIM_CONFIG.git,
35
+ ...(config.git || {})
40
36
  },
41
37
  customizations: {
42
- ...types_js_1.DEFAULT_FRAIM_CONFIG.customizations,
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
- mcp: {
46
- ...types_js_1.DEFAULT_FRAIM_CONFIG.mcp,
47
- ...config.mcp
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 = types_js_1.DEFAULT_FRAIM_CONFIG.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 { ...types_js_1.DEFAULT_FRAIM_CONFIG };
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
- name: answers.projectName || types_js_1.DEFAULT_FRAIM_CONFIG.project.name,
24
- type: answers.projectType || types_js_1.DEFAULT_FRAIM_CONFIG.project.type,
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
- defaultBranch: answers.defaultBranch || types_js_1.DEFAULT_FRAIM_CONFIG.git.defaultBranch,
39
- branchNaming: answers.branchNaming || types_js_1.DEFAULT_FRAIM_CONFIG.git.branchNaming,
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 Type:** ${config.project.type}
110
- - **Primary Language:** ${config.project.primaryLanguage}
111
- - **Framework:** ${config.project.framework || 'Not specified'}
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.framework}
139
- - **Test Location:** ${config.testing.testLocation}
140
- - **Test Naming:** ${config.testing.testNaming}
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}} -> "Ashley Calendar AI"
15
+ * - {{config.project.name}} -> "My Project"
16
16
  * - {{config.git.defaultBranch}} -> "master"
17
17
  * - {{config.testing.framework}} -> "tsx-test"
18
18
  */
@@ -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
- type: 'other',
16
- primaryLanguage: 'typescript',
17
- database: 'none',
18
- orm: 'none'
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
- rulesPath: '.fraim/rules',
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: 'AI Agent'
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
- const pkgPath = (0, path_1.join)(__dirname, '..', 'package.json');
86
- const pkg = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf8'));
87
- this.serverVersion = pkg.version;
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
- console.error('❌ FRAIM AUTH: Error during verification:', error);
221
- res.status(500).json({ error: 'Internal Server Error' });
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
- if (customizations.rulesPath) {
288
- const rulesPath = (0, path_1.join)(process.cwd(), customizations.rulesPath);
289
- if ((0, fs_1.existsSync)(rulesPath)) {
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
- if (customizations.workflowsPath) {
295
- const workflowsPath = (0, path_1.join)(process.cwd(), customizations.workflowsPath);
296
- if ((0, fs_1.existsSync)(workflowsPath)) {
297
- this.indexDirectory(workflowsPath, 'fraim/workflows');
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
- if (customizations.templatesPath) {
302
- const templatesPath = (0, path_1.join)(process.cwd(), customizations.templatesPath);
303
- if ((0, fs_1.existsSync)(templatesPath)) {
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
- if (customizations.scriptsPath) {
309
- const scriptsPath = (0, path_1.join)(process.cwd(), customizations.scriptsPath);
310
- if ((0, fs_1.existsSync)(scriptsPath)) {
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 (e.g., customer-development subdirectory)
967
- const alternativePaths = [
968
- `workflows/customer-development/${normalizedName}.md`,
969
- `workflows/business-development/${normalizedName}.md`
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 ashleyPort = (0, git_utils_1.getPort)();
1217
- const defaultFraimPort = process.env.FRAIM_MCP_PORT ? parseInt(process.env.FRAIM_MCP_PORT) : ashleyPort + 2;
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
+ }