fraim-framework 2.0.48 → 2.0.49

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.
@@ -177,19 +177,16 @@ Tag tests appropriately to ensure proper test suite execution:
177
177
  - **`unit`**: Tests that verify individual functions or classes in isolation
178
178
  - **`e2e`**: End-to-end tests that verify complete user workflows
179
179
 
180
- **Smoke Test Execution Rules:**
181
- If ANY tests are tagged "smoke" or you're modifying core functionality:
182
- 1. **MANDATORY**: Run full smoke test suite: `npm run test-smoke-ci`
183
- 2. **MANDATORY**: Include smoke test output in evidence
184
- 3. **MANDATORY**: All smoke tests must pass before proceeding
185
- 4. **BLOCKING**: Smoke test failures block phase completion
186
-
187
180
  ### Step 7: Basic Compilation Check
188
181
 
189
182
  **Verify code compiles:**
190
- ```bash
191
- npx tsc --noEmit --skipLibCheck
192
- ```
183
+
184
+ **Build Commands**: Always check `.fraim/config.json` first for `customizations.validation.buildCommand`, use that if available, otherwise default to `npm run build`.
185
+
186
+ **What to Look For**:
187
+ - Exit code 0 (success)
188
+ - No TypeScript compilation errors
189
+ - Clean build output
193
190
 
194
191
  **Must exit with code 0** (no errors)
195
192
 
@@ -23,16 +23,17 @@ All technical checks pass, confirming:
23
23
 
24
24
  ### Step 0: Read Project Validation Commands 📖
25
25
 
26
- **FIRST**: Read your project's validation commands from `.fraim/config.json`:
26
+ **FIRST**: Check your project's custom commands:
27
27
 
28
28
  ```bash
29
- # Read the config to get project-specific validation commands
30
- cat .fraim/config.json
31
- ```
29
+ # Check for custom build command
30
+ cat .fraim/config.json | grep -A 3 "buildCommand"
32
31
 
33
- Look for `customizations.validation.buildCommand` and `customizations.validation.smokeTestCommand`.
32
+ # Check for custom smoke test command
33
+ cat .fraim/config.json | grep -A 3 "smokeTestCommand"
34
+ ```
34
35
 
35
- **Use these commands instead of the generic ones below if they exist.**
36
+ **Use the configured commands if they exist, otherwise use the defaults shown below.**
36
37
 
37
38
  ### Step 1: Build Compilation Check ✅
38
39
 
@@ -43,7 +44,9 @@ Look for `customizations.validation.buildCommand` and `customizations.validation
43
44
 
44
45
  **Commands to Run** (use customizations.validation.buildCommand from config if available):
45
46
  ```bash
46
- # Run build (use customizations.validation.buildCommand if available, otherwise default)
47
+ # Use project-specific build command from .fraim/config.json if available:
48
+ # - Look for customizations.validation.buildCommand
49
+ # - Use that command if available, otherwise default to:
47
50
  npm run build
48
51
  ```
49
52
 
@@ -164,6 +167,7 @@ npm run build && npm test && git status
164
167
 
165
168
  ### Technical Standards
166
169
  - Read `.fraim/config.json` for the architecture document path (`customizations.architectureDoc`), then read the local architecture document to understand the full technical standards
170
+ - **Config Commands**: Always check `.fraim/config.json` first for `customizations.validation.buildCommand` and `customizations.validation.smokeTestCommand`, use those if available, otherwise use defaults
167
171
  - Use Successful Debugging Patterns from `rules/successful-debugging-patterns.md` via `get_fraim_file`
168
172
  - When using git commands directly (if MCP tools unavailable), read `rules/git-safe-commands.md` via `get_fraim_file`
169
173
 
@@ -181,6 +185,9 @@ npm run build && npm test && git status
181
185
 
182
186
  **Run smoke tests specifically:**
183
187
  ```bash
188
+ # Use project-specific command from .fraim/config.json if available
189
+ # Check: cat .fraim/config.json | grep "smokeTestCommand"
190
+ # Use configured command or default to:
184
191
  npm run test-smoke-ci
185
192
  ```
186
193
 
@@ -18,6 +18,10 @@ This is THE critical phase where you prove your implementation actually works. N
18
18
 
19
19
  ## RULES FOR THIS PHASE
20
20
 
21
+ ### Config-Aware Testing
22
+ **CRITICAL**: Always check `.fraim/config.json` first for project-specific test commands:
23
+ - `customizations.validation.testSuiteCommand` - Use this for comprehensive testing (default: `npm test`)
24
+
21
25
  ### Success Criteria
22
26
  Read `registry/rules/agent-success-criteria.md` via `get_fraim_file` for the complete framework. Focus on:
