agileflow 3.2.1 → 3.4.0
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 +6 -6
- package/lib/feature-flags.js +32 -4
- package/lib/skill-loader.js +0 -1
- package/package.json +1 -1
- package/scripts/agileflow-statusline.sh +81 -0
- package/scripts/babysit-clear-restore.js +154 -0
- package/scripts/claude-tmux.sh +120 -24
- package/scripts/claude-watchdog.sh +225 -0
- package/scripts/generators/agent-registry.js +14 -1
- package/scripts/generators/inject-babysit.js +22 -9
- package/scripts/generators/inject-help.js +19 -9
- package/scripts/lib/README-portable-tasks.md +424 -0
- package/scripts/lib/audit-cleanup.js +250 -0
- package/scripts/lib/audit-registry.js +248 -0
- package/scripts/lib/configure-detect.js +20 -0
- package/scripts/lib/feature-catalog.js +13 -2
- package/scripts/lib/gate-enforcer.js +295 -0
- package/scripts/lib/model-profiles.js +98 -0
- package/scripts/lib/signal-detectors.js +1 -1
- package/scripts/lib/skill-catalog.js +557 -0
- package/scripts/lib/skill-recommender.js +311 -0
- package/scripts/lib/tdd-phase-manager.js +455 -0
- package/scripts/lib/team-events.js +76 -8
- package/scripts/lib/tmux-group-colors.js +113 -0
- package/scripts/messaging-bridge.js +209 -1
- package/scripts/spawn-audit-sessions.js +549 -0
- package/scripts/team-manager.js +37 -16
- package/scripts/tmux-close-windows.sh +180 -0
- package/scripts/tmux-restore-window.sh +67 -0
- package/scripts/tmux-save-closed-window.sh +35 -0
- package/src/core/agents/ads-audit-budget.md +181 -0
- package/src/core/agents/ads-audit-compliance.md +169 -0
- package/src/core/agents/ads-audit-creative.md +164 -0
- package/src/core/agents/ads-audit-google.md +226 -0
- package/src/core/agents/ads-audit-meta.md +183 -0
- package/src/core/agents/ads-audit-tracking.md +197 -0
- package/src/core/agents/ads-consensus.md +322 -0
- package/src/core/agents/brainstorm-analyzer-features.md +169 -0
- package/src/core/agents/brainstorm-analyzer-growth.md +161 -0
- package/src/core/agents/brainstorm-analyzer-integration.md +172 -0
- package/src/core/agents/brainstorm-analyzer-market.md +147 -0
- package/src/core/agents/brainstorm-analyzer-ux.md +167 -0
- package/src/core/agents/brainstorm-consensus.md +237 -0
- package/src/core/agents/completeness-analyzer-api.md +190 -0
- package/src/core/agents/completeness-analyzer-conditional.md +201 -0
- package/src/core/agents/completeness-analyzer-handlers.md +159 -0
- package/src/core/agents/completeness-analyzer-imports.md +159 -0
- package/src/core/agents/completeness-analyzer-routes.md +182 -0
- package/src/core/agents/completeness-analyzer-state.md +188 -0
- package/src/core/agents/completeness-analyzer-stubs.md +198 -0
- package/src/core/agents/completeness-consensus.md +286 -0
- package/src/core/agents/perf-consensus.md +2 -2
- package/src/core/agents/security-consensus.md +2 -2
- package/src/core/agents/seo-analyzer-content.md +167 -0
- package/src/core/agents/seo-analyzer-images.md +187 -0
- package/src/core/agents/seo-analyzer-performance.md +206 -0
- package/src/core/agents/seo-analyzer-schema.md +176 -0
- package/src/core/agents/seo-analyzer-sitemap.md +172 -0
- package/src/core/agents/seo-analyzer-technical.md +144 -0
- package/src/core/agents/seo-consensus.md +289 -0
- package/src/core/agents/test-consensus.md +2 -2
- package/src/core/commands/ads/audit.md +375 -0
- package/src/core/commands/ads/budget.md +97 -0
- package/src/core/commands/ads/competitor.md +112 -0
- package/src/core/commands/ads/creative.md +85 -0
- package/src/core/commands/ads/google.md +112 -0
- package/src/core/commands/ads/landing.md +119 -0
- package/src/core/commands/ads/linkedin.md +112 -0
- package/src/core/commands/ads/meta.md +91 -0
- package/src/core/commands/ads/microsoft.md +115 -0
- package/src/core/commands/ads/plan.md +321 -0
- package/src/core/commands/ads/tiktok.md +129 -0
- package/src/core/commands/ads/youtube.md +124 -0
- package/src/core/commands/ads.md +128 -0
- package/src/core/commands/babysit.md +250 -1344
- package/src/core/commands/code/completeness.md +466 -0
- package/src/core/commands/{audit → code}/legal.md +26 -16
- package/src/core/commands/{audit → code}/logic.md +27 -16
- package/src/core/commands/{audit → code}/performance.md +30 -20
- package/src/core/commands/{audit → code}/security.md +32 -19
- package/src/core/commands/{audit → code}/test.md +30 -20
- package/src/core/commands/{discovery → ideate}/brief.md +12 -12
- package/src/core/commands/{discovery/new.md → ideate/discover.md} +13 -13
- package/src/core/commands/ideate/features.md +435 -0
- package/src/core/commands/seo/audit.md +373 -0
- package/src/core/commands/seo/competitor.md +174 -0
- package/src/core/commands/seo/content.md +107 -0
- package/src/core/commands/seo/geo.md +229 -0
- package/src/core/commands/seo/hreflang.md +140 -0
- package/src/core/commands/seo/images.md +96 -0
- package/src/core/commands/seo/page.md +198 -0
- package/src/core/commands/seo/plan.md +163 -0
- package/src/core/commands/seo/programmatic.md +131 -0
- package/src/core/commands/seo/references/cwv-thresholds.md +64 -0
- package/src/core/commands/seo/references/eeat-framework.md +110 -0
- package/src/core/commands/seo/references/quality-gates.md +91 -0
- package/src/core/commands/seo/references/schema-types.md +102 -0
- package/src/core/commands/seo/schema.md +183 -0
- package/src/core/commands/seo/sitemap.md +97 -0
- package/src/core/commands/seo/technical.md +100 -0
- package/src/core/commands/seo.md +107 -0
- package/src/core/commands/skill/list.md +68 -212
- package/src/core/commands/skill/recommend.md +216 -0
- package/src/core/commands/tdd-next.md +238 -0
- package/src/core/commands/tdd.md +210 -0
- package/src/core/experts/_core-expertise.yaml +105 -0
- package/src/core/experts/analytics/expertise.yaml +5 -99
- package/src/core/experts/codebase-query/expertise.yaml +3 -72
- package/src/core/experts/compliance/expertise.yaml +6 -72
- package/src/core/experts/database/expertise.yaml +9 -52
- package/src/core/experts/documentation/expertise.yaml +7 -140
- package/src/core/experts/integrations/expertise.yaml +7 -127
- package/src/core/experts/mentor/expertise.yaml +8 -35
- package/src/core/experts/monitoring/expertise.yaml +7 -49
- package/src/core/experts/performance/expertise.yaml +1 -26
- package/src/core/experts/security/expertise.yaml +9 -34
- package/src/core/experts/ui/expertise.yaml +6 -36
- package/src/core/knowledge/ads/ad-audit-checklist-scoring.md +424 -0
- package/src/core/knowledge/ads/ad-optimization-logic.md +590 -0
- package/src/core/knowledge/ads/ad-technical-specifications.md +385 -0
- package/src/core/knowledge/ads/definitive-advertising-reference-2026.md +506 -0
- package/src/core/knowledge/ads/paid-advertising-research-2026.md +445 -0
- package/src/core/templates/agileflow-metadata.json +15 -1
- package/tools/cli/installers/ide/_base-ide.js +42 -5
- package/tools/cli/installers/ide/claude-code.js +13 -4
- package/tools/cli/lib/content-injector.js +160 -12
- package/tools/cli/lib/docs-setup.js +1 -1
- package/src/core/commands/skill/create.md +0 -698
- package/src/core/commands/skill/delete.md +0 -316
- package/src/core/commands/skill/edit.md +0 -359
- package/src/core/commands/skill/test.md +0 -394
- package/src/core/commands/skill/upgrade.md +0 -552
- package/src/core/templates/skill-template.md +0 -117
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* audit-registry.js - Static registry mapping audit types to analyzers
|
|
3
|
+
*
|
|
4
|
+
* Centralizes the mapping of 6 audit commands to their analyzer agents,
|
|
5
|
+
* consensus coordinators, and depth configurations. Previously this info
|
|
6
|
+
* was duplicated across 6 .md command files.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* const { getAuditType, getAnalyzersForAudit } = require('./audit-registry');
|
|
10
|
+
* const security = getAuditType('security');
|
|
11
|
+
* const focused = getAnalyzersForAudit('security', 'deep', ['injection', 'auth']);
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Complete audit type registry.
|
|
16
|
+
* Each entry defines: name, short prefix for tmux, color for tab groups,
|
|
17
|
+
* all analyzers with their subagent_type, which are quick vs deep,
|
|
18
|
+
* and the consensus coordinator.
|
|
19
|
+
*/
|
|
20
|
+
const AUDIT_TYPES = {
|
|
21
|
+
logic: {
|
|
22
|
+
name: 'Logic Analysis',
|
|
23
|
+
prefix: 'Logic',
|
|
24
|
+
color: '#7aa2f7', // sky
|
|
25
|
+
command: 'code/logic',
|
|
26
|
+
analyzers: {
|
|
27
|
+
edge: { subagent_type: 'logic-analyzer-edge', label: 'Edge Cases' },
|
|
28
|
+
invariant: { subagent_type: 'logic-analyzer-invariant', label: 'Invariants' },
|
|
29
|
+
flow: { subagent_type: 'logic-analyzer-flow', label: 'Control Flow' },
|
|
30
|
+
type: { subagent_type: 'logic-analyzer-type', label: 'Type Safety' },
|
|
31
|
+
race: { subagent_type: 'logic-analyzer-race', label: 'Race Conditions' },
|
|
32
|
+
},
|
|
33
|
+
consensus: { subagent_type: 'logic-consensus', label: 'Logic Consensus' },
|
|
34
|
+
quick_analyzers: ['edge', 'invariant', 'flow', 'type', 'race'],
|
|
35
|
+
deep_analyzers: ['edge', 'invariant', 'flow', 'type', 'race'],
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
security: {
|
|
39
|
+
name: 'Security Vulnerability',
|
|
40
|
+
prefix: 'Sec',
|
|
41
|
+
color: '#f7768e', // coral
|
|
42
|
+
command: 'code/security',
|
|
43
|
+
analyzers: {
|
|
44
|
+
injection: { subagent_type: 'security-analyzer-injection', label: 'Injection' },
|
|
45
|
+
auth: { subagent_type: 'security-analyzer-auth', label: 'Authentication' },
|
|
46
|
+
authz: { subagent_type: 'security-analyzer-authz', label: 'Authorization' },
|
|
47
|
+
secrets: { subagent_type: 'security-analyzer-secrets', label: 'Secrets' },
|
|
48
|
+
input: { subagent_type: 'security-analyzer-input', label: 'Input Validation' },
|
|
49
|
+
deps: { subagent_type: 'security-analyzer-deps', label: 'Dependencies' },
|
|
50
|
+
infra: { subagent_type: 'security-analyzer-infra', label: 'Infrastructure' },
|
|
51
|
+
api: { subagent_type: 'security-analyzer-api', label: 'API Security' },
|
|
52
|
+
},
|
|
53
|
+
consensus: { subagent_type: 'security-consensus', label: 'Security Consensus' },
|
|
54
|
+
quick_analyzers: ['injection', 'auth', 'authz', 'secrets', 'input'],
|
|
55
|
+
deep_analyzers: ['injection', 'auth', 'authz', 'secrets', 'input', 'deps', 'infra', 'api'],
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
performance: {
|
|
59
|
+
name: 'Performance Bottleneck',
|
|
60
|
+
prefix: 'Perf',
|
|
61
|
+
color: '#73daca', // mint
|
|
62
|
+
command: 'code/performance',
|
|
63
|
+
analyzers: {
|
|
64
|
+
queries: { subagent_type: 'perf-analyzer-queries', label: 'Queries' },
|
|
65
|
+
rendering: { subagent_type: 'perf-analyzer-rendering', label: 'Rendering' },
|
|
66
|
+
memory: { subagent_type: 'perf-analyzer-memory', label: 'Memory' },
|
|
67
|
+
bundle: { subagent_type: 'perf-analyzer-bundle', label: 'Bundle Size' },
|
|
68
|
+
compute: { subagent_type: 'perf-analyzer-compute', label: 'Compute' },
|
|
69
|
+
network: { subagent_type: 'perf-analyzer-network', label: 'Network' },
|
|
70
|
+
caching: { subagent_type: 'perf-analyzer-caching', label: 'Caching' },
|
|
71
|
+
assets: { subagent_type: 'perf-analyzer-assets', label: 'Assets' },
|
|
72
|
+
},
|
|
73
|
+
consensus: { subagent_type: 'perf-consensus', label: 'Performance Consensus' },
|
|
74
|
+
quick_analyzers: ['queries', 'rendering', 'memory', 'bundle', 'compute'],
|
|
75
|
+
deep_analyzers: [
|
|
76
|
+
'queries',
|
|
77
|
+
'rendering',
|
|
78
|
+
'memory',
|
|
79
|
+
'bundle',
|
|
80
|
+
'compute',
|
|
81
|
+
'network',
|
|
82
|
+
'caching',
|
|
83
|
+
'assets',
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
test: {
|
|
88
|
+
name: 'Test Quality',
|
|
89
|
+
prefix: 'Test',
|
|
90
|
+
color: '#e0af68', // amber
|
|
91
|
+
command: 'code/test',
|
|
92
|
+
analyzers: {
|
|
93
|
+
coverage: { subagent_type: 'test-analyzer-coverage', label: 'Coverage' },
|
|
94
|
+
fragility: { subagent_type: 'test-analyzer-fragility', label: 'Fragility' },
|
|
95
|
+
mocking: { subagent_type: 'test-analyzer-mocking', label: 'Mocking' },
|
|
96
|
+
assertions: { subagent_type: 'test-analyzer-assertions', label: 'Assertions' },
|
|
97
|
+
structure: { subagent_type: 'test-analyzer-structure', label: 'Structure' },
|
|
98
|
+
integration: { subagent_type: 'test-analyzer-integration', label: 'Integration' },
|
|
99
|
+
maintenance: { subagent_type: 'test-analyzer-maintenance', label: 'Maintenance' },
|
|
100
|
+
patterns: { subagent_type: 'test-analyzer-patterns', label: 'Anti-Patterns' },
|
|
101
|
+
},
|
|
102
|
+
consensus: { subagent_type: 'test-consensus', label: 'Test Consensus' },
|
|
103
|
+
quick_analyzers: ['coverage', 'fragility', 'mocking', 'assertions', 'structure'],
|
|
104
|
+
deep_analyzers: [
|
|
105
|
+
'coverage',
|
|
106
|
+
'fragility',
|
|
107
|
+
'mocking',
|
|
108
|
+
'assertions',
|
|
109
|
+
'structure',
|
|
110
|
+
'integration',
|
|
111
|
+
'maintenance',
|
|
112
|
+
'patterns',
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
completeness: {
|
|
117
|
+
name: 'Completeness',
|
|
118
|
+
prefix: 'Comp',
|
|
119
|
+
color: '#bb9af7', // violet
|
|
120
|
+
command: 'code/completeness',
|
|
121
|
+
analyzers: {
|
|
122
|
+
handlers: { subagent_type: 'completeness-analyzer-handlers', label: 'Handlers' },
|
|
123
|
+
routes: { subagent_type: 'completeness-analyzer-routes', label: 'Routes' },
|
|
124
|
+
api: { subagent_type: 'completeness-analyzer-api', label: 'API Endpoints' },
|
|
125
|
+
stubs: { subagent_type: 'completeness-analyzer-stubs', label: 'Stubs' },
|
|
126
|
+
state: { subagent_type: 'completeness-analyzer-state', label: 'State' },
|
|
127
|
+
imports: { subagent_type: 'completeness-analyzer-imports', label: 'Imports' },
|
|
128
|
+
conditional: { subagent_type: 'completeness-analyzer-conditional', label: 'Conditionals' },
|
|
129
|
+
},
|
|
130
|
+
consensus: { subagent_type: 'completeness-consensus', label: 'Completeness Consensus' },
|
|
131
|
+
quick_analyzers: ['handlers', 'routes', 'api', 'stubs', 'state'],
|
|
132
|
+
deep_analyzers: ['handlers', 'routes', 'api', 'stubs', 'state', 'imports', 'conditional'],
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
legal: {
|
|
136
|
+
name: 'Legal Risk',
|
|
137
|
+
prefix: 'Legal',
|
|
138
|
+
color: '#9ece6a', // lime
|
|
139
|
+
command: 'code/legal',
|
|
140
|
+
analyzers: {
|
|
141
|
+
privacy: { subagent_type: 'legal-analyzer-privacy', label: 'Privacy' },
|
|
142
|
+
terms: { subagent_type: 'legal-analyzer-terms', label: 'Terms' },
|
|
143
|
+
a11y: { subagent_type: 'legal-analyzer-a11y', label: 'Accessibility' },
|
|
144
|
+
licensing: { subagent_type: 'legal-analyzer-licensing', label: 'Licensing' },
|
|
145
|
+
consumer: { subagent_type: 'legal-analyzer-consumer', label: 'Consumer' },
|
|
146
|
+
security: { subagent_type: 'legal-analyzer-security', label: 'Security' },
|
|
147
|
+
ai: { subagent_type: 'legal-analyzer-ai', label: 'AI Compliance' },
|
|
148
|
+
content: { subagent_type: 'legal-analyzer-content', label: 'Content' },
|
|
149
|
+
international: { subagent_type: 'legal-analyzer-international', label: 'International' },
|
|
150
|
+
},
|
|
151
|
+
consensus: { subagent_type: 'legal-consensus', label: 'Legal Consensus' },
|
|
152
|
+
quick_analyzers: ['privacy', 'terms', 'a11y', 'licensing', 'consumer'],
|
|
153
|
+
deep_analyzers: [
|
|
154
|
+
'privacy',
|
|
155
|
+
'terms',
|
|
156
|
+
'a11y',
|
|
157
|
+
'licensing',
|
|
158
|
+
'consumer',
|
|
159
|
+
'security',
|
|
160
|
+
'ai',
|
|
161
|
+
'content',
|
|
162
|
+
'international',
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get audit type configuration.
|
|
169
|
+
*
|
|
170
|
+
* @param {string} type - Audit type key (logic, security, performance, test, completeness, legal)
|
|
171
|
+
* @returns {object|null} Audit type config or null if invalid
|
|
172
|
+
*/
|
|
173
|
+
function getAuditType(type) {
|
|
174
|
+
return AUDIT_TYPES[type] || null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get all valid audit type keys.
|
|
179
|
+
*
|
|
180
|
+
* @returns {string[]} Array of audit type keys
|
|
181
|
+
*/
|
|
182
|
+
function getAuditTypeKeys() {
|
|
183
|
+
return Object.keys(AUDIT_TYPES);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get analyzers for a given audit type, depth, and focus.
|
|
188
|
+
*
|
|
189
|
+
* @param {string} type - Audit type key
|
|
190
|
+
* @param {string} [depth='quick'] - 'quick', 'deep', or 'ultradeep'
|
|
191
|
+
* @param {string[]} [focus] - Array of focus areas, or null/['all'] for all
|
|
192
|
+
* @returns {{ analyzers: Array<{ key: string, subagent_type: string, label: string }>, consensus: object }|null}
|
|
193
|
+
*/
|
|
194
|
+
function getAnalyzersForAudit(type, depth, focus) {
|
|
195
|
+
const audit = AUDIT_TYPES[type];
|
|
196
|
+
if (!audit) return null;
|
|
197
|
+
|
|
198
|
+
const effectiveDepth = depth === 'ultradeep' ? 'deep' : depth || 'quick';
|
|
199
|
+
const analyzerKeys = effectiveDepth === 'deep' ? audit.deep_analyzers : audit.quick_analyzers;
|
|
200
|
+
|
|
201
|
+
// Filter by focus if specified
|
|
202
|
+
let selectedKeys = analyzerKeys;
|
|
203
|
+
if (focus && focus.length > 0 && !focus.includes('all')) {
|
|
204
|
+
selectedKeys = analyzerKeys.filter(key => focus.includes(key));
|
|
205
|
+
// If focus specifies keys not in current depth, include them anyway
|
|
206
|
+
for (const f of focus) {
|
|
207
|
+
if (audit.analyzers[f] && !selectedKeys.includes(f)) {
|
|
208
|
+
selectedKeys.push(f);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const analyzers = selectedKeys.map(key => ({
|
|
214
|
+
key,
|
|
215
|
+
subagent_type: audit.analyzers[key].subagent_type,
|
|
216
|
+
label: audit.analyzers[key].label,
|
|
217
|
+
}));
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
analyzers,
|
|
221
|
+
consensus: audit.consensus,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get analyzer count for a given audit type at each depth.
|
|
227
|
+
*
|
|
228
|
+
* @param {string} type - Audit type key
|
|
229
|
+
* @returns {{ quick: number, deep: number, total: number }|null}
|
|
230
|
+
*/
|
|
231
|
+
function getAnalyzerCounts(type) {
|
|
232
|
+
const audit = AUDIT_TYPES[type];
|
|
233
|
+
if (!audit) return null;
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
quick: audit.quick_analyzers.length,
|
|
237
|
+
deep: audit.deep_analyzers.length,
|
|
238
|
+
total: Object.keys(audit.analyzers).length,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
module.exports = {
|
|
243
|
+
AUDIT_TYPES,
|
|
244
|
+
getAuditType,
|
|
245
|
+
getAuditTypeKeys,
|
|
246
|
+
getAnalyzersForAudit,
|
|
247
|
+
getAnalyzerCounts,
|
|
248
|
+
};
|
|
@@ -11,6 +11,7 @@ const { execFileSync } = require('child_process');
|
|
|
11
11
|
const { c, log, header, readJSON } = require('./configure-utils');
|
|
12
12
|
const { tryOptional } = require('../../lib/errors');
|
|
13
13
|
const { FEATURES } = require('./configure-features');
|
|
14
|
+
const ff = require('../../lib/feature-flags');
|
|
14
15
|
|
|
15
16
|
// ============================================================================
|
|
16
17
|
// CONTENT HASH HELPERS
|
|
@@ -121,6 +122,11 @@ function detectConfig(version) {
|
|
|
121
122
|
outdated: false,
|
|
122
123
|
mode: 'full',
|
|
123
124
|
},
|
|
125
|
+
agentteams: {
|
|
126
|
+
enabled: false,
|
|
127
|
+
mode: 'subagent',
|
|
128
|
+
tools: [],
|
|
129
|
+
},
|
|
124
130
|
},
|
|
125
131
|
metadata: { exists: false, version: null },
|
|
126
132
|
currentVersion: version,
|
|
@@ -304,6 +310,12 @@ function detectStatusLine(settings, status) {
|
|
|
304
310
|
* Detect metadata file configuration
|
|
305
311
|
*/
|
|
306
312
|
function detectMetadata(status, version) {
|
|
313
|
+
// Agent Teams detection (independent of metadata file - reads env vars)
|
|
314
|
+
const rootDir = process.cwd();
|
|
315
|
+
status.features.agentteams.enabled = ff.isAgentTeamsEnabled({ rootDir });
|
|
316
|
+
status.features.agentteams.mode = ff.getAgentTeamsMode({ rootDir });
|
|
317
|
+
status.features.agentteams.tools = ff.getAvailableTools({ rootDir });
|
|
318
|
+
|
|
307
319
|
const metaPath = 'docs/00-meta/agileflow-metadata.json';
|
|
308
320
|
if (!fs.existsSync(metaPath)) return;
|
|
309
321
|
|
|
@@ -542,6 +554,14 @@ function printStatus(status) {
|
|
|
542
554
|
}
|
|
543
555
|
}
|
|
544
556
|
|
|
557
|
+
// Agent Teams
|
|
558
|
+
const at = status.features.agentteams;
|
|
559
|
+
if (at.enabled) {
|
|
560
|
+
log(` Agent Teams: ${at.mode} (${at.tools.length} tools)`, c.green);
|
|
561
|
+
} else {
|
|
562
|
+
log(` Agent Teams: ${at.mode}`, c.dim);
|
|
563
|
+
}
|
|
564
|
+
|
|
545
565
|
// Metadata version
|
|
546
566
|
if (status.metadata.exists) {
|
|
547
567
|
log(`\nMetadata: v${status.metadata.version}`, c.dim);
|
|
@@ -116,7 +116,7 @@ const FEATURE_CATALOG = [
|
|
|
116
116
|
feature: 'discovery',
|
|
117
117
|
name: 'Discovery',
|
|
118
118
|
description: 'Brainstorm, research, and synthesize findings into a Product Brief',
|
|
119
|
-
how_to_use: '/agileflow:
|
|
119
|
+
how_to_use: '/agileflow:ideate:discover "<topic>"',
|
|
120
120
|
category: 'workflow',
|
|
121
121
|
detector: null,
|
|
122
122
|
auto_mode: null,
|
|
@@ -168,7 +168,18 @@ const FEATURE_CATALOG = [
|
|
|
168
168
|
feature: 'logic-audit',
|
|
169
169
|
name: 'Logic Audit',
|
|
170
170
|
description: 'Multi-agent analysis for edge cases, race conditions, type bugs, and dead code',
|
|
171
|
-
how_to_use: '/agileflow:
|
|
171
|
+
how_to_use: '/agileflow:code:logic',
|
|
172
|
+
category: 'analysis',
|
|
173
|
+
detector: null,
|
|
174
|
+
auto_mode: null,
|
|
175
|
+
prerequisites: null,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
feature: 'completeness-audit',
|
|
179
|
+
name: 'Completeness Audit',
|
|
180
|
+
description:
|
|
181
|
+
'Multi-agent analysis for forgotten features, dead handlers, stub code, and incomplete implementations',
|
|
182
|
+
how_to_use: '/agileflow:code:completeness',
|
|
172
183
|
category: 'analysis',
|
|
173
184
|
detector: null,
|
|
174
185
|
auto_mode: null,
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gate-enforcer.js - Workflow Gate Enforcement for Babysit Strict Mode
|
|
3
|
+
*
|
|
4
|
+
* Filters AskUserQuestion options based on workflow gate status.
|
|
5
|
+
* When STRICT=true in babysit, certain options are removed or blocked
|
|
6
|
+
* based on whether gates have been satisfied (tests passed, review done, etc.).
|
|
7
|
+
*
|
|
8
|
+
* Gates:
|
|
9
|
+
* - tests_passed: Must run and pass tests before commit
|
|
10
|
+
* - review_done: Must run code review for 5+ source files before commit
|
|
11
|
+
* - logic_audit_done: Must run logic audit before commit (advisory in non-strict)
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const { filterOptions, getGateStatus, updateGate } = require('./gate-enforcer');
|
|
15
|
+
* const filtered = filterOptions(options, gateState, { strict: true });
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Constants
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Gate types that can block workflow progression
|
|
26
|
+
*/
|
|
27
|
+
const GATES = {
|
|
28
|
+
TESTS_PASSED: 'tests_passed',
|
|
29
|
+
REVIEW_DONE: 'review_done',
|
|
30
|
+
LOGIC_AUDIT_DONE: 'logic_audit_done',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Actions that require specific gates to be satisfied
|
|
35
|
+
*/
|
|
36
|
+
const GATE_REQUIREMENTS = {
|
|
37
|
+
commit: {
|
|
38
|
+
strict: [GATES.TESTS_PASSED],
|
|
39
|
+
strict_5plus: [GATES.TESTS_PASSED, GATES.REVIEW_DONE],
|
|
40
|
+
},
|
|
41
|
+
next_story: {
|
|
42
|
+
strict: [GATES.TESTS_PASSED],
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Patterns that identify commit-related options
|
|
48
|
+
*/
|
|
49
|
+
const COMMIT_PATTERNS = [/^commit/i, /^git commit/i, /commit.*changes/i, /commit:?\s/i];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Patterns that identify "skip" options
|
|
53
|
+
*/
|
|
54
|
+
const SKIP_PATTERNS = [/skip.*test/i, /skip.*review/i, /skip.*audit/i, /skip.*verif/i];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Patterns that identify "next story" options
|
|
58
|
+
*/
|
|
59
|
+
const NEXT_STORY_PATTERNS = [/continue to/i, /next story/i, /move to.*US-/i];
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Gate State Management
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Create initial gate state for a workflow session
|
|
67
|
+
* @param {Object} options
|
|
68
|
+
* @param {number} options.filesChanged - Number of source files modified
|
|
69
|
+
* @param {boolean} options.strict - Whether strict mode is enabled
|
|
70
|
+
* @returns {Object} Initial gate state
|
|
71
|
+
*/
|
|
72
|
+
function createGateState(options = {}) {
|
|
73
|
+
const { filesChanged = 0, strict = false } = options;
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
strict,
|
|
77
|
+
files_changed: filesChanged,
|
|
78
|
+
gates: {
|
|
79
|
+
[GATES.TESTS_PASSED]: false,
|
|
80
|
+
[GATES.REVIEW_DONE]: false,
|
|
81
|
+
[GATES.LOGIC_AUDIT_DONE]: false,
|
|
82
|
+
},
|
|
83
|
+
history: [],
|
|
84
|
+
created_at: new Date().toISOString(),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Update a gate's status
|
|
90
|
+
* @param {Object} gateState - Current gate state
|
|
91
|
+
* @param {string} gate - Gate name from GATES
|
|
92
|
+
* @param {boolean} passed - Whether the gate passed
|
|
93
|
+
* @param {Object} metadata - Additional info (e.g., test count, review findings)
|
|
94
|
+
* @returns {Object} Updated gate state
|
|
95
|
+
*/
|
|
96
|
+
function updateGate(gateState, gate, passed, metadata = {}) {
|
|
97
|
+
if (!Object.values(GATES).includes(gate)) {
|
|
98
|
+
throw new Error(`Unknown gate: ${gate}. Valid: ${Object.values(GATES).join(', ')}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const updated = {
|
|
102
|
+
...gateState,
|
|
103
|
+
gates: {
|
|
104
|
+
...gateState.gates,
|
|
105
|
+
[gate]: passed,
|
|
106
|
+
},
|
|
107
|
+
history: [
|
|
108
|
+
...gateState.history,
|
|
109
|
+
{
|
|
110
|
+
gate,
|
|
111
|
+
passed,
|
|
112
|
+
at: new Date().toISOString(),
|
|
113
|
+
...metadata,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return updated;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Check if all required gates for an action are satisfied
|
|
123
|
+
* @param {Object} gateState - Current gate state
|
|
124
|
+
* @param {string} action - Action to check ('commit', 'next_story')
|
|
125
|
+
* @returns {{ allowed: boolean, missing: string[] }}
|
|
126
|
+
*/
|
|
127
|
+
function checkGates(gateState, action) {
|
|
128
|
+
if (!gateState.strict) {
|
|
129
|
+
return { allowed: true, missing: [] };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let requirements;
|
|
133
|
+
if (action === 'commit' && gateState.files_changed >= 5) {
|
|
134
|
+
requirements = GATE_REQUIREMENTS.commit.strict_5plus || [];
|
|
135
|
+
} else {
|
|
136
|
+
requirements = (GATE_REQUIREMENTS[action] && GATE_REQUIREMENTS[action].strict) || [];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const missing = requirements.filter(gate => !gateState.gates[gate]);
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
allowed: missing.length === 0,
|
|
143
|
+
missing,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ============================================================================
|
|
148
|
+
// Option Filtering
|
|
149
|
+
// ============================================================================
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if an option label matches any pattern in a list
|
|
153
|
+
* @param {string} label - Option label text
|
|
154
|
+
* @param {RegExp[]} patterns - Patterns to match against
|
|
155
|
+
* @returns {boolean}
|
|
156
|
+
*/
|
|
157
|
+
function matchesPattern(label, patterns) {
|
|
158
|
+
if (typeof label !== 'string') return false;
|
|
159
|
+
return patterns.some(p => p.test(label));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Filter AskUserQuestion options based on gate state
|
|
164
|
+
*
|
|
165
|
+
* In strict mode:
|
|
166
|
+
* - Remove "commit" options if tests haven't passed
|
|
167
|
+
* - Remove "commit" options if review not done (5+ files)
|
|
168
|
+
* - Remove "skip tests/review/audit" options
|
|
169
|
+
* - Add gate status hints to option descriptions
|
|
170
|
+
*
|
|
171
|
+
* In non-strict mode:
|
|
172
|
+
* - Options are returned unchanged (soft guidance only)
|
|
173
|
+
*
|
|
174
|
+
* @param {Object[]} options - AskUserQuestion options array
|
|
175
|
+
* @param {Object} gateState - Current gate state
|
|
176
|
+
* @returns {Object[]} Filtered options
|
|
177
|
+
*/
|
|
178
|
+
function filterOptions(options, gateState) {
|
|
179
|
+
if (!Array.isArray(options)) return [];
|
|
180
|
+
if (!gateState || !gateState.strict) {
|
|
181
|
+
return options;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return options
|
|
185
|
+
.filter(opt => {
|
|
186
|
+
const label = opt.label || '';
|
|
187
|
+
|
|
188
|
+
// In strict mode, remove skip options entirely
|
|
189
|
+
if (matchesPattern(label, SKIP_PATTERNS)) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check commit gates
|
|
194
|
+
if (matchesPattern(label, COMMIT_PATTERNS)) {
|
|
195
|
+
const { allowed } = checkGates(gateState, 'commit');
|
|
196
|
+
if (!allowed) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check next story gates
|
|
202
|
+
if (matchesPattern(label, NEXT_STORY_PATTERNS)) {
|
|
203
|
+
const { allowed } = checkGates(gateState, 'next_story');
|
|
204
|
+
if (!allowed) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return true;
|
|
210
|
+
})
|
|
211
|
+
.map(opt => {
|
|
212
|
+
// Add gate status hints to descriptions
|
|
213
|
+
const label = opt.label || '';
|
|
214
|
+
|
|
215
|
+
if (matchesPattern(label, COMMIT_PATTERNS) && gateState.strict) {
|
|
216
|
+
const checks = [];
|
|
217
|
+
if (gateState.gates[GATES.TESTS_PASSED]) checks.push('tests passed');
|
|
218
|
+
if (gateState.gates[GATES.REVIEW_DONE]) checks.push('review done');
|
|
219
|
+
if (gateState.gates[GATES.LOGIC_AUDIT_DONE]) checks.push('audit done');
|
|
220
|
+
|
|
221
|
+
if (checks.length > 0) {
|
|
222
|
+
return {
|
|
223
|
+
...opt,
|
|
224
|
+
description: `${opt.description || ''} [Gates: ${checks.join(', ')}]`.trim(),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return opt;
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get a human-readable gate status summary
|
|
235
|
+
* @param {Object} gateState - Current gate state
|
|
236
|
+
* @returns {string} Status summary
|
|
237
|
+
*/
|
|
238
|
+
function getGateStatusSummary(gateState) {
|
|
239
|
+
if (!gateState || !gateState.strict) {
|
|
240
|
+
return 'Strict mode: OFF (soft guidance)';
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const lines = [`Strict mode: ON | ${gateState.files_changed} files changed`];
|
|
244
|
+
|
|
245
|
+
for (const [gate, passed] of Object.entries(gateState.gates)) {
|
|
246
|
+
const icon = passed ? '✅' : '⬜';
|
|
247
|
+
const name = gate.replace(/_/g, ' ');
|
|
248
|
+
lines.push(` ${icon} ${name}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return lines.join('\n');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get blocking message for a failed gate check
|
|
256
|
+
* @param {string[]} missingGates - List of unsatisfied gates
|
|
257
|
+
* @returns {string} Human-readable blocking message
|
|
258
|
+
*/
|
|
259
|
+
function getBlockingMessage(missingGates) {
|
|
260
|
+
const messages = {
|
|
261
|
+
[GATES.TESTS_PASSED]: 'Run tests first (tests must pass before committing)',
|
|
262
|
+
[GATES.REVIEW_DONE]: 'Run code review first (required for 5+ modified files)',
|
|
263
|
+
[GATES.LOGIC_AUDIT_DONE]: 'Run logic audit first',
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
return missingGates.map(gate => `🚫 ${messages[gate] || gate}`).join('\n');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ============================================================================
|
|
270
|
+
// Exports
|
|
271
|
+
// ============================================================================
|
|
272
|
+
|
|
273
|
+
module.exports = {
|
|
274
|
+
// Constants
|
|
275
|
+
GATES,
|
|
276
|
+
GATE_REQUIREMENTS,
|
|
277
|
+
|
|
278
|
+
// State management
|
|
279
|
+
createGateState,
|
|
280
|
+
updateGate,
|
|
281
|
+
checkGates,
|
|
282
|
+
|
|
283
|
+
// Option filtering
|
|
284
|
+
filterOptions,
|
|
285
|
+
matchesPattern,
|
|
286
|
+
|
|
287
|
+
// Reporting
|
|
288
|
+
getGateStatusSummary,
|
|
289
|
+
getBlockingMessage,
|
|
290
|
+
|
|
291
|
+
// Patterns (for testing)
|
|
292
|
+
COMMIT_PATTERNS,
|
|
293
|
+
SKIP_PATTERNS,
|
|
294
|
+
NEXT_STORY_PATTERNS,
|
|
295
|
+
};
|