chati-dev 4.3.1 → 4.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -13
- package/bin/chati.js +28 -4
- package/framework/agents/build/dev.md +5 -1
- package/framework/agents/discover/greenfield-wu.md +16 -1
- package/framework/agents/plan/tasks.md +31 -0
- package/framework/agents/plan/ux-brand-architect.md +21 -2
- package/framework/agents/plan/ux-component-engineer.md +10 -0
- package/framework/agents/quality/qa-implementation.md +1 -1
- package/framework/agents/quality/qa-planning.md +1 -1
- package/framework/agents/quality/qa-visual.md +24 -21
- package/framework/config.yaml +5 -4
- package/framework/constitution.md +60 -11
- package/framework/context/governance.md +12 -10
- package/framework/context/root.md +2 -2
- package/framework/data/entity-registry.yaml +12 -4
- package/framework/data/qa-rubrics.yaml +335 -0
- package/framework/domains/constitution.yaml +3 -3
- package/framework/executors/json-validate.js +80 -0
- package/framework/executors/npm-script.js +72 -0
- package/framework/executors/path-check.js +67 -0
- package/framework/executors/registry.yaml +36 -0
- package/framework/hooks/advance-trigger.js +47 -3
- package/framework/hooks/constitution-guard.js +9 -4
- package/framework/hooks/git-push-authority.js +113 -0
- package/framework/hooks/license-guard.js +59 -9
- package/framework/hooks/model-governance.js +40 -17
- package/framework/hooks/prism-engine.js +78 -11
- package/framework/hooks/session-digest.js +12 -3
- package/framework/hooks/settings.json +7 -3
- package/framework/hooks/style-guard.js +52 -3
- package/framework/hooks/team-quality-gate.js +43 -5
- package/framework/i18n/en.yaml +3 -3
- package/framework/i18n/es.yaml +3 -3
- package/framework/i18n/fr.yaml +3 -3
- package/framework/i18n/pt.yaml +3 -3
- package/framework/intelligence/confidence.yaml +85 -6
- package/framework/intelligence/context-engine.md +9 -5
- package/framework/intelligence/decision-engine.md +1 -1
- package/framework/manifest.json +1351 -0
- package/framework/manifest.sig +1 -0
- package/framework/orchestrator/chati-router.js +1 -1
- package/framework/orchestrator/chati-update.md +19 -3
- package/framework/orchestrator/chati.md +24 -3
- package/framework/quality-gates/planning-gate.md +2 -0
- package/framework/scaffold/motion-premium/scaffold.yaml +2 -1
- package/framework/scaffold/motion-premium-3d/components/webgl/WebGLContext.tsx.template +1 -1
- package/framework/scaffold/saas-dashboard/README.md +58 -0
- package/framework/scaffold/saas-dashboard/app/dashboard/page.tsx.template +546 -0
- package/framework/scaffold/saas-dashboard/app/globals.css.template +746 -0
- package/framework/scaffold/saas-dashboard/app/layout.tsx.template +192 -0
- package/framework/scaffold/saas-dashboard/app/shell.css.template +1070 -0
- package/framework/scaffold/saas-dashboard/components/data/Chart.tsx.template +725 -0
- package/framework/scaffold/saas-dashboard/components/data/DataTable.tsx.template +623 -0
- package/framework/scaffold/saas-dashboard/components/data/DataTable.types.ts.template +104 -0
- package/framework/scaffold/saas-dashboard/components/data/DataTablePagination.tsx.template +234 -0
- package/framework/scaffold/saas-dashboard/components/data/SkeletonBlock.tsx.template +111 -0
- package/framework/scaffold/saas-dashboard/components/data/StatCard.tsx.template +393 -0
- package/framework/scaffold/saas-dashboard/components/data/StatCardGrid.tsx.template +121 -0
- package/framework/scaffold/saas-dashboard/components/feedback/EmptyState.tsx.template +281 -0
- package/framework/scaffold/saas-dashboard/components/interaction/CommandPalette.tsx.template +767 -0
- package/framework/scaffold/saas-dashboard/components/interaction/Field.tsx.template +351 -0
- package/framework/scaffold/saas-dashboard/components/interaction/Form.tsx.template +424 -0
- package/framework/scaffold/saas-dashboard/components/primitives/Badge.tsx.template +193 -0
- package/framework/scaffold/saas-dashboard/components/primitives/Button.tsx.template +261 -0
- package/framework/scaffold/saas-dashboard/components/primitives/Card.tsx.template +287 -0
- package/framework/scaffold/saas-dashboard/components/primitives/Input.tsx.template +310 -0
- package/framework/scaffold/saas-dashboard/components/primitives/Spinner.tsx.template +135 -0
- package/framework/scaffold/saas-dashboard/components/primitives/index.ts.template +60 -0
- package/framework/scaffold/saas-dashboard/components/shell/AppShell.tsx.template +174 -0
- package/framework/scaffold/saas-dashboard/components/shell/Breadcrumb.tsx.template +186 -0
- package/framework/scaffold/saas-dashboard/components/shell/Container.tsx.template +86 -0
- package/framework/scaffold/saas-dashboard/components/shell/NavItem.tsx.template +147 -0
- package/framework/scaffold/saas-dashboard/components/shell/Sidebar.tsx.template +378 -0
- package/framework/scaffold/saas-dashboard/components/shell/SidebarContext.tsx.template +169 -0
- package/framework/scaffold/saas-dashboard/components/shell/ThemeToggle.tsx.template +128 -0
- package/framework/scaffold/saas-dashboard/components/shell/Topbar.tsx.template +301 -0
- package/framework/scaffold/saas-dashboard/lib/brand.ts.template +324 -0
- package/framework/scaffold/saas-dashboard/scaffold.yaml +225 -0
- package/framework/schemas/session.schema.json +5 -0
- package/framework/schemas/task.schema.json +9 -0
- package/framework/scripts/visual-qa.js +87 -1
- package/framework/tasks/orchestrator-health.md +4 -4
- package/framework/tasks/qa-impl-verdict.md +11 -5
- package/framework/templates/qa-gate-tmpl.yaml +24 -11
- package/package.json +4 -4
- package/src/config/claude-settings-generator.js +9 -6
- package/src/config/context-file-generator.js +0 -6
- package/src/dashboard/renderer.js +0 -36
- package/src/executors/runner.js +204 -0
- package/src/installer/core.js +35 -4
- package/src/installer/manifest.js +30 -4
- package/src/installer/signing-public-key.pem +1 -1
- package/src/installer/templates.js +5 -6
- package/src/installer/validator.js +2 -1
- package/src/intelligence/registry-manager.js +3 -2
- package/src/license/client.js +9 -3
- package/src/license/commands.js +12 -2
- package/src/license/machine-id.js +42 -1
- package/src/memory/gotchas.js +58 -40
- package/src/memory/magic-docs.js +7 -2
- package/src/memory/session-digest.js +9 -4
- package/src/orchestrator/cli.js +195 -23
- package/src/orchestrator/deviation-handler.js +5 -3
- package/src/orchestrator/doctor.js +98 -16
- package/src/orchestrator/pipeline-manager.js +44 -22
- package/src/orchestrator/session-manager.js +56 -8
- package/src/telemetry/sender.js +7 -19
- package/src/terminal/run-parallel.js +14 -14
- package/src/terminal/run-team.js +3 -3
- package/src/upgrade/backup.js +22 -44
- package/src/upgrade/migrator.js +51 -12
- package/src/upgrade/tracked-files-detector.js +34 -16
- package/src/utils/constitution-meta.js +12 -0
- package/src/utils/feature-flags.js +26 -2
- package/src/utils/flatten-entities.js +4 -40
- package/src/utils/schema-validator.js +0 -14
- package/src/wizard/i18n.js +3 -3
- package/src/api/index.js +0 -120
- package/src/autonomy/autonomous-gate.js +0 -294
- package/src/autonomy/build-loop.js +0 -281
- package/src/autonomy/build-state.js +0 -286
- package/src/autonomy/cause-analyzer.js +0 -177
- package/src/autonomy/escalation.js +0 -214
- package/src/autonomy/index.js +0 -51
- package/src/autonomy/mode-manager.js +0 -225
- package/src/autonomy/mode-suggester.js +0 -283
- package/src/autonomy/progress-reporter.js +0 -275
- package/src/autonomy/safety-net.js +0 -370
- package/src/config/agent-customizer.js +0 -231
- package/src/decision/analyzer.js +0 -291
- package/src/decision/engine.js +0 -250
- package/src/decision/index.js +0 -38
- package/src/decision/registry-healer.js +0 -468
- package/src/decision/registry-updater.js +0 -339
- package/src/extensions/loader.js +0 -145
- package/src/extensions/registry.js +0 -134
- package/src/gates/circuit-breaker.js +0 -151
- package/src/gates/g1-planning-complete.js +0 -154
- package/src/gates/g2-qa-planning.js +0 -156
- package/src/gates/g3-implementation.js +0 -215
- package/src/gates/g4-qa-implementation.js +0 -240
- package/src/gates/g5-deploy-ready.js +0 -181
- package/src/gates/gate-base.js +0 -185
- package/src/gates/index.js +0 -46
- package/src/health/auto-fix.js +0 -216
- package/src/health/engine.js +0 -246
- package/src/merger/semantic-merger.js +0 -292
- package/src/preview/detector.js +0 -238
- package/src/preview/index.js +0 -20
- package/src/preview/launcher.js +0 -235
- package/src/preview/log-buffer.js +0 -103
- package/src/quality/metrics-collector.js +0 -281
- package/src/quality/test-runner.js +0 -366
- package/src/tasks/executor.js +0 -195
- package/src/tasks/index.js +0 -4
- package/src/tasks/loader.js +0 -210
- package/src/tasks/router.js +0 -182
- package/src/utils/event-bus.js +0 -126
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Suggests optimal execution mode based on project context
|
|
3
|
-
* Analyzes risk factors to recommend human-in-the-loop vs autonomous execution
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { EXECUTION_MODES } from './mode-manager.js';
|
|
7
|
-
|
|
8
|
-
// Factor weights for mode suggestion
|
|
9
|
-
const WEIGHTS = {
|
|
10
|
-
GREENFIELD: 30,
|
|
11
|
-
HIGH_TASK_COUNT: 20,
|
|
12
|
-
RISK_DOMAIN: 25,
|
|
13
|
-
HAS_HISTORY: 25,
|
|
14
|
-
RECENT_GOTCHA: 15,
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// Risk domains that increase score towards human-in-the-loop
|
|
18
|
-
const HIGH_RISK_DOMAINS = [
|
|
19
|
-
'security',
|
|
20
|
-
'database',
|
|
21
|
-
'infrastructure',
|
|
22
|
-
'authentication',
|
|
23
|
-
'authorization',
|
|
24
|
-
'payment',
|
|
25
|
-
'data-migration',
|
|
26
|
-
'encryption',
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Suggest execution mode based on project context.
|
|
31
|
-
*
|
|
32
|
-
* Factors:
|
|
33
|
-
* - Project type: greenfield → human (higher risk), brownfield → autonomous
|
|
34
|
-
* - Complexity: high (>10 tasks) → human, low → autonomous
|
|
35
|
-
* - Risk: security/infra → human, UI/docs → autonomous
|
|
36
|
-
* - History: first time → human, executed before → autonomous
|
|
37
|
-
* - Gotchas: recent gotchas in domain → human
|
|
38
|
-
*
|
|
39
|
-
* @param {object} context
|
|
40
|
-
* @param {boolean} context.isGreenfield
|
|
41
|
-
* @param {number} [context.taskCount] - Estimated task count
|
|
42
|
-
* @param {string[]} [context.riskDomains] - e.g. ['security', 'database', 'infrastructure']
|
|
43
|
-
* @param {boolean} [context.hasHistory] - Project was built before
|
|
44
|
-
* @param {number} [context.recentGotchas] - Number of recent gotchas
|
|
45
|
-
* @returns {{ suggestion: string, confidence: number, factors: object[], reasoning: string }}
|
|
46
|
-
*/
|
|
47
|
-
export function suggestMode(context) {
|
|
48
|
-
const factors = [];
|
|
49
|
-
let humanScore = 0;
|
|
50
|
-
|
|
51
|
-
// Factor 1: Project type
|
|
52
|
-
if (context.isGreenfield) {
|
|
53
|
-
humanScore += WEIGHTS.GREENFIELD;
|
|
54
|
-
factors.push({
|
|
55
|
-
name: 'project_type',
|
|
56
|
-
value: 'greenfield',
|
|
57
|
-
impact: WEIGHTS.GREENFIELD,
|
|
58
|
-
direction: 'human',
|
|
59
|
-
reason: 'Greenfield projects have higher uncertainty',
|
|
60
|
-
});
|
|
61
|
-
} else {
|
|
62
|
-
humanScore -= WEIGHTS.GREENFIELD;
|
|
63
|
-
factors.push({
|
|
64
|
-
name: 'project_type',
|
|
65
|
-
value: 'brownfield',
|
|
66
|
-
impact: -WEIGHTS.GREENFIELD,
|
|
67
|
-
direction: 'autonomous',
|
|
68
|
-
reason: 'Brownfield projects have established patterns',
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Factor 2: Task complexity
|
|
73
|
-
const taskCount = context.taskCount || 0;
|
|
74
|
-
if (taskCount > 10) {
|
|
75
|
-
humanScore += WEIGHTS.HIGH_TASK_COUNT;
|
|
76
|
-
factors.push({
|
|
77
|
-
name: 'task_count',
|
|
78
|
-
value: taskCount,
|
|
79
|
-
impact: WEIGHTS.HIGH_TASK_COUNT,
|
|
80
|
-
direction: 'human',
|
|
81
|
-
reason: `High task count (${taskCount}) increases complexity`,
|
|
82
|
-
});
|
|
83
|
-
} else if (taskCount > 0) {
|
|
84
|
-
factors.push({
|
|
85
|
-
name: 'task_count',
|
|
86
|
-
value: taskCount,
|
|
87
|
-
impact: 0,
|
|
88
|
-
direction: 'neutral',
|
|
89
|
-
reason: `Moderate task count (${taskCount}) is manageable`,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Factor 3: Risk domains
|
|
94
|
-
const riskDomains = context.riskDomains || [];
|
|
95
|
-
const highRiskCount = riskDomains.filter(domain =>
|
|
96
|
-
HIGH_RISK_DOMAINS.includes(domain.toLowerCase())
|
|
97
|
-
).length;
|
|
98
|
-
|
|
99
|
-
if (highRiskCount > 0) {
|
|
100
|
-
const riskImpact = Math.min(highRiskCount * WEIGHTS.RISK_DOMAIN, 75); // Cap at 75
|
|
101
|
-
humanScore += riskImpact;
|
|
102
|
-
factors.push({
|
|
103
|
-
name: 'risk_domains',
|
|
104
|
-
value: riskDomains,
|
|
105
|
-
impact: riskImpact,
|
|
106
|
-
direction: 'human',
|
|
107
|
-
reason: `High-risk domains detected: ${riskDomains.join(', ')}`,
|
|
108
|
-
});
|
|
109
|
-
} else if (riskDomains.length > 0) {
|
|
110
|
-
factors.push({
|
|
111
|
-
name: 'risk_domains',
|
|
112
|
-
value: riskDomains,
|
|
113
|
-
impact: 0,
|
|
114
|
-
direction: 'neutral',
|
|
115
|
-
reason: `Low-risk domains: ${riskDomains.join(', ')}`,
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Factor 4: Project history
|
|
120
|
-
if (context.hasHistory) {
|
|
121
|
-
humanScore -= WEIGHTS.HAS_HISTORY;
|
|
122
|
-
factors.push({
|
|
123
|
-
name: 'history',
|
|
124
|
-
value: true,
|
|
125
|
-
impact: -WEIGHTS.HAS_HISTORY,
|
|
126
|
-
direction: 'autonomous',
|
|
127
|
-
reason: 'Project has been built before',
|
|
128
|
-
});
|
|
129
|
-
} else {
|
|
130
|
-
humanScore += WEIGHTS.HAS_HISTORY;
|
|
131
|
-
factors.push({
|
|
132
|
-
name: 'history',
|
|
133
|
-
value: false,
|
|
134
|
-
impact: WEIGHTS.HAS_HISTORY,
|
|
135
|
-
direction: 'human',
|
|
136
|
-
reason: 'First-time project execution',
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Factor 5: Recent gotchas
|
|
141
|
-
const recentGotchas = context.recentGotchas || 0;
|
|
142
|
-
if (recentGotchas > 0) {
|
|
143
|
-
const gotchaImpact = Math.min(recentGotchas * WEIGHTS.RECENT_GOTCHA, 45); // Cap at 3 gotchas
|
|
144
|
-
humanScore += gotchaImpact;
|
|
145
|
-
factors.push({
|
|
146
|
-
name: 'gotchas',
|
|
147
|
-
value: recentGotchas,
|
|
148
|
-
impact: gotchaImpact,
|
|
149
|
-
direction: 'human',
|
|
150
|
-
reason: `${recentGotchas} recent gotcha(s) detected`,
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Calculate confidence based on factor strength
|
|
155
|
-
const totalImpact = factors.reduce((sum, f) => sum + Math.abs(f.impact), 0);
|
|
156
|
-
const confidence = Math.min(Math.round((totalImpact / 150) * 100), 100);
|
|
157
|
-
|
|
158
|
-
// Determine suggestion
|
|
159
|
-
const suggestion = humanScore > 50
|
|
160
|
-
? EXECUTION_MODES.HUMAN_IN_THE_LOOP
|
|
161
|
-
: EXECUTION_MODES.AUTONOMOUS;
|
|
162
|
-
|
|
163
|
-
// Build reasoning
|
|
164
|
-
const reasoning = buildReasoning(suggestion, humanScore, factors);
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
suggestion,
|
|
168
|
-
confidence,
|
|
169
|
-
factors,
|
|
170
|
-
reasoning,
|
|
171
|
-
score: humanScore,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Calculate risk score from project context.
|
|
177
|
-
* @param {object} context
|
|
178
|
-
* @returns {{ score: number, level: 'low'|'medium'|'high', factors: string[] }}
|
|
179
|
-
*/
|
|
180
|
-
export function calculateRiskScore(context) {
|
|
181
|
-
const riskFactors = [];
|
|
182
|
-
let score = 0;
|
|
183
|
-
|
|
184
|
-
// Greenfield adds base risk
|
|
185
|
-
if (context.isGreenfield) {
|
|
186
|
-
score += 25;
|
|
187
|
-
riskFactors.push('greenfield project');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// High task count adds complexity risk
|
|
191
|
-
if (context.taskCount > 10) {
|
|
192
|
-
score += 20;
|
|
193
|
-
riskFactors.push(`high task count (${context.taskCount})`);
|
|
194
|
-
} else if (context.taskCount > 5) {
|
|
195
|
-
score += 10;
|
|
196
|
-
riskFactors.push(`moderate task count (${context.taskCount})`);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Risk domains
|
|
200
|
-
const riskDomains = context.riskDomains || [];
|
|
201
|
-
const highRiskCount = riskDomains.filter(domain =>
|
|
202
|
-
HIGH_RISK_DOMAINS.includes(domain.toLowerCase())
|
|
203
|
-
).length;
|
|
204
|
-
|
|
205
|
-
score += highRiskCount * 15;
|
|
206
|
-
if (highRiskCount > 0) {
|
|
207
|
-
riskFactors.push(`${highRiskCount} high-risk domain(s)`);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// No history adds risk
|
|
211
|
-
if (!context.hasHistory) {
|
|
212
|
-
score += 15;
|
|
213
|
-
riskFactors.push('no project history');
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Recent gotchas
|
|
217
|
-
if (context.recentGotchas > 0) {
|
|
218
|
-
score += Math.min(context.recentGotchas * 10, 30);
|
|
219
|
-
riskFactors.push(`${context.recentGotchas} recent gotcha(s)`);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Determine level
|
|
223
|
-
let level;
|
|
224
|
-
if (score < 30) {
|
|
225
|
-
level = 'low';
|
|
226
|
-
} else if (score < 60) {
|
|
227
|
-
level = 'medium';
|
|
228
|
-
} else {
|
|
229
|
-
level = 'high';
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
score,
|
|
234
|
-
level,
|
|
235
|
-
factors: riskFactors,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Build human-readable reasoning for mode suggestion.
|
|
241
|
-
* @param {string} suggestion
|
|
242
|
-
* @param {number} score
|
|
243
|
-
* @param {object[]} factors
|
|
244
|
-
* @returns {string}
|
|
245
|
-
*/
|
|
246
|
-
function buildReasoning(suggestion, score, factors) {
|
|
247
|
-
const lines = [];
|
|
248
|
-
|
|
249
|
-
if (suggestion === EXECUTION_MODES.HUMAN_IN_THE_LOOP) {
|
|
250
|
-
lines.push(`Recommending human-in-the-loop mode (score: ${Math.round(score)}/100 towards human).`);
|
|
251
|
-
} else {
|
|
252
|
-
lines.push(`Recommending autonomous mode (score: ${Math.round(score)}/100 towards human, below threshold).`);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const humanFactors = factors.filter(f => f.direction === 'human');
|
|
256
|
-
const autoFactors = factors.filter(f => f.direction === 'autonomous');
|
|
257
|
-
|
|
258
|
-
if (humanFactors.length > 0) {
|
|
259
|
-
lines.push('');
|
|
260
|
-
lines.push('Factors favoring human validation:');
|
|
261
|
-
humanFactors.forEach(f => {
|
|
262
|
-
lines.push(` • ${f.reason} (+${f.impact})`);
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (autoFactors.length > 0) {
|
|
267
|
-
lines.push('');
|
|
268
|
-
lines.push('Factors favoring autonomous execution:');
|
|
269
|
-
autoFactors.forEach(f => {
|
|
270
|
-
lines.push(` • ${f.reason} (${f.impact})`);
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return lines.join('\n');
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Get high-risk domain list.
|
|
279
|
-
* @returns {string[]}
|
|
280
|
-
*/
|
|
281
|
-
export function getHighRiskDomains() {
|
|
282
|
-
return [...HIGH_RISK_DOMAINS];
|
|
283
|
-
}
|
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Progress reporting for autonomous execution
|
|
3
|
-
* Reports progress to user without blocking the pipeline
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Build a progress report for the user.
|
|
8
|
-
* @param {object} pipelineState
|
|
9
|
-
* @param {object} [options] - { verbose: boolean }
|
|
10
|
-
* @returns {{ summary: string, details: object, progress: number }}
|
|
11
|
-
*/
|
|
12
|
-
export function buildProgressReport(pipelineState, options = {}) {
|
|
13
|
-
const { verbose = false } = options;
|
|
14
|
-
|
|
15
|
-
const progress = calculateCompletion(pipelineState);
|
|
16
|
-
const summary = verbose
|
|
17
|
-
? formatDetailedReport(pipelineState)
|
|
18
|
-
: formatProgressLine(pipelineState);
|
|
19
|
-
|
|
20
|
-
const details = {
|
|
21
|
-
currentAgent: pipelineState.current_agent,
|
|
22
|
-
currentStage: pipelineState.current_stage,
|
|
23
|
-
agentsCompleted: countCompletedAgents(pipelineState),
|
|
24
|
-
agentsTotal: countTotalAgents(pipelineState),
|
|
25
|
-
lastScore: pipelineState.last_score,
|
|
26
|
-
warnings: pipelineState.warnings || [],
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
summary,
|
|
31
|
-
details,
|
|
32
|
-
progress,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Format progress as a compact one-line status.
|
|
38
|
-
* Example: "[DISCOVER 2/3] brief ✅ → detail 🔄 (score: 92%)" or "[PLAN 3/6] phases 🔄 (score: 92%)"
|
|
39
|
-
* @param {object} pipelineState
|
|
40
|
-
* @returns {string}
|
|
41
|
-
*/
|
|
42
|
-
export function formatProgressLine(pipelineState) {
|
|
43
|
-
const stage = pipelineState.current_stage || 'INIT';
|
|
44
|
-
const currentAgent = pipelineState.current_agent || 'none';
|
|
45
|
-
const agentStatus = pipelineState.agent_status || {};
|
|
46
|
-
|
|
47
|
-
// Count completed vs total in current stage
|
|
48
|
-
const stageAgents = getStageAgents(stage);
|
|
49
|
-
const completed = stageAgents.filter(a => agentStatus[a] === 'completed').length;
|
|
50
|
-
const total = stageAgents.length;
|
|
51
|
-
|
|
52
|
-
// Build agent chain
|
|
53
|
-
const agentChain = buildAgentChain(stageAgents, agentStatus, currentAgent);
|
|
54
|
-
|
|
55
|
-
// Add score if available
|
|
56
|
-
const scoreInfo = pipelineState.last_score
|
|
57
|
-
? ` (score: ${pipelineState.last_score}%)`
|
|
58
|
-
: '';
|
|
59
|
-
|
|
60
|
-
return `[${stage} ${completed}/${total}] ${agentChain}${scoreInfo}`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Build a detailed multi-line report.
|
|
65
|
-
* @param {object} pipelineState
|
|
66
|
-
* @returns {string}
|
|
67
|
-
*/
|
|
68
|
-
export function formatDetailedReport(pipelineState) {
|
|
69
|
-
const lines = [];
|
|
70
|
-
const progress = calculateCompletion(pipelineState);
|
|
71
|
-
const stage = pipelineState.current_stage || 'INIT';
|
|
72
|
-
const agentStatus = pipelineState.agent_status || {};
|
|
73
|
-
|
|
74
|
-
lines.push(`Progress: ${progress}%`);
|
|
75
|
-
lines.push(`Stage: ${stage}`);
|
|
76
|
-
lines.push('');
|
|
77
|
-
|
|
78
|
-
// DISCOVER agents
|
|
79
|
-
lines.push('DISCOVER:');
|
|
80
|
-
const discoverAgents = getStageAgents('DISCOVER');
|
|
81
|
-
discoverAgents.forEach(agent => {
|
|
82
|
-
const status = agentStatus[agent] || 'pending';
|
|
83
|
-
const icon = getStatusIcon(status);
|
|
84
|
-
const isActive = agent === pipelineState.current_agent;
|
|
85
|
-
const prefix = isActive ? '→ ' : ' ';
|
|
86
|
-
lines.push(`${prefix}${icon} ${agent}`);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
lines.push('');
|
|
90
|
-
|
|
91
|
-
// PLAN agents
|
|
92
|
-
lines.push('PLAN:');
|
|
93
|
-
const planAgents = getStageAgents('PLAN');
|
|
94
|
-
planAgents.forEach(agent => {
|
|
95
|
-
const status = agentStatus[agent] || 'pending';
|
|
96
|
-
const icon = getStatusIcon(status);
|
|
97
|
-
const isActive = agent === pipelineState.current_agent;
|
|
98
|
-
const prefix = isActive ? '→ ' : ' ';
|
|
99
|
-
lines.push(`${prefix}${icon} ${agent}`);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
lines.push('');
|
|
103
|
-
|
|
104
|
-
// BUILD agents
|
|
105
|
-
lines.push('BUILD:');
|
|
106
|
-
const buildAgents = getStageAgents('BUILD');
|
|
107
|
-
buildAgents.forEach(agent => {
|
|
108
|
-
const status = agentStatus[agent] || 'pending';
|
|
109
|
-
const icon = getStatusIcon(status);
|
|
110
|
-
const isActive = agent === pipelineState.current_agent;
|
|
111
|
-
const prefix = isActive ? '→ ' : ' ';
|
|
112
|
-
lines.push(`${prefix}${icon} ${agent}`);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
lines.push('');
|
|
116
|
-
|
|
117
|
-
// DEPLOY agents
|
|
118
|
-
lines.push('DEPLOY:');
|
|
119
|
-
const deployAgents = getStageAgents('DEPLOY');
|
|
120
|
-
deployAgents.forEach(agent => {
|
|
121
|
-
const status = agentStatus[agent] || 'pending';
|
|
122
|
-
const icon = getStatusIcon(status);
|
|
123
|
-
const isActive = agent === pipelineState.current_agent;
|
|
124
|
-
const prefix = isActive ? '→ ' : ' ';
|
|
125
|
-
lines.push(`${prefix}${icon} ${agent}`);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// Add warnings if any
|
|
129
|
-
if (pipelineState.warnings && pipelineState.warnings.length > 0) {
|
|
130
|
-
lines.push('');
|
|
131
|
-
lines.push('Warnings:');
|
|
132
|
-
pipelineState.warnings.forEach(warning => {
|
|
133
|
-
lines.push(` ⚠ ${warning}`);
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return lines.join('\n');
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Calculate overall pipeline completion percentage.
|
|
142
|
-
* @param {object} pipelineState
|
|
143
|
-
* @returns {number} 0-100
|
|
144
|
-
*/
|
|
145
|
-
export function calculateCompletion(pipelineState) {
|
|
146
|
-
const agentStatus = pipelineState.agent_status || {};
|
|
147
|
-
const allAgents = getAllAgents();
|
|
148
|
-
|
|
149
|
-
const completed = Object.values(agentStatus).filter(s => s === 'completed').length;
|
|
150
|
-
const total = allAgents.length;
|
|
151
|
-
|
|
152
|
-
return total > 0 ? Math.round((completed / total) * 100) : 0;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Get agents for a specific stage.
|
|
157
|
-
* @param {string} stage
|
|
158
|
-
* @returns {string[]}
|
|
159
|
-
*/
|
|
160
|
-
function getStageAgents(stage) {
|
|
161
|
-
const stages = {
|
|
162
|
-
DISCOVER: ['greenfield-wu', 'brownfield-wu', 'brief'],
|
|
163
|
-
PLAN: ['detail', 'architect', 'ux', 'phases', 'tasks'], // qa-planning runs inside Planning Team; score synced via team dissolution
|
|
164
|
-
BUILD: ['dev', 'qa-implementation'],
|
|
165
|
-
DEPLOY: ['devops'],
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
return stages[stage] || [];
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Get all agents in order.
|
|
173
|
-
* @returns {string[]}
|
|
174
|
-
*/
|
|
175
|
-
function getAllAgents() {
|
|
176
|
-
return [
|
|
177
|
-
...getStageAgents('DISCOVER'),
|
|
178
|
-
...getStageAgents('PLAN'),
|
|
179
|
-
...getStageAgents('BUILD'),
|
|
180
|
-
...getStageAgents('DEPLOY'),
|
|
181
|
-
];
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Build agent chain visualization.
|
|
186
|
-
* @param {string[]} agents
|
|
187
|
-
* @param {object} agentStatus
|
|
188
|
-
* @param {string} currentAgent
|
|
189
|
-
* @returns {string}
|
|
190
|
-
*/
|
|
191
|
-
function buildAgentChain(agents, agentStatus, currentAgent) {
|
|
192
|
-
const parts = [];
|
|
193
|
-
|
|
194
|
-
for (let i = 0; i < agents.length; i++) {
|
|
195
|
-
const agent = agents[i];
|
|
196
|
-
const status = agentStatus[agent] || 'pending';
|
|
197
|
-
const isActive = agent === currentAgent;
|
|
198
|
-
|
|
199
|
-
if (status === 'completed') {
|
|
200
|
-
parts.push(`${agent} ✅`);
|
|
201
|
-
} else if (isActive) {
|
|
202
|
-
parts.push(`${agent} 🔄`);
|
|
203
|
-
} else if (status === 'failed') {
|
|
204
|
-
parts.push(`${agent} ❌`);
|
|
205
|
-
} else {
|
|
206
|
-
// Don't show pending agents in compact view
|
|
207
|
-
break;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (i < agents.length - 1 && (status === 'completed' || isActive)) {
|
|
211
|
-
parts.push('→');
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return parts.join(' ');
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Get status icon for agent.
|
|
220
|
-
* @param {string} status
|
|
221
|
-
* @returns {string}
|
|
222
|
-
*/
|
|
223
|
-
function getStatusIcon(status) {
|
|
224
|
-
const icons = {
|
|
225
|
-
completed: '✅',
|
|
226
|
-
in_progress: '🔄',
|
|
227
|
-
failed: '❌',
|
|
228
|
-
pending: '⏸',
|
|
229
|
-
skipped: '⏭',
|
|
230
|
-
preview: '👁',
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
return icons[status] || '⏸';
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Count completed agents.
|
|
238
|
-
* @param {object} pipelineState
|
|
239
|
-
* @returns {number}
|
|
240
|
-
*/
|
|
241
|
-
function countCompletedAgents(pipelineState) {
|
|
242
|
-
const agentStatus = pipelineState.agent_status || {};
|
|
243
|
-
return Object.values(agentStatus).filter(s => s === 'completed').length;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Count total agents.
|
|
248
|
-
* @param {object} _pipelineState
|
|
249
|
-
* @returns {number}
|
|
250
|
-
*/
|
|
251
|
-
function countTotalAgents(_pipelineState) {
|
|
252
|
-
return getAllAgents().length;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Get progress summary by stage.
|
|
257
|
-
* @param {object} pipelineState
|
|
258
|
-
* @returns {{ discover: number, plan: number, build: number, deploy: number }}
|
|
259
|
-
*/
|
|
260
|
-
export function getStageProgress(pipelineState) {
|
|
261
|
-
const agentStatus = pipelineState.agent_status || {};
|
|
262
|
-
|
|
263
|
-
const getProgress = (stage) => {
|
|
264
|
-
const agents = getStageAgents(stage);
|
|
265
|
-
const completed = agents.filter(a => agentStatus[a] === 'completed').length;
|
|
266
|
-
return agents.length > 0 ? Math.round((completed / agents.length) * 100) : 0;
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
return {
|
|
270
|
-
discover: getProgress('DISCOVER'),
|
|
271
|
-
plan: getProgress('PLAN'),
|
|
272
|
-
build: getProgress('BUILD'),
|
|
273
|
-
deploy: getProgress('DEPLOY'),
|
|
274
|
-
};
|
|
275
|
-
}
|