fraim-framework 2.0.75 โ†’ 2.0.76

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/README.md CHANGED
@@ -66,7 +66,7 @@ R - Retrospectives: Continuous learning from experience
66
66
 
67
67
  ### โœ… **The FRAIM Solution**
68
68
 
69
- #### ๐Ÿ›ก๏ธ **Agent Integrity & Test Ethics** (`.ai-agents/rules/integrity-and-test-ethics.md`)
69
+ #### ๐Ÿ›ก๏ธ **Agent Integrity & Test Ethics**
70
70
  **Problem**: Agents claim "tests pass" when they actually fail
71
71
  **Solution**: Mandatory evidence collection, test immutability rules, and accountability frameworks
72
72
  ```bash
@@ -74,7 +74,7 @@ R - Retrospectives: Continuous learning from experience
74
74
  # After FRAIM: "Here's the test output proving all 47 tests pass"
75
75
  ```
76
76
 
77
- #### ๐Ÿงช **Comprehensive Testing Guidelines** (`.ai-agents/rules/agent-testing-guidelines.md`)
77
+ #### ๐Ÿงช **Comprehensive Testing Guidelines**
78
78
  **Problem**: Superficial testing that misses real issues
79
79
  **Solution**: Multi-layer validation (database, API, UI, integration) with mandatory evidence
80
80
  ```bash
@@ -82,7 +82,7 @@ R - Retrospectives: Continuous learning from experience
82
82
  # After: Test real systems, prove it works, show evidence
83
83
  ```
84
84
 
85
- #### ๐Ÿ—ฃ๏ธ **Clear Communication Standards** (`.ai-agents/rules/communication.md`)
85
+ #### ๐Ÿ—ฃ๏ธ **Clear Communication Standards**
86
86
  **Problem**: Vague progress reports and unclear accountability
87
87
  **Solution**: Structured progress updates with concrete evidence and absolute accountability
88
88
  ```bash
@@ -90,7 +90,7 @@ R - Retrospectives: Continuous learning from experience
90
90
  # After: "Fixed API timeout, tests passing, evidence attached, ready for review"
91
91
  ```
92
92
 
93
- #### ๐Ÿ—๏ธ **Architectural Discipline** (`.ai-agents/rules/architecture.md`)
93
+ #### ๐Ÿ—๏ธ **Architectural Discipline**
94
94
  **Problem**: Agents create architectural chaos and technical debt
95
95
  **Solution**: Clean separation of concerns, type safety, and testability patterns
96
96
  ```bash
@@ -98,7 +98,7 @@ R - Retrospectives: Continuous learning from experience
98
98
  # After: Clean layers with proper boundaries and validation
99
99
  ```
100
100
 
101
- #### ๐ŸŽฏ **Spike-First Development** (`.ai-agents/rules/spike-first-development.md`)
101
+ #### ๐ŸŽฏ **Spike-First Development**
102
102
  **Problem**: Agents build complex solutions without validating assumptions
103
103
  **Solution**: 5-15 minute proof-of-concepts before major implementation
104
104
  ```bash
@@ -106,7 +106,7 @@ R - Retrospectives: Continuous learning from experience
106
106
  # After: 10-minute spike, validate approach, then build confidently
107
107
  ```
108
108
 
109
- #### ๐Ÿ”„ **Continuous Learning System** (`.ai-agents/rules/continuous-learning.md`)
109
+ #### ๐Ÿ”„ **Continuous Learning System**
110
110
  **Problem**: Same mistakes repeated across projects
111
111
  **Solution**: Retrospective-driven knowledge capture and pattern recognition
112
112
  ```bash
@@ -114,7 +114,7 @@ R - Retrospectives: Continuous learning from experience
114
114
  # After: Knowledge accumulates, patterns emerge, quality improves
115
115
  ```
116
116
 
117
- #### ๐Ÿงน **Simplicity Discipline** (`.ai-agents/rules/simplicity.md`)
117
+ #### ๐Ÿงน **Simplicity Discipline**
118
118
  **Problem**: Over-engineered solutions that are hard to maintain
119
119
  **Solution**: "Keep it simple" principles with complexity budgets
120
120
  ```bash
@@ -122,7 +122,7 @@ R - Retrospectives: Continuous learning from experience
122
122
  # After: Minimal, focused solution that actually works
123
123
  ```
124
124
 
