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,210 +0,0 @@
1
- /**
2
- * Task Loader — Parses task definition files (YAML frontmatter + markdown body).
3
- *
4
- * Task files live in chati.dev/tasks/{agent}-{action}.md and contain:
5
- * - YAML frontmatter: id, agent, trigger, phase, requires_input, parallelizable, outputs, handoff_to, autonomous_gate, criteria
6
- * - Markdown body: Step-by-step instructions for the agent
7
- */
8
-
9
- import { existsSync, readFileSync, readdirSync } from 'fs';
10
- import { join } from 'path';
11
-
12
- /**
13
- * Parse a single task definition file.
14
- * @param {string} filePath - Absolute path to .md file with YAML frontmatter
15
- * @returns {{ loaded: boolean, task: object|null, error: string|null }}
16
- */
17
- export function loadTask(filePath) {
18
- if (!existsSync(filePath)) {
19
- return { loaded: false, task: null, error: `Task file not found: ${filePath}` };
20
- }
21
-
22
- try {
23
- const raw = readFileSync(filePath, 'utf-8');
24
- return parseTaskContent(raw, filePath);
25
- } catch (err) {
26
- return { loaded: false, task: null, error: `Failed to read ${filePath}: ${err.message}` };
27
- }
28
- }
29
-
30
- /**
31
- * Parse task content string (YAML frontmatter + markdown body).
32
- * @param {string} content - Raw file content
33
- * @param {string} [source] - Source file path for error messages
34
- * @returns {{ loaded: boolean, task: object|null, error: string|null }}
35
- */
36
- export function parseTaskContent(content, source = 'unknown') {
37
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
38
-
39
- if (!frontmatterMatch) {
40
- return { loaded: false, task: null, error: `No YAML frontmatter found in ${source}` };
41
- }
42
-
43
- const yamlStr = frontmatterMatch[1];
44
- const body = frontmatterMatch[2].trim();
45
-
46
- try {
47
- const meta = parseSimpleYaml(yamlStr);
48
-
49
- if (!meta.id) {
50
- return { loaded: false, task: null, error: `Missing required field 'id' in ${source}` };
51
- }
52
- if (!meta.agent) {
53
- return { loaded: false, task: null, error: `Missing required field 'agent' in ${source}` };
54
- }
55
-
56
- return {
57
- loaded: true,
58
- task: {
59
- id: meta.id,
60
- agent: meta.agent,
61
- trigger: meta.trigger || 'orchestrator',
62
- phase: meta.phase || 'plan',
63
- requires_input: meta.requires_input === true || meta.requires_input === 'true',
64
- parallelizable: meta.parallelizable === true || meta.parallelizable === 'true',
65
- outputs: parseArray(meta.outputs),
66
- handoff_to: meta.handoff_to || null,
67
- autonomous_gate: meta.autonomous_gate !== false && meta.autonomous_gate !== 'false',
68
- criteria: parseArray(meta.criteria),
69
- instructions: body,
70
- source,
71
- },
72
- error: null,
73
- };
74
- } catch (err) {
75
- return { loaded: false, task: null, error: `YAML parse error in ${source}: ${err.message}` };
76
- }
77
- }
78
-
79
- /**
80
- * Load all task definitions from a directory.
81
- * @param {string} tasksDir - Path to chati.dev/tasks/
82
- * @returns {{ tasks: Map<string, object>, errors: string[] }}
83
- */
84
- export function loadAllTasks(tasksDir) {
85
- const tasks = new Map();
86
- const errors = [];
87
-
88
- if (!existsSync(tasksDir)) {
89
- errors.push(`Tasks directory not found: ${tasksDir}`);
90
- return { tasks, errors };
91
- }
92
-
93
- const files = readdirSync(tasksDir).filter(f => f.endsWith('.md'));
94
-
95
- for (const file of files) {
96
- const result = loadTask(join(tasksDir, file));
97
- if (result.loaded) {
98
- tasks.set(result.task.id, result.task);
99
- } else {
100
- errors.push(result.error);
101
- }
102
- }
103
-
104
- return { tasks, errors };
105
- }
106
-
107
- /**
108
- * Get all tasks for a specific agent.
109
- * @param {Map<string, object>} taskMap - Map from loadAllTasks
110
- * @param {string} agentName - Agent name to filter by
111
- * @returns {object[]}
112
- */
113
- export function getAgentTasks(taskMap, agentName) {
114
- const result = [];
115
- for (const task of taskMap.values()) {
116
- if (task.agent === agentName) {
117
- result.push(task);
118
- }
119
- }
120
- return result;
121
- }
122
-
123
- /**
124
- * Get a task's info summary (without instructions body).
125
- * @param {object} task - Task object from loadTask
126
- * @returns {object}
127
- */
128
- export function getTaskSummary(task) {
129
- return {
130
- id: task.id,
131
- agent: task.agent,
132
- phase: task.phase,
133
- parallelizable: task.parallelizable,
134
- outputs: task.outputs,
135
- handoff_to: task.handoff_to,
136
- criteria_count: task.criteria.length,
137
- };
138
- }
139
-
140
- /**
141
- * Simple YAML parser for frontmatter (no external deps).
142
- * Handles key: value, key: [array], and key: true/false.
143
- */
144
- function parseSimpleYaml(str) {
145
- const result = {};
146
- let currentKey = null;
147
- let currentArray = null;
148
-
149
- for (const line of str.split('\n')) {
150
- const trimmed = line.trim();
151
- if (!trimmed || trimmed.startsWith('#')) continue;
152
-
153
- // Array item under current key
154
- if (trimmed.startsWith('- ') && currentKey && currentArray) {
155
- currentArray.push(trimmed.slice(2).trim());
156
- continue;
157
- }
158
-
159
- // Key: value pair
160
- const kvMatch = trimmed.match(/^(\w[\w_-]*):\s*(.*)$/);
161
- if (kvMatch) {
162
- // Save previous array if any
163
- if (currentKey && currentArray) {
164
- result[currentKey] = currentArray;
165
- }
166
-
167
- const key = kvMatch[1];
168
- const val = kvMatch[2].trim();
169
-
170
- if (val === '') {
171
- // Could be start of an array
172
- currentKey = key;
173
- currentArray = [];
174
- } else if (val.startsWith('[') && val.endsWith(']')) {
175
- // Inline array: [a, b, c]
176
- result[key] = val.slice(1, -1).split(',').map(s => s.trim()).filter(Boolean);
177
- currentKey = null;
178
- currentArray = null;
179
- } else if (val === 'true') {
180
- result[key] = true;
181
- currentKey = null;
182
- currentArray = null;
183
- } else if (val === 'false') {
184
- result[key] = false;
185
- currentKey = null;
186
- currentArray = null;
187
- } else {
188
- result[key] = val;
189
- currentKey = null;
190
- currentArray = null;
191
- }
192
- }
193
- }
194
-
195
- // Save trailing array
196
- if (currentKey && currentArray) {
197
- result[currentKey] = currentArray;
198
- }
199
-
200
- return result;
201
- }
202
-
203
- /**
204
- * Ensure a value is an array.
205
- */
206
- function parseArray(val) {
207
- if (Array.isArray(val)) return val;
208
- if (typeof val === 'string') return [val];
209
- return [];
210
- }
@@ -1,182 +0,0 @@
1
- /**
2
- * Task Router — Maps orchestrator intents to the correct task definition.
3
- *
4
- * The router is used internally by the orchestrator to determine
5
- * which task an agent should execute based on:
6
- * - Current pipeline position
7
- * - Agent state
8
- * - User intent signals
9
- */
10
-
11
- import { loadAllTasks, getAgentTasks } from './loader.js';
12
-
13
- /**
14
- * Task Router instance.
15
- * Holds the loaded task registry and provides routing methods.
16
- */
17
- export class TaskRouter {
18
- /**
19
- * @param {string} tasksDir - Path to chati.dev/tasks/
20
- */
21
- constructor(tasksDir) {
22
- this.tasksDir = tasksDir;
23
- this.taskMap = new Map();
24
- this.errors = [];
25
- this.loaded = false;
26
- }
27
-
28
- /**
29
- * Initialize the router by loading all task definitions.
30
- * @returns {this}
31
- */
32
- load() {
33
- const result = loadAllTasks(this.tasksDir);
34
- this.taskMap = result.tasks;
35
- this.errors = result.errors;
36
- this.loaded = true;
37
- return this;
38
- }
39
-
40
- /**
41
- * Route to the correct task for an agent at a given pipeline position.
42
- *
43
- * Routing priority:
44
- * 1. Exact task ID match (if provided)
45
- * 2. Agent's consolidate task (if pipeline position = consolidate)
46
- * 3. Agent's first non-consolidate task (default entry point)
47
- *
48
- * @param {object} intent
49
- * @param {string} intent.agent - Target agent name
50
- * @param {string} [intent.taskId] - Explicit task ID (highest priority)
51
- * @param {string} [intent.action] - Action keyword (e.g., 'consolidate', 'extract')
52
- * @param {string} [intent.phase] - Pipeline phase filter
53
- * @returns {{ found: boolean, task: object|null, reason: string }}
54
- */
55
- route(intent) {
56
- if (!this.loaded) {
57
- return { found: false, task: null, reason: 'Router not loaded. Call load() first.' };
58
- }
59
-
60
- if (!intent || !intent.agent) {
61
- return { found: false, task: null, reason: 'No agent specified in routing intent.' };
62
- }
63
-
64
- // 1. Exact task ID match
65
- if (intent.taskId) {
66
- const task = this.taskMap.get(intent.taskId);
67
- if (task) {
68
- return { found: true, task, reason: `Exact match: ${intent.taskId}` };
69
- }
70
- return { found: false, task: null, reason: `Task ID '${intent.taskId}' not found in registry.` };
71
- }
72
-
73
- // Get all tasks for this agent
74
- const agentTasks = getAgentTasks(this.taskMap, intent.agent);
75
- if (agentTasks.length === 0) {
76
- return { found: false, task: null, reason: `No tasks registered for agent '${intent.agent}'.` };
77
- }
78
-
79
- // Filter by phase if provided
80
- let candidates = agentTasks;
81
- if (intent.phase) {
82
- const phaseFiltered = agentTasks.filter(t => t.phase === intent.phase);
83
- if (phaseFiltered.length > 0) {
84
- candidates = phaseFiltered;
85
- }
86
- }
87
-
88
- // 2. Action keyword match
89
- if (intent.action) {
90
- const actionTask = candidates.find(t =>
91
- t.id.includes(intent.action) || t.id.endsWith(`-${intent.action}`)
92
- );
93
- if (actionTask) {
94
- return { found: true, task: actionTask, reason: `Action match: ${intent.action}` };
95
- }
96
- }
97
-
98
- // 3. Consolidate task (if action is consolidate or implied)
99
- if (intent.action === 'consolidate') {
100
- const consolidateTask = candidates.find(t => t.id.endsWith('-consolidate'));
101
- if (consolidateTask) {
102
- return { found: true, task: consolidateTask, reason: 'Consolidate task selected.' };
103
- }
104
- }
105
-
106
- // 4. Default: first non-consolidate task (entry point)
107
- const entryTask = candidates.find(t => !t.id.endsWith('-consolidate')) || candidates[0];
108
- return { found: true, task: entryTask, reason: `Default entry: ${entryTask.id}` };
109
- }
110
-
111
- /**
112
- * Get the next task in an agent's task sequence.
113
- * Uses the handoff_to field to determine chaining.
114
- *
115
- * @param {string} currentTaskId - Currently executing task ID
116
- * @returns {{ found: boolean, task: object|null, reason: string }}
117
- */
118
- getNextTask(currentTaskId) {
119
- const current = this.taskMap.get(currentTaskId);
120
- if (!current) {
121
- return { found: false, task: null, reason: `Current task '${currentTaskId}' not found.` };
122
- }
123
-
124
- // If handoff_to is an agent name, it means we switch agents (orchestrator handles this)
125
- // If handoff_to is a task ID within the same agent, it's internal chaining
126
- if (!current.handoff_to) {
127
- return { found: false, task: null, reason: 'No handoff defined for this task.' };
128
- }
129
-
130
- // Check if handoff_to is a task ID
131
- const nextTask = this.taskMap.get(current.handoff_to);
132
- if (nextTask) {
133
- return { found: true, task: nextTask, reason: `Internal chain: ${current.handoff_to}` };
134
- }
135
-
136
- // Otherwise it's an agent-level handoff (handled by orchestrator)
137
- return {
138
- found: false,
139
- task: null,
140
- reason: `Agent handoff to '${current.handoff_to}'. Orchestrator handles inter-agent routing.`,
141
- };
142
- }
143
-
144
- /**
145
- * Get all parallelizable tasks for a given agent.
146
- * @param {string} agentName
147
- * @returns {object[]}
148
- */
149
- getParallelTasks(agentName) {
150
- return getAgentTasks(this.taskMap, agentName).filter(t => t.parallelizable);
151
- }
152
-
153
- /**
154
- * Get routing statistics.
155
- * @returns {{ totalTasks: number, byAgent: object, byPhase: object, errors: number }}
156
- */
157
- getStats() {
158
- const byAgent = {};
159
- const byPhase = {};
160
-
161
- for (const task of this.taskMap.values()) {
162
- byAgent[task.agent] = (byAgent[task.agent] || 0) + 1;
163
- byPhase[task.phase] = (byPhase[task.phase] || 0) + 1;
164
- }
165
-
166
- return {
167
- totalTasks: this.taskMap.size,
168
- byAgent,
169
- byPhase,
170
- errors: this.errors.length,
171
- };
172
- }
173
- }
174
-
175
- /**
176
- * Create and load a TaskRouter instance.
177
- * @param {string} tasksDir - Path to chati.dev/tasks/
178
- * @returns {TaskRouter}
179
- */
180
- export function createRouter(tasksDir) {
181
- return new TaskRouter(tasksDir).load();
182
- }
@@ -1,126 +0,0 @@
1
- /**
2
- * @fileoverview Singleton event bus for the chati.dev framework.
3
- *
4
- * Provides a centralized EventEmitter for observing state changes
5
- * across subsystems (build-loop, spawner, pipeline, safety-net).
6
- */
7
-
8
- /**
9
- * @deprecated Not currently imported by any production module.
10
- * Retained for potential future integration. Review for removal or integration by v5.0.
11
- */
12
-
13
- import { EventEmitter } from 'node:events';
14
-
15
- // ---------------------------------------------------------------------------
16
- // Event Names
17
- // ---------------------------------------------------------------------------
18
-
19
- /**
20
- * All framework event names, organized by subsystem.
21
- */
22
- export const EVENTS = {
23
- // Build loop
24
- BUILD_STARTED: 'build:started',
25
- BUILD_COMPLETED: 'build:completed',
26
- BUILD_FAILED: 'build:failed',
27
- TASK_STARTED: 'build:task:started',
28
- TASK_COMPLETED: 'build:task:completed',
29
- TASK_FAILED: 'build:task:failed',
30
- TASK_EXHAUSTED: 'build:task:exhausted',
31
-
32
- // Spawner
33
- TERMINAL_SPAWNED: 'terminal:spawned',
34
- TERMINAL_EXITED: 'terminal:exited',
35
- TERMINAL_KILLED: 'terminal:killed',
36
-
37
- // Pipeline
38
- PHASE_ADVANCED: 'pipeline:phase:advanced',
39
- AGENT_COMPLETED: 'pipeline:agent:completed',
40
- QA_GATE_PASSED: 'pipeline:qa:passed',
41
- QA_GATE_FAILED: 'pipeline:qa:failed',
42
-
43
- // Safety
44
- SAFETY_TRIGGERED: 'safety:triggered',
45
-
46
- // Health
47
- HEALTH_CHECK_COMPLETED: 'health:completed',
48
-
49
- // Escalation
50
- ESCALATION_TRIGGERED: 'escalation:triggered',
51
- ESCALATION_PAUSED: 'escalation:paused',
52
- };
53
-
54
- // ---------------------------------------------------------------------------
55
- // Bus Instance
56
- // ---------------------------------------------------------------------------
57
-
58
- const bus = new EventEmitter();
59
- bus.setMaxListeners(50);
60
-
61
- // ---------------------------------------------------------------------------
62
- // Public API
63
- // ---------------------------------------------------------------------------
64
-
65
- /**
66
- * Emit an event with data. Automatically adds a timestamp.
67
- *
68
- * @param {string} event - Event name from EVENTS
69
- * @param {object} [data={}] - Event payload
70
- */
71
- export function emit(event, data = {}) {
72
- bus.emit(event, { ...data, timestamp: Date.now() });
73
- }
74
-
75
- /**
76
- * Subscribe to an event.
77
- *
78
- * @param {string} event - Event name
79
- * @param {function} handler - Event handler
80
- */
81
- export function on(event, handler) {
82
- bus.on(event, handler);
83
- }
84
-
85
- /**
86
- * Subscribe to an event once.
87
- *
88
- * @param {string} event - Event name
89
- * @param {function} handler - Event handler
90
- */
91
- export function once(event, handler) {
92
- bus.once(event, handler);
93
- }
94
-
95
- /**
96
- * Unsubscribe from an event.
97
- *
98
- * @param {string} event - Event name
99
- * @param {function} handler - Event handler to remove
100
- */
101
- export function off(event, handler) {
102
- bus.off(event, handler);
103
- }
104
-
105
- /**
106
- * Remove all listeners for an event, or all events if no event specified.
107
- *
108
- * @param {string} [event] - Event name (optional)
109
- */
110
- export function removeAllListeners(event) {
111
- if (event) {
112
- bus.removeAllListeners(event);
113
- } else {
114
- bus.removeAllListeners();
115
- }
116
- }
117
-
118
- /**
119
- * Get the count of listeners for an event.
120
- *
121
- * @param {string} event - Event name
122
- * @returns {number}
123
- */
124
- export function listenerCount(event) {
125
- return bus.listenerCount(event);
126
- }