fraim-framework 2.0.49 → 2.0.52
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/dist/registry/ai-manager-rules/retrospective.md +116 -0
- package/dist/registry/ai-manager-rules/spec-phases/spec-competitor-analysis.md +105 -0
- package/dist/src/ai-manager/ai-manager.js +46 -42
- package/dist/src/ai-manager/phase-flow.js +18 -1
- package/dist/src/cli/commands/sync.js +18 -2
- package/dist/tests/test-ai-coach-edge-cases.js +10 -5
- package/dist/tests/test-ai-coach-mcp-integration.js +23 -5
- package/dist/tests/test-ai-coach-performance.js +3 -3
- package/dist/tests/test-ai-coach-workflows.js +45 -18
- package/dist/tests/test-pr-review-workflow.js +7 -3
- package/package.json +2 -2
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Phase: Retrospective
|
|
2
|
+
|
|
3
|
+
## INTENT
|
|
4
|
+
To conduct a comprehensive retrospective of the completed work while full context is available, capturing learnings and insights before branch cleanup and resolution. This phase applies to all workflow types (spec, design, implement, test).
|
|
5
|
+
|
|
6
|
+
## TRIGGER
|
|
7
|
+
This phase is triggered after PR approval (`wait-for-pr-review` completion) and before `resolve` phase for all workflow types.
|
|
8
|
+
|
|
9
|
+
## CONTEXT ADVANTAGES
|
|
10
|
+
- **Full Work Context**: All changes, decisions, and challenges are fresh in memory
|
|
11
|
+
- **Branch Available**: Can reference specific commits, diffs, and work artifacts
|
|
12
|
+
- **Problem-Solution Mapping**: Clear connection between original problem and completed solution
|
|
13
|
+
- **Fresh Insights**: Recent experience provides accurate reflection on what worked/didn't work
|
|
14
|
+
- **PR Feedback Available**: Can analyze and learn from user feedback received during review
|
|
15
|
+
- **Complete Journey**: Can analyze entire workflow from start to finish
|
|
16
|
+
|
|
17
|
+
## WORKFLOW
|
|
18
|
+
|
|
19
|
+
### Step 1: Retrospective Preparation
|
|
20
|
+
- Ensure PR is approved and ready for merge
|
|
21
|
+
- Gather work artifacts (commits, documents, test results, etc.)
|
|
22
|
+
- Review original issue requirements and acceptance criteria
|
|
23
|
+
- **Collect PR feedback**: Review all comments, suggestions, and feedback received during PR review
|
|
24
|
+
- Analyze user feedback patterns and themes
|
|
25
|
+
- Identify key decision points and challenges encountered
|
|
26
|
+
|
|
27
|
+
### Step 2: Create Retrospective Document
|
|
28
|
+
- Create (or use existing) retrospective file: `retrospectives/issue-{issue-number}-{kebab-title}-postmortem.md`
|
|
29
|
+
- Use retrospective template: `get_fraim_file({ path: "templates/retrospective/RETROSPECTIVE-TEMPLATE.md" })`
|
|
30
|
+
- Document while work context is fresh and complete
|
|
31
|
+
|
|
32
|
+
### Step 3: Work Analysis
|
|
33
|
+
- **What Went Well**: Successful approaches, tools, and techniques used
|
|
34
|
+
- **What Went Poorly**: Challenges, obstacles, and inefficiencies encountered
|
|
35
|
+
- **Root Cause Analysis**: Why problems occurred, not just what happened
|
|
36
|
+
- **Decision Analysis**: Review key decisions made during the work
|
|
37
|
+
- **PR Feedback Analysis**: What user feedback revealed about the work quality
|
|
38
|
+
- **Process Effectiveness**: How well the workflow supported the work
|
|
39
|
+
|
|
40
|
+
### Step 4: Learning Extraction
|
|
41
|
+
- **Key Learnings**: Specific insights that can help future work of this type
|
|
42
|
+
- **Process Improvements**: Workflow or rule changes that would help
|
|
43
|
+
- **Tool/Technique Discoveries**: New approaches or tools that proved valuable
|
|
44
|
+
- **Anti-Patterns Identified**: Approaches to avoid in future work
|
|
45
|
+
- **User Feedback Patterns**: Common themes in user feedback to address
|
|
46
|
+
- **Quality Insights**: What contributed to or detracted from work quality
|
|
47
|
+
|
|
48
|
+
### Step 5: Prevention Measures
|
|
49
|
+
- **Specific Actions**: Concrete steps to prevent similar issues in future work
|
|
50
|
+
- **Rule Updates**: Suggestions for updating FRAIM rules or workflows
|
|
51
|
+
- **Knowledge Sharing**: Insights that should be shared with other agents
|
|
52
|
+
- **Process Enhancements**: Workflow improvements based on experience
|
|
53
|
+
- **Feedback Integration**: How to better incorporate user feedback in future work
|
|
54
|
+
- **Quality Improvements**: Changes to improve work quality
|
|
55
|
+
|
|
56
|
+
### Step 6: Validation and Completion
|
|
57
|
+
- Ensure retrospective meets quality criteria
|
|
58
|
+
- Validate all template sections are complete
|
|
59
|
+
- Confirm learnings are actionable and specific
|
|
60
|
+
- Verify PR feedback has been analyzed and learned from
|
|
61
|
+
- Check that prevention measures are concrete and implementable
|
|
62
|
+
- Mark phase complete and proceed to resolve
|
|
63
|
+
|
|
64
|
+
## VALIDATION CRITERIA
|
|
65
|
+
- ✅ Retrospective document created using template
|
|
66
|
+
- ✅ Root cause analysis completed (not just symptoms)
|
|
67
|
+
- ✅ Prevention measures documented and actionable
|
|
68
|
+
- ✅ Key learnings extracted and documented
|
|
69
|
+
- ✅ Process improvements identified
|
|
70
|
+
- ✅ PR feedback analyzed and lessons captured
|
|
71
|
+
- ✅ All template sections completed with substantial content
|
|
72
|
+
- ✅ Retrospective quality meets standards
|
|
73
|
+
- ✅ Learnings are specific to the workflow type and transferable
|
|
74
|
+
|
|
75
|
+
## PHASE COMPLETION
|
|
76
|
+
This phase is complete when:
|
|
77
|
+
1. Comprehensive retrospective document is created
|
|
78
|
+
2. All validation criteria are met
|
|
79
|
+
3. Learnings are documented for future reference
|
|
80
|
+
4. PR feedback has been systematically analyzed
|
|
81
|
+
5. Prevention measures are specific and actionable
|
|
82
|
+
6. Process improvements are identified
|
|
83
|
+
7. Agent is ready to proceed to resolve phase
|
|
84
|
+
|
|
85
|
+
## WORKFLOW-SPECIFIC FOCUS AREAS
|
|
86
|
+
|
|
87
|
+
### Spec Retrospective Focus
|
|
88
|
+
- Requirements gathering effectiveness
|
|
89
|
+
- Stakeholder engagement quality
|
|
90
|
+
- Competitive analysis thoroughness
|
|
91
|
+
- User experience definition clarity
|
|
92
|
+
- Validation plan completeness
|
|
93
|
+
|
|
94
|
+
### Design Retrospective Focus
|
|
95
|
+
- Technical decision quality
|
|
96
|
+
- Architecture choice rationale
|
|
97
|
+
- Design review feedback incorporation
|
|
98
|
+
- Technical feasibility assessment
|
|
99
|
+
- Implementation guidance clarity
|
|
100
|
+
|
|
101
|
+
### Implement Retrospective Focus
|
|
102
|
+
- Code quality and maintainability
|
|
103
|
+
- Technical approach effectiveness
|
|
104
|
+
- Testing strategy success
|
|
105
|
+
- Performance considerations
|
|
106
|
+
- Integration challenges
|
|
107
|
+
|
|
108
|
+
### Test Retrospective Focus
|
|
109
|
+
- Test coverage adequacy
|
|
110
|
+
- Quality assurance approach effectiveness
|
|
111
|
+
- Testing strategy validation
|
|
112
|
+
- Bug detection capability
|
|
113
|
+
- Test maintenance considerations
|
|
114
|
+
|
|
115
|
+
## NEXT PHASE
|
|
116
|
+
After completing retrospective, proceed to `resolve` phase for final merge and cleanup.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Phase: spec-competitor-analysis
|
|
2
|
+
|
|
3
|
+
## INTENT
|
|
4
|
+
To conduct systematic competitive research and analysis for the feature being specified, ensuring comprehensive understanding of competitive landscape and clear differentiation strategy.
|
|
5
|
+
|
|
6
|
+
## OUTCOME
|
|
7
|
+
A comprehensive competitive analysis that:
|
|
8
|
+
- Documents all configured competitors and their approaches
|
|
9
|
+
- Identifies new competitors through systematic research
|
|
10
|
+
- Proposes configuration updates with user approval
|
|
11
|
+
- Defines clear differentiation strategy and competitive positioning
|
|
12
|
+
- Updates the specification with complete competitive landscape analysis
|
|
13
|
+
|
|
14
|
+
## TRIGGER
|
|
15
|
+
This phase is triggered after `spec-spec` completion and before `spec-completeness-review`.
|
|
16
|
+
|
|
17
|
+
## CONTEXT ADVANTAGES
|
|
18
|
+
- **Full Feature Context**: Complete understanding of proposed feature and requirements
|
|
19
|
+
- **Fresh Research**: Current competitive landscape information
|
|
20
|
+
- **Strategic Positioning**: Ability to influence feature design based on competitive insights
|
|
21
|
+
- **Configuration-Driven**: Systematic analysis of known competitors plus discovery of new ones
|
|
22
|
+
|
|
23
|
+
## WORKFLOW
|
|
24
|
+
|
|
25
|
+
### Step 1: Load Competitor Configuration
|
|
26
|
+
- Read `.fraim/config.json` competitors section
|
|
27
|
+
- Extract configured competitors and their competitive spaces
|
|
28
|
+
- Note any existing competitive intelligence
|
|
29
|
+
|
|
30
|
+
### Step 2: Research Configured Competitors
|
|
31
|
+
For each competitor in config:
|
|
32
|
+
- Research their current solution for this feature area
|
|
33
|
+
- Analyze their strengths and weaknesses in this space
|
|
34
|
+
- Gather customer feedback and reviews
|
|
35
|
+
- Assess market position and pricing approach
|
|
36
|
+
- Document findings in structured format
|
|
37
|
+
|
|
38
|
+
### Step 3: Discover Additional Competitors
|
|
39
|
+
- Use web search to find other competitors in the feature space
|
|
40
|
+
- Research market for competitors not in configuration
|
|
41
|
+
- Identify direct, adjacent, and emerging competitors
|
|
42
|
+
- Focus on competitors specifically relevant to this feature
|
|
43
|
+
|
|
44
|
+
### Step 4: Competitor Configuration Updates
|
|
45
|
+
- **Identify New Competitors**: Present newly discovered competitors to user
|
|
46
|
+
- **Configuration Proposal**: Suggest adding relevant competitors to `.fraim/config.json`
|
|
47
|
+
- **User Approval**: Ask user to approve additions: "Found new competitor 'X' in space 'Y'. Add to config?"
|
|
48
|
+
- **Auto-Update**: If user approves, automatically update configuration file
|
|
49
|
+
- **Documentation**: Note configuration changes in competitive analysis
|
|
50
|
+
|
|
51
|
+
### Step 5: Competitive Analysis
|
|
52
|
+
- Compare competitor approaches to our proposed solution
|
|
53
|
+
- Identify competitive advantages and disadvantages
|
|
54
|
+
- Develop differentiation strategy
|
|
55
|
+
- Plan competitive response strategies
|
|
56
|
+
- Define market positioning approach
|
|
57
|
+
|
|
58
|
+
### Step 6: Update Specification
|
|
59
|
+
- Update Competitive Landscape section with findings
|
|
60
|
+
- Ensure all configured competitors are covered
|
|
61
|
+
- Include newly discovered competitors
|
|
62
|
+
- Add competitive positioning strategy
|
|
63
|
+
- Document research sources and methodology
|
|
64
|
+
- Validate analysis completeness
|
|
65
|
+
|
|
66
|
+
## VALIDATION
|
|
67
|
+
|
|
68
|
+
### Phase Complete When:
|
|
69
|
+
- ✅ All configured competitors analyzed
|
|
70
|
+
- ✅ Additional competitors discovered through research
|
|
71
|
+
- ✅ User consulted on adding new competitors to config
|
|
72
|
+
- ✅ Configuration updated with user-approved competitors
|
|
73
|
+
- ✅ Competitive positioning strategy defined
|
|
74
|
+
- ✅ Differentiation advantages clearly articulated
|
|
75
|
+
- ✅ Research sources documented
|
|
76
|
+
- ✅ Analysis covers both configured and discovered competitors
|
|
77
|
+
- ✅ Specification competitive landscape section updated
|
|
78
|
+
|
|
79
|
+
### Report Back:
|
|
80
|
+
When this phase is complete, call:
|
|
81
|
+
```javascript
|
|
82
|
+
seekCoachingOnNextStep({
|
|
83
|
+
workflowType: "spec",
|
|
84
|
+
issueNumber: "your-issue-number",
|
|
85
|
+
currentPhase: "spec-competitor-analysis",
|
|
86
|
+
status: "complete",
|
|
87
|
+
evidence: {
|
|
88
|
+
competitorsAnalyzed: ["competitor1", "competitor2"],
|
|
89
|
+
newCompetitorsFound: ["new-competitor1"],
|
|
90
|
+
configurationUpdated: true,
|
|
91
|
+
differentiationStrategy: "summary of strategy",
|
|
92
|
+
specificationUpdated: true
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Phase Incomplete If:
|
|
98
|
+
- Configuration competitors not fully researched
|
|
99
|
+
- No additional competitor discovery attempted
|
|
100
|
+
- User not consulted on configuration updates
|
|
101
|
+
- Competitive positioning strategy missing
|
|
102
|
+
- Specification not updated with findings
|
|
103
|
+
|
|
104
|
+
## NEXT PHASE
|
|
105
|
+
After completing competitor-analysis, proceed to `spec-completeness-review` phase.
|
|
@@ -155,8 +155,11 @@ ${this.getValidPhasesForIssueType(issueType).join(' → ')}
|
|
|
155
155
|
* Generate completion message for completed phases
|
|
156
156
|
*/
|
|
157
157
|
async generateCompletionMessage(args) {
|
|
158
|
-
//
|
|
159
|
-
|
|
158
|
+
// Determine next phase and provide its instructions
|
|
159
|
+
const issueType = this.extractIssueType(args.findings, args.evidence);
|
|
160
|
+
const nextPhase = (0, phase_flow_js_1.getNextPhase)(args.currentPhase, args.workflowType, issueType);
|
|
161
|
+
if (!nextPhase) {
|
|
162
|
+
// This is the final phase - show completion message
|
|
160
163
|
return `# 🎉 ${args.workflowType.charAt(0).toUpperCase() + args.workflowType.slice(1)} Complete!
|
|
161
164
|
|
|
162
165
|
**Congratulations!** You have successfully completed all ${args.workflowType} phases for issue #${args.issueNumber}.
|
|
@@ -165,30 +168,11 @@ ${this.getValidPhasesForIssueType(issueType).join(' → ')}
|
|
|
165
168
|
${this.getAccomplishmentsForWorkflow(args.workflowType)}
|
|
166
169
|
|
|
167
170
|
## 📋 Final Evidence Summary
|
|
168
|
-
${JSON.stringify(args.evidence || {}, null, 2)}
|
|
171
|
+
${JSON.stringify(args.evidence || args.findings || {}, null, 2)}
|
|
169
172
|
|
|
170
173
|
`;
|
|
171
174
|
}
|
|
172
175
|
else {
|
|
173
|
-
// Determine next phase and provide its instructions
|
|
174
|
-
const issueType = this.extractIssueType(args.findings, args.evidence);
|
|
175
|
-
const nextPhase = (0, phase_flow_js_1.getNextPhase)(args.currentPhase, args.workflowType, issueType);
|
|
176
|
-
if (!nextPhase) {
|
|
177
|
-
return `# 🎉 Phase Complete!
|
|
178
|
-
|
|
179
|
-
**Phase:** ${args.currentPhase}
|
|
180
|
-
**Status:** Complete
|
|
181
|
-
|
|
182
|
-
Great work! You've completed the ${args.currentPhase} phase.
|
|
183
|
-
|
|
184
|
-
## Evidence Summary
|
|
185
|
-
${JSON.stringify(args.evidence || args.findings || {}, null, 2)}
|
|
186
|
-
|
|
187
|
-
## 🚨 Error: No Next Phase Found
|
|
188
|
-
Unable to determine the next phase. This might indicate an issue with the phase flow logic.
|
|
189
|
-
Please check your workflow type and current phase.
|
|
190
|
-
`;
|
|
191
|
-
}
|
|
192
176
|
// Get instructions for the next phase
|
|
193
177
|
const nextPhaseInstructions = await this.getPhaseInstructions(nextPhase, args.workflowType);
|
|
194
178
|
return `# 🎉 Phase Complete!
|
|
@@ -351,9 +335,20 @@ Remember: Take your time and follow each step carefully. The phase guidance cont
|
|
|
351
335
|
*/
|
|
352
336
|
async getPhaseInstructions(phase, workflowType) {
|
|
353
337
|
try {
|
|
354
|
-
//
|
|
338
|
+
// Special handling for shared phases
|
|
339
|
+
const sharedPhases = ['retrospective', 'submit-pr', 'wait-for-pr-review', 'address-pr-feedback'];
|
|
355
340
|
let phasePath;
|
|
356
|
-
if (
|
|
341
|
+
if (sharedPhases.includes(phase)) {
|
|
342
|
+
// Check if it's in root ai-manager-rules directory (like retrospective)
|
|
343
|
+
if (phase === 'retrospective') {
|
|
344
|
+
phasePath = `ai-manager-rules/${phase}.md`;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
// Other shared phases are in shared-phases subdirectory
|
|
348
|
+
phasePath = `ai-manager-rules/shared-phases/${phase}.md`;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
else if (workflowType === 'implement') {
|
|
357
352
|
phasePath = `ai-manager-rules/implement-phases/${phase}.md`;
|
|
358
353
|
}
|
|
359
354
|
else {
|
|
@@ -364,26 +359,35 @@ Remember: Take your time and follow each step carefully. The phase guidance cont
|
|
|
364
359
|
if (phaseFile && (0, fs_1.existsSync)(phaseFile.fullPath)) {
|
|
365
360
|
return (0, fs_1.readFileSync)(phaseFile.fullPath, 'utf8');
|
|
366
361
|
}
|
|
367
|
-
// If not found
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
362
|
+
// If not found and not a shared phase, try shared-phases directory
|
|
363
|
+
if (!sharedPhases.includes(phase)) {
|
|
364
|
+
const sharedPhasePath = `ai-manager-rules/shared-phases/${phase}.md`;
|
|
365
|
+
phaseFile = this.fileIndex.get(sharedPhasePath);
|
|
366
|
+
if (phaseFile && (0, fs_1.existsSync)(phaseFile.fullPath)) {
|
|
367
|
+
return (0, fs_1.readFileSync)(phaseFile.fullPath, 'utf8');
|
|
368
|
+
}
|
|
372
369
|
}
|
|
373
370
|
// Fallback: try to read directly from filesystem
|
|
374
371
|
const { join } = require('path');
|
|
375
|
-
const possiblePaths = [
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
372
|
+
const possiblePaths = [];
|
|
373
|
+
if (sharedPhases.includes(phase)) {
|
|
374
|
+
if (phase === 'retrospective') {
|
|
375
|
+
// Retrospective is in root ai-manager-rules directory
|
|
376
|
+
possiblePaths.push(join(process.cwd(), 'dist', 'registry', `ai-manager-rules/${phase}.md`), join(process.cwd(), 'registry', `ai-manager-rules/${phase}.md`), join(__dirname, '..', '..', 'registry', `ai-manager-rules/${phase}.md`), join(__dirname, '..', 'registry', `ai-manager-rules/${phase}.md`));
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
// Other shared phases are in shared-phases subdirectory
|
|
380
|
+
const sharedPhasePath = `ai-manager-rules/shared-phases/${phase}.md`;
|
|
381
|
+
possiblePaths.push(join(process.cwd(), 'dist', 'registry', sharedPhasePath), join(process.cwd(), 'registry', sharedPhasePath), join(__dirname, '..', '..', 'registry', sharedPhasePath), join(__dirname, '..', 'registry', sharedPhasePath));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
// For workflow-specific phases
|
|
386
|
+
possiblePaths.push(join(process.cwd(), 'dist', 'registry', phasePath), join(process.cwd(), 'registry', phasePath), join(__dirname, '..', '..', 'registry', phasePath), join(__dirname, '..', 'registry', phasePath));
|
|
387
|
+
// Also try shared-phases directory
|
|
388
|
+
const sharedPhasePath = `ai-manager-rules/shared-phases/${phase}.md`;
|
|
389
|
+
possiblePaths.push(join(process.cwd(), 'dist', 'registry', sharedPhasePath), join(process.cwd(), 'registry', sharedPhasePath), join(__dirname, '..', '..', 'registry', sharedPhasePath), join(__dirname, '..', 'registry', sharedPhasePath));
|
|
390
|
+
}
|
|
387
391
|
for (const fullPath of possiblePaths) {
|
|
388
392
|
if ((0, fs_1.existsSync)(fullPath)) {
|
|
389
393
|
console.log(`📖 AI Coach: Reading phase file from ${fullPath}`);
|
|
@@ -392,7 +396,7 @@ Remember: Take your time and follow each step carefully. The phase guidance cont
|
|
|
392
396
|
}
|
|
393
397
|
return `# Phase: ${phase}
|
|
394
398
|
|
|
395
|
-
**Error**: Phase instructions not found
|
|
399
|
+
**Error**: Phase instructions not found
|
|
396
400
|
|
|
397
401
|
Tried paths:
|
|
398
402
|
${possiblePaths.map(p => `- ${p}`).join('\n')}
|
|
@@ -23,6 +23,7 @@ const BUG_PHASE_FLOW = [
|
|
|
23
23
|
'implement-completeness-review',
|
|
24
24
|
'submit-pr',
|
|
25
25
|
'wait-for-pr-review',
|
|
26
|
+
'retrospective',
|
|
26
27
|
];
|
|
27
28
|
const FEATURE_PHASE_FLOW = [
|
|
28
29
|
'implement-scoping',
|
|
@@ -34,24 +35,29 @@ const FEATURE_PHASE_FLOW = [
|
|
|
34
35
|
'implement-completeness-review',
|
|
35
36
|
'submit-pr',
|
|
36
37
|
'wait-for-pr-review',
|
|
38
|
+
'retrospective',
|
|
37
39
|
];
|
|
38
40
|
const SPEC_PHASE_FLOW = [
|
|
39
41
|
'spec-spec',
|
|
42
|
+
'spec-competitor-analysis',
|
|
40
43
|
'spec-completeness-review',
|
|
41
44
|
'submit-pr',
|
|
42
45
|
'wait-for-pr-review',
|
|
46
|
+
'retrospective',
|
|
43
47
|
];
|
|
44
48
|
const DESIGN_PHASE_FLOW = [
|
|
45
49
|
'design-design',
|
|
46
50
|
'design-completeness-review',
|
|
47
51
|
'submit-pr',
|
|
48
52
|
'wait-for-pr-review',
|
|
53
|
+
'retrospective',
|
|
49
54
|
];
|
|
50
55
|
const TEST_PHASE_FLOW = [
|
|
51
56
|
'test-test',
|
|
52
57
|
'test-validate',
|
|
53
58
|
'submit-pr',
|
|
54
59
|
'wait-for-pr-review',
|
|
60
|
+
'retrospective',
|
|
55
61
|
];
|
|
56
62
|
/**
|
|
57
63
|
* Get the next phase in the workflow
|
|
@@ -169,6 +175,9 @@ function getPhaseOnFailure(failedPhase, workflowType, issueType // eslint-disabl
|
|
|
169
175
|
case 'wait-for-pr-review':
|
|
170
176
|
// PR feedback should go to address-pr-feedback phase
|
|
171
177
|
return 'address-pr-feedback';
|
|
178
|
+
case 'retrospective':
|
|
179
|
+
// If retrospective fails, retry the same phase
|
|
180
|
+
return 'retrospective';
|
|
172
181
|
case 'address-pr-feedback':
|
|
173
182
|
// If addressing PR feedback fails, retry the same phase
|
|
174
183
|
return 'address-pr-feedback';
|
|
@@ -181,12 +190,16 @@ function getPhaseOnFailure(failedPhase, workflowType, issueType // eslint-disabl
|
|
|
181
190
|
switch (failedPhase) {
|
|
182
191
|
case 'spec-spec':
|
|
183
192
|
return 'spec-spec'; // Start over at spec
|
|
193
|
+
case 'spec-competitor-analysis':
|
|
194
|
+
return 'spec-spec'; // Go back to improve spec if competitive analysis fails
|
|
184
195
|
case 'spec-completeness-review':
|
|
185
|
-
return 'spec-spec'; // Go back to improve
|
|
196
|
+
return 'spec-spec'; // Go back to improve competitive analysis
|
|
186
197
|
case 'submit-pr':
|
|
187
198
|
return 'spec-completeness-review';
|
|
188
199
|
case 'wait-for-pr-review':
|
|
189
200
|
return 'address-pr-feedback'; // PR feedback goes to address phase
|
|
201
|
+
case 'retrospective':
|
|
202
|
+
return 'retrospective'; // Retry retrospective if it fails
|
|
190
203
|
case 'address-pr-feedback':
|
|
191
204
|
return 'address-pr-feedback'; // Retry addressing feedback
|
|
192
205
|
default:
|
|
@@ -204,6 +217,8 @@ function getPhaseOnFailure(failedPhase, workflowType, issueType // eslint-disabl
|
|
|
204
217
|
return 'design-completeness-review';
|
|
205
218
|
case 'wait-for-pr-review':
|
|
206
219
|
return 'address-pr-feedback'; // PR feedback goes to address phase
|
|
220
|
+
case 'retrospective':
|
|
221
|
+
return 'retrospective'; // Retry retrospective if it fails
|
|
207
222
|
case 'address-pr-feedback':
|
|
208
223
|
return 'address-pr-feedback'; // Retry addressing feedback
|
|
209
224
|
default:
|
|
@@ -221,6 +236,8 @@ function getPhaseOnFailure(failedPhase, workflowType, issueType // eslint-disabl
|
|
|
221
236
|
return 'test-validate';
|
|
222
237
|
case 'wait-for-pr-review':
|
|
223
238
|
return 'address-pr-feedback'; // PR feedback goes to address phase
|
|
239
|
+
case 'retrospective':
|
|
240
|
+
return 'retrospective'; // Retry retrospective if it fails
|
|
224
241
|
case 'address-pr-feedback':
|
|
225
242
|
return 'address-pr-feedback'; // Retry addressing feedback
|
|
226
243
|
default:
|
|
@@ -20,8 +20,15 @@ const runSync = async (options) => {
|
|
|
20
20
|
const workflowsRelativePath = config.customizations?.workflowsPath || '.fraim/workflows';
|
|
21
21
|
const workflowsDir = path_1.default.resolve(projectRoot, workflowsRelativePath);
|
|
22
22
|
const digestPath = path_1.default.join(fraimDir, '.digest');
|
|
23
|
-
// Check for CLI updates first
|
|
24
|
-
|
|
23
|
+
// Check for CLI updates first (but skip during installation to prevent loops)
|
|
24
|
+
const isPostInstall = process.env.npm_lifecycle_event === 'postinstall' ||
|
|
25
|
+
process.env.npm_lifecycle_script === 'postinstall';
|
|
26
|
+
if (!options.skipUpdates && !isPostInstall) {
|
|
27
|
+
await checkAndUpdateCLI();
|
|
28
|
+
}
|
|
29
|
+
else if (isPostInstall) {
|
|
30
|
+
console.log(chalk_1.default.gray('⏭️ Skipping auto-update check during installation to prevent loops.'));
|
|
31
|
+
}
|
|
25
32
|
// In npm package, stubs are in node_modules/@fraim/framework/registry/stubs/workflows
|
|
26
33
|
// We need to handle both "running from source" (src/cli/commands) and "running from dist" (dist/src/cli/commands)
|
|
27
34
|
// Try 4 levels up (dist/src/cli/commands -> root)
|
|
@@ -168,6 +175,14 @@ const runSync = async (options) => {
|
|
|
168
175
|
exports.runSync = runSync;
|
|
169
176
|
async function checkAndUpdateCLI() {
|
|
170
177
|
try {
|
|
178
|
+
// Additional safety: check if we're in a global npm install context
|
|
179
|
+
const isGlobalInstall = process.env.npm_config_global === 'true' ||
|
|
180
|
+
process.env.npm_config_prefix ||
|
|
181
|
+
process.cwd().includes('node_modules');
|
|
182
|
+
if (isGlobalInstall) {
|
|
183
|
+
console.log(chalk_1.default.gray('⏭️ Skipping auto-update during global installation.'));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
171
186
|
console.log(chalk_1.default.blue('🔍 Checking for FRAIM CLI updates...'));
|
|
172
187
|
const currentVersion = (0, version_utils_1.getFraimVersion)();
|
|
173
188
|
const latestVersion = await getLatestNpmVersionHttp('fraim-framework');
|
|
@@ -270,4 +285,5 @@ async function updateGlobalPackageHttp(packageName, version) {
|
|
|
270
285
|
exports.syncCommand = new commander_1.Command('sync')
|
|
271
286
|
.description('Sync workflow stubs from the framework registry')
|
|
272
287
|
.option('-f, --force', 'Force sync even if digest matches')
|
|
288
|
+
.option('--skip-updates', 'Skip checking for CLI updates')
|
|
273
289
|
.action(exports.runSync);
|
|
@@ -289,11 +289,16 @@ const phase_flow_js_1 = require("../src/ai-manager/phase-flow.js");
|
|
|
289
289
|
}
|
|
290
290
|
});
|
|
291
291
|
(0, node_test_1.it)('should handle boundary conditions in phase flows', () => {
|
|
292
|
-
// Test last phase in each workflow
|
|
293
|
-
node_assert_1.default.strictEqual((0, phase_flow_js_1.getNextPhase)('
|
|
294
|
-
node_assert_1.default.strictEqual((0, phase_flow_js_1.getNextPhase)('
|
|
295
|
-
node_assert_1.default.strictEqual((0, phase_flow_js_1.getNextPhase)('
|
|
296
|
-
node_assert_1.default.strictEqual((0, phase_flow_js_1.getNextPhase)('
|
|
292
|
+
// Test last phase in each workflow - retrospective is now the final phase
|
|
293
|
+
node_assert_1.default.strictEqual((0, phase_flow_js_1.getNextPhase)('retrospective', 'spec'), null);
|
|
294
|
+
node_assert_1.default.strictEqual((0, phase_flow_js_1.getNextPhase)('retrospective', 'design'), null);
|
|
295
|
+
node_assert_1.default.strictEqual((0, phase_flow_js_1.getNextPhase)('retrospective', 'implement', 'bug'), null);
|
|
296
|
+
node_assert_1.default.strictEqual((0, phase_flow_js_1.getNextPhase)('retrospective', 'implement', 'feature'), null);
|
|
297
|
+
// Test that wait-for-pr-review transitions to retrospective
|
|
298
|
+
node_assert_1.default.strictEqual((0, phase_flow_js_1.getNextPhase)('wait-for-pr-review', 'spec'), 'retrospective');
|
|
299
|
+
node_assert_1.default.strictEqual((0, phase_flow_js_1.getNextPhase)('wait-for-pr-review', 'design'), 'retrospective');
|
|
300
|
+
node_assert_1.default.strictEqual((0, phase_flow_js_1.getNextPhase)('wait-for-pr-review', 'implement', 'bug'), 'retrospective');
|
|
301
|
+
node_assert_1.default.strictEqual((0, phase_flow_js_1.getNextPhase)('wait-for-pr-review', 'implement', 'feature'), 'retrospective');
|
|
297
302
|
});
|
|
298
303
|
});
|
|
299
304
|
(0, node_test_1.describe)('Concurrent Access Handling', () => {
|
|
@@ -157,7 +157,7 @@ const shared_server_utils_js_1 = require("./shared-server-utils.js");
|
|
|
157
157
|
node_assert_1.default.strictEqual(response.status, 200);
|
|
158
158
|
const content = response.data.result.content[0];
|
|
159
159
|
node_assert_1.default.ok(content.text.includes('Phase Complete!'), 'Should indicate phase completion');
|
|
160
|
-
node_assert_1.default.ok(content.text.includes('Next Phase: spec-
|
|
160
|
+
node_assert_1.default.ok(content.text.includes('Next Phase: spec-competitor-analysis'), 'Should provide next phase');
|
|
161
161
|
});
|
|
162
162
|
(0, node_test_1.it)('should handle incomplete phase with help message', async () => {
|
|
163
163
|
const axios = require('axios');
|
|
@@ -196,12 +196,11 @@ const shared_server_utils_js_1 = require("./shared-server-utils.js");
|
|
|
196
196
|
arguments: {
|
|
197
197
|
workflowType: 'spec',
|
|
198
198
|
issueNumber: '123',
|
|
199
|
-
currentPhase: '
|
|
199
|
+
currentPhase: 'retrospective',
|
|
200
200
|
status: 'complete',
|
|
201
201
|
evidence: {
|
|
202
202
|
issueType: 'feature',
|
|
203
|
-
|
|
204
|
-
reviewComplete: true
|
|
203
|
+
retrospectiveComplete: true
|
|
205
204
|
}
|
|
206
205
|
}
|
|
207
206
|
}
|
|
@@ -369,7 +368,7 @@ const shared_server_utils_js_1 = require("./shared-server-utils.js");
|
|
|
369
368
|
});
|
|
370
369
|
node_assert_1.default.strictEqual(response.status, 200);
|
|
371
370
|
content = response.data.result.content[0];
|
|
372
|
-
node_assert_1.default.ok(content.text.includes('Next Phase: spec-
|
|
371
|
+
node_assert_1.default.ok(content.text.includes('Next Phase: spec-competitor-analysis'), 'Should transition to review');
|
|
373
372
|
// Complete review phase
|
|
374
373
|
response = await axios.post((0, shared_server_utils_js_1.getMcpEndpoint)(), {
|
|
375
374
|
jsonrpc: '2.0',
|
|
@@ -426,6 +425,25 @@ const shared_server_utils_js_1 = require("./shared-server-utils.js");
|
|
|
426
425
|
});
|
|
427
426
|
node_assert_1.default.strictEqual(response.status, 200);
|
|
428
427
|
content = response.data.result.content[0];
|
|
428
|
+
node_assert_1.default.ok(content.text.includes('Next Phase: retrospective'), 'Should transition to retrospective');
|
|
429
|
+
// Complete retrospective
|
|
430
|
+
response = await axios.post((0, shared_server_utils_js_1.getMcpEndpoint)(), {
|
|
431
|
+
jsonrpc: '2.0',
|
|
432
|
+
id: 6,
|
|
433
|
+
method: 'tools/call',
|
|
434
|
+
params: {
|
|
435
|
+
name: 'seekCoachingOnNextStep',
|
|
436
|
+
arguments: {
|
|
437
|
+
workflowType: 'spec',
|
|
438
|
+
issueNumber: '999',
|
|
439
|
+
currentPhase: 'retrospective',
|
|
440
|
+
status: 'complete',
|
|
441
|
+
evidence: { issueType: 'feature', retrospectiveComplete: true }
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
node_assert_1.default.strictEqual(response.status, 200);
|
|
446
|
+
content = response.data.result.content[0];
|
|
429
447
|
node_assert_1.default.ok(content.text.includes('Spec Complete!'), 'Should show completion');
|
|
430
448
|
});
|
|
431
449
|
});
|
|
@@ -75,17 +75,17 @@ const path_1 = require("path");
|
|
|
75
75
|
evidence: { issueType: 'feature', specComplete: true }
|
|
76
76
|
});
|
|
77
77
|
const duration = Date.now() - startTime;
|
|
78
|
-
node_assert_1.default.ok(result.includes('Next Phase: spec-
|
|
78
|
+
node_assert_1.default.ok(result.includes('Next Phase: spec-competitor-analysis'));
|
|
79
79
|
node_assert_1.default.ok(duration < 1000, `Phase transition should complete within 1 second (took ${duration}ms)`);
|
|
80
80
|
});
|
|
81
81
|
(0, node_test_1.it)('should handle completion messages efficiently', async () => {
|
|
82
82
|
const startTime = Date.now();
|
|
83
83
|
const result = await coach.handleCoachingRequest({
|
|
84
84
|
workflowType: 'spec',
|
|
85
|
-
currentPhase: '
|
|
85
|
+
currentPhase: 'retrospective',
|
|
86
86
|
status: 'complete',
|
|
87
87
|
issueNumber: '123',
|
|
88
|
-
evidence: { issueType: 'feature',
|
|
88
|
+
evidence: { issueType: 'feature', retrospectiveComplete: true }
|
|
89
89
|
});
|
|
90
90
|
const duration = Date.now() - startTime;
|
|
91
91
|
node_assert_1.default.ok(result.includes('Spec Complete!'));
|
|
@@ -53,35 +53,44 @@ const path_1 = require("path");
|
|
|
53
53
|
});
|
|
54
54
|
(0, node_test_1.describe)('Phase Flow Logic', () => {
|
|
55
55
|
(0, node_test_1.it)('should return correct next phase for implement bug workflow', () => {
|
|
56
|
-
const phases = ['implement-scoping', 'implement-repro', 'implement-code', 'implement-validate', 'implement-regression', 'implement-completeness-review', 'submit-pr', 'wait-for-pr-review'];
|
|
56
|
+
const phases = ['implement-scoping', 'implement-repro', 'implement-code', 'implement-validate', 'implement-regression', 'implement-completeness-review', 'submit-pr', 'wait-for-pr-review', 'retrospective'];
|
|
57
57
|
for (let i = 0; i < phases.length - 1; i++) {
|
|
58
58
|
const nextPhase = (0, phase_flow_js_1.getNextPhase)(phases[i], 'implement', 'bug');
|
|
59
59
|
node_assert_1.default.strictEqual(nextPhase, phases[i + 1], `Next phase after ${phases[i]} should be ${phases[i + 1]}`);
|
|
60
60
|
}
|
|
61
61
|
// Last phase should return null
|
|
62
|
-
const lastPhase = (0, phase_flow_js_1.getNextPhase)('
|
|
63
|
-
node_assert_1.default.strictEqual(lastPhase, null, '
|
|
62
|
+
const lastPhase = (0, phase_flow_js_1.getNextPhase)('retrospective', 'implement', 'bug');
|
|
63
|
+
node_assert_1.default.strictEqual(lastPhase, null, 'retrospective phase should return null as next phase');
|
|
64
64
|
});
|
|
65
65
|
(0, node_test_1.it)('should return correct next phase for implement feature workflow', () => {
|
|
66
|
-
const phases = ['implement-scoping', 'implement-spike', 'implement-code', 'implement-validate', 'implement-smoke', 'implement-regression', 'implement-completeness-review', 'submit-pr', 'wait-for-pr-review'];
|
|
66
|
+
const phases = ['implement-scoping', 'implement-spike', 'implement-code', 'implement-validate', 'implement-smoke', 'implement-regression', 'implement-completeness-review', 'submit-pr', 'wait-for-pr-review', 'retrospective'];
|
|
67
67
|
for (let i = 0; i < phases.length - 1; i++) {
|
|
68
68
|
const nextPhase = (0, phase_flow_js_1.getNextPhase)(phases[i], 'implement', 'feature');
|
|
69
69
|
node_assert_1.default.strictEqual(nextPhase, phases[i + 1], `Next phase after ${phases[i]} should be ${phases[i + 1]}`);
|
|
70
70
|
}
|
|
71
|
+
// Last phase should return null
|
|
72
|
+
const lastPhase = (0, phase_flow_js_1.getNextPhase)('retrospective', 'implement', 'feature');
|
|
73
|
+
node_assert_1.default.strictEqual(lastPhase, null, 'retrospective phase should return null as next phase');
|
|
71
74
|
});
|
|
72
75
|
(0, node_test_1.it)('should return correct next phase for spec workflow', () => {
|
|
73
|
-
const phases = ['spec-spec', 'spec-completeness-review', 'submit-pr', 'wait-for-pr-review'];
|
|
76
|
+
const phases = ['spec-spec', 'spec-competitor-analysis', 'spec-completeness-review', 'submit-pr', 'wait-for-pr-review', 'retrospective'];
|
|
74
77
|
for (let i = 0; i < phases.length - 1; i++) {
|
|
75
78
|
const nextPhase = (0, phase_flow_js_1.getNextPhase)(phases[i], 'spec');
|
|
76
79
|
node_assert_1.default.strictEqual(nextPhase, phases[i + 1], `Next phase after ${phases[i]} should be ${phases[i + 1]}`);
|
|
77
80
|
}
|
|
81
|
+
// Last phase should return null
|
|
82
|
+
const lastPhase = (0, phase_flow_js_1.getNextPhase)('retrospective', 'spec');
|
|
83
|
+
node_assert_1.default.strictEqual(lastPhase, null, 'retrospective phase should return null as next phase');
|
|
78
84
|
});
|
|
79
85
|
(0, node_test_1.it)('should return correct next phase for design workflow', () => {
|
|
80
|
-
const phases = ['design-design', 'design-completeness-review', 'submit-pr', 'wait-for-pr-review'];
|
|
86
|
+
const phases = ['design-design', 'design-completeness-review', 'submit-pr', 'wait-for-pr-review', 'retrospective'];
|
|
81
87
|
for (let i = 0; i < phases.length - 1; i++) {
|
|
82
88
|
const nextPhase = (0, phase_flow_js_1.getNextPhase)(phases[i], 'design');
|
|
83
89
|
node_assert_1.default.strictEqual(nextPhase, phases[i + 1], `Next phase after ${phases[i]} should be ${phases[i + 1]}`);
|
|
84
90
|
}
|
|
91
|
+
// Last phase should return null
|
|
92
|
+
const lastPhase = (0, phase_flow_js_1.getNextPhase)('retrospective', 'design');
|
|
93
|
+
node_assert_1.default.strictEqual(lastPhase, null, 'retrospective phase should return null as next phase');
|
|
85
94
|
});
|
|
86
95
|
(0, node_test_1.it)('should validate phases correctly for each workflow type', () => {
|
|
87
96
|
// Test implement workflow phases
|
|
@@ -110,16 +119,16 @@ const path_1 = require("path");
|
|
|
110
119
|
});
|
|
111
120
|
(0, node_test_1.it)('should return correct phases for each workflow type', () => {
|
|
112
121
|
const bugPhases = (0, phase_flow_js_1.getPhasesForWorkflow)('implement', 'bug');
|
|
113
|
-
const expectedBugPhases = ['implement-scoping', 'implement-repro', 'implement-code', 'implement-validate', 'implement-regression', 'implement-completeness-review', 'submit-pr', 'wait-for-pr-review'];
|
|
122
|
+
const expectedBugPhases = ['implement-scoping', 'implement-repro', 'implement-code', 'implement-validate', 'implement-regression', 'implement-completeness-review', 'submit-pr', 'wait-for-pr-review', 'retrospective'];
|
|
114
123
|
node_assert_1.default.deepStrictEqual(bugPhases, expectedBugPhases);
|
|
115
124
|
const featurePhases = (0, phase_flow_js_1.getPhasesForWorkflow)('implement', 'feature');
|
|
116
|
-
const expectedFeaturePhases = ['implement-scoping', 'implement-spike', 'implement-code', 'implement-validate', 'implement-smoke', 'implement-regression', 'implement-completeness-review', 'submit-pr', 'wait-for-pr-review'];
|
|
125
|
+
const expectedFeaturePhases = ['implement-scoping', 'implement-spike', 'implement-code', 'implement-validate', 'implement-smoke', 'implement-regression', 'implement-completeness-review', 'submit-pr', 'wait-for-pr-review', 'retrospective'];
|
|
117
126
|
node_assert_1.default.deepStrictEqual(featurePhases, expectedFeaturePhases);
|
|
118
127
|
const specPhases = (0, phase_flow_js_1.getPhasesForWorkflow)('spec');
|
|
119
|
-
const expectedSpecPhases = ['spec-spec', 'spec-completeness-review', 'submit-pr', 'wait-for-pr-review'];
|
|
128
|
+
const expectedSpecPhases = ['spec-spec', 'spec-competitor-analysis', 'spec-completeness-review', 'submit-pr', 'wait-for-pr-review', 'retrospective'];
|
|
120
129
|
node_assert_1.default.deepStrictEqual(specPhases, expectedSpecPhases);
|
|
121
130
|
const designPhases = (0, phase_flow_js_1.getPhasesForWorkflow)('design');
|
|
122
|
-
const expectedDesignPhases = ['design-design', 'design-completeness-review', 'submit-pr', 'wait-for-pr-review'];
|
|
131
|
+
const expectedDesignPhases = ['design-design', 'design-completeness-review', 'submit-pr', 'wait-for-pr-review', 'retrospective'];
|
|
123
132
|
node_assert_1.default.deepStrictEqual(designPhases, expectedDesignPhases);
|
|
124
133
|
});
|
|
125
134
|
(0, node_test_1.it)('should throw error for invalid workflow types', () => {
|
|
@@ -213,16 +222,16 @@ const path_1 = require("path");
|
|
|
213
222
|
evidence: { issueType: 'feature', specComplete: true }
|
|
214
223
|
});
|
|
215
224
|
node_assert_1.default.ok(result.includes('Phase Complete!'), 'Should indicate phase completion');
|
|
216
|
-
node_assert_1.default.ok(result.includes('Next Phase: spec-
|
|
217
|
-
node_assert_1.default.ok(result.includes('Phase:
|
|
225
|
+
node_assert_1.default.ok(result.includes('Next Phase: spec-competitor-analysis'), 'Should provide next phase instructions');
|
|
226
|
+
node_assert_1.default.ok(result.includes('Phase: spec-competitor-analysis'), 'Should include next phase content');
|
|
218
227
|
});
|
|
219
228
|
(0, node_test_1.it)('should provide completion message for wait-for-pr-review phase', async () => {
|
|
220
229
|
const result = await coach.handleCoachingRequest({
|
|
221
230
|
workflowType: 'spec',
|
|
222
|
-
currentPhase: '
|
|
231
|
+
currentPhase: 'retrospective',
|
|
223
232
|
status: 'complete',
|
|
224
233
|
issueNumber: '123',
|
|
225
|
-
evidence: { issueType: 'feature',
|
|
234
|
+
evidence: { issueType: 'feature', retrospectiveComplete: true }
|
|
226
235
|
});
|
|
227
236
|
node_assert_1.default.ok(result.includes('Spec Complete!'), 'Should show workflow completion message');
|
|
228
237
|
node_assert_1.default.ok(result.includes('Congratulations!'), 'Should include congratulations');
|
|
@@ -326,10 +335,10 @@ const path_1 = require("path");
|
|
|
326
335
|
for (const workflow of workflows) {
|
|
327
336
|
const result = await coach.handleCoachingRequest({
|
|
328
337
|
workflowType: workflow.type,
|
|
329
|
-
currentPhase: '
|
|
338
|
+
currentPhase: 'retrospective',
|
|
330
339
|
status: 'complete',
|
|
331
340
|
issueNumber: '123',
|
|
332
|
-
evidence: { issueType: 'feature',
|
|
341
|
+
evidence: { issueType: 'feature', retrospectiveComplete: true }
|
|
333
342
|
});
|
|
334
343
|
node_assert_1.default.ok(result.includes(workflow.expectedText), `${workflow.type} workflow should show correct accomplishments`);
|
|
335
344
|
}
|
|
@@ -398,6 +407,15 @@ const path_1 = require("path");
|
|
|
398
407
|
issueNumber,
|
|
399
408
|
evidence: { issueType: 'feature', specComplete: true }
|
|
400
409
|
});
|
|
410
|
+
node_assert_1.default.ok(result.includes('Next Phase: spec-competitor-analysis'), 'Should transition to competitor analysis');
|
|
411
|
+
// Complete competitor analysis
|
|
412
|
+
result = await coach.handleCoachingRequest({
|
|
413
|
+
workflowType,
|
|
414
|
+
currentPhase: 'spec-competitor-analysis',
|
|
415
|
+
status: 'complete',
|
|
416
|
+
issueNumber,
|
|
417
|
+
evidence: { issueType: 'feature', competitorAnalysisComplete: true }
|
|
418
|
+
});
|
|
401
419
|
node_assert_1.default.ok(result.includes('Next Phase: spec-completeness-review'), 'Should transition to completeness review');
|
|
402
420
|
// Complete completeness review
|
|
403
421
|
result = await coach.handleCoachingRequest({
|
|
@@ -425,14 +443,23 @@ const path_1 = require("path");
|
|
|
425
443
|
issueNumber,
|
|
426
444
|
evidence: { issueType: 'feature', prApproved: true }
|
|
427
445
|
});
|
|
446
|
+
node_assert_1.default.ok(result.includes('Next Phase: retrospective'), 'Should transition to retrospective');
|
|
447
|
+
// Complete retrospective
|
|
448
|
+
result = await coach.handleCoachingRequest({
|
|
449
|
+
workflowType,
|
|
450
|
+
currentPhase: 'retrospective',
|
|
451
|
+
status: 'complete',
|
|
452
|
+
issueNumber,
|
|
453
|
+
evidence: { issueType: 'feature', retrospectiveComplete: true }
|
|
454
|
+
});
|
|
428
455
|
node_assert_1.default.ok(result.includes('Spec Complete!'), 'Should show completion message');
|
|
429
456
|
});
|
|
430
457
|
(0, node_test_1.it)('should complete full implement bug workflow', async () => {
|
|
431
458
|
const issueNumber = '456';
|
|
432
459
|
const workflowType = 'implement';
|
|
433
460
|
const issueType = 'bug';
|
|
434
|
-
// Test key phases in bug workflow
|
|
435
|
-
const phases = ['implement-scoping', 'implement-repro', 'implement-code', 'wait-for-pr-review'];
|
|
461
|
+
// Test key phases in bug workflow including retrospective
|
|
462
|
+
const phases = ['implement-scoping', 'implement-repro', 'implement-code', 'wait-for-pr-review', 'retrospective'];
|
|
436
463
|
for (let i = 0; i < phases.length; i++) {
|
|
437
464
|
const phase = phases[i];
|
|
438
465
|
const isLast = i === phases.length - 1;
|
|
@@ -25,7 +25,9 @@ async function testPRReviewPhaseFlow() {
|
|
|
25
25
|
const nextAfterSubmitPR = (0, phase_flow_js_1.getNextPhase)('submit-pr', 'implement', 'bug');
|
|
26
26
|
node_assert_1.strict.equal(nextAfterSubmitPR, 'wait-for-pr-review', 'After submit-pr should go to wait-for-pr-review');
|
|
27
27
|
const nextAfterWaitPR = (0, phase_flow_js_1.getNextPhase)('wait-for-pr-review', 'implement', 'bug');
|
|
28
|
-
node_assert_1.strict.equal(nextAfterWaitPR,
|
|
28
|
+
node_assert_1.strict.equal(nextAfterWaitPR, 'retrospective', 'After wait-for-pr-review should go to retrospective');
|
|
29
|
+
const nextAfterRetrospective = (0, phase_flow_js_1.getNextPhase)('retrospective', 'implement', 'bug');
|
|
30
|
+
node_assert_1.strict.equal(nextAfterRetrospective, null, 'retrospective should be the last phase');
|
|
29
31
|
console.log('✅ PR review phase flow tests passed');
|
|
30
32
|
return true;
|
|
31
33
|
}
|
|
@@ -187,8 +189,10 @@ async function testRealWorldPRReviewScenario() {
|
|
|
187
189
|
currentPhase = nextPhase;
|
|
188
190
|
}
|
|
189
191
|
// Test PR review completion (approved)
|
|
190
|
-
const
|
|
191
|
-
node_assert_1.strict.equal(
|
|
192
|
+
const nextPhaseAfterPR = (0, phase_flow_js_1.getNextPhase)('wait-for-pr-review', workflowType, issueType);
|
|
193
|
+
node_assert_1.strict.equal(nextPhaseAfterPR, 'retrospective', 'wait-for-pr-review should go to retrospective when approved');
|
|
194
|
+
const finalPhase = (0, phase_flow_js_1.getNextPhase)('retrospective', workflowType, issueType);
|
|
195
|
+
node_assert_1.strict.equal(finalPhase, null, 'retrospective should be final phase when approved');
|
|
192
196
|
// Test PR review with changes requested (failure scenario)
|
|
193
197
|
const failurePhase = (0, phase_flow_js_1.getPhaseOnFailure)('wait-for-pr-review', workflowType, issueType);
|
|
194
198
|
node_assert_1.strict.equal(failurePhase, 'address-pr-feedback', 'PR changes requested should route to address-pr-feedback');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fraim-framework",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.52",
|
|
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": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"view-signups": "tsx scripts/view-signups.ts",
|
|
22
22
|
"fraim:init": "npm run build && node bin/fraim.js init",
|
|
23
23
|
"fraim:sync": "node bin/fraim.js sync",
|
|
24
|
-
"postinstall": "fraim sync || echo 'FRAIM setup skipped.'",
|
|
24
|
+
"postinstall": "fraim sync --skip-updates || echo 'FRAIM setup skipped.'",
|
|
25
25
|
"prepublishOnly": "npm run build",
|
|
26
26
|
"release": "npm version patch && npm publish",
|
|
27
27
|
"test-smoke-ci": "tsx --test tests/test-genericization.ts tests/test-cli.ts tests/test-stub-registry.ts tests/test-sync-stubs.ts",
|