125
- #### ๐Ÿ”ง **Git Safety & Timeout Management** (`.ai-agents/rules/git-safe-commands.md`)
125
+ #### ๐Ÿ”ง **Git Safety & Timeout Management**
126
126
  **Problem**: Agents hang on interactive Git commands and long-running tasks, requiring human intervention
127
127
  **Solution**: Safe Git commands and timeout scripts with output visibility
128
128
  ```bash
@@ -131,7 +131,7 @@ R - Retrospectives: Continuous learning from experience
131
131
  # Example: exec-with-timeout.ts runs tests with timeout and saves output to files
132
132
  ```
133
133
 
134
- #### ๐Ÿ”„ **Merge Requirements & Branch Safety** (`.ai-agents/rules/merge-requirements.md`)
134
+ #### ๐Ÿ”„ **Merge Requirements & Branch Safety**
135
135
  **Problem**: Agents accidentally overwrite master branch or create merge conflicts
136
136
  **Solution**: Mandatory rebase workflows with conflict resolution patterns
137
137
  ```bash
@@ -139,7 +139,7 @@ R - Retrospectives: Continuous learning from experience
139
139
  # After: Rebase-on-master with force-with-lease for safety
140
140
  ```
141
141
 
142
- #### ๐Ÿ› **Systematic Debugging Patterns** (`.ai-agents/rules/successful-debugging-patterns.md`)
142
+ #### ๐Ÿ› **Systematic Debugging Patterns**
143
143
  **Problem**: Agents struggle with complex debugging and repeat the same mistakes
144
144
  **Solution**: Structured debugging methodology with evidence collection and pattern recognition
145
145
  ```bash
@@ -147,7 +147,7 @@ R - Retrospectives: Continuous learning from experience
147
147
  # After: Systematic approach with documented patterns and regression tests
148
148
  ```
149
149
 
150
- #### ๐Ÿ“‹ **Package Scripts & Output Visibility** (`.ai-agents/scripts/` and `sample_package.json`)
150
+ #### ๐Ÿ“‹ **Package Scripts & Output Visibility**
151
151
  **Problem**: Long-running tasks hang agents and provide no visibility into progress
152
152
  **Solution**: Background execution with log files and timeout management
