chati-dev 4.3.0 → 4.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.
Files changed (156) hide show
  1. package/README.md +18 -13
  2. package/bin/chati.js +32 -4
  3. package/framework/agents/build/dev.md +9 -5
  4. package/framework/agents/discover/brief.md +2 -0
  5. package/framework/agents/discover/brownfield-wu.md +2 -0
  6. package/framework/agents/discover/greenfield-wu.md +18 -1
  7. package/framework/agents/plan/detail.md +2 -0
  8. package/framework/agents/plan/tasks.md +31 -0
  9. package/framework/agents/plan/ux-brand-architect.md +21 -2
  10. package/framework/agents/plan/ux-component-engineer.md +10 -0
  11. package/framework/agents/quality/qa-implementation.md +1 -1
  12. package/framework/agents/quality/qa-planning.md +1 -1
  13. package/framework/agents/quality/qa-visual.md +33 -21
  14. package/framework/config.yaml +5 -4
  15. package/framework/constitution.md +58 -11
  16. package/framework/context/governance.md +12 -10
  17. package/framework/context/root.md +2 -2
  18. package/framework/data/entity-registry.yaml +12 -4
  19. package/framework/data/qa-rubrics.yaml +335 -0
  20. package/framework/domains/constitution.yaml +3 -3
  21. package/framework/executors/json-validate.js +80 -0
  22. package/framework/executors/npm-script.js +72 -0
  23. package/framework/executors/path-check.js +67 -0
  24. package/framework/executors/registry.yaml +36 -0
  25. package/framework/hooks/advance-trigger.js +47 -3
  26. package/framework/hooks/constitution-guard.js +9 -4
  27. package/framework/hooks/git-push-authority.js +113 -0
  28. package/framework/hooks/license-guard.js +59 -9
  29. package/framework/hooks/model-governance.js +40 -17
  30. package/framework/hooks/prism-engine.js +78 -11
  31. package/framework/hooks/session-digest.js +12 -3
  32. package/framework/hooks/settings.json +7 -3
  33. package/framework/hooks/style-guard.js +52 -3
  34. package/framework/hooks/team-quality-gate.js +43 -5
  35. package/framework/i18n/en.yaml +3 -3
  36. package/framework/i18n/es.yaml +3 -3
  37. package/framework/i18n/fr.yaml +3 -3
  38. package/framework/i18n/pt.yaml +3 -3
  39. package/framework/intelligence/confidence.yaml +85 -6
  40. package/framework/intelligence/context-engine.md +9 -5
  41. package/framework/intelligence/decision-engine.md +1 -1
  42. package/framework/orchestrator/chati-router.js +59 -5
  43. package/framework/orchestrator/chati-update.md +19 -3
  44. package/framework/orchestrator/chati.md +20 -1
  45. package/framework/quality-gates/planning-gate.md +2 -0
  46. package/framework/rules/human-writing-style.md +47 -0
  47. package/framework/scaffold/motion-premium/README.md +12 -2
  48. package/framework/scaffold/motion-premium/lib/animations/tokens.ts.template +1 -1
  49. package/framework/scaffold/motion-premium/scaffold.yaml +6 -1
  50. package/framework/scaffold/motion-premium-3d/README.md +10 -0
  51. package/framework/scaffold/motion-premium-3d/components/webgl/WebGLContext.tsx.template +1 -1
  52. package/framework/scaffold/motion-premium-3d/scaffold.yaml +3 -0
  53. package/framework/scaffold/saas-dashboard/README.md +58 -0
  54. package/framework/scaffold/saas-dashboard/app/dashboard/page.tsx.template +546 -0
  55. package/framework/scaffold/saas-dashboard/app/globals.css.template +746 -0
  56. package/framework/scaffold/saas-dashboard/app/layout.tsx.template +192 -0
  57. package/framework/scaffold/saas-dashboard/app/shell.css.template +1070 -0
  58. package/framework/scaffold/saas-dashboard/components/data/Chart.tsx.template +725 -0
  59. package/framework/scaffold/saas-dashboard/components/data/DataTable.tsx.template +623 -0
  60. package/framework/scaffold/saas-dashboard/components/data/DataTable.types.ts.template +104 -0
  61. package/framework/scaffold/saas-dashboard/components/data/DataTablePagination.tsx.template +234 -0
  62. package/framework/scaffold/saas-dashboard/components/data/SkeletonBlock.tsx.template +111 -0
  63. package/framework/scaffold/saas-dashboard/components/data/StatCard.tsx.template +393 -0
  64. package/framework/scaffold/saas-dashboard/components/data/StatCardGrid.tsx.template +121 -0
  65. package/framework/scaffold/saas-dashboard/components/feedback/EmptyState.tsx.template +281 -0
  66. package/framework/scaffold/saas-dashboard/components/interaction/CommandPalette.tsx.template +767 -0
  67. package/framework/scaffold/saas-dashboard/components/interaction/Field.tsx.template +351 -0
  68. package/framework/scaffold/saas-dashboard/components/interaction/Form.tsx.template +424 -0
  69. package/framework/scaffold/saas-dashboard/components/primitives/Badge.tsx.template +193 -0
  70. package/framework/scaffold/saas-dashboard/components/primitives/Button.tsx.template +261 -0
  71. package/framework/scaffold/saas-dashboard/components/primitives/Card.tsx.template +287 -0
  72. package/framework/scaffold/saas-dashboard/components/primitives/Input.tsx.template +310 -0
  73. package/framework/scaffold/saas-dashboard/components/primitives/Spinner.tsx.template +135 -0
  74. package/framework/scaffold/saas-dashboard/components/primitives/index.ts.template +60 -0
  75. package/framework/scaffold/saas-dashboard/components/shell/AppShell.tsx.template +174 -0
  76. package/framework/scaffold/saas-dashboard/components/shell/Breadcrumb.tsx.template +186 -0
  77. package/framework/scaffold/saas-dashboard/components/shell/Container.tsx.template +86 -0
  78. package/framework/scaffold/saas-dashboard/components/shell/NavItem.tsx.template +147 -0
  79. package/framework/scaffold/saas-dashboard/components/shell/Sidebar.tsx.template +378 -0
  80. package/framework/scaffold/saas-dashboard/components/shell/SidebarContext.tsx.template +169 -0
  81. package/framework/scaffold/saas-dashboard/components/shell/ThemeToggle.tsx.template +128 -0
  82. package/framework/scaffold/saas-dashboard/components/shell/Topbar.tsx.template +301 -0
  83. package/framework/scaffold/saas-dashboard/lib/brand.ts.template +324 -0
  84. package/framework/scaffold/saas-dashboard/scaffold.yaml +225 -0
  85. package/framework/schemas/session.schema.json +5 -0
  86. package/framework/schemas/task.schema.json +9 -0
  87. package/framework/scripts/visual-qa.js +101 -3
  88. package/framework/tasks/orchestrator-health.md +4 -4
  89. package/framework/tasks/qa-impl-verdict.md +11 -5
  90. package/framework/templates/brandbook-html-tmpl.md +1 -1
  91. package/framework/templates/qa-gate-tmpl.yaml +24 -11
  92. package/package.json +4 -3
  93. package/src/config/context-file-generator.js +0 -6
  94. package/src/dashboard/renderer.js +0 -36
  95. package/src/executors/runner.js +204 -0
  96. package/src/installer/core.js +13 -3
  97. package/src/installer/templates.js +5 -6
  98. package/src/intelligence/registry-manager.js +1 -1
  99. package/src/license/client.js +9 -3
  100. package/src/license/commands.js +12 -2
  101. package/src/license/machine-id.js +42 -1
  102. package/src/memory/gotchas.js +58 -40
  103. package/src/memory/magic-docs.js +1 -1
  104. package/src/memory/session-digest.js +9 -4
  105. package/src/orchestrator/cli.js +186 -15
  106. package/src/orchestrator/doctor.js +98 -16
  107. package/src/orchestrator/pipeline-manager.js +44 -22
  108. package/src/orchestrator/session-manager.js +64 -8
  109. package/src/telemetry/sender.js +7 -19
  110. package/src/terminal/run-team.js +3 -3
  111. package/src/upgrade/tracked-files-detector.js +34 -16
  112. package/src/utils/feature-flags.js +1 -1
  113. package/src/utils/flatten-entities.js +4 -40
  114. package/src/utils/schema-validator.js +0 -14
  115. package/src/wizard/i18n.js +3 -3
  116. package/src/api/index.js +0 -120
  117. package/src/autonomy/autonomous-gate.js +0 -294
  118. package/src/autonomy/build-loop.js +0 -281
  119. package/src/autonomy/build-state.js +0 -286
  120. package/src/autonomy/cause-analyzer.js +0 -177
  121. package/src/autonomy/escalation.js +0 -214
  122. package/src/autonomy/index.js +0 -51
  123. package/src/autonomy/mode-manager.js +0 -225
  124. package/src/autonomy/mode-suggester.js +0 -283
  125. package/src/autonomy/progress-reporter.js +0 -275
  126. package/src/autonomy/safety-net.js +0 -370
  127. package/src/config/agent-customizer.js +0 -231
  128. package/src/decision/analyzer.js +0 -291
  129. package/src/decision/engine.js +0 -250
  130. package/src/decision/index.js +0 -38
  131. package/src/decision/registry-healer.js +0 -468
  132. package/src/decision/registry-updater.js +0 -339
  133. package/src/extensions/loader.js +0 -145
  134. package/src/extensions/registry.js +0 -134
  135. package/src/gates/circuit-breaker.js +0 -151
  136. package/src/gates/g1-planning-complete.js +0 -154
  137. package/src/gates/g2-qa-planning.js +0 -156
  138. package/src/gates/g3-implementation.js +0 -215
  139. package/src/gates/g4-qa-implementation.js +0 -240
  140. package/src/gates/g5-deploy-ready.js +0 -181
  141. package/src/gates/gate-base.js +0 -185
  142. package/src/gates/index.js +0 -46
  143. package/src/health/auto-fix.js +0 -216
  144. package/src/health/engine.js +0 -246
  145. package/src/merger/semantic-merger.js +0 -292
  146. package/src/preview/detector.js +0 -238
  147. package/src/preview/index.js +0 -20
  148. package/src/preview/launcher.js +0 -235
  149. package/src/preview/log-buffer.js +0 -103
  150. package/src/quality/metrics-collector.js +0 -281
  151. package/src/quality/test-runner.js +0 -366
  152. package/src/tasks/executor.js +0 -195
  153. package/src/tasks/index.js +0 -4
  154. package/src/tasks/loader.js +0 -210
  155. package/src/tasks/router.js +0 -182
  156. package/src/utils/event-bus.js +0 -126
