agileflow 3.4.0 → 3.4.2
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/CHANGELOG.md +10 -0
- package/README.md +4 -4
- package/package.json +1 -1
- package/scripts/agileflow-welcome.js +79 -0
- package/scripts/claude-tmux.sh +12 -36
- package/scripts/lib/ac-test-matcher.js +452 -0
- package/scripts/lib/audit-registry.js +94 -2
- package/scripts/lib/configure-features.js +35 -0
- package/scripts/lib/model-profiles.js +25 -5
- package/scripts/lib/quality-gates.js +163 -0
- package/scripts/lib/signal-detectors.js +43 -0
- package/scripts/lib/status-writer.js +255 -0
- package/scripts/lib/story-claiming.js +128 -45
- package/scripts/lib/task-sync.js +32 -38
- package/scripts/lib/tmux-audit-monitor.js +611 -0
- package/scripts/lib/tmux-group-colors.js +2 -2
- package/scripts/lib/tool-registry.yaml +241 -0
- package/scripts/lib/tool-shed.js +441 -0
- package/scripts/native-team-observer.js +219 -0
- package/scripts/obtain-context.js +14 -0
- package/scripts/ralph-loop.js +30 -5
- package/scripts/smart-detect.js +21 -0
- package/scripts/spawn-audit-sessions.js +373 -45
- package/scripts/team-manager.js +19 -0
- package/src/core/agents/a11y-analyzer-aria.md +155 -0
- package/src/core/agents/a11y-analyzer-forms.md +162 -0
- package/src/core/agents/a11y-analyzer-keyboard.md +175 -0
- package/src/core/agents/a11y-analyzer-semantic.md +153 -0
- package/src/core/agents/a11y-analyzer-visual.md +158 -0
- package/src/core/agents/a11y-consensus.md +248 -0
- package/src/core/agents/ads-consensus.md +74 -0
- package/src/core/agents/ads-generate.md +145 -0
- package/src/core/agents/ads-performance-tracker.md +197 -0
- package/src/core/agents/api-quality-analyzer-conventions.md +148 -0
- package/src/core/agents/api-quality-analyzer-docs.md +176 -0
- package/src/core/agents/api-quality-analyzer-errors.md +183 -0
- package/src/core/agents/api-quality-analyzer-pagination.md +171 -0
- package/src/core/agents/api-quality-analyzer-versioning.md +143 -0
- package/src/core/agents/api-quality-consensus.md +214 -0
- package/src/core/agents/arch-analyzer-circular.md +148 -0
- package/src/core/agents/arch-analyzer-complexity.md +171 -0
- package/src/core/agents/arch-analyzer-coupling.md +146 -0
- package/src/core/agents/arch-analyzer-layering.md +151 -0
- package/src/core/agents/arch-analyzer-patterns.md +162 -0
- package/src/core/agents/arch-consensus.md +227 -0
- package/src/core/commands/adr.md +1 -0
- package/src/core/commands/ads/audit.md +67 -5
- package/src/core/commands/ads/generate.md +238 -0
- package/src/core/commands/ads/health.md +327 -0
- package/src/core/commands/ads/test-plan.md +317 -0
- package/src/core/commands/ads/track.md +288 -0
- package/src/core/commands/ads.md +28 -16
- package/src/core/commands/assign.md +1 -0
- package/src/core/commands/audit.md +43 -6
- package/src/core/commands/babysit.md +90 -6
- package/src/core/commands/baseline.md +1 -0
- package/src/core/commands/blockers.md +1 -0
- package/src/core/commands/board.md +1 -0
- package/src/core/commands/changelog.md +1 -0
- package/src/core/commands/choose.md +1 -0
- package/src/core/commands/ci.md +1 -0
- package/src/core/commands/code/accessibility.md +347 -0
- package/src/core/commands/code/api.md +297 -0
- package/src/core/commands/code/architecture.md +297 -0
- package/src/core/commands/code/completeness.md +43 -6
- package/src/core/commands/code/legal.md +43 -6
- package/src/core/commands/code/logic.md +43 -6
- package/src/core/commands/code/performance.md +43 -6
- package/src/core/commands/code/security.md +43 -6
- package/src/core/commands/code/test.md +43 -6
- package/src/core/commands/configure.md +1 -0
- package/src/core/commands/council.md +1 -0
- package/src/core/commands/deploy.md +1 -0
- package/src/core/commands/diagnose.md +1 -0
- package/src/core/commands/docs.md +1 -0
- package/src/core/commands/epic/edit.md +213 -0
- package/src/core/commands/epic.md +1 -0
- package/src/core/commands/export.md +238 -0
- package/src/core/commands/help.md +16 -1
- package/src/core/commands/ideate/discover.md +7 -3
- package/src/core/commands/ideate/features.md +65 -4
- package/src/core/commands/ideate/new.md +158 -124
- package/src/core/commands/impact.md +1 -0
- package/src/core/commands/learn/explain.md +118 -0
- package/src/core/commands/learn/glossary.md +135 -0
- package/src/core/commands/learn/patterns.md +138 -0
- package/src/core/commands/learn/tour.md +126 -0
- package/src/core/commands/migrate/codemods.md +151 -0
- package/src/core/commands/migrate/plan.md +131 -0
- package/src/core/commands/migrate/scan.md +114 -0
- package/src/core/commands/migrate/validate.md +119 -0
- package/src/core/commands/multi-expert.md +1 -0
- package/src/core/commands/pr.md +1 -0
- package/src/core/commands/review.md +1 -0
- package/src/core/commands/seo/audit.md +61 -6
- package/src/core/commands/sprint.md +1 -0
- package/src/core/commands/status/undo.md +191 -0
- package/src/core/commands/status.md +1 -0
- package/src/core/commands/story/edit.md +204 -0
- package/src/core/commands/story/view.md +29 -7
- package/src/core/commands/story-validate.md +1 -0
- package/src/core/commands/story.md +1 -0
- package/src/core/commands/tdd.md +1 -0
- package/src/core/commands/team/start.md +10 -6
- package/src/core/commands/tests.md +1 -0
- package/src/core/commands/verify.md +27 -1
- package/src/core/commands/workflow.md +2 -0
- package/src/core/teams/backend.json +41 -0
- package/src/core/teams/frontend.json +41 -0
- package/src/core/teams/qa.json +41 -0
- package/src/core/teams/solo.json +35 -0
- package/src/core/templates/agileflow-metadata.json +5 -0
- package/tools/cli/commands/setup.js +85 -3
- package/tools/cli/commands/update.js +42 -0
- package/tools/cli/installers/ide/claude-code.js +68 -0
|
@@ -132,6 +132,98 @@ const AUDIT_TYPES = {
|
|
|
132
132
|
deep_analyzers: ['handlers', 'routes', 'api', 'stubs', 'state', 'imports', 'conditional'],
|
|
133
133
|
},
|
|
134
134
|
|
|
135
|
+
brainstorm: {
|
|
136
|
+
name: 'Feature Brainstorm',
|
|
137
|
+
prefix: 'Brain',
|
|
138
|
+
color: '#c0caf5', // lavender
|
|
139
|
+
command: 'ideate/features',
|
|
140
|
+
analyzers: {
|
|
141
|
+
features: { subagent_type: 'brainstorm-analyzer-features', label: 'Feature Gaps' },
|
|
142
|
+
ux: { subagent_type: 'brainstorm-analyzer-ux', label: 'UX Improvements' },
|
|
143
|
+
market: { subagent_type: 'brainstorm-analyzer-market', label: 'Market Features' },
|
|
144
|
+
growth: { subagent_type: 'brainstorm-analyzer-growth', label: 'Growth & Engagement' },
|
|
145
|
+
integration: { subagent_type: 'brainstorm-analyzer-integration', label: 'Integrations' },
|
|
146
|
+
},
|
|
147
|
+
consensus: { subagent_type: 'brainstorm-consensus', label: 'Brainstorm Consensus' },
|
|
148
|
+
quick_analyzers: ['features', 'ux', 'market'],
|
|
149
|
+
deep_analyzers: ['features', 'ux', 'market', 'growth', 'integration'],
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
ideate: {
|
|
153
|
+
name: 'Ideation',
|
|
154
|
+
prefix: 'Idea',
|
|
155
|
+
color: '#ff9e64', // orange
|
|
156
|
+
command: 'ideate/new',
|
|
157
|
+
analyzers: {
|
|
158
|
+
security: { subagent_type: 'agileflow-security', label: 'Security' },
|
|
159
|
+
performance: { subagent_type: 'agileflow-performance', label: 'Performance' },
|
|
160
|
+
refactor: { subagent_type: 'agileflow-refactor', label: 'Code Quality' },
|
|
161
|
+
ui: { subagent_type: 'agileflow-ui', label: 'UX/Design' },
|
|
162
|
+
testing: { subagent_type: 'agileflow-testing', label: 'Testing' },
|
|
163
|
+
api: { subagent_type: 'agileflow-api', label: 'API/Architecture' },
|
|
164
|
+
accessibility: { subagent_type: 'agileflow-accessibility', label: 'Accessibility' },
|
|
165
|
+
compliance: { subagent_type: 'agileflow-compliance', label: 'Compliance' },
|
|
166
|
+
database: { subagent_type: 'agileflow-database', label: 'Database' },
|
|
167
|
+
monitoring: { subagent_type: 'agileflow-monitoring', label: 'Monitoring' },
|
|
168
|
+
qa: { subagent_type: 'agileflow-qa', label: 'QA' },
|
|
169
|
+
analytics: { subagent_type: 'agileflow-analytics', label: 'Analytics' },
|
|
170
|
+
documentation: { subagent_type: 'agileflow-documentation', label: 'Documentation' },
|
|
171
|
+
},
|
|
172
|
+
consensus: null, // ideation does its own synthesis (no consensus coordinator)
|
|
173
|
+
quick_analyzers: ['security', 'performance', 'refactor', 'ui', 'testing', 'api'],
|
|
174
|
+
deep_analyzers: [
|
|
175
|
+
'security',
|
|
176
|
+
'performance',
|
|
177
|
+
'refactor',
|
|
178
|
+
'ui',
|
|
179
|
+
'testing',
|
|
180
|
+
'api',
|
|
181
|
+
'accessibility',
|
|
182
|
+
'compliance',
|
|
183
|
+
'database',
|
|
184
|
+
'monitoring',
|
|
185
|
+
'qa',
|
|
186
|
+
'analytics',
|
|
187
|
+
'documentation',
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
seo: {
|
|
192
|
+
name: 'SEO Audit',
|
|
193
|
+
prefix: 'SEO',
|
|
194
|
+
color: '#ff9e64', // rose/orange
|
|
195
|
+
command: 'seo/audit',
|
|
196
|
+
analyzers: {
|
|
197
|
+
technical: { subagent_type: 'seo-analyzer-technical', label: 'Technical SEO' },
|
|
198
|
+
content: { subagent_type: 'seo-analyzer-content', label: 'Content Quality' },
|
|
199
|
+
schema: { subagent_type: 'seo-analyzer-schema', label: 'Schema Markup' },
|
|
200
|
+
images: { subagent_type: 'seo-analyzer-images', label: 'Image Optimization' },
|
|
201
|
+
performance: { subagent_type: 'seo-analyzer-performance', label: 'Core Web Vitals' },
|
|
202
|
+
sitemap: { subagent_type: 'seo-analyzer-sitemap', label: 'Sitemap' },
|
|
203
|
+
},
|
|
204
|
+
consensus: { subagent_type: 'seo-consensus', label: 'SEO Consensus' },
|
|
205
|
+
quick_analyzers: ['technical', 'content', 'schema', 'images', 'performance', 'sitemap'],
|
|
206
|
+
deep_analyzers: ['technical', 'content', 'schema', 'images', 'performance', 'sitemap'],
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
ads: {
|
|
210
|
+
name: 'Ads Audit',
|
|
211
|
+
prefix: 'Ads',
|
|
212
|
+
color: '#89ddff', // ice
|
|
213
|
+
command: 'ads/audit',
|
|
214
|
+
analyzers: {
|
|
215
|
+
google: { subagent_type: 'ads-audit-google', label: 'Google Ads' },
|
|
216
|
+
meta: { subagent_type: 'ads-audit-meta', label: 'Meta Ads' },
|
|
217
|
+
budget: { subagent_type: 'ads-audit-budget', label: 'Budget & Bidding' },
|
|
218
|
+
creative: { subagent_type: 'ads-audit-creative', label: 'Creative Quality' },
|
|
219
|
+
tracking: { subagent_type: 'ads-audit-tracking', label: 'Conversion Tracking' },
|
|
220
|
+
compliance: { subagent_type: 'ads-audit-compliance', label: 'Compliance' },
|
|
221
|
+
},
|
|
222
|
+
consensus: { subagent_type: 'ads-consensus', label: 'Ads Consensus' },
|
|
223
|
+
quick_analyzers: ['google', 'meta', 'budget', 'creative', 'tracking', 'compliance'],
|
|
224
|
+
deep_analyzers: ['google', 'meta', 'budget', 'creative', 'tracking', 'compliance'],
|
|
225
|
+
},
|
|
226
|
+
|
|
135
227
|
legal: {
|
|
136
228
|
name: 'Legal Risk',
|
|
137
229
|
prefix: 'Legal',
|
|
@@ -167,7 +259,7 @@ const AUDIT_TYPES = {
|
|
|
167
259
|
/**
|
|
168
260
|
* Get audit type configuration.
|
|
169
261
|
*
|
|
170
|
-
* @param {string} type - Audit type key (logic, security, performance, test, completeness, legal)
|
|
262
|
+
* @param {string} type - Audit type key (logic, security, performance, test, completeness, legal, ideate)
|
|
171
263
|
* @returns {object|null} Audit type config or null if invalid
|
|
172
264
|
*/
|
|
173
265
|
function getAuditType(type) {
|
|
@@ -195,7 +287,7 @@ function getAnalyzersForAudit(type, depth, focus) {
|
|
|
195
287
|
const audit = AUDIT_TYPES[type];
|
|
196
288
|
if (!audit) return null;
|
|
197
289
|
|
|
198
|
-
const effectiveDepth = depth === 'ultradeep' ? 'deep' : depth || 'quick';
|
|
290
|
+
const effectiveDepth = depth === 'ultradeep' || depth === 'extreme' ? 'deep' : depth || 'quick';
|
|
199
291
|
const analyzerKeys = effectiveDepth === 'deep' ? audit.deep_analyzers : audit.quick_analyzers;
|
|
200
292
|
|
|
201
293
|
// Filter by focus if specified
|
|
@@ -393,6 +393,25 @@ function enableFeature(feature, options = {}, version) {
|
|
|
393
393
|
if (feature === 'agentteams') {
|
|
394
394
|
settings.env = settings.env || {};
|
|
395
395
|
settings.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '1';
|
|
396
|
+
|
|
397
|
+
// Register PostToolUse hooks for native team observability
|
|
398
|
+
if (!settings.hooks) settings.hooks = {};
|
|
399
|
+
if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = [];
|
|
400
|
+
const observerCmd = 'node $CLAUDE_PROJECT_DIR/.agileflow/scripts/native-team-observer.js';
|
|
401
|
+
for (const matcher of ['TeamCreate', 'SendMessage', 'ListTeams']) {
|
|
402
|
+
const exists = settings.hooks.PostToolUse.some(
|
|
403
|
+
h =>
|
|
404
|
+
h.matcher === matcher &&
|
|
405
|
+
h.hooks?.some(hk => hk.command && hk.command.includes('native-team-observer'))
|
|
406
|
+
);
|
|
407
|
+
if (!exists) {
|
|
408
|
+
settings.hooks.PostToolUse.push({
|
|
409
|
+
matcher,
|
|
410
|
+
hooks: [{ type: 'command', command: observerCmd, timeout: 5000 }],
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
396
415
|
writeJSON('.claude/settings.json', settings);
|
|
397
416
|
updateMetadata(
|
|
398
417
|
{
|
|
@@ -408,6 +427,7 @@ function enableFeature(feature, options = {}, version) {
|
|
|
408
427
|
);
|
|
409
428
|
success('Native Agent Teams enabled');
|
|
410
429
|
info('Set CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 in .claude/settings.json');
|
|
430
|
+
info('Registered PostToolUse hooks for native team observability');
|
|
411
431
|
info('Claude Code will use native TeamCreate/SendMessage tools');
|
|
412
432
|
info('Fallback: subagent mode (Task/TaskOutput) when native is unavailable');
|
|
413
433
|
return true;
|
|
@@ -954,6 +974,20 @@ function disableFeature(feature, version) {
|
|
|
954
974
|
delete settings.env;
|
|
955
975
|
}
|
|
956
976
|
}
|
|
977
|
+
|
|
978
|
+
// Remove PostToolUse hooks for native team observer
|
|
979
|
+
if (settings.hooks?.PostToolUse) {
|
|
980
|
+
settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(
|
|
981
|
+
h => !h.hooks?.some(hk => hk.command && hk.command.includes('native-team-observer'))
|
|
982
|
+
);
|
|
983
|
+
if (settings.hooks.PostToolUse.length === 0) {
|
|
984
|
+
delete settings.hooks.PostToolUse;
|
|
985
|
+
}
|
|
986
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
987
|
+
delete settings.hooks;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
957
991
|
writeJSON('.claude/settings.json', settings);
|
|
958
992
|
updateMetadata(
|
|
959
993
|
{
|
|
@@ -969,6 +1003,7 @@ function disableFeature(feature, version) {
|
|
|
969
1003
|
);
|
|
970
1004
|
success('Native Agent Teams disabled');
|
|
971
1005
|
info('Removed CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS from .claude/settings.json');
|
|
1006
|
+
info('Removed PostToolUse hooks for native team observer');
|
|
972
1007
|
info('AgileFlow will use subagent mode (Task/TaskOutput) for multi-agent orchestration');
|
|
973
1008
|
return true;
|
|
974
1009
|
}
|
|
@@ -60,9 +60,10 @@ function isValidModel(model) {
|
|
|
60
60
|
*
|
|
61
61
|
* @param {string} model - Model name
|
|
62
62
|
* @param {number} [analyzerCount=5] - Number of analyzers
|
|
63
|
-
* @
|
|
63
|
+
* @param {number} [partitions=1] - Number of partitions (extreme mode)
|
|
64
|
+
* @returns {{ multiplier: number, model: string, perAnalyzerCost: string, totalEstimate: string, partitions?: number, totalSessions?: number }}
|
|
64
65
|
*/
|
|
65
|
-
function estimateCost(model, analyzerCount) {
|
|
66
|
+
function estimateCost(model, analyzerCount, partitions) {
|
|
66
67
|
let MODEL_PRICING;
|
|
67
68
|
try {
|
|
68
69
|
MODEL_PRICING = require('./team-events').MODEL_PRICING;
|
|
@@ -75,19 +76,38 @@ function estimateCost(model, analyzerCount) {
|
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
const count = analyzerCount || 5;
|
|
79
|
+
const partCount = typeof partitions === 'number' && partitions > 1 ? partitions : 1;
|
|
78
80
|
const resolved = resolveModel(model);
|
|
79
81
|
const pricing = MODEL_PRICING[resolved] || MODEL_PRICING.haiku;
|
|
80
82
|
const haikuPricing = MODEL_PRICING.haiku;
|
|
81
83
|
|
|
82
84
|
const multiplier = pricing.output / haikuPricing.output;
|
|
83
|
-
const
|
|
85
|
+
const perAnalyzerCostNum =
|
|
86
|
+
(pricing.input * 50000) / 1_000_000 + (pricing.output * 10000) / 1_000_000;
|
|
87
|
+
const perAnalyzer = `$${perAnalyzerCostNum.toFixed(3)}`;
|
|
84
88
|
|
|
85
|
-
|
|
89
|
+
// For extreme mode: each partition has a coordinator + all analyzers as sub-agents
|
|
90
|
+
// Estimated cost per partition coordinator session in USD (~10k input + 2k output at haiku rates)
|
|
91
|
+
const coordinatorCostUSD = 0.05;
|
|
92
|
+
const totalSessions = partCount * count;
|
|
93
|
+
const totalCost =
|
|
94
|
+
partCount > 1
|
|
95
|
+
? partCount * coordinatorCostUSD + totalSessions * perAnalyzerCostNum
|
|
96
|
+
: count * perAnalyzerCostNum;
|
|
97
|
+
|
|
98
|
+
const result = {
|
|
86
99
|
multiplier: Math.round(multiplier * 100) / 100,
|
|
87
100
|
model: resolved,
|
|
88
101
|
perAnalyzerCost: perAnalyzer,
|
|
89
|
-
totalEstimate: `~$${
|
|
102
|
+
totalEstimate: `~$${totalCost.toFixed(2)}`,
|
|
90
103
|
};
|
|
104
|
+
|
|
105
|
+
if (partCount > 1) {
|
|
106
|
+
result.partitions = partCount;
|
|
107
|
+
result.totalSessions = totalSessions;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return result;
|
|
91
111
|
}
|
|
92
112
|
|
|
93
113
|
module.exports = {
|
|
@@ -561,6 +561,164 @@ function createValidationReport(gateResults, options = {}) {
|
|
|
561
561
|
return lines.join('\n');
|
|
562
562
|
}
|
|
563
563
|
|
|
564
|
+
// ============================================================================
|
|
565
|
+
// CI Feedback Loop
|
|
566
|
+
// ============================================================================
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Default CI feedback loop configuration
|
|
570
|
+
*/
|
|
571
|
+
const CI_FEEDBACK_DEFAULTS = {
|
|
572
|
+
enabled: true,
|
|
573
|
+
max_rounds: 3,
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Load CI feedback loop config from agileflow-metadata.json
|
|
578
|
+
* @param {string} projectRoot - Project root directory
|
|
579
|
+
* @returns {Object} CI feedback loop config
|
|
580
|
+
*/
|
|
581
|
+
function loadCIFeedbackConfig(projectRoot) {
|
|
582
|
+
const metadataPath = path.join(projectRoot, 'docs', '00-meta', 'agileflow-metadata.json');
|
|
583
|
+
try {
|
|
584
|
+
if (fs.existsSync(metadataPath)) {
|
|
585
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
|
|
586
|
+
if (metadata.ci_feedback_loops) {
|
|
587
|
+
return {
|
|
588
|
+
...CI_FEEDBACK_DEFAULTS,
|
|
589
|
+
...metadata.ci_feedback_loops,
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
} catch {
|
|
594
|
+
// Fall through to defaults
|
|
595
|
+
}
|
|
596
|
+
return { ...CI_FEEDBACK_DEFAULTS };
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Execute a CI feedback loop - runs gates, and if they fail, returns
|
|
601
|
+
* structured feedback for the agent to retry (up to max_rounds).
|
|
602
|
+
*
|
|
603
|
+
* This implements the Stripe "Minions" pattern: deterministic CI check
|
|
604
|
+
* followed by agent retry, with a hard iteration limit.
|
|
605
|
+
*
|
|
606
|
+
* @param {Object[]} gates - Quality gate definitions to check
|
|
607
|
+
* @param {Object} options - Loop options
|
|
608
|
+
* @param {string} [options.projectRoot] - Project root directory
|
|
609
|
+
* @param {number} [options.maxRounds] - Override max retry rounds (default: from config)
|
|
610
|
+
* @param {number} [options.currentRound] - Current round number (1-based, default: 1)
|
|
611
|
+
* @param {string} [options.cwd] - Working directory for gate execution
|
|
612
|
+
* @returns {Object} Loop result with status and agent feedback
|
|
613
|
+
*/
|
|
614
|
+
function executeCIFeedbackLoop(gates, options = {}) {
|
|
615
|
+
const { projectRoot = process.cwd(), maxRounds, currentRound = 1, cwd } = options;
|
|
616
|
+
|
|
617
|
+
const config = loadCIFeedbackConfig(projectRoot);
|
|
618
|
+
|
|
619
|
+
if (!config.enabled) {
|
|
620
|
+
return {
|
|
621
|
+
status: 'disabled',
|
|
622
|
+
message: 'CI feedback loops are disabled in agileflow-metadata.json',
|
|
623
|
+
should_retry: false,
|
|
624
|
+
round: currentRound,
|
|
625
|
+
max_rounds: 0,
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const effectiveMaxRounds = maxRounds || config.max_rounds || CI_FEEDBACK_DEFAULTS.max_rounds;
|
|
630
|
+
|
|
631
|
+
// Execute all gates
|
|
632
|
+
const gateResults = executeGates(gates, { cwd, stopOnFailure: false });
|
|
633
|
+
|
|
634
|
+
if (gateResults.passed) {
|
|
635
|
+
return {
|
|
636
|
+
status: 'passed',
|
|
637
|
+
message: `All ${gateResults.passed_count} gates passed on round ${currentRound}`,
|
|
638
|
+
should_retry: false,
|
|
639
|
+
round: currentRound,
|
|
640
|
+
max_rounds: effectiveMaxRounds,
|
|
641
|
+
gate_results: gateResults,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Gates failed - determine if we should retry
|
|
646
|
+
const hasRoundsLeft = currentRound < effectiveMaxRounds;
|
|
647
|
+
|
|
648
|
+
if (!hasRoundsLeft) {
|
|
649
|
+
return {
|
|
650
|
+
status: 'exhausted',
|
|
651
|
+
message: `Gates failed after ${currentRound}/${effectiveMaxRounds} rounds. Escalating to human.`,
|
|
652
|
+
should_retry: false,
|
|
653
|
+
round: currentRound,
|
|
654
|
+
max_rounds: effectiveMaxRounds,
|
|
655
|
+
gate_results: gateResults,
|
|
656
|
+
failures: gateResults.results
|
|
657
|
+
.filter(r => r.status === GATE_STATUS.FAILED || r.status === GATE_STATUS.ERROR)
|
|
658
|
+
.map(r => ({
|
|
659
|
+
gate: r.gate,
|
|
660
|
+
message: r.message,
|
|
661
|
+
output: r.output,
|
|
662
|
+
error: r.error,
|
|
663
|
+
})),
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Build structured feedback for agent retry
|
|
668
|
+
const failures = gateResults.results.filter(
|
|
669
|
+
r => r.status === GATE_STATUS.FAILED || r.status === GATE_STATUS.ERROR
|
|
670
|
+
);
|
|
671
|
+
|
|
672
|
+
const feedbackLines = [
|
|
673
|
+
`## CI Feedback Loop - Round ${currentRound}/${effectiveMaxRounds}`,
|
|
674
|
+
'',
|
|
675
|
+
`**${failures.length} gate(s) failed.** ${effectiveMaxRounds - currentRound} retry round(s) remaining.`,
|
|
676
|
+
'',
|
|
677
|
+
'### Failures',
|
|
678
|
+
'',
|
|
679
|
+
];
|
|
680
|
+
|
|
681
|
+
for (const failure of failures) {
|
|
682
|
+
feedbackLines.push(`#### ${failure.gate} (${failure.type})`);
|
|
683
|
+
feedbackLines.push(`- **Status**: ${failure.status}`);
|
|
684
|
+
feedbackLines.push(`- **Message**: ${failure.message}`);
|
|
685
|
+
if (failure.output) {
|
|
686
|
+
feedbackLines.push('- **Output**:');
|
|
687
|
+
feedbackLines.push('```');
|
|
688
|
+
feedbackLines.push(failure.output);
|
|
689
|
+
feedbackLines.push('```');
|
|
690
|
+
}
|
|
691
|
+
if (failure.error) {
|
|
692
|
+
feedbackLines.push('- **Error**:');
|
|
693
|
+
feedbackLines.push('```');
|
|
694
|
+
feedbackLines.push(failure.error);
|
|
695
|
+
feedbackLines.push('```');
|
|
696
|
+
}
|
|
697
|
+
feedbackLines.push('');
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
feedbackLines.push('### Action Required');
|
|
701
|
+
feedbackLines.push('');
|
|
702
|
+
feedbackLines.push('Fix the failing gates above, then re-run verification.');
|
|
703
|
+
|
|
704
|
+
return {
|
|
705
|
+
status: 'retry',
|
|
706
|
+
message: `Round ${currentRound}/${effectiveMaxRounds} failed. Agent should fix and retry.`,
|
|
707
|
+
should_retry: true,
|
|
708
|
+
round: currentRound,
|
|
709
|
+
max_rounds: effectiveMaxRounds,
|
|
710
|
+
next_round: currentRound + 1,
|
|
711
|
+
gate_results: gateResults,
|
|
712
|
+
agent_feedback: feedbackLines.join('\n'),
|
|
713
|
+
failures: failures.map(r => ({
|
|
714
|
+
gate: r.gate,
|
|
715
|
+
message: r.message,
|
|
716
|
+
output: r.output,
|
|
717
|
+
error: r.error,
|
|
718
|
+
})),
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
|
|
564
722
|
// ============================================================================
|
|
565
723
|
// Exports
|
|
566
724
|
// ============================================================================
|
|
@@ -595,4 +753,9 @@ module.exports = {
|
|
|
595
753
|
|
|
596
754
|
// Reporting
|
|
597
755
|
createValidationReport,
|
|
756
|
+
|
|
757
|
+
// CI Feedback Loop
|
|
758
|
+
CI_FEEDBACK_DEFAULTS,
|
|
759
|
+
loadCIFeedbackConfig,
|
|
760
|
+
executeCIFeedbackLoop,
|
|
598
761
|
};
|
|
@@ -233,6 +233,24 @@ const FEATURE_DETECTORS = {
|
|
|
233
233
|
});
|
|
234
234
|
},
|
|
235
235
|
|
|
236
|
+
'scale-adaptive': signals => {
|
|
237
|
+
const { scale } = signals;
|
|
238
|
+
if (!scale || !scale.tier) return null;
|
|
239
|
+
// Only trigger when scale info provides actionable guidance
|
|
240
|
+
const rec = scale.recommendations;
|
|
241
|
+
if (!rec) return null;
|
|
242
|
+
// Suggest scale-adaptive workflow when project is not medium (the default)
|
|
243
|
+
if (scale.tier === 'medium') return null;
|
|
244
|
+
const label = scale.tier.charAt(0).toUpperCase() + scale.tier.slice(1);
|
|
245
|
+
return recommend('scale-adaptive', {
|
|
246
|
+
priority: scale.tier === 'enterprise' || scale.tier === 'large' ? 'medium' : 'low',
|
|
247
|
+
trigger: `${label} project detected (${scale.metrics.files} files, ${scale.metrics.stories} stories) — ${rec.description}`,
|
|
248
|
+
action: 'suggest',
|
|
249
|
+
command: '/agileflow:workflow',
|
|
250
|
+
phase: 'pre-story',
|
|
251
|
+
});
|
|
252
|
+
},
|
|
253
|
+
|
|
236
254
|
// =========================================================================
|
|
237
255
|
// PLANNING PHASE
|
|
238
256
|
// =========================================================================
|
|
@@ -517,6 +535,29 @@ const FEATURE_DETECTORS = {
|
|
|
517
535
|
});
|
|
518
536
|
},
|
|
519
537
|
|
|
538
|
+
'ac-verify': signals => {
|
|
539
|
+
const { story, tests } = signals;
|
|
540
|
+
if (!story || story.status !== 'in-progress') return null;
|
|
541
|
+
if (!tests || tests.passing !== true) return null; // Only after tests pass
|
|
542
|
+
if (!storyHasAC(story)) return null;
|
|
543
|
+
// Check if AC already verified (count by index to avoid extra keys)
|
|
544
|
+
const acStatus = story.ac_status || {};
|
|
545
|
+
const acList = story.acceptance_criteria || story.ac || [];
|
|
546
|
+
const verifiedCount = acList.filter(
|
|
547
|
+
(_, i) =>
|
|
548
|
+
acStatus[i] === 'verified' || acStatus[i] === 'auto-verified' || acStatus[i] === true
|
|
549
|
+
).length;
|
|
550
|
+
if (verifiedCount === acList.length) return null;
|
|
551
|
+
const unverifiedCount = acList.length - verifiedCount;
|
|
552
|
+
return recommend('ac-verify', {
|
|
553
|
+
priority: 'high',
|
|
554
|
+
trigger: `Tests pass but ${unverifiedCount}/${acList.length} AC unverified`,
|
|
555
|
+
action: 'suggest',
|
|
556
|
+
command: '/agileflow:audit',
|
|
557
|
+
phase: 'post-impl',
|
|
558
|
+
});
|
|
559
|
+
},
|
|
560
|
+
|
|
520
561
|
// =========================================================================
|
|
521
562
|
// POST-IMPLEMENTATION PHASE
|
|
522
563
|
// =========================================================================
|
|
@@ -704,6 +745,7 @@ const PHASE_MAP = {
|
|
|
704
745
|
'workflow',
|
|
705
746
|
'template',
|
|
706
747
|
'configure',
|
|
748
|
+
'scale-adaptive',
|
|
707
749
|
],
|
|
708
750
|
planning: [
|
|
709
751
|
'impact',
|
|
@@ -728,6 +770,7 @@ const PHASE_MAP = {
|
|
|
728
770
|
'serve',
|
|
729
771
|
],
|
|
730
772
|
'post-impl': [
|
|
773
|
+
'ac-verify',
|
|
731
774
|
'review',
|
|
732
775
|
'logic-audit',
|
|
733
776
|
'docs',
|