fraim-framework 2.0.38 → 2.0.41
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 +8 -0
- package/dist/src/ai-manager/ai-manager.js +162 -0
- package/dist/src/cli/setup/auto-mcp-setup.js +1 -1
- package/dist/src/cli/setup/ide-detector.js +6 -4
- package/dist/src/cli/setup/mcp-config-generator.js +30 -1
- package/dist/src/fraim-mcp-server.js +198 -0
- package/dist/tests/test-ai-manager.js +113 -0
- package/dist/tests/test-complete-setup-flow.js +1 -1
- package/dist/tests/test-ide-detector.js +2 -2
- package/dist/tests/test-improved-setup.js +2 -2
- package/dist/tests/test-mcp-config-generator.js +31 -2
- package/package.json +2 -1
- package/registry/scripts/code-quality-check.sh +566 -559
- package/registry/scripts/prep-issue.sh +7 -0
- package/registry/scripts/verify-pr-comments.sh +74 -70
- /package/registry/stubs/workflows/{convert-to-pdf.md → marketing/convert-to-pdf.md} +0 -0
package/README.md
CHANGED
|
@@ -255,6 +255,14 @@ FRAIM/
|
|
|
255
255
|
|
|
256
256
|
## 🚀 **Get Started in 60 Seconds**
|
|
257
257
|
|
|
258
|
+
### **⚠️ Prerequisites**
|
|
259
|
+
|
|
260
|
+
**Shell Requirements:**
|
|
261
|
+
- **Windows**: Must use Git Bash (install from https://git-scm.com/download/win)
|
|
262
|
+
- **macOS/Linux**: Default terminal works fine
|
|
263
|
+
|
|
264
|
+
**Why Git Bash on Windows?** All FRAIM scripts use Unix-style paths and Bash commands. Git Bash ensures consistent behavior across platforms.
|
|
265
|
+
|
|
258
266
|
### **Install & Initialize**
|
|
259
267
|
```bash
|
|
260
268
|
npm install -g fraim-framework
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Simplified AI Manager
|
|
4
|
+
*
|
|
5
|
+
* Single AI manager integrated into fraim-mcp-server that reads validation rules
|
|
6
|
+
* from pre-defined JSON files and provides structured review instructions.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.AIManager = void 0;
|
|
10
|
+
const fs_1 = require("fs");
|
|
11
|
+
const path_1 = require("path");
|
|
12
|
+
class AIManager {
|
|
13
|
+
constructor(rulesPath) {
|
|
14
|
+
// Default to rules directory - server-side only, not sent to client
|
|
15
|
+
this.rulesPath = rulesPath || (0, path_1.join)(__dirname, '..', '..', 'registry', 'ai-manager-rules');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generate review instructions for a workflow phase
|
|
19
|
+
*/
|
|
20
|
+
generateReviewInstructions(context) {
|
|
21
|
+
console.log(`🤖 AI Manager: Generating review instructions for ${context.workflowType} phase`);
|
|
22
|
+
const rules = this.loadWorkflowRules(context.workflowType);
|
|
23
|
+
if (!rules) {
|
|
24
|
+
throw new Error(`No rules found for workflow type: ${context.workflowType}`);
|
|
25
|
+
}
|
|
26
|
+
return this.formatReviewInstructions(rules, context);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Evaluate agent's review report and provide decision
|
|
30
|
+
*/
|
|
31
|
+
evaluateReport(report, context) {
|
|
32
|
+
console.log(`🤖 AI Manager: Evaluating review report for ${context.workflowType} phase`);
|
|
33
|
+
const iterationCount = report.iterationCount || 1;
|
|
34
|
+
const maxIterations = 3;
|
|
35
|
+
if (report.pass) {
|
|
36
|
+
return {
|
|
37
|
+
action: 'PROCEED',
|
|
38
|
+
message: 'Review passed. Ready to submit PR to human for review.',
|
|
39
|
+
nextSteps: [
|
|
40
|
+
'Update issue status to "status:needs-review"',
|
|
41
|
+
'Remove "status:wip" label',
|
|
42
|
+
'Include complete review results in evidence document',
|
|
43
|
+
'Submit PR for human review with evidence'
|
|
44
|
+
],
|
|
45
|
+
iterationCount
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Check if max iterations reached
|
|
50
|
+
if (iterationCount >= maxIterations) {
|
|
51
|
+
return {
|
|
52
|
+
action: 'ESCALATE',
|
|
53
|
+
message: `Maximum iterations (${maxIterations}) reached. Escalating to human review despite failing validation.`,
|
|
54
|
+
nextSteps: [
|
|
55
|
+
'Update issue status to "status:needs-review"',
|
|
56
|
+
'Add "ai-manager:max-iterations" label to indicate escalation reason',
|
|
57
|
+
'Include all iteration attempts and failure reasons in evidence document',
|
|
58
|
+
'Submit PR for human review with detailed iteration history',
|
|
59
|
+
'Human reviewer should focus on the recurring validation failures'
|
|
60
|
+
],
|
|
61
|
+
iterationCount,
|
|
62
|
+
maxIterationsReached: true
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
return {
|
|
67
|
+
action: 'ITERATE',
|
|
68
|
+
message: `Review failed. Address the identified issues before proceeding. (Iteration ${iterationCount}/${maxIterations})`,
|
|
69
|
+
nextSteps: [
|
|
70
|
+
'Fix all issues identified in the failure reasons',
|
|
71
|
+
'Re-run validation steps to verify fixes',
|
|
72
|
+
`Request new review instructions with iterationCount: ${iterationCount + 1}`,
|
|
73
|
+
'Do not submit PR until review passes or max iterations reached'
|
|
74
|
+
],
|
|
75
|
+
iterationCount
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Load workflow rules from JSON file (server-side only)
|
|
82
|
+
*/
|
|
83
|
+
loadWorkflowRules(workflowType) {
|
|
84
|
+
const rulesFile = (0, path_1.join)(this.rulesPath, `${workflowType}.json`);
|
|
85
|
+
if (!(0, fs_1.existsSync)(rulesFile)) {
|
|
86
|
+
console.warn(`⚠️ No rules file found for workflow: ${workflowType} at ${rulesFile}`);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const content = (0, fs_1.readFileSync)(rulesFile, 'utf8');
|
|
91
|
+
return JSON.parse(content);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.error(`❌ Error loading rules for ${workflowType}:`, error);
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Format review instructions with proper template structure
|
|
100
|
+
*/
|
|
101
|
+
formatReviewInstructions(rules, context) {
|
|
102
|
+
const instructions = `# AI Manager Review Instructions
|
|
103
|
+
|
|
104
|
+
## Workflow: ${rules.workflowType.toUpperCase()}
|
|
105
|
+
**Issue:** #${context.issueNumber}
|
|
106
|
+
**Phase:** ${context.phase}
|
|
107
|
+
|
|
108
|
+
## Description
|
|
109
|
+
${rules.description}
|
|
110
|
+
|
|
111
|
+
## Iteration Tracking
|
|
112
|
+
**IMPORTANT**: You must track your iteration count for this phase. This is your attempt number for this specific workflow phase (spec, implement, test, etc.).
|
|
113
|
+
|
|
114
|
+
- **First attempt**: Use iterationCount: 1
|
|
115
|
+
- **After first failure**: Use iterationCount: 2
|
|
116
|
+
- **After second failure**: Use iterationCount: 3
|
|
117
|
+
- **Maximum iterations**: 3 (after 3rd failure, work will be escalated to human review)
|
|
118
|
+
|
|
119
|
+
## Validation Steps
|
|
120
|
+
Complete each step below and evaluate whether you meet the pass criteria:
|
|
121
|
+
|
|
122
|
+
${rules.validationRules.map(rule => `
|
|
123
|
+
### Step ${rule.step}: ${rule.description}
|
|
124
|
+
${rule.command ? `**Command:** \`${rule.command.replace('{issue_number}', context.issueNumber)}\`` : ''}
|
|
125
|
+
**Pass Criteria:** ${rule.passCriteria}
|
|
126
|
+
**Weight:** ${rule.weight.toUpperCase()}${rule.weight === 'blocking' ? ' (Must pass to proceed)' : ' (Should pass for quality)'}
|
|
127
|
+
**Category:** ${rule.category}
|
|
128
|
+
`).join('')}
|
|
129
|
+
|
|
130
|
+
## Grading Criteria
|
|
131
|
+
|
|
132
|
+
### Pass Requirements
|
|
133
|
+
${rules.gradingCriteria.passRequirements.map(req => `- ${req}`).join('\n')}
|
|
134
|
+
|
|
135
|
+
### Fail Requirements
|
|
136
|
+
${rules.gradingCriteria.failRequirements.map(req => `- ${req}`).join('\n')}
|
|
137
|
+
|
|
138
|
+
## Reporting Format
|
|
139
|
+
${rules.reportingFormat.instructions}
|
|
140
|
+
|
|
141
|
+
**Required Response Format:**
|
|
142
|
+
\`\`\`json
|
|
143
|
+
{
|
|
144
|
+
"pass": true/false,
|
|
145
|
+
"reasons": ["reason1", "reason2"], // Only required if pass=false
|
|
146
|
+
"iterationCount": 1 // REQUIRED: Your current iteration number for this phase
|
|
147
|
+
}
|
|
148
|
+
\`\`\`
|
|
149
|
+
|
|
150
|
+
## Iteration Limits
|
|
151
|
+
- **Maximum 3 iterations** per workflow phase
|
|
152
|
+
- After 3 failed attempts, work will be **automatically escalated** to human review
|
|
153
|
+
- Track your attempts carefully - include iterationCount in every report
|
|
154
|
+
- Each workflow phase (spec, implement, test) has its own iteration counter
|
|
155
|
+
|
|
156
|
+
## Next Steps
|
|
157
|
+
After completing your review, report back with the JSON response above including your iteration count. The AI Manager will evaluate your report and provide next steps based on your progress and iteration count.
|
|
158
|
+
`;
|
|
159
|
+
return instructions;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
exports.AIManager = AIManager;
|
|
@@ -286,7 +286,7 @@ const autoConfigureMCP = async (fraimKey, githubToken, selectedIDEs) => {
|
|
|
286
286
|
const detectedIDEs = (0, ide_detector_1.detectInstalledIDEs)();
|
|
287
287
|
if (detectedIDEs.length === 0) {
|
|
288
288
|
console.log(chalk_1.default.yellow('⚠️ No supported IDEs detected.'));
|
|
289
|
-
console.log(chalk_1.default.gray('Supported IDEs: Claude
|
|
289
|
+
console.log(chalk_1.default.gray('Supported IDEs: Claude, Antigravity, Kiro, Cursor, VSCode, Codex, Windsurf'));
|
|
290
290
|
console.log(chalk_1.default.blue('\n💡 You can install an IDE and run setup again later.'));
|
|
291
291
|
console.log(chalk_1.default.gray(' Or continue with manual MCP configuration.'));
|
|
292
292
|
const continueAnyway = await (0, prompts_1.default)({
|
|
@@ -19,6 +19,7 @@ const checkMultiplePaths = (paths) => {
|
|
|
19
19
|
};
|
|
20
20
|
const detectClaude = () => {
|
|
21
21
|
const paths = [
|
|
22
|
+
'~/.claude.json',
|
|
22
23
|
'~/.claude',
|
|
23
24
|
'~/Library/Application Support/Claude',
|
|
24
25
|
'~/AppData/Roaming/Claude'
|
|
@@ -54,16 +55,17 @@ const detectWindsurf = () => {
|
|
|
54
55
|
};
|
|
55
56
|
exports.IDE_CONFIGS = [
|
|
56
57
|
{
|
|
57
|
-
name: 'Claude
|
|
58
|
-
configPath: '~/.claude
|
|
58
|
+
name: 'Claude',
|
|
59
|
+
configPath: '~/.claude.json',
|
|
59
60
|
configFormat: 'json',
|
|
60
|
-
configType: '
|
|
61
|
+
configType: 'claude',
|
|
61
62
|
detectMethod: detectClaude,
|
|
62
63
|
alternativePaths: [
|
|
64
|
+
'~/.claude/settings.json',
|
|
63
65
|
'~/Library/Application Support/Claude/settings.json',
|
|
64
66
|
'~/AppData/Roaming/Claude/settings.json'
|
|
65
67
|
],
|
|
66
|
-
description: 'Anthropic Claude
|
|
68
|
+
description: 'Anthropic Claude application'
|
|
67
69
|
},
|
|
68
70
|
{
|
|
69
71
|
name: 'Antigravity',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateMCPConfig = exports.generateWindsurfMCPServers = exports.generateCodexMCPServers = exports.generateKiroMCPServers = exports.generateStandardMCPServers = void 0;
|
|
3
|
+
exports.generateMCPConfig = exports.generateWindsurfMCPServers = exports.generateCodexMCPServers = exports.generateKiroMCPServers = exports.generateClaudeMCPServers = exports.generateStandardMCPServers = void 0;
|
|
4
4
|
const generateStandardMCPServers = (fraimKey, githubToken) => ({
|
|
5
5
|
mcpServers: {
|
|
6
6
|
git: {
|
|
@@ -26,6 +26,33 @@ const generateStandardMCPServers = (fraimKey, githubToken) => ({
|
|
|
26
26
|
}
|
|
27
27
|
});
|
|
28
28
|
exports.generateStandardMCPServers = generateStandardMCPServers;
|
|
29
|
+
const generateClaudeMCPServers = (fraimKey, githubToken) => ({
|
|
30
|
+
mcpServers: {
|
|
31
|
+
git: {
|
|
32
|
+
command: "npx",
|
|
33
|
+
args: ["-y", "@cyanheads/git-mcp-server"]
|
|
34
|
+
},
|
|
35
|
+
github: {
|
|
36
|
+
type: "http",
|
|
37
|
+
url: "https://api.githubcopilot.com/mcp/",
|
|
38
|
+
headers: {
|
|
39
|
+
Authorization: `Bearer ${githubToken}`
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
playwright: {
|
|
43
|
+
command: "npx",
|
|
44
|
+
args: ["-y", "@playwright/mcp"]
|
|
45
|
+
},
|
|
46
|
+
fraim: {
|
|
47
|
+
type: "http",
|
|
48
|
+
url: "https://fraim.wellnessatwork.me",
|
|
49
|
+
headers: {
|
|
50
|
+
"x-api-key": fraimKey
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
exports.generateClaudeMCPServers = generateClaudeMCPServers;
|
|
29
56
|
const generateKiroMCPServers = (fraimKey, githubToken) => ({
|
|
30
57
|
mcpServers: {
|
|
31
58
|
git: {
|
|
@@ -102,6 +129,8 @@ const generateMCPConfig = (configType, fraimKey, githubToken) => {
|
|
|
102
129
|
switch (configType) {
|
|
103
130
|
case 'standard':
|
|
104
131
|
return (0, exports.generateStandardMCPServers)(fraimKey, githubToken);
|
|
132
|
+
case 'claude':
|
|
133
|
+
return (0, exports.generateClaudeMCPServers)(fraimKey, githubToken);
|
|
105
134
|
case 'kiro':
|
|
106
135
|
return (0, exports.generateKiroMCPServers)(fraimKey, githubToken);
|
|
107
136
|
case 'codex':
|
|
@@ -45,6 +45,7 @@ const path_1 = require("path");
|
|
|
45
45
|
const git_utils_1 = require("./utils/git-utils");
|
|
46
46
|
const config_loader_1 = require("./fraim/config-loader");
|
|
47
47
|
const db_service_1 = require("./fraim/db-service");
|
|
48
|
+
const ai_manager_1 = require("./ai-manager/ai-manager");
|
|
48
49
|
const issues_1 = require("./fraim/issues");
|
|
49
50
|
const crypto_1 = require("crypto");
|
|
50
51
|
const dotenv = __importStar(require("dotenv"));
|
|
@@ -169,6 +170,7 @@ class FraimMCPServer {
|
|
|
169
170
|
// Initialize database service
|
|
170
171
|
this.dbService = new db_service_1.FraimDbService();
|
|
171
172
|
this.sessionManager = new SessionManager(this.dbService);
|
|
173
|
+
this.aiManager = new ai_manager_1.AIManager();
|
|
172
174
|
// Load FRAIM configuration
|
|
173
175
|
this.config = (0, config_loader_1.loadFraimConfig)();
|
|
174
176
|
// Find registry directory (check dist first for production, then source)
|
|
@@ -979,6 +981,98 @@ Supports dry-run mode to preview the operation.`,
|
|
|
979
981
|
},
|
|
980
982
|
required: ['title', 'body']
|
|
981
983
|
}
|
|
984
|
+
},
|
|
985
|
+
{
|
|
986
|
+
name: 'ai_manager_request_review',
|
|
987
|
+
description: `Request self-review instructions from AI Manager after completing workflow phase.
|
|
988
|
+
|
|
989
|
+
The AI Manager provides detailed review instructions including:
|
|
990
|
+
- Step-by-step validation criteria
|
|
991
|
+
- Pass/fail criteria for each step
|
|
992
|
+
- Commands to run for verification
|
|
993
|
+
- Grading guidelines and reporting format
|
|
994
|
+
|
|
995
|
+
Use this when you believe your work is complete and ready for review.
|
|
996
|
+
Currently supports: spec phase (more phases coming soon).`,
|
|
997
|
+
inputSchema: {
|
|
998
|
+
type: 'object',
|
|
999
|
+
properties: {
|
|
1000
|
+
workflowType: {
|
|
1001
|
+
type: 'string',
|
|
1002
|
+
description: 'Type of workflow phase completed',
|
|
1003
|
+
enum: ['spec', 'design', 'implement', 'test']
|
|
1004
|
+
},
|
|
1005
|
+
issueNumber: {
|
|
1006
|
+
type: 'string',
|
|
1007
|
+
description: 'Issue number being worked on'
|
|
1008
|
+
},
|
|
1009
|
+
phase: {
|
|
1010
|
+
type: 'string',
|
|
1011
|
+
description: 'Specific phase name (e.g., "specification", "implementation")'
|
|
1012
|
+
}
|
|
1013
|
+
},
|
|
1014
|
+
required: ['workflowType', 'issueNumber', 'phase']
|
|
1015
|
+
}
|
|
1016
|
+
},
|
|
1017
|
+
{
|
|
1018
|
+
name: 'ai_manager_report_grade',
|
|
1019
|
+
description: `Report your self-assessment to AI Manager after completing self-review.
|
|
1020
|
+
|
|
1021
|
+
Provide results from review in JSON format:
|
|
1022
|
+
- pass: true/false based on review assessment
|
|
1023
|
+
- reasons: array of failure reasons (only if pass=false)
|
|
1024
|
+
- iterationCount: REQUIRED - your current iteration number for this phase
|
|
1025
|
+
|
|
1026
|
+
AI Manager will evaluate your report and provide next steps:
|
|
1027
|
+
- PROCEED: Ready to submit PR for human review
|
|
1028
|
+
- ITERATE: Fix issues and retry (max 3 iterations)
|
|
1029
|
+
- ESCALATE: Max iterations reached, escalate to human review
|
|
1030
|
+
|
|
1031
|
+
IMPORTANT: Track your iteration count per workflow phase. Each phase (spec, implement, test) has its own counter.
|
|
1032
|
+
Maximum 3 iterations per phase before automatic escalation to human review.`,
|
|
1033
|
+
inputSchema: {
|
|
1034
|
+
type: 'object',
|
|
1035
|
+
properties: {
|
|
1036
|
+
workflowType: {
|
|
1037
|
+
type: 'string',
|
|
1038
|
+
description: 'Type of workflow phase',
|
|
1039
|
+
enum: ['spec', 'design', 'implement', 'test']
|
|
1040
|
+
},
|
|
1041
|
+
issueNumber: {
|
|
1042
|
+
type: 'string',
|
|
1043
|
+
description: 'Issue number being worked on'
|
|
1044
|
+
},
|
|
1045
|
+
phase: {
|
|
1046
|
+
type: 'string',
|
|
1047
|
+
description: 'Specific phase name'
|
|
1048
|
+
},
|
|
1049
|
+
report: {
|
|
1050
|
+
type: 'object',
|
|
1051
|
+
description: 'Your self-assessment report in JSON format',
|
|
1052
|
+
properties: {
|
|
1053
|
+
pass: {
|
|
1054
|
+
type: 'boolean',
|
|
1055
|
+
description: 'Whether your work passes all validation criteria'
|
|
1056
|
+
},
|
|
1057
|
+
reasons: {
|
|
1058
|
+
type: 'array',
|
|
1059
|
+
description: 'Array of specific reasons for failure (only required if pass=false)',
|
|
1060
|
+
items: {
|
|
1061
|
+
type: 'string'
|
|
1062
|
+
}
|
|
1063
|
+
},
|
|
1064
|
+
iterationCount: {
|
|
1065
|
+
type: 'number',
|
|
1066
|
+
description: 'REQUIRED: Your current iteration number for this workflow phase (1, 2, or 3)',
|
|
1067
|
+
minimum: 1,
|
|
1068
|
+
maximum: 3
|
|
1069
|
+
}
|
|
1070
|
+
},
|
|
1071
|
+
required: ['pass', 'iterationCount']
|
|
1072
|
+
}
|
|
1073
|
+
},
|
|
1074
|
+
required: ['workflowType', 'issueNumber', 'phase', 'report']
|
|
1075
|
+
}
|
|
982
1076
|
}
|
|
983
1077
|
]
|
|
984
1078
|
};
|
|
@@ -1016,6 +1110,10 @@ Supports dry-run mode to preview the operation.`,
|
|
|
1016
1110
|
};
|
|
1017
1111
|
case 'fraim_connect':
|
|
1018
1112
|
return await this.handleFraimConnect(toolArgs, context.apiKey, context.userId);
|
|
1113
|
+
case 'ai_manager_request_review':
|
|
1114
|
+
return await this.handleAIManagerRequestReview(toolArgs);
|
|
1115
|
+
case 'ai_manager_report_grade':
|
|
1116
|
+
return await this.handleAIManagerReportGrade(toolArgs);
|
|
1019
1117
|
default:
|
|
1020
1118
|
throw new Error(`Unknown tool: ${toolName} `);
|
|
1021
1119
|
}
|
|
@@ -1450,6 +1548,106 @@ If \`.fraim/config.json\` doesn't exist:
|
|
|
1450
1548
|
sessionId: sessionId
|
|
1451
1549
|
};
|
|
1452
1550
|
}
|
|
1551
|
+
async handleAIManagerRequestReview(args) {
|
|
1552
|
+
try {
|
|
1553
|
+
console.log(`🤖 AI Manager: Generating review instructions for ${args.workflowType} phase`);
|
|
1554
|
+
const instructions = this.aiManager.generateReviewInstructions({
|
|
1555
|
+
workflowType: args.workflowType,
|
|
1556
|
+
issueNumber: args.issueNumber,
|
|
1557
|
+
phase: args.phase
|
|
1558
|
+
});
|
|
1559
|
+
return {
|
|
1560
|
+
content: [{
|
|
1561
|
+
type: 'text',
|
|
1562
|
+
text: instructions
|
|
1563
|
+
}]
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
catch (error) {
|
|
1567
|
+
console.error('❌ AI Manager request review failed:', error);
|
|
1568
|
+
return {
|
|
1569
|
+
content: [{
|
|
1570
|
+
type: 'text',
|
|
1571
|
+
text: `# ❌ AI Manager Request Failed\n\n**Error**: ${error instanceof Error ? error.message : 'Unknown error'}\n\nPlease check your request parameters and try again.`
|
|
1572
|
+
}],
|
|
1573
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
async handleAIManagerReportGrade(args) {
|
|
1578
|
+
try {
|
|
1579
|
+
console.log(`🤖 AI Manager: Evaluating review report for ${args.workflowType} phase`);
|
|
1580
|
+
const decision = this.aiManager.evaluateReport(args.report, {
|
|
1581
|
+
workflowType: args.workflowType,
|
|
1582
|
+
issueNumber: args.issueNumber,
|
|
1583
|
+
phase: args.phase
|
|
1584
|
+
});
|
|
1585
|
+
// Format response for agent
|
|
1586
|
+
let response = `# 🤖 AI Manager Evaluation Result\n\n`;
|
|
1587
|
+
const actionEmoji = decision.action === 'PROCEED' ? '✅' :
|
|
1588
|
+
decision.action === 'ESCALATE' ? '⚠️' : '🔄';
|
|
1589
|
+
response += `## Decision: ${actionEmoji} ${decision.action}\n\n`;
|
|
1590
|
+
response += `**Message**: ${decision.message}\n\n`;
|
|
1591
|
+
if (decision.iterationCount) {
|
|
1592
|
+
response += `**Iteration Count**: ${decision.iterationCount}/3\n\n`;
|
|
1593
|
+
}
|
|
1594
|
+
response += `## Next Steps\n\n`;
|
|
1595
|
+
decision.nextSteps.forEach((step, index) => {
|
|
1596
|
+
response += `${index + 1}. ${step}\n`;
|
|
1597
|
+
});
|
|
1598
|
+
response += `\n`;
|
|
1599
|
+
if (decision.action === 'PROCEED') {
|
|
1600
|
+
response += `## 🎉 Ready for Human Review!\n\n`;
|
|
1601
|
+
response += `Your work has passed AI Manager validation. You should now:\n\n`;
|
|
1602
|
+
response += `1. **Submit PR** with complete evidence document\n`;
|
|
1603
|
+
response += `2. **Update issue labels** to status:needs-review\n`;
|
|
1604
|
+
response += `3. **Include this AI Manager validation** in your evidence\n\n`;
|
|
1605
|
+
}
|
|
1606
|
+
else if (decision.action === 'ESCALATE') {
|
|
1607
|
+
response += `## ⚠️ Escalated to Human Review\n\n`;
|
|
1608
|
+
response += `Maximum iterations reached. Your work will be reviewed by a human despite validation failures:\n\n`;
|
|
1609
|
+
response += `1. **Submit PR** with detailed iteration history\n`;
|
|
1610
|
+
response += `2. **Add escalation label** (ai-manager:max-iterations)\n`;
|
|
1611
|
+
response += `3. **Document all attempts** and failure reasons in evidence\n`;
|
|
1612
|
+
response += `4. **Human reviewer** will focus on recurring validation issues\n\n`;
|
|
1613
|
+
}
|
|
1614
|
+
else {
|
|
1615
|
+
response += `## 🔧 Work Required\n\n`;
|
|
1616
|
+
response += `Your work needs improvement. You should:\n\n`;
|
|
1617
|
+
response += `1. **Address all failure reasons** listed below\n`;
|
|
1618
|
+
response += `2. **Re-run validation steps** to verify fixes\n`;
|
|
1619
|
+
response += `3. **Request new review** using ai_manager_request_review\n`;
|
|
1620
|
+
response += `4. **Include iterationCount: ${(decision.iterationCount || 1) + 1}** in next report\n`;
|
|
1621
|
+
response += `5. **Do NOT submit PR** until review passes or escalates\n\n`;
|
|
1622
|
+
}
|
|
1623
|
+
response += `## Your Report Summary\n\n`;
|
|
1624
|
+
response += `**Pass**: ${args.report.pass}\n`;
|
|
1625
|
+
response += `**Iteration**: ${args.report.iterationCount || 1}/3\n`;
|
|
1626
|
+
if (args.report.reasons && args.report.reasons.length > 0) {
|
|
1627
|
+
response += `**Failure Reasons**:\n`;
|
|
1628
|
+
args.report.reasons.forEach((reason) => {
|
|
1629
|
+
response += `- ${reason}\n`;
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
return {
|
|
1633
|
+
content: [{
|
|
1634
|
+
type: 'text',
|
|
1635
|
+
text: response
|
|
1636
|
+
}],
|
|
1637
|
+
decision: decision
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
catch (error) {
|
|
1641
|
+
console.error('❌ AI Manager report evaluation failed:', error);
|
|
1642
|
+
return {
|
|
1643
|
+
content: [{
|
|
1644
|
+
type: 'text',
|
|
1645
|
+
text: `# ❌ AI Manager Evaluation Failed\n\n**Error**: ${error instanceof Error ? error.message : 'Unknown error'}\n\nPlease check your report format and try again.`
|
|
1646
|
+
}],
|
|
1647
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1453
1651
|
async start(port = 3002) {
|
|
1454
1652
|
try {
|
|
1455
1653
|
// Connect to database before starting server
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for AI Manager - Simplified Review System
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
const node_test_1 = require("node:test");
|
|
10
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
11
|
+
const ai_manager_1 = require("../src/ai-manager/ai-manager");
|
|
12
|
+
(0, node_test_1.describe)('AI Manager', () => {
|
|
13
|
+
let aiManager;
|
|
14
|
+
(0, node_test_1.beforeEach)(() => {
|
|
15
|
+
aiManager = new ai_manager_1.AIManager();
|
|
16
|
+
});
|
|
17
|
+
(0, node_test_1.describe)('generateReviewInstructions', () => {
|
|
18
|
+
(0, node_test_1.test)('should generate spec workflow instructions', () => {
|
|
19
|
+
const context = {
|
|
20
|
+
workflowType: 'spec',
|
|
21
|
+
issueNumber: '123',
|
|
22
|
+
phase: 'specification'
|
|
23
|
+
};
|
|
24
|
+
const instructions = aiManager.generateReviewInstructions(context);
|
|
25
|
+
(0, node_assert_1.default)(typeof instructions === 'string');
|
|
26
|
+
(0, node_assert_1.default)(instructions.includes('AI Manager Review Instructions'));
|
|
27
|
+
(0, node_assert_1.default)(instructions.includes('spec'));
|
|
28
|
+
(0, node_assert_1.default)(instructions.includes('123'));
|
|
29
|
+
(0, node_assert_1.default)(instructions.includes('iterationCount'));
|
|
30
|
+
(0, node_assert_1.default)(instructions.includes('Maximum 3 iterations'));
|
|
31
|
+
});
|
|
32
|
+
(0, node_test_1.test)('should throw error for unknown workflow type', () => {
|
|
33
|
+
const context = {
|
|
34
|
+
workflowType: 'unknown',
|
|
35
|
+
issueNumber: '123',
|
|
36
|
+
phase: 'test'
|
|
37
|
+
};
|
|
38
|
+
node_assert_1.default.throws(() => {
|
|
39
|
+
aiManager.generateReviewInstructions(context);
|
|
40
|
+
}, /No rules found for workflow type: unknown/);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
(0, node_test_1.describe)('evaluateReport', () => {
|
|
44
|
+
(0, node_test_1.test)('should return PROCEED for passing report', () => {
|
|
45
|
+
const report = {
|
|
46
|
+
pass: true,
|
|
47
|
+
iterationCount: 1
|
|
48
|
+
};
|
|
49
|
+
const context = {
|
|
50
|
+
workflowType: 'spec',
|
|
51
|
+
issueNumber: '123',
|
|
52
|
+
phase: 'specification'
|
|
53
|
+
};
|
|
54
|
+
const decision = aiManager.evaluateReport(report, context);
|
|
55
|
+
node_assert_1.default.strictEqual(decision.action, 'PROCEED');
|
|
56
|
+
(0, node_assert_1.default)(decision.message.includes('Ready to submit PR'));
|
|
57
|
+
(0, node_assert_1.default)(decision.nextSteps.length > 0);
|
|
58
|
+
node_assert_1.default.strictEqual(decision.iterationCount, 1);
|
|
59
|
+
});
|
|
60
|
+
(0, node_test_1.test)('should return ITERATE for failing report within iteration limit', () => {
|
|
61
|
+
const report = {
|
|
62
|
+
pass: false,
|
|
63
|
+
reasons: ['Missing spec document', 'Template not followed'],
|
|
64
|
+
iterationCount: 2
|
|
65
|
+
};
|
|
66
|
+
const context = {
|
|
67
|
+
workflowType: 'spec',
|
|
68
|
+
issueNumber: '123',
|
|
69
|
+
phase: 'specification'
|
|
70
|
+
};
|
|
71
|
+
const decision = aiManager.evaluateReport(report, context);
|
|
72
|
+
node_assert_1.default.strictEqual(decision.action, 'ITERATE');
|
|
73
|
+
(0, node_assert_1.default)(decision.message.includes('Address the identified issues'));
|
|
74
|
+
(0, node_assert_1.default)(decision.message.includes('Iteration 2/3'));
|
|
75
|
+
(0, node_assert_1.default)(decision.nextSteps.length > 0);
|
|
76
|
+
node_assert_1.default.strictEqual(decision.iterationCount, 2);
|
|
77
|
+
});
|
|
78
|
+
(0, node_test_1.test)('should return ESCALATE when max iterations reached', () => {
|
|
79
|
+
const report = {
|
|
80
|
+
pass: false,
|
|
81
|
+
reasons: ['Still missing spec document', 'Template still not followed'],
|
|
82
|
+
iterationCount: 3
|
|
83
|
+
};
|
|
84
|
+
const context = {
|
|
85
|
+
workflowType: 'spec',
|
|
86
|
+
issueNumber: '123',
|
|
87
|
+
phase: 'specification'
|
|
88
|
+
};
|
|
89
|
+
const decision = aiManager.evaluateReport(report, context);
|
|
90
|
+
node_assert_1.default.strictEqual(decision.action, 'ESCALATE');
|
|
91
|
+
(0, node_assert_1.default)(decision.message.includes('Maximum iterations'));
|
|
92
|
+
(0, node_assert_1.default)(decision.message.includes('Escalating to human review'));
|
|
93
|
+
(0, node_assert_1.default)(decision.nextSteps.length > 0);
|
|
94
|
+
node_assert_1.default.strictEqual(decision.iterationCount, 3);
|
|
95
|
+
node_assert_1.default.strictEqual(decision.maxIterationsReached, true);
|
|
96
|
+
});
|
|
97
|
+
(0, node_test_1.test)('should default to iteration 1 if not provided', () => {
|
|
98
|
+
const report = {
|
|
99
|
+
pass: false,
|
|
100
|
+
reasons: ['Missing spec document']
|
|
101
|
+
// iterationCount not provided
|
|
102
|
+
};
|
|
103
|
+
const context = {
|
|
104
|
+
workflowType: 'spec',
|
|
105
|
+
issueNumber: '123',
|
|
106
|
+
phase: 'specification'
|
|
107
|
+
};
|
|
108
|
+
const decision = aiManager.evaluateReport(report, context);
|
|
109
|
+
node_assert_1.default.strictEqual(decision.action, 'ITERATE');
|
|
110
|
+
node_assert_1.default.strictEqual(decision.iterationCount, 1);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -95,7 +95,7 @@ const token_validator_1 = require("../src/cli/setup/token-validator");
|
|
|
95
95
|
(0, node_test_1.test)('Path expansion works correctly across platforms', () => {
|
|
96
96
|
const testPaths = [
|
|
97
97
|
'~/.fraim/config.json',
|
|
98
|
-
'~/.claude
|
|
98
|
+
'~/.claude.json',
|
|
99
99
|
'~/.kiro/settings/mcp.json'
|
|
100
100
|
];
|
|
101
101
|
testPaths.forEach(testPath => {
|
|
@@ -19,14 +19,14 @@ const os_1 = __importDefault(require("os"));
|
|
|
19
19
|
node_assert_1.default.strictEqual(result, absolutePath);
|
|
20
20
|
});
|
|
21
21
|
(0, node_test_1.test)('IDE_CONFIGS should contain all expected IDEs', () => {
|
|
22
|
-
const expectedIDEs = ['Claude
|
|
22
|
+
const expectedIDEs = ['Claude', 'Antigravity', 'Kiro', 'Cursor', 'VSCode', 'Codex', 'Windsurf'];
|
|
23
23
|
const actualIDEs = ide_detector_1.IDE_CONFIGS.map(ide => ide.name);
|
|
24
24
|
expectedIDEs.forEach(expectedIDE => {
|
|
25
25
|
(0, node_assert_1.default)(actualIDEs.includes(expectedIDE), `Missing IDE: ${expectedIDE}`);
|
|
26
26
|
});
|
|
27
27
|
});
|
|
28
28
|
(0, node_test_1.test)('IDE_CONFIGS should have valid config types', () => {
|
|
29
|
-
const validConfigTypes = ['standard', 'kiro', 'codex', 'windsurf'];
|
|
29
|
+
const validConfigTypes = ['standard', 'kiro', 'codex', 'windsurf', 'claude'];
|
|
30
30
|
ide_detector_1.IDE_CONFIGS.forEach(ide => {
|
|
31
31
|
(0, node_assert_1.default)(validConfigTypes.includes(ide.configType), `Invalid config type for ${ide.name}: ${ide.configType}`);
|
|
32
32
|
});
|
|
@@ -30,12 +30,12 @@ const mcp_config_generator_1 = require("../src/cli/setup/mcp-config-generator");
|
|
|
30
30
|
const claude = (0, ide_detector_1.findIDEByName)('claude');
|
|
31
31
|
const cursor = (0, ide_detector_1.findIDEByName)('cursor');
|
|
32
32
|
const vscode = (0, ide_detector_1.findIDEByName)('vscode');
|
|
33
|
-
(0, node_assert_1.default)(claude?.name === 'Claude
|
|
33
|
+
(0, node_assert_1.default)(claude?.name === 'Claude', 'Should find Claude');
|
|
34
34
|
(0, node_assert_1.default)(cursor?.name === 'Cursor', 'Should find Cursor');
|
|
35
35
|
(0, node_assert_1.default)(vscode?.name === 'VSCode', 'Should find VSCode');
|
|
36
36
|
// Test partial matching
|
|
37
37
|
const claudePartial = (0, ide_detector_1.findIDEByName)('clau');
|
|
38
|
-
(0, node_assert_1.default)(claudePartial?.name === 'Claude
|
|
38
|
+
(0, node_assert_1.default)(claudePartial?.name === 'Claude', 'Should find Claude with partial name');
|
|
39
39
|
console.log('✅ IDE name matching works correctly');
|
|
40
40
|
});
|
|
41
41
|
(0, node_test_1.it)('should expand paths correctly across platforms', () => {
|