@@ -1,51 +0,0 @@
1
- /**
2
- * @fileoverview Autonomy module barrel exports
3
- * Execution modes, quality gates, progress reporting, and safety nets
4
- */
5
-
6
- export {
7
- EXECUTION_MODES,
8
- getCurrentMode,
9
- setExecutionMode,
10
- canActAutonomously,
11
- getAlwaysHumanAgents,
12
- getModeHistory,
13
- clearExecutionMode,
14
- getModeStatistics,
15
- } from './mode-manager.js';
16
-
17
- export {
18
- suggestMode,
19
- calculateRiskScore,
20
- getHighRiskDomains,
21
- } from './mode-suggester.js';
22
-
23
- export {
24
- GATE_RESULTS,
25
- evaluateGate,
26
- getGateThreshold,
27
- resolveGateAction,
28
- getGateHistory,
29
- recordGateEvaluation,
30
- getGateStatistics,
31
- getAgentGateStatistics,
32
- clearGateHistory,
33
- } from './autonomous-gate.js';
34
-
35
- export {
36
- buildProgressReport,
37
- formatProgressLine,
38
- formatDetailedReport,
39
- calculateCompletion,
40
- getStageProgress,
41
- } from './progress-reporter.js';
42
-
43
- export {
44
- SAFETY_TRIGGERS,
45
- checkSafety,
46
- evaluateTrigger,
47
- getRecommendedAction,
48
- getCriticalRiskKeywords,
49
- isCriticalRisk,
50
- buildSafetyReport,
51
- } from './safety-net.js';
@@ -1,225 +0,0 @@
1
- /**
2
- * @fileoverview Manages execution mode transitions for chati.dev
3
- * Handles human-in-the-loop vs autonomous execution modes
4
- */
5
-
6
- import { loadSession, updateSession } from '../orchestrator/session-manager.js';
7
-
8
- export const EXECUTION_MODES = {
9
- HUMAN_IN_THE_LOOP: 'human-in-the-loop',
10
- AUTONOMOUS: 'autonomous',
11
- };
12
-
13
- // Agents that ALWAYS require human validation
14
- const ALWAYS_HUMAN_AGENTS = ['brief', 'deviation'];
15
-
16
- /**
17
- * Get current execution mode from session.
18
- * @param {string} projectDir
19
- * @returns {{ mode: string, setAt: string|null, reason: string|null }}
20
- */
21
- export function getCurrentMode(projectDir) {
22
- const result = loadSession(projectDir);
23
-
24
- if (!result.loaded || !result.session) {
25
- return {
26
- mode: EXECUTION_MODES.HUMAN_IN_THE_LOOP,
27
- setAt: null,
28
- reason: null,
29
- };
30
- }
31
-
32
- const session = result.session;
33
-
34
- if (!session.execution_mode) {
35
- return {
36
- mode: EXECUTION_MODES.HUMAN_IN_THE_LOOP,
37
- setAt: null,
38
- reason: null,
39
- };
40
- }
41
-
42
- return {
43
- mode: session.execution_mode.mode || EXECUTION_MODES.HUMAN_IN_THE_LOOP,
44
- setAt: session.execution_mode.setAt || null,
45
- reason: session.execution_mode.reason || null,
46
- };
47
- }
48
-
49
- /**
50
- * Set execution mode with reason and persist to session.
51
- * @param {string} projectDir
52
- * @param {string} mode - 'human-in-the-loop' | 'autonomous'
53
- * @param {string} reason - Why this mode was selected
54
- * @returns {{ set: boolean, previous: string }}
55
- */
56
- export function setExecutionMode(projectDir, mode, reason) {
57
- if (!Object.values(EXECUTION_MODES).includes(mode)) {
58
- throw new Error(`Invalid execution mode: ${mode}`);
59
- }
60
-
61
- const result = loadSession(projectDir);
62
-
63
- if (!result.loaded || !result.session) {
64
- return {
65
- set: false,
66
- previous: EXECUTION_MODES.HUMAN_IN_THE_LOOP,
67
- error: result.error || 'Failed to load session',
68
- };
69
- }
70
-
71
- const session = result.session;
72
- const previous = session.execution_mode?.mode || EXECUTION_MODES.HUMAN_IN_THE_LOOP;
73
-
74
- // Initialize mode_transitions array if it doesn't exist
75
- if (!session.mode_transitions) {
76
- session.mode_transitions = [];
77
- }
78
-
79
- // Record the transition
80
- const transition = {
81
- from: previous,
82
- to: mode,
83
- timestamp: new Date().toISOString(),
84
- reason,
85
- };
86
-
87
- session.mode_transitions.push(transition);
88
-
89
- // Update current mode
90
- const updates = {
91
- execution_mode: {
92
- mode,
93
- setAt: transition.timestamp,
94
- reason,
95
- },
96
- mode_transitions: session.mode_transitions,
97
- };
98
-
99
- updateSession(projectDir, updates);
100
-
101
- return {
102
- set: true,
103
- previous,
104
- };
105
- }
106
-
107
- /**
108
- * Check if current mode allows autonomous action for a given agent.
109
- * Even in autonomous mode, some agents always require human input (brief, deviation).
110
- * @param {string} mode
111
- * @param {string} agentName
112
- * @returns {{ allowed: boolean, reason: string }}
113
- */
114
- export function canActAutonomously(mode, agentName) {
115
- // Brief and deviation ALWAYS require human input
116
- if (ALWAYS_HUMAN_AGENTS.includes(agentName)) {
117
- return {
118
- allowed: false,
119
- reason: `${agentName} always requires human interaction`,
120
- };
121
- }
122
-
123
- // In human-in-the-loop mode, nothing is autonomous
124
- if (mode === EXECUTION_MODES.HUMAN_IN_THE_LOOP) {
125
- return {
126
- allowed: false,
127
- reason: 'Human-in-the-loop mode requires user validation',
128
- };
129
- }
130
-
131
- // In autonomous mode, all non-always-human agents can act autonomously
132
- return {
133
- allowed: true,
134
- reason: 'Autonomous mode enabled for this agent',
135
- };
136
- }
137
-
138
- /**
139
- * Get agents that ALWAYS require human validation regardless of mode.
140
- * @returns {string[]}
141
- */
142
- export function getAlwaysHumanAgents() {
143
- return [...ALWAYS_HUMAN_AGENTS];
144
- }
145
-
146
- /**
147
- * Get mode transition history.
148
- * @param {string} projectDir
149
- * @returns {object[]}
150
- */
151
- export function getModeHistory(projectDir) {
152
- const result = loadSession(projectDir);
153
- if (!result.loaded || !result.session) {
154
- return [];
155
- }
156
- return result.session.mode_transitions || [];
157
- }
158
-
159
- /**
160
- * Clear execution mode (reset to default).
161
- * @param {string} projectDir
162
- * @returns {{ cleared: boolean }}
163
- */
164
- export function clearExecutionMode(projectDir) {
165
- const result = loadSession(projectDir);
166
-
167
- if (!result.loaded || !result.session) {
168
- return {
169
- cleared: false,
170
- error: result.error || 'Failed to load session',
171
- };
172
- }
173
-
174
- const previous = result.session.execution_mode;
175
-
176
- updateSession(projectDir, { execution_mode: null });
177
-
178
- return {
179
- cleared: true,
180
- previous: previous || null,
181
- };
182
- }
183
-
184
- /**
185
- * Get mode statistics from history.
186
- * @param {string} projectDir
187
- * @returns {{ totalTransitions: number, timeInAutonomous: number, timeInHuman: number, currentStreak: number }}
188
- */
189
- export function getModeStatistics(projectDir) {
190
- const history = getModeHistory(projectDir);
191
- const current = getCurrentMode(projectDir);
192
-
193
- let timeInAutonomous = 0;
194
- let timeInHuman = 0;
195
- let currentStreak = 0;
196
-
197
- for (let i = 0; i < history.length; i++) {
198
- const transition = history[i];
199
- const nextTransition = history[i + 1];
200
-
201
- // Calculate time in each mode
202
- if (nextTransition) {
203
- const duration = new Date(nextTransition.timestamp) - new Date(transition.timestamp);
204
- if (transition.to === EXECUTION_MODES.AUTONOMOUS) {
205
- timeInAutonomous += duration;
206
- } else {
207
- timeInHuman += duration;
208
- }
209
- }
210
-
211
- // Calculate current streak
212
- if (transition.to === current.mode) {
213
- currentStreak++;
214
- } else {
215
- currentStreak = 0;
216
- }
217
- }
218
-
219
- return {
220
- totalTransitions: history.length,
221
- timeInAutonomous: Math.floor(timeInAutonomous / 1000), // seconds
222
- timeInHuman: Math.floor(timeInHuman / 1000), // seconds
223
- currentStreak,
224
- };
225
- }
@@ -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
- }