153
153
  ```bash
@@ -217,42 +217,6 @@ R - Retrospectives: Continuous learning from experience
217
217
 
218
218
  ## ๐Ÿ—๏ธ **Enterprise-Grade Framework Structure**
219
219
 
220
- ```
221
- FRAIM/
222
- โ”œโ”€โ”€ ๐Ÿง  .ai-agents/rules/ # Guardrails - The "HR Handbook" for AI agents
223
- โ”‚ โ”œโ”€โ”€ integrity-and-test-ethics.md # Prevents "fake it till you make it"
224
- โ”‚ โ”œโ”€โ”€ agent-testing-guidelines.md # Ensures real validation, not claims
225
- โ”‚ โ”œโ”€โ”€ communication.md # Clear accountability and progress
226
- โ”‚ โ”œโ”€โ”€ architecture.md # Prevents technical debt
227
- โ”‚ โ”œโ”€โ”€ spike-first-development.md # Validates before building
228
- โ”‚ โ”œโ”€โ”€ continuous-learning.md # Learns from mistakes
229
- โ”‚ โ”œโ”€โ”€ simplicity.md # Prevents over-engineering
230
- โ”‚ โ”œโ”€โ”€ git-safe-commands.md # Prevents agent hangs on Git commands
231
- โ”‚ โ”œโ”€โ”€ merge-requirements.md # Safe branch management and rebasing
232
- โ”‚ โ”œโ”€โ”€ successful-debugging-patterns.md # Systematic debugging approach
233
- โ”‚ โ”œโ”€โ”€ local-development.md # Local development best practices
234
- โ”‚ โ”œโ”€โ”€ pr-workflow-completeness.md # PR workflow validation
235
- โ”‚ โ””โ”€โ”€ software-development-lifecycle.md # Full SDLC integration
236
- โ”‚
237
- โ”œโ”€โ”€ ๐Ÿ”„ .ai-agents/workflows/ # Process - The "Project Management Office"
238
- โ”‚ โ”œโ”€โ”€ spec.md # Specification and requirements phase
239
- โ”‚ โ”œโ”€โ”€ design.md # Structured design phase
240
- โ”‚ โ”œโ”€โ”€ implement.md # Implementation with validation
241
- โ”‚ โ”œโ”€โ”€ test.md # Comprehensive testing
242
- โ”‚ โ”œโ”€โ”€ resolve.md # Issue resolution workflow
243
- โ”‚ โ””โ”€โ”€ retrospect.md # Learning and improvement
244
- โ”‚
245
- โ”œโ”€โ”€ ๐Ÿ“‹ .ai-agents/templates/ # Human Interfaces - The "Standard Operating Procedures"
246
- โ”‚ โ”œโ”€โ”€ evidence/ # Proof of work templates
247
- โ”‚ โ”œโ”€โ”€ retrospective/ # Learning capture templates
248
- โ”‚ โ””โ”€โ”€ specs/ # Specification templates
249
- โ”‚
250
- โ””โ”€โ”€ ๐Ÿ› ๏ธ Scripts & Automation # Determinism - The "DevOps Pipeline"
251
- โ”œโ”€โ”€ prep-issue.sh # Issue preparation
252
- โ”œโ”€โ”€ exec-with-timeout.ts # Command execution with timeout & output visibility
253
- โ””โ”€โ”€ cleanup-branch.ts # Branch management
254
- ```
255
-
256
220
  ## ๐Ÿš€ **Get Started in 60 Seconds**
257
221
 
258
222
  ### **โš ๏ธ Prerequisites**
@@ -293,63 +257,6 @@ fraim sync # Sync latest workflows and rules
293
257
 
294
258
  **๐Ÿ’ก Pro Tip**: Use `fraim add-ide` when you install a new IDE after initial setup. It reuses your existing FRAIM and GitHub keys, making it much faster than running full setup again.
295
259
 
296
- ### **๐Ÿ”ง Customize for Your Project**
297
-
298
- After installation, customize these files for your specific project:
299
-
300
- #### **1. Architecture Rules** (`.ai-agents/rules/architecture.md`)
301
- - **Purpose**: Define your system's architectural patterns and boundaries
302
- - **Customize**: Replace generic examples with your specific architecture patterns
303
- - **Example**: Add your database patterns, API structures, service boundaries
304
-
305
- #### **2. Branch Cleanup Script** (`.ai-agents/scripts/cleanup-branch.ts`)
306
- - **Purpose**: Project-specific cleanup when branches are deleted
307
- - **Customize**: Add your cleanup logic in the `insertYourCodeHere()` method
308
- - **Example**: Database cleanup, cache invalidation, temporary file removal
309
-
310
- #### **3. Package Scripts** (`sample_package.json` โ†’ `package.json`)
311
- - **Purpose**: Testing and build scripts for your project
312
- - **Adopt**: Copy relevant scripts from `sample_package.json` into your main `package.json`
313
- - **Customize**: Update test commands, build processes, and framework-specific commands
314
- - **Example**: Replace `baml-cli generate` with your framework's generate command
315
-
316
- #### **4. Test Utilities** (`test-utils.ts` โ†’ your project)
317
- - **Purpose**: Generic test framework with tagging system
318
- - **Adopt**: Copy `test-utils.ts` into your project and import in your test files
319
- - **Customize**: Extend `BaseTestCase` interface for your specific test needs
320
- - **Example**: Create domain-specific test cases using the tagging system
321
-
322
- #### **5. Test Structure** (`examples/simple-webapp/example-test.ts`)
323
- - **Purpose**: Example test cases with proper tagging
324
- - **Adopt**: Use as a template for your project's test structure
325
- - **Customize**: Replace with your project's test cases and domain-specific tags
326
- - **Example**: Add your specific test scenarios and validation patterns
327
-
328
- ## ๐ŸŽฏ **The Transformation: From Developer to AI Manager**
329
-
330
- ### **Phase 1: Setup (5 minutes)**
331
- ```bash
332
- npx fraim-framework init
333
- # โœ… Repository configured with AI management rules
334
- # โœ… Agents assigned to specific roles (Design, Implementation, Testing)
335
- # โœ… Workflows and templates installed
336
- ```
337
-
338
- ### **Phase 2: Issue Management (30 seconds per issue)**
339
- ```bash
340
- gh issue create --title "Add user authentication" --label "ai-agent:cursor"
341
- # โœ… Git issue is centralized coordination point
342
- # โœ… Agent assignment handled
343
- # โœ… Ready for work
344
- ```
345
-
346
- ### **Phase 3: Orchestrated Development**
347
- ```bash
348
- .ai-agents/scripts/prep-issue.sh #issue_number
349
- # โœ… Local clone and new branch created
350
- # โœ… Coding agent initiated
351
- # โœ… Tell coding agent which phase it should proceed with
352
- ```
353
260
 
354
261
  ## ๐ŸŒŸ **Why FRAIM is the Future**
355
262
 
@@ -414,4 +321,4 @@ Stop fighting with AI agents. Start orchestrating them.
414
321
  [![GitHub stars](https://img.shields.io/github/stars/mathursrus/FRAIM.svg)](https://github.com/mathursrus/FRAIM/stargazers)
415
322
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
416
323
 
417
- </div>
324
+ </div>
package/bin/fraim-mcp.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * FRAIM MCP Server - Smart Entry Point
@@ -12,6 +12,7 @@ const path_1 = __importDefault(require("path"));
12
12
  const os_1 = __importDefault(require("os"));
13
13
  const ide_detector_1 = require("../setup/ide-detector");
14
14
  const mcp_config_generator_1 = require("../setup/mcp-config-generator");
15
+ const codex_local_config_1 = require("../setup/codex-local-config");
15
16
  const loadGlobalConfig = () => {
16
17
  const globalConfigPath = path_1.default.join(os_1.default.homedir(), '.fraim', 'config.json');
17
18
  if (!fs_1.default.existsSync(globalConfigPath)) {
@@ -129,26 +130,18 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
129
130
  console.log(chalk_1.default.gray(` ๐Ÿ“‹ Found existing TOML config`));
130
131
  }
131
132
  const newTomlContent = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, githubToken);
132
- let finalContent = existingTomlContent;
133
133
  const serversToAdd = ['fraim', 'git', 'github', 'playwright'];
134
- const addedServers = [];
135
- for (const server of serversToAdd) {
136
- if (!existingTomlContent.includes(`[mcp_servers.${server}]`)) {
137
- const serverRegex = new RegExp(`\\[mcp_servers\\.${server}\\][\\s\\S]*?(?=\\[mcp_servers\\.|$)`, 'g');
138
- const serverMatch = newTomlContent.match(serverRegex);
139
- if (serverMatch) {
140
- finalContent += '\n' + serverMatch[0].trim() + '\n';
141
- addedServers.push(server);
142
- }
143
- }
144
- else {
145
- console.log(chalk_1.default.gray(` โญ๏ธ Skipped ${server} (already exists)`));
146
- }
147
- }
148
- fs_1.default.writeFileSync(configPath, finalContent);
149
- addedServers.forEach(server => {
134
+ const mergeResult = (0, mcp_config_generator_1.mergeTomlMCPServers)(existingTomlContent, newTomlContent, serversToAdd);
135
+ fs_1.default.writeFileSync(configPath, mergeResult.content);
136
+ mergeResult.addedServers.forEach(server => {
150
137
  console.log(chalk_1.default.green(` โœ… Added ${server} MCP server`));
151
138
  });
139
+ mergeResult.replacedServers.forEach(server => {
140
+ console.log(chalk_1.default.green(` โœ… Updated ${server} MCP server`));
141
+ });
142
+ mergeResult.skippedServers.forEach(server => {
143
+ console.log(chalk_1.default.gray(` โญ๏ธ Skipped ${server} (already configured or not requested)`));
144
+ });
152
145
  }
153
146
  else {
154
147
  // Handle JSON format
@@ -183,6 +176,11 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
183
176
  });
184
177
  }
185
178
  console.log(chalk_1.default.green(`โœ… Updated ${configPath}`));
179
+ if (ide.configType === 'codex') {
180
+ const localResult = (0, codex_local_config_1.ensureCodexLocalConfig)(process.cwd());
181
+ const status = localResult.created ? 'Created' : localResult.updated ? 'Updated' : 'Verified';
182
+ console.log(chalk_1.default.green(` โœ… ${status} local Codex config: ${localResult.path}`));
183
+ }
186
184
  };
187
185
  const listSupportedIDEs = () => {
188
186
  const allIDEs = (0, ide_detector_1.getAllSupportedIDEs)();
@@ -12,6 +12,8 @@ const chalk_1 = __importDefault(require("chalk"));
12
12
  const sync_1 = require("./sync");
13
13
  const platform_detection_1 = require("../utils/platform-detection");
14
14
  const version_utils_1 = require("../utils/version-utils");
15
+ const ide_detector_1 = require("../setup/ide-detector");
16
+ const codex_local_config_1 = require("../setup/codex-local-config");
15
17
  const checkGlobalSetup = () => {
16
18
  const fraimUserDir = process.env.FRAIM_USER_DIR || path_1.default.join(os_1.default.homedir(), '.fraim');
17
19
  const globalConfigPath = path_1.default.join(fraimUserDir, 'config.json');
@@ -63,9 +65,7 @@ const runInitProject = async () => {
63
65
  project: {
64
66
  name: projectName
65
67
  },
66
- customizations: {
67
- workflowsPath: '.fraim/workflows'
68
- }
68
+ customizations: {}
69
69
  };
70
70
  console.log(chalk_1.default.blue(' Conversational mode: No platform integration'));
71
71
  console.log(chalk_1.default.gray(` Project: ${projectName}`));
@@ -88,9 +88,7 @@ const runInitProject = async () => {
88
88
  name: detection.repository.name
89
89
  },
90
90
  repository: detection.repository,
91
- customizations: {
92
- workflowsPath: '.fraim/workflows'
93
- }
91
+ customizations: {}
94
92
  };
95
93
  console.log(chalk_1.default.blue(` Platform: ${detection.provider.toUpperCase()}`));
96
94
  if (detection.provider === 'github') {
@@ -115,9 +113,7 @@ const runInitProject = async () => {
115
113
  project: {
116
114
  name: repoName
117
115
  },
118
- customizations: {
119
- workflowsPath: '.fraim/workflows'
120
- }
116
+ customizations: {}
121
117
  };
122
118
  console.log(chalk_1.default.gray(' Please update .fraim/config.json if you add a repository later.'));
123
119
  }
@@ -135,6 +131,12 @@ const runInitProject = async () => {
135
131
  });
136
132
  // Sync workflows from registry
137
133
  await (0, sync_1.runSync)({});
134
+ const codexAvailable = (0, ide_detector_1.detectInstalledIDEs)().some((ide) => ide.configType === 'codex');
135
+ if (codexAvailable) {
136
+ const codexLocalResult = (0, codex_local_config_1.ensureCodexLocalConfig)(projectRoot);
137
+ const status = codexLocalResult.created ? 'Created' : codexLocalResult.updated ? 'Updated' : 'Verified';
138
+ console.log(chalk_1.default.green(`โœ… ${status} project Codex config at ${codexLocalResult.path}`));
139
+ }
138
140
  console.log(chalk_1.default.green('\nโœ… FRAIM project initialized!'));
139
141
  console.log(chalk_1.default.cyan('Try: Ask your AI agent "list fraim workflows"'));
140
142
  };
@@ -108,7 +108,11 @@ exports.overrideCommand = new commander_1.Command('override')
108
108
  }
109
109
  }
110
110
  };
111
- await axios_1.default.post(`${serverUrl}/mcp`, connectRequest, { headers, timeout: 30000 });
111
+ const connectResponse = await axios_1.default.post(`${serverUrl}/mcp`, connectRequest, { headers, timeout: 30000 });
112
+ const sessionId = connectResponse.data?.result?.sessionId;
113
+ if (typeof sessionId !== 'string' || sessionId.trim().length === 0) {
114
+ throw new Error('fraim_connect did not return a sessionId');
115
+ }
112
116
  // Now determine MCP tool and arguments based on path
113
117
  let toolName;
114
118
  let toolArgs;
@@ -132,9 +136,15 @@ exports.overrideCommand = new commander_1.Command('override')
132
136
  method: 'tools/call',
133
137
  params: {
134
138
  name: toolName,
139
+ sessionId: sessionId.trim(),
135
140
  arguments: toolArgs
136
141
  }
137
142
  };
143
+ // Mirror sessionId in arguments for compatibility with both server and proxy paths.
144
+ mcpRequest.params.arguments = {
145
+ ...mcpRequest.params.arguments,
146
+ sessionId: sessionId.trim()
147
+ };
138
148
  const response = await axios_1.default.post(`${serverUrl}/mcp`, mcpRequest, {
139
149
  headers,
140
150
  timeout: 30000
@@ -11,6 +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 codex_local_config_1 = require("./codex-local-config");
14
15
  const promptForIDESelection = async (detectedIDEs, githubToken) => {
15
16
  console.log(chalk_1.default.green(`โœ… Found ${detectedIDEs.length} IDEs that can be configured with FRAIM:\n`));
16
17
  detectedIDEs.forEach((ide, index) => {
@@ -228,28 +229,18 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
228
229
  console.log(chalk_1.default.gray(` ๐Ÿ“‹ Found existing TOML config`));
229
230
  }
230
231
  const newTomlContent = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, githubToken);
231
- // For TOML, we'll append new servers if they don't exist
232
- let finalContent = existingTomlContent;
233
232
  const serversToAdd = ['fraim', 'git', 'github', 'playwright'];
234
- const addedServers = [];
235
- for (const server of serversToAdd) {
236
- if (!existingTomlContent.includes(`[mcp_servers.${server}]`)) {
237
- // Extract this server's config from the new content
238
- const serverRegex = new RegExp(`\\[mcp_servers\\.${server}\\][\\s\\S]*?(?=\\[mcp_servers\\.|$)`, 'g');
239
- const serverMatch = newTomlContent.match(serverRegex);
240
- if (serverMatch) {
241
- finalContent += '\n' + serverMatch[0].trim() + '\n';
242
- addedServers.push(server);
243
- }
244
- }
245
- else {
246
- console.log(chalk_1.default.gray(` โญ๏ธ Skipped ${server} (already exists)`));
247
- }
248
- }
249
- fs_1.default.writeFileSync(configPath, finalContent);
250
- addedServers.forEach(server => {
233
+ const mergeResult = (0, mcp_config_generator_1.mergeTomlMCPServers)(existingTomlContent, newTomlContent, serversToAdd);
234
+ fs_1.default.writeFileSync(configPath, mergeResult.content);
235
+ mergeResult.addedServers.forEach(server => {
251
236
  console.log(chalk_1.default.green(` โœ… Added ${server} MCP server`));
252
237
  });
238
+ mergeResult.replacedServers.forEach(server => {
239
+ console.log(chalk_1.default.green(` โœ… Updated ${server} MCP server`));
240
+ });
241
+ mergeResult.skippedServers.forEach(server => {
242
+ console.log(chalk_1.default.gray(` โญ๏ธ Skipped ${server} (already configured or not requested)`));
243
+ });
253
244
  }
254
245
  else {
255
246
  // For JSON configs - intelligent merging
@@ -283,6 +274,11 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
283
274
  });
284
275
  }
285
276
  console.log(chalk_1.default.green(`โœ… Updated ${configPath}`));
277
+ if (ide.configType === 'codex') {
278
+ const localResult = (0, codex_local_config_1.ensureCodexLocalConfig)(process.cwd());
279
+ const status = localResult.created ? 'Created' : localResult.updated ? 'Updated' : 'Verified';
280
+ console.log(chalk_1.default.green(` โœ… ${status} local Codex config: ${localResult.path}`));
281
+ }
286
282
  };
287
283
  const autoConfigureMCP = async (fraimKey, githubToken, selectedIDEs) => {
288
284
  const detectedIDEs = (0, ide_detector_1.detectInstalledIDEs)();
@@ -0,0 +1,37 @@
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.ensureCodexLocalConfig = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const mcp_config_generator_1 = require("./mcp-config-generator");
10
+ const escapeTomlString = (value) => value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
11
+ const buildFraimCwdBlock = (projectRoot) => `
12
+ [mcp_servers.fraim]
13
+ cwd = "${escapeTomlString(projectRoot)}"
14
+ `;
15
+ const ensureCodexLocalConfig = (projectRoot) => {
16
+ const codexDir = path_1.default.join(projectRoot, '.codex');
17
+ const codexConfigPath = path_1.default.join(codexDir, 'config.toml');
18
+ if (!fs_1.default.existsSync(codexDir)) {
19
+ fs_1.default.mkdirSync(codexDir, { recursive: true });
20
+ }
21
+ const hadExistingFile = fs_1.default.existsSync(codexConfigPath);
22
+ const existingContent = hadExistingFile ? fs_1.default.readFileSync(codexConfigPath, 'utf8') : '';
23
+ const generatedContent = buildFraimCwdBlock(projectRoot);
24
+ const mergeResult = (0, mcp_config_generator_1.mergeTomlMCPServers)(existingContent, generatedContent, ['fraim']);
25
+ const normalizedExisting = existingContent.replace(/\r\n/g, '\n');
26
+ const normalizedMerged = mergeResult.content.replace(/\r\n/g, '\n');
27
+ const shouldWrite = !hadExistingFile || normalizedExisting !== normalizedMerged;
28
+ if (shouldWrite) {
29
+ fs_1.default.writeFileSync(codexConfigPath, mergeResult.content);
30
+ }
31
+ return {
32
+ path: codexConfigPath,
33
+ created: !hadExistingFile,
34
+ updated: shouldWrite && hadExistingFile
35
+ };
36
+ };
37
+ exports.ensureCodexLocalConfig = ensureCodexLocalConfig;
@@ -1,6 +1,87 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateMCPConfig = exports.generateWindsurfMCPServers = exports.generateVSCodeMCPServers = exports.generateCodexMCPServers = exports.generateKiroMCPServers = exports.generateClaudeMCPServers = exports.generateStandardMCPServers = void 0;
3
+ exports.generateMCPConfig = exports.generateWindsurfMCPServers = exports.generateVSCodeMCPServers = exports.generateCodexMCPServers = exports.generateKiroMCPServers = exports.generateClaudeMCPServers = exports.generateStandardMCPServers = exports.mergeTomlMCPServers = exports.extractTomlMcpServerBlock = void 0;
4
+ const escapeTomlString = (value) => value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
5
+ const findTomlServerBlockRange = (content, server) => {
6
+ const lines = content.split(/\r?\n/);
7
+ const serverSection = `mcp_servers.${server}`;
8
+ const serverHeader = `[${serverSection}]`;
9
+ const start = lines.findIndex(line => line.trim() === serverHeader);
10
+ if (start === -1) {
11
+ return null;
12
+ }
13
+ let end = lines.length;
14
+ for (let i = start + 1; i < lines.length; i++) {
15
+ const sectionMatch = lines[i].trim().match(/^\[([^\]]+)\]$/);
16
+ if (!sectionMatch)
17
+ continue;
18
+ const sectionName = sectionMatch[1];
19
+ if (sectionName === serverSection || sectionName.startsWith(`${serverSection}.`)) {
20
+ continue;
21
+ }
22
+ end = i;
23
+ break;
24
+ }
25
+ return { start, end };
26
+ };
27
+ const extractTomlMcpServerBlock = (content, server) => {
28
+ const range = findTomlServerBlockRange(content, server);
29
+ if (!range)
30
+ return null;
31
+ const lines = content.split(/\r?\n/);
32
+ return lines.slice(range.start, range.end).join('\n').trim();
33
+ };
34
+ exports.extractTomlMcpServerBlock = extractTomlMcpServerBlock;
35
+ const removeTomlMcpServerBlock = (content, server) => {
36
+ const range = findTomlServerBlockRange(content, server);
37
+ if (!range)
38
+ return content;
39
+ const lines = content.split(/\r?\n/);
40
+ const updated = [...lines.slice(0, range.start), ...lines.slice(range.end)].join('\n');
41
+ return updated.replace(/\n{3,}/g, '\n\n').trimEnd();
42
+ };
43
+ const appendTomlBlock = (content, block) => {
44
+ const trimmedContent = content.trimEnd();
45
+ const trimmedBlock = block.trim();
46
+ if (!trimmedBlock)
47
+ return trimmedContent;
48
+ if (!trimmedContent)
49
+ return `${trimmedBlock}\n`;
50
+ return `${trimmedContent}\n\n${trimmedBlock}\n`;
51
+ };
52
+ const mergeTomlMCPServers = (existingContent, generatedContent, servers) => {
53
+ let merged = existingContent || '';
54
+ const addedServers = [];
55
+ const replacedServers = [];
56
+ const skippedServers = [];
57
+ for (const server of servers) {
58
+ const newBlock = (0, exports.extractTomlMcpServerBlock)(generatedContent, server);
59
+ if (!newBlock) {
60
+ skippedServers.push(server);
61
+ continue;
62
+ }
63
+ const existingBlock = (0, exports.extractTomlMcpServerBlock)(merged, server);
64
+ if (existingBlock && existingBlock.trim() === newBlock.trim()) {
65
+ skippedServers.push(server);
66
+ continue;
67
+ }
68
+ if (existingBlock) {
69
+ merged = removeTomlMcpServerBlock(merged, server);
70
+ merged = appendTomlBlock(merged, newBlock);
71
+ replacedServers.push(server);
72
+ continue;
73
+ }
74
+ merged = appendTomlBlock(merged, newBlock);
75
+ addedServers.push(server);
76
+ }
77
+ return {
78
+ content: merged.trimEnd() + '\n',
79
+ addedServers,
80
+ replacedServers,
81
+ skippedServers
82
+ };
83
+ };
84
+ exports.mergeTomlMCPServers = mergeTomlMCPServers;
4
85
  const generateStandardMCPServers = (fraimKey, githubToken) => {
5
86
  const servers = {
6
87
  git: {
@@ -93,17 +174,19 @@ const generateKiroMCPServers = (fraimKey, githubToken) => {
93
174
  };
94
175
  exports.generateKiroMCPServers = generateKiroMCPServers;
95
176
  const generateCodexMCPServers = (fraimKey, githubToken) => {
96
- let config = `
97
- [mcp_servers.git]
98
- command = "npx"
177
+ const escapedFraimKey = escapeTomlString(fraimKey);
178
+ const escapedGithubToken = escapeTomlString(githubToken);
179
+ let config = `
180
+ [mcp_servers.git]
181
+ command = "npx"
99
182
  args = ["-y", "@cyanheads/git-mcp-server"]
100
183
  `;
101
184
  // Only add GitHub server if token is provided
102
185
  if (githubToken) {
103
- config += `
104
- [mcp_servers.github]
105
- url = "https://api.githubcopilot.com/mcp/"
106
- bearer_token_env_var = "GIT_TOKEN"
186
+ config += `
187
+ [mcp_servers.github]
188
+ url = "https://api.githubcopilot.com/mcp/"
189
+ http_headers = { Authorization = "Bearer ${escapedGithubToken}" }
107
190
  `;
108
191
  }
109
192
  config += `
@@ -114,9 +197,9 @@ args = ["-y", "@playwright/mcp"]
114
197
  [mcp_servers.fraim]
115
198
  command = "fraim-mcp"
116
199
 
117
- [mcp_servers.fraim.env]
118
- FRAIM_API_KEY = "${fraimKey}"
119
- FRAIM_REMOTE_URL = "https://fraim.wellnessatwork.me"
200
+ [mcp_servers.fraim.env]
201
+ FRAIM_API_KEY = "${escapedFraimKey}"
202
+ FRAIM_REMOTE_URL = "https://fraim.wellnessatwork.me"
120
203
  `;
121
204
  return config;
122
205
  };
@@ -46,22 +46,16 @@ function loadFraimConfig() {
46
46
  ...(config.customizations || {})
47
47
  }
48
48
  };
49
- // Add optional fields only if they exist in the config
50
- if (config.persona) {
51
- mergedConfig.persona = config.persona;
52
- }
53
- if (config.marketing) {
54
- mergedConfig.marketing = config.marketing;
55
- }
56
- if (config.database) {
57
- mergedConfig.database = config.database;
58
- }
49
+ // Add optional workflow-driven fields only if they exist in the config
59
50
  if (config.compliance) {
60
51
  mergedConfig.compliance = config.compliance;
61
52
  }
62
53
  if (config.learning) {
63
54
  mergedConfig.learning = config.learning;
64
55
  }
56
+ if (config.competitors && typeof config.competitors === 'object') {
57
+ mergedConfig.competitors = config.competitors;
58
+ }
65
59
  console.log(`๐Ÿ“‹ Loaded FRAIM config from .fraim/config.json (version ${mergedConfig.version})`);
66
60
  // Warn about deprecated git config
67
61
  if (config.git && !config.repository) {
@@ -4,7 +4,7 @@
4
4
  * TypeScript types for .fraim/config.json
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.MINIMAL_FRAIM_CONFIG = exports.DEFAULT_FRAIM_CONFIG = void 0;
7
+ exports.DEFAULT_FRAIM_CONFIG = void 0;
8
8
  /**
9
9
  * Default configuration values
10
10
  */
@@ -19,22 +19,5 @@ exports.DEFAULT_FRAIM_CONFIG = {
19
19
  name: '',
20
20
  defaultBranch: 'main'
21
21
  },
22
- customizations: {
23
- workflowsPath: '.fraim/workflows'
24
- }
25
- };
26
- /**
27
- * Minimal config template for new projects
28
- */
29
- exports.MINIMAL_FRAIM_CONFIG = {
30
- version: '2.0.47',
31
- project: {
32
- name: 'My Project'
33
- },
34
- repository: {
35
- provider: 'github',
36
- owner: 'your-username',
37
- name: 'your-repo',
38
- defaultBranch: 'main'
39
- }
22
+ customizations: {}
40
23
  };