23
27
  - **Integrity** (honest reporting of validation results - never claim something works if you didn't test it)
@@ -86,17 +90,17 @@ grep -r "as any" src/ || echo "✅ No type bypassing found"
86
90
 
87
91
  **Commands to Run** (check .fraim/config.json for project-specific commands first):
88
92
  ```bash
89
- # 1. FIRST: Read your project's validation commands from .fraim/config.json
90
- cat .fraim/config.json | grep -A 10 "validation"
93
+ # 1. FIRST: Check for custom test commands
94
+ cat .fraim/config.json | grep -A 5 "validation"
91
95
 
92
- # 2. Run the comprehensive test suite (this runs ALL tests)
96
+ # 2. Run comprehensive test suite for current work
97
+ # Use customizations.validation.testSuiteCommand if available, otherwise:
93
98
  npm test
94
99
 
95
- # 3. Also run smoke tests specifically to verify core functionality
96
- npm run test-smoke-ci
97
-
98
- # 4. Run all tests in CI mode for complete coverage
99
- npm run test-all-ci
100
+ # 3. Run targeted tests for your specific changes (if applicable)
101
+ # Example: If you modified user authentication, run auth-related tests
102
+ # npm run test -- --grep "auth"
103
+ # OR: npm run test test-{issue-number}-*.ts
100
104
  ```
101
105
 
102
106
  **What to Look For**:
@@ -0,0 +1,188 @@
1
+ # Phase: address-pr-feedback
2
+
3
+ ## INTENT
4
+ To systematically address all PR feedback items by making targeted changes, replying to comments, and preparing for re-validation.
5
+
6
+ ## OUTCOME
7
+ All PR feedback addressed with:
8
+ - Targeted changes made for each feedback item
9
+ - Feedback file updated with resolutions
10
+ - PR comments replied to with explanations
11
+ - Changes validated and ready for re-review
12
+
13
+ ## RULES FOR THIS PHASE
14
+
15
+ ### Success Criteria
16
+ Read `registry/rules/agent-success-criteria.md` via `get_fraim_file` for the complete framework. Focus especially on:
17
+ - **Integrity** (document all changes made)
18
+ - **Correctness** (address feedback accurately)
19
+ - **Completeness** (address every UNADDRESSED item)
20
+
21
+ ### Simplicity Principles
22
+ Read `registry/rules/simplicity.md` via `get_fraim_file` for complete guidelines. Critical for feedback phase:
23
+ - **Targeted Changes**: Make minimal changes to address specific feedback
24
+ - **Manual Validation**: Test changes manually before updating PR
25
+ - **Clear Communication**: Reply to each comment with clear explanations
26
+
27
+ ## PRINCIPLES
28
+ - **Feedback-Driven**: Address only the specific issues raised in PR comments
29
+ - **Minimal Changes**: Avoid scope creep or unnecessary refactoring
30
+ - **Clear Communication**: Explain resolutions clearly to reviewers
31
+ - **Validation**: Ensure changes work before updating PR
32
+
33
+ ## 📋 WORKFLOW
34
+
35
+ ### Step 1: Read PR Feedback File
36
+
37
+ **MANDATORY**: Read the feedback file created by `wait-for-pr-review`:
38
+ - File location: `docs/evidence/{issue_number}-{workflow_type}-feedback.md`
39
+ - Identify all UNADDRESSED feedback items
40
+ - Understand the context and requirements for each item
41
+
42
+ ### Step 2: Address Each Feedback Item
43
+
44
+ **For each UNADDRESSED item:**
45
+
46
+ 1. **Understand the Feedback**:
47
+ - Read the comment carefully
48
+ - Identify what specific change is requested
49
+ - Determine the scope of the change needed
50
+
51
+ 2. **Make Targeted Changes**:
52
+ - Make minimal, focused changes to address the specific feedback
53
+ - Avoid unrelated changes or improvements
54
+ - Keep changes as small as possible while fully addressing the feedback
55
+
56
+ 3. **Update Feedback File**:
57
+ - Change status from UNADDRESSED to ADDRESSED
58
+ - Document your resolution approach
59
+ - Include details of what you changed
60
+
61
+ **Feedback File Update Format**:
62
+ ```markdown
63
+ ### Comment X - ADDRESSED
64
+ - **Author**: reviewer_name
65
+ - **Type**: pr_comment|review_comment|review
66
+ - **File**: file_path (if applicable)
67
+ - **Line**: line_number (if applicable)
68
+ - **Comment**: Original feedback text
69
+ - **Status**: ADDRESSED
70
+ - **Resolution**: Detailed explanation of how you addressed this feedback
71
+ - **Changes Made**: Specific files/lines modified
72
+ ```
73
+
74
+ ### Step 3: Validate Changes
75
+
76
+ **Test your changes:**
77
+ - Run compilation check: `npx tsc --noEmit --skipLibCheck`
78
+ - Run relevant tests to ensure no regressions
79
+ - Test manually if the feedback involves UI/API behavior
80
+ - Verify the specific issue mentioned in feedback is resolved
81
+
82
+ ### Step 4: Reply to PR Comments
83
+
84
+ **For each addressed comment:**
85
+ - Reply to the original PR comment
86
+ - Explain what you changed and how it addresses their concern
87
+ - Reference specific files/lines if helpful
88
+ - Be clear and professional in your communication
89
+
90
+ **Example reply format**:
91
+ ```
92
+ ✅ **Addressed**: [Brief description of what you changed]
93
+
94
+ **Changes made**:
95
+ - [Specific change 1]
96
+ - [Specific change 2]
97
+
98
+ **Files modified**: `path/to/file.ts`, `path/to/test.ts`
99
+
100
+ This should resolve the [specific issue mentioned]. Please let me know if you need any clarification!
101
+ ```
102
+
103
+ ### Step 5: Commit and Push Changes
104
+
105
+ **Commit your changes:**
106
+ - Use clear commit messages that reference the feedback
107
+ - Example: `Address PR feedback: Fix error handling in auth service`
108
+ - Push changes to the PR branch
109
+
110
+ ## 📸 EVIDENCE REQUIREMENTS
111
+
112
+ You MUST provide concrete evidence for all claims:
113
+
114
+ 1. **All Feedback Addressed**: Every UNADDRESSED item in feedback file is now ADDRESSED
115
+ 2. **Changes Made**: Specific code changes made for each feedback item
116
+ 3. **Validation Complete**: Tests pass and changes work as expected
117
+ 4. **PR Updated**: Changes committed, pushed, and comments replied to
118
+
119
+ ## VALIDATION
120
+
121
+ ### Phase Complete When:
122
+ - ✅ All UNADDRESSED items in feedback file are now ADDRESSED
123
+ - ✅ Feedback file updated with resolution details for each item
124
+ - ✅ Targeted changes made to address each specific feedback
125
+ - ✅ Changes validated (compilation, tests, manual testing)
126
+ - ✅ PR comments replied to with clear explanations
127
+ - ✅ Changes committed and pushed to PR branch
128
+
129
+ ### Phase Incomplete If:
130
+ - ❌ Any UNADDRESSED items remain in feedback file
131
+ - ❌ Changes don't actually address the feedback given
132
+ - ❌ Tests failing after changes
133
+ - ❌ Compilation errors introduced
134
+ - ❌ PR comments not replied to
135
+ - ❌ Changes not committed/pushed
136
+
137
+ ### Report Back:
138
+ When you complete this phase, call:
139
+
140
+ ```javascript
141
+ seekCoachingOnNextStep({
142
+ workflowType: "{workflow_type}",
143
+ issueNumber: "{issue_number}",
144
+ currentPhase: "address-pr-feedback",
145
+ status: "complete",
146
+ findings: {
147
+ feedbackItemsAddressed: {total_items_addressed},
148
+ changesValidated: true
149
+ },
150
+ evidence: {
151
+ feedbackFileUpdated: "All UNADDRESSED items now ADDRESSED with resolutions",
152
+ changesMade: "Brief summary of changes made",
153
+ validationResults: "Compilation and tests pass",
154
+ prCommentsReplied: "Replied to all PR comments with explanations"
155
+ }
156
+ })
157
+ ```
158
+
159
+ If feedback addressing incomplete, iterate:
160
+ ```javascript
161
+ seekCoachingOnNextStep({
162
+ workflowType: "{workflow_type}",
163
+ issueNumber: "{issue_number}",
164
+ currentPhase: "address-pr-feedback",
165
+ status: "incomplete",
166
+ findings: {
167
+ uncertainties: ["Issues encountered", "What needs to be fixed"]
168
+ }
169
+ })
170
+ ```
171
+
172
+ ## SCRIPTS
173
+
174
+ **Compile TypeScript:**
175
+ ```bash
176
+ npx tsc --noEmit --skipLibCheck
177
+ ```
178
+
179
+ **Run tests:**
180
+ ```bash
181
+ npm test
182
+ ```
183
+
184
+ **Check git status:**
185
+ ```bash
186
+ git status
187
+ git diff
188
+ ```
@@ -61,6 +61,25 @@ const TEST_PHASE_FLOW = [
61
61
  * @returns Next phase or null if at end of workflow
62
62
  */
63
63
  function getNextPhase(currentPhase, workflowType, issueType) {
64
+ // Special handling for address-pr-feedback phase
65
+ if (currentPhase === 'address-pr-feedback') {
66
+ // Success path: After addressing feedback, go back to appropriate validation phase
67
+ if (workflowType === 'implement') {
68
+ return 'implement-validate';
69
+ }
70
+ else if (workflowType === 'spec') {
71
+ return 'spec-completeness-review';
72
+ }
73
+ else if (workflowType === 'design') {
74
+ return 'design-completeness-review';
75
+ }
76
+ else if (workflowType === 'test') {
77
+ return 'test-validate';
78
+ }
79
+ else {
80
+ throw new Error(`Unknown workflow type: ${workflowType}`);
81
+ }
82
+ }
64
83
  let flow;
65
84
  if (workflowType === 'implement') {
66
85
  if (!issueType) {
@@ -121,10 +140,11 @@ function isPhaseValidForWorkflow(phase, workflowType, issueType) {
121
140
  * Get the phase to return to when a phase fails
122
141
  * @param failedPhase - The phase that failed
123
142
  * @param workflowType - Type of workflow
124
- * @param issueType - Type of issue (only used for implement workflow)
143
+ * @param issueType - Type of issue (currently unused - failure handling is the same for bug/feature)
125
144
  * @returns Phase to return to
126
145
  */
127
- function getPhaseOnFailure(failedPhase, workflowType, issueType) {
146
+ function getPhaseOnFailure(failedPhase, workflowType, issueType // eslint-disable-line @typescript-eslint/no-unused-vars
147
+ ) {
128
148
  if (workflowType === 'implement') {
129
149
  // Implement workflow failure handling
130
150
  switch (failedPhase) {
@@ -147,9 +167,11 @@ function getPhaseOnFailure(failedPhase, workflowType, issueType) {
147
167
  case 'submit-pr':
148
168
  return 'implement-completeness-review'; // PR submission issues, review completeness
149
169
  case 'wait-for-pr-review':
150
- // This phase determines where to go based on PR comment analysis
151
- // Will be handled by the PR comment analysis logic
152
- return 'implement-code'; // Default fallback for PR feedback
170
+ // PR feedback should go to address-pr-feedback phase
171
+ return 'address-pr-feedback';
172
+ case 'address-pr-feedback':
173
+ // If addressing PR feedback fails, retry the same phase
174
+ return 'address-pr-feedback';
153
175
  default:
154
176
  return 'implement-code'; // Default fallback
155
177
  }
@@ -164,7 +186,9 @@ function getPhaseOnFailure(failedPhase, workflowType, issueType) {
164
186
  case 'submit-pr':
165
187
  return 'spec-completeness-review';
166
188
  case 'wait-for-pr-review':
167
- return 'spec-spec'; // PR feedback usually means spec changes needed
189
+ return 'address-pr-feedback'; // PR feedback goes to address phase
190
+ case 'address-pr-feedback':
191
+ return 'address-pr-feedback'; // Retry addressing feedback
168
192
  default:
169
193
  return 'spec-spec';
170
194
  }
@@ -179,7 +203,9 @@ function getPhaseOnFailure(failedPhase, workflowType, issueType) {
179
203
  case 'submit-pr':
180
204
  return 'design-completeness-review';
181
205
  case 'wait-for-pr-review':
182
- return 'design-design'; // PR feedback usually means design changes needed
206
+ return 'address-pr-feedback'; // PR feedback goes to address phase
207
+ case 'address-pr-feedback':
208
+ return 'address-pr-feedback'; // Retry addressing feedback
183
209
  default:
184
210
  return 'design-design';
185
211
  }
@@ -194,7 +220,9 @@ function getPhaseOnFailure(failedPhase, workflowType, issueType) {
194
220
  case 'submit-pr':
195
221
  return 'test-validate';
196
222
  case 'wait-for-pr-review':
197
- return 'test-test'; // PR feedback usually means test changes needed
223
+ return 'address-pr-feedback'; // PR feedback goes to address phase
224
+ case 'address-pr-feedback':
225
+ return 'address-pr-feedback'; // Retry addressing feedback
198
226
  default:
199
227
  return 'test-test';
200
228
  }
@@ -18,11 +18,17 @@ class FraimDbService {
18
18
  // Use fraim_ prefix for standalone server collections
19
19
  this.keysCollection = this.db.collection('fraim_api_keys');
20
20
  this.sessionsCollection = this.db.collection('fraim_telemetry_sessions');
21
+ this.signupsCollection = this.db.collection('fraim_website_signups');
22
+ this.salesCollection = this.db.collection('fraim_sales_inquiries');
21
23
  // Create indexes
22
24
  await this.keysCollection.createIndex({ key: 1 }, { unique: true });
23
25
  await this.sessionsCollection.createIndex({ sessionId: 1 }, { unique: true });
24
26
  await this.sessionsCollection.createIndex({ userId: 1 });
25
27
  await this.sessionsCollection.createIndex({ lastActive: -1 });
28
+ await this.signupsCollection.createIndex({ email: 1 }, { unique: true });
29
+ await this.signupsCollection.createIndex({ timestamp: -1 });
30
+ await this.salesCollection.createIndex({ email: 1 });
31
+ await this.salesCollection.createIndex({ timestamp: -1 });
26
32
  console.log(`✅ Connected to Fraim DB: ${dbName}`);
27
33
  }
28
34
  async createSession(session) {
@@ -82,6 +88,46 @@ class FraimDbService {
82
88
  const random = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
83
89
  return `${prefix}${random}`;
84
90
  }
91
+ async createWebsiteSignup(signup) {
92
+ if (!this.signupsCollection)
93
+ throw new Error('DB not connected');
94
+ try {
95
+ await this.signupsCollection.insertOne(signup);
96
+ }
97
+ catch (error) {
98
+ // Handle duplicate email gracefully
99
+ if (error.code === 11000) {
100
+ throw new Error('Email already registered');
101
+ }
102
+ throw error;
103
+ }
104
+ }
105
+ async getWebsiteSignups(limit = 100) {
106
+ if (!this.signupsCollection)
107
+ throw new Error('DB not connected');
108
+ return await this.signupsCollection.find({})
109
+ .sort({ timestamp: -1 })
110
+ .limit(limit)
111
+ .toArray();
112
+ }
113
+ async getSignupByEmail(email) {
114
+ if (!this.signupsCollection)
115
+ throw new Error('DB not connected');
116
+ return await this.signupsCollection.findOne({ email });
117
+ }
118
+ async createSalesInquiry(inquiry) {
119
+ if (!this.salesCollection)
120
+ throw new Error('DB not connected');
121
+ await this.salesCollection.insertOne(inquiry);
122
+ }
123
+ async getSalesInquiries(limit = 100) {
124
+ if (!this.salesCollection)
125
+ throw new Error('DB not connected');
126
+ return await this.salesCollection.find({})
127
+ .sort({ timestamp: -1 })
128
+ .limit(limit)
129
+ .toArray();
130
+ }
85
131
  async close() {
86
132
  await this.client.close();
87
133
  }
@@ -226,6 +226,10 @@ class FraimMCPServer {
226
226
  next();
227
227
  }
228
228
  async versionCheck(req, res, next) {
229
+ // Skip version check in test environment
230
+ if (process.env.NODE_ENV === 'test') {
231
+ return next();
232
+ }
229
233
  // Only check for /mcp, /files routes, or root POST (MCP) where AI agents are active
230
234
  const isMcpTraffic = req.path.startsWith('/mcp') ||
231
235
  req.path.startsWith('/files') ||
@@ -277,8 +281,8 @@ class FraimMCPServer {
277
281
  * Middleware to authenticate requests via API key
278
282
  */
279
283
  async authMiddleware(req, res, next) {
280
- // Skip auth for public health check and admin routes (handled by adminAuthMiddleware)
281
- if (req.path === '/health' || req.path.startsWith('/admin')) {
284
+ // Skip auth for public health check, admin routes, website signup, and sales inquiries
285
+ if (req.path === '/health' || req.path.startsWith('/admin') || req.path === '/api/signup' || req.path === '/api/sales') {
282
286
  return next();
283
287
  }
284
288
  const apiKey = req.headers['x-api-key'] || req.query['api-key'];
@@ -372,12 +376,30 @@ class FraimMCPServer {
372
376
  const isActive = await this.sessionManager.updateActivity(apiKey);
373
377
  if (!isActive) {
374
378
  console.log(`⛔ Telemetry blocked request: ${req.method} ${req.path} (No Session for ${apiKey})`);
375
- // Enforce Handshake
379
+ // Enforce Handshake with clear instructions for agents
376
380
  res.status(400).json({
377
381
  jsonrpc: '2.0',
378
382
  error: {
379
383
  code: -32600,
380
- message: "⛔ Session Not Started. Please call the 'fraim_connect' tool first to register your environment and begin the session."
384
+ message: `⛔ Session Not Started. REQUIRED ACTION: Call 'fraim_connect' tool first with these parameters:
385
+
386
+ {
387
+ "agent": {
388
+ "name": "YourAgentName",
389
+ "model": "your-model-name"
390
+ },
391
+ "machine": {
392
+ "hostname": "use os.hostname()",
393
+ "platform": "use process.platform"
394
+ },
395
+ "repo": {
396
+ "url": "git remote URL",
397
+ "owner": "repo owner",
398
+ "name": "repo name"
399
+ }
400
+ }
401
+
402
+ After successful fraim_connect, all other FRAIM tools will work. This is required for telemetry and session management.`
381
403
  },
382
404
  id: req.body?.id || null
383
405
  });
@@ -510,6 +532,13 @@ class FraimMCPServer {
510
532
  else if (pathLower.includes('script') || normalizedPath.startsWith('scripts/') || normalizedPath.startsWith('fraim/scripts/')) {
511
533
  type = 'script';
512
534
  }
535
+ else if (normalizedPath.includes('ai-manager-rules/') && normalizedPath.includes('-phases/')) {
536
+ type = 'phase';
537
+ const parts = normalizedPath.split('/');
538
+ if (parts.length > 2) {
539
+ category = parts[parts.length - 2]; // e.g., 'implement-phases', 'spec-phases'
540
+ }
541
+ }
513
542
  // Extract keywords from filename
514
543
  const keywords = this.extractKeywords(entry.name, normalizedPath);
515
544
  const metadata = {
@@ -768,6 +797,120 @@ class FraimMCPServer {
768
797
  const success = await this.dbService.revokeApiKey(key);
769
798
  res.json({ revoked: success });
770
799
  });
800
+ // Website signup endpoint (public, no auth required)
801
+ this.app.post('/api/signup', async (req, res) => {
802
+ try {
803
+ const { email, company, useCase, source } = req.body;
804
+ if (!email) {
805
+ return res.status(400).json({ error: 'Email is required' });
806
+ }
807
+ // Basic email validation
808
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
809
+ if (!emailRegex.test(email)) {
810
+ return res.status(400).json({ error: 'Invalid email format' });
811
+ }
812
+ // Get client IP and user agent for analytics
813
+ const ipAddress = req.ip || req.connection.remoteAddress || req.headers['x-forwarded-for'];
814
+ const userAgent = req.headers['user-agent'];
815
+ const signup = {
816
+ email: email.toLowerCase().trim(),
817
+ company: company?.trim() || undefined,
818
+ useCase: useCase || undefined,
819
+ source: source || 'website',
820
+ timestamp: new Date(),
821
+ ipAddress,
822
+ userAgent
823
+ };
824
+ await this.dbService.createWebsiteSignup(signup);
825
+ console.log(`✅ New website signup: ${email} (${company || 'No company'}) - ${useCase || 'No use case'}`);
826
+ res.json({
827
+ success: true,
828
+ message: 'Successfully joined the waitlist!'
829
+ });
830
+ }
831
+ catch (error) {
832
+ console.error('❌ Website signup error:', error);
833
+ if (error.message === 'Email already registered') {
834
+ return res.status(409).json({
835
+ error: 'Email already registered',
836
+ message: 'This email is already on our waitlist.'
837
+ });
838
+ }
839
+ res.status(500).json({
840
+ error: 'Internal server error',
841
+ message: 'Failed to process signup. Please try again.'
842
+ });
843
+ }
844
+ });
845
+ // Sales inquiry endpoint (public, no auth required)
846
+ this.app.post('/api/sales', async (req, res) => {
847
+ try {
848
+ const { email, company, projectDetails, teamSize, timeline, budget, source } = req.body;
849
+ if (!email || !company || !projectDetails) {
850
+ return res.status(400).json({
851
+ error: 'Missing required fields',
852
+ message: 'Email, company, project details are required'
853
+ });
854
+ }
855
+ // Basic email validation
856
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
857
+ if (!emailRegex.test(email)) {
858
+ return res.status(400).json({ error: 'Invalid email format' });
859
+ }
860
+ // Get client IP and user agent for analytics
861
+ const ipAddress = req.ip || req.connection.remoteAddress || req.headers['x-forwarded-for'];
862
+ const userAgent = req.headers['user-agent'];
863
+ const inquiry = {
864
+ email: email.toLowerCase().trim(),
865
+ company: company.trim(),
866
+ projectDetails: projectDetails.trim(),
867
+ teamSize: teamSize?.trim() || undefined,
868
+ timeline: timeline?.trim() || undefined,
869
+ budget: budget?.trim() || undefined,
870
+ source: source || 'website',
871
+ timestamp: new Date(),
872
+ ipAddress,
873
+ userAgent
874
+ };
875
+ await this.dbService.createSalesInquiry(inquiry);
876
+ console.log(`💼 New sales inquiry: ${email} from ${company} - Team: ${teamSize}`);
877
+ res.json({
878
+ success: true,
879
+ message: 'Sales inquiry submitted successfully! We\'ll be in touch soon.'
880
+ });
881
+ }
882
+ catch (error) {
883
+ console.error('❌ Sales inquiry error:', error);
884
+ res.status(500).json({
885
+ error: 'Internal server error',
886
+ message: 'Failed to process sales inquiry. Please try again.'
887
+ });
888
+ }
889
+ });
890
+ // Get signups endpoint (admin only)
891
+ this.app.get('/admin/signups', async (req, res) => {
892
+ try {
893
+ const limit = parseInt(req.query.limit) || 100;
894
+ const signups = await this.dbService.getWebsiteSignups(limit);
895
+ res.json({ signups, total: signups.length });
896
+ }
897
+ catch (error) {
898
+ console.error('❌ Failed to get signups:', error);
899
+ res.status(500).json({ error: 'Failed to retrieve signups' });
900
+ }
901
+ });
902
+ // Get sales inquiries endpoint (admin only)
903
+ this.app.get('/admin/sales', async (req, res) => {
904
+ try {
905
+ const limit = parseInt(req.query.limit) || 100;
906
+ const inquiries = await this.dbService.getSalesInquiries(limit);
907
+ res.json({ inquiries, total: inquiries.length });
908
+ }
909
+ catch (error) {
910
+ console.error('❌ Failed to get sales inquiries:', error);
911
+ res.status(500).json({ error: 'Failed to retrieve sales inquiries' });
912
+ }
913
+ });
771
914
  // MCP endpoint - POST for actual MCP requests
772
915
  // Handle MCP requests on both /mcp (standard) and / (easier config)
773
916
  this.app.post(['/', '/mcp'], async (req, res) => {
@@ -868,12 +1011,22 @@ Call this before fetching any workflow or file to understand the system.`,
868
1011
  },
869
1012
  {
870
1013
  name: 'fraim_connect',
871
- description: `Handshake to start a FRAIM session. MUST be called at the start of every session.
1014
+ description: `🚨 REQUIRED FIRST CALL: Handshake to start a FRAIM session. MUST be called at the start of every session.
1015
+
1016
+ ⚠️ CRITICAL: If you get a "Session Not Started" error from any FRAIM tool, call this tool first.
1017
+
872
1018
  Registers your environment (machine/repo/agent) for telemetry and unlocks other tools.
873
1019
 
874
1020
  **Agent Information**: REQUIRED - Provide your actual agent name and model (validated against known agents).
875
1021
  **Machine Information**: Use os.hostname() and process.platform - required for telemetry.
876
- **Repository Information**: FRAIM will auto-detect from .fraim/config.json if available, or provide git remote URL.`,
1022
+ **Repository Information**: Extract and send this information from .fraim/config.json.
1023
+
1024
+ Example call:
1025
+ {
1026
+ "agent": {"name": "Claude", "model": "claude-3.5-sonnet"},
1027
+ "machine": {"hostname": "my-machine", "platform": "darwin"},
1028
+ "repo": {"url": "https://github.com/owner/repo", "owner": "owner", "name": "repo"}
1029
+ }`,
877
1030
  inputSchema: {
878
1031
  type: 'object',
879
1032
  properties: {
@@ -43,9 +43,9 @@ async function testPRReviewFailureHandling() {
43
43
  // Test submit-pr failure
44
44
  const submitPRFailure = (0, phase_flow_js_1.getPhaseOnFailure)('submit-pr', 'implement', 'bug');
45
45
  node_assert_1.strict.equal(submitPRFailure, 'implement-completeness-review', 'submit-pr failure should return to completeness-review');
46
- // Test wait-for-pr-review failure (default)
46
+ // Test wait-for-pr-review failure (should go to address-pr-feedback)
47
47
  const waitPRFailure = (0, phase_flow_js_1.getPhaseOnFailure)('wait-for-pr-review', 'implement', 'bug');
48
- node_assert_1.strict.equal(waitPRFailure, 'implement-code', 'wait-for-pr-review failure should default to implement-code');
48
+ node_assert_1.strict.equal(waitPRFailure, 'address-pr-feedback', 'wait-for-pr-review failure should go to address-pr-feedback');
49
49
  // Test all workflow types have new phases
50
50
  const specPhases = (0, phase_flow_js_1.getPhasesForWorkflow)('spec');
51
51
  (0, node_assert_1.strict)(specPhases.includes('submit-pr'), 'Spec workflow should include submit-pr');
@@ -191,10 +191,14 @@ async function testRealWorldPRReviewScenario() {
191
191
  node_assert_1.strict.equal(finalPhase, null, 'wait-for-pr-review should be final phase when approved');
192
192
  // Test PR review with changes requested (failure scenario)
193
193
  const failurePhase = (0, phase_flow_js_1.getPhaseOnFailure)('wait-for-pr-review', workflowType, issueType);
194
- node_assert_1.strict.equal(failurePhase, 'implement-code', 'PR changes requested should route to implement-code by default');
195
- // Test iteration: after fixing code, go through validation again
196
- let iterationPhase = 'implement-code';
197
- const iterationFlow = ['implement-validate', 'implement-smoke', 'implement-regression', 'implement-completeness-review', 'submit-pr', 'wait-for-pr-review'];
194
+ node_assert_1.strict.equal(failurePhase, 'address-pr-feedback', 'PR changes requested should route to address-pr-feedback');
195
+ // Test iteration: after addressing feedback, go through validation again
196
+ let iterationPhase = 'address-pr-feedback';
197
+ // After addressing feedback, should go to validation phase
198
+ const nextAfterFeedback = (0, phase_flow_js_1.getNextPhase)(iterationPhase, workflowType, issueType);
199
+ node_assert_1.strict.equal(nextAfterFeedback, 'implement-validate', 'After address-pr-feedback should go to implement-validate');
200
+ iterationPhase = nextAfterFeedback;
201
+ const iterationFlow = ['implement-smoke', 'implement-regression', 'implement-completeness-review', 'submit-pr', 'wait-for-pr-review'];
198
202
  for (const expectedNext of iterationFlow) {
199
203
  const nextPhase = (0, phase_flow_js_1.getNextPhase)(iterationPhase, workflowType, issueType);
200
204
  node_assert_1.strict.equal(nextPhase, expectedNext, `Iteration: after ${iterationPhase} should go to ${expectedNext}`);
@@ -92,7 +92,11 @@ async function testRehydrationAndExemptions() {
92
92
  params: {
93
93
  name: 'fraim_connect',
94
94
  arguments: {
95
- machine: { hostname: 'repro-host' },
95
+ agent: {
96
+ name: 'Claude',
97
+ model: 'test-model'
98
+ },
99
+ machine: { hostname: 'repro-host', platform: 'test' },
96
100
  repo: { url: 'http://github.com/repro' }
97
101
  }
98
102
  }
@@ -94,6 +94,10 @@ async function testTelemetryFlow() {
94
94
  params: {
95
95
  name: 'fraim_connect',
96
96
  arguments: {
97
+ agent: {
98
+ name: 'Claude',
99
+ model: 'test-model'
100
+ },
97
101
  machine: { hostname: 'test-host', platform: 'test-os' },
98
102
  repo: { url: 'http://github.com/test/repo' }
99
103
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim-framework",
3
- "version": "2.0.48",
3
+ "version": "2.0.49",
4
4
  "description": "FRAIM v2: Framework for Rigor-based AI Management - Transform from solo developer to AI manager orchestrating production-ready code with enterprise-grade discipline",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -15,8 +15,10 @@
15
15
  "test": "node scripts/test-with-server.js",
16
16
  "start:fraim": "tsx src/fraim-mcp-server.ts",
17
17
  "dev:fraim": "tsx --watch src/fraim-mcp-server.ts",
18
+ "serve:website": "node fraim-pro/serve.js",
18
19
  "watch:fraimlogs": "tsx scripts/watch-fraim-logs.ts",
19
20
  "manage-keys": "tsx scripts/fraim/manage-keys.ts",
21
+ "view-signups": "tsx scripts/view-signups.ts",
20
22
  "fraim:init": "npm run build && node bin/fraim.js init",
21
23
  "fraim:sync": "node bin/fraim.js sync",
22
24
  "postinstall": "fraim sync || echo 'FRAIM setup skipped.'",
@@ -10,7 +10,21 @@
10
10
  const fs = require('fs');
11
11
  const path = require('path');
12
12
 
13
- function createWebsiteStructure(projectName, outputDir = '.') {
13
+ // Read configuration from .fraim/config.json if available
14
+ function loadConfig() {
15
+ const configPath = path.join(process.cwd(), '.fraim', 'config.json');
16
+ if (fs.existsSync(configPath)) {
17
+ try {
18
+ return JSON.parse(fs.readFileSync(configPath, 'utf8'));
19
+ } catch (error) {
20
+ console.warn('Warning: Could not parse .fraim/config.json, using defaults');
21
+ return {};
22
+ }
23
+ }
24
+ return {};
25
+ }
26
+
27
+ function createWebsiteStructure(projectName, outputDir = process.cwd()) {
14
28
  const websiteDir = path.join(outputDir, projectName);
15
29
 
16
30
  // Create main directory
@@ -531,9 +545,10 @@ Thumbs.db
531
545
 
532
546
  // CLI usage
533
547
  if (require.main === module) {
548
+ const config = loadConfig();
534
549
  const args = process.argv.slice(2);
535
550
  const projectName = args[0];
536
- const outputDir = args[1] || '.';
551
+ const outputDir = args[1] || process.cwd();
537
552
 
538
553
  if (!projectName) {
539
554
  console.log('Usage: node create-website-structure.js <project-name> [output-directory]');
@@ -3,6 +3,7 @@
3
3
  import { execSync } from 'child_process';
4
4
  import { readFileSync, existsSync } from 'fs';
5
5
  import { join } from 'path';
6
+ import * as path from 'path';
6
7
 
7
8
  /**
8
9
  * Production Server Profiler
@@ -0,0 +1,11 @@
1
+ # FRAIM Workflow: soc2-evidence-generator
2
+
3
+ > [!IMPORTANT]
4
+ > This is a **FRAIM-managed workflow stub**.
5
+ > To load the full context (rules, templates, and execution steps), ask your AI agent to:
6
+ > `@fraim get_fraim_workflow("soc2-evidence-generator")`
7
+ >
8
+ > DO NOT EXECUTE.
9
+
10
+ ## Intent
11
+ Generate comprehensive SOC2 compliance evidence packages by automatically collecting, documenting, and formatting evidence from project systems to demonstrate adherence to Trust Service Criteria during annual audits.
@@ -0,0 +1,11 @@
1
+ # FRAIM Workflow: build-skillset
2
+
3
+ > [!IMPORTANT]
4
+ > This is a **FRAIM-managed workflow stub**.
5
+ > To load the full context (rules, templates, and execution steps), ask your AI agent to:
6
+ > `@fraim get_fraim_workflow("build-skillset")`
7
+ >
8
+ > DO NOT EXECUTE.
9
+
10
+ ## Intent
11
+ Create new project-specific skills through conversation with the user. This workflow allows agents to learn and document custom workflows, processes, and knowledge that are specific to the current project or team.
@@ -0,0 +1,11 @@
1
+ # FRAIM Workflow: contract-review-analysis
2
+
3
+ > [!IMPORTANT]
4
+ > This is a **FRAIM-managed workflow stub**.
5
+ > To load the full context (rules, templates, and execution steps), ask your AI agent to:
6
+ > `@fraim get_fraim_workflow("contract-review-analysis")`
7
+ >
8
+ > DO NOT EXECUTE.
9
+
10
+ ## Intent
11
+ Provide thorough, strategic legal contract review that identifies risks, strengthens protections, and optimizes business positioning. This skill enables comprehensive analysis of SOWs, NDAs, Terms & Conditions, and other legal documents to protect interests while maintaining business relationships.
@@ -0,0 +1,11 @@
1
+ # FRAIM Workflow: saas-contract-development
2
+
3
+ > [!IMPORTANT]
4
+ > This is a **FRAIM-managed workflow stub**.
5
+ > To load the full context (rules, templates, and execution steps), ask your AI agent to:
6
+ > `@fraim get_fraim_workflow("saas-contract-development")`
7
+ >
8
+ > DO NOT EXECUTE.
9
+
10
+ ## Intent
11
+ Create comprehensive, legally protective contract packages for SaaS engagements including SOWs, Terms & Conditions, and strategic pricing negotiations. This skill enables rapid development of professional-grade legal documents that protect IP, limit liability, and position for business growth.