agentic-qe 3.8.10 → 3.8.12

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 (119) hide show
  1. package/.claude/skills/skills-manifest.json +1 -1
  2. package/CHANGELOG.md +40 -0
  3. package/dist/cli/bundle.js +1345 -1003
  4. package/dist/cli/command-registry.js +5 -1
  5. package/dist/cli/commands/pipeline.d.ts +16 -0
  6. package/dist/cli/commands/pipeline.js +314 -0
  7. package/dist/cli/commands/ruvector-commands.js +17 -0
  8. package/dist/cli/commands/token-usage.js +24 -1
  9. package/dist/cli/handlers/heartbeat-handler.d.ts +26 -0
  10. package/dist/cli/handlers/heartbeat-handler.js +382 -0
  11. package/dist/cli/handlers/index.d.ts +2 -0
  12. package/dist/cli/handlers/index.js +2 -0
  13. package/dist/cli/handlers/routing-handler.d.ts +22 -0
  14. package/dist/cli/handlers/routing-handler.js +227 -0
  15. package/dist/cli/index.js +2 -0
  16. package/dist/coordination/deterministic-actions.d.ts +36 -0
  17. package/dist/coordination/deterministic-actions.js +257 -0
  18. package/dist/coordination/workflow-orchestrator.d.ts +18 -1
  19. package/dist/coordination/workflow-orchestrator.js +113 -3
  20. package/dist/coordination/workflow-types.d.ts +19 -1
  21. package/dist/coordination/workflow-types.js +3 -0
  22. package/dist/coordination/yaml-pipeline-loader.d.ts +1 -0
  23. package/dist/coordination/yaml-pipeline-loader.js +34 -0
  24. package/dist/domains/code-intelligence/coordinator-gnn.d.ts +21 -0
  25. package/dist/domains/code-intelligence/coordinator-gnn.js +102 -0
  26. package/dist/domains/contract-testing/coordinator.js +13 -0
  27. package/dist/domains/coverage-analysis/coordinator.js +5 -0
  28. package/dist/domains/defect-intelligence/coordinator.d.ts +1 -0
  29. package/dist/domains/defect-intelligence/coordinator.js +43 -0
  30. package/dist/domains/quality-assessment/coordinator.js +26 -0
  31. package/dist/domains/test-generation/coordinator.js +14 -0
  32. package/dist/integrations/agentic-flow/reasoning-bank/experience-replay.d.ts +11 -0
  33. package/dist/integrations/agentic-flow/reasoning-bank/experience-replay.js +44 -1
  34. package/dist/integrations/rl-suite/algorithms/eprop.d.ts +79 -0
  35. package/dist/integrations/rl-suite/algorithms/eprop.js +284 -0
  36. package/dist/integrations/rl-suite/algorithms/index.d.ts +2 -1
  37. package/dist/integrations/rl-suite/algorithms/index.js +2 -1
  38. package/dist/integrations/rl-suite/index.d.ts +2 -2
  39. package/dist/integrations/rl-suite/index.js +2 -2
  40. package/dist/integrations/rl-suite/interfaces.d.ts +3 -3
  41. package/dist/integrations/rl-suite/interfaces.js +1 -1
  42. package/dist/integrations/rl-suite/orchestrator.d.ts +2 -2
  43. package/dist/integrations/rl-suite/orchestrator.js +3 -2
  44. package/dist/integrations/rl-suite/reward-signals.d.ts +1 -1
  45. package/dist/integrations/rl-suite/reward-signals.js +1 -1
  46. package/dist/integrations/ruvector/coherence-gate-cohomology.d.ts +41 -0
  47. package/dist/integrations/ruvector/coherence-gate-cohomology.js +47 -0
  48. package/dist/integrations/ruvector/coherence-gate-core.d.ts +200 -0
  49. package/dist/integrations/ruvector/coherence-gate-core.js +294 -0
  50. package/dist/integrations/ruvector/coherence-gate-energy.d.ts +136 -0
  51. package/dist/integrations/ruvector/coherence-gate-energy.js +373 -0
  52. package/dist/integrations/ruvector/coherence-gate-vector.d.ts +38 -0
  53. package/dist/integrations/ruvector/coherence-gate-vector.js +76 -0
  54. package/dist/integrations/ruvector/coherence-gate.d.ts +10 -311
  55. package/dist/integrations/ruvector/coherence-gate.js +10 -652
  56. package/dist/integrations/ruvector/cold-tier-trainer.d.ts +103 -0
  57. package/dist/integrations/ruvector/cold-tier-trainer.js +377 -0
  58. package/dist/integrations/ruvector/cusum-detector.d.ts +70 -0
  59. package/dist/integrations/ruvector/cusum-detector.js +142 -0
  60. package/dist/integrations/ruvector/delta-tracker.d.ts +122 -0
  61. package/dist/integrations/ruvector/delta-tracker.js +311 -0
  62. package/dist/integrations/ruvector/domain-transfer.d.ts +79 -1
  63. package/dist/integrations/ruvector/domain-transfer.js +158 -2
  64. package/dist/integrations/ruvector/eprop-learner.d.ts +135 -0
  65. package/dist/integrations/ruvector/eprop-learner.js +351 -0
  66. package/dist/integrations/ruvector/feature-flags.d.ts +177 -0
  67. package/dist/integrations/ruvector/feature-flags.js +145 -0
  68. package/dist/integrations/ruvector/graphmae-encoder.d.ts +88 -0
  69. package/dist/integrations/ruvector/graphmae-encoder.js +360 -0
  70. package/dist/integrations/ruvector/hdc-fingerprint.d.ts +127 -0
  71. package/dist/integrations/ruvector/hdc-fingerprint.js +222 -0
  72. package/dist/integrations/ruvector/hopfield-memory.d.ts +97 -0
  73. package/dist/integrations/ruvector/hopfield-memory.js +238 -0
  74. package/dist/integrations/ruvector/index.d.ts +13 -2
  75. package/dist/integrations/ruvector/index.js +46 -2
  76. package/dist/integrations/ruvector/mincut-wrapper.d.ts +7 -0
  77. package/dist/integrations/ruvector/mincut-wrapper.js +54 -2
  78. package/dist/integrations/ruvector/reservoir-replay.d.ts +172 -0
  79. package/dist/integrations/ruvector/reservoir-replay.js +335 -0
  80. package/dist/integrations/ruvector/solver-adapter.d.ts +93 -0
  81. package/dist/integrations/ruvector/solver-adapter.js +299 -0
  82. package/dist/integrations/ruvector/sona-persistence.d.ts +33 -0
  83. package/dist/integrations/ruvector/sona-persistence.js +47 -0
  84. package/dist/integrations/ruvector/spectral-sparsifier.d.ts +154 -0
  85. package/dist/integrations/ruvector/spectral-sparsifier.js +389 -0
  86. package/dist/integrations/ruvector/temporal-causality.d.ts +63 -0
  87. package/dist/integrations/ruvector/temporal-causality.js +317 -0
  88. package/dist/learning/pattern-promotion.d.ts +63 -0
  89. package/dist/learning/pattern-promotion.js +235 -1
  90. package/dist/learning/pattern-store.d.ts +2 -0
  91. package/dist/learning/pattern-store.js +187 -1
  92. package/dist/learning/sqlite-persistence.d.ts +2 -0
  93. package/dist/learning/sqlite-persistence.js +4 -0
  94. package/dist/mcp/bundle.js +477 -380
  95. package/dist/mcp/handlers/heartbeat-handlers.d.ts +67 -0
  96. package/dist/mcp/handlers/heartbeat-handlers.js +180 -0
  97. package/dist/mcp/handlers/index.d.ts +2 -1
  98. package/dist/mcp/handlers/index.js +5 -1
  99. package/dist/mcp/handlers/task-handlers.d.ts +28 -0
  100. package/dist/mcp/handlers/task-handlers.js +39 -0
  101. package/dist/mcp/protocol-server.js +45 -1
  102. package/dist/mcp/server.js +41 -1
  103. package/dist/optimization/index.d.ts +2 -0
  104. package/dist/optimization/index.js +1 -0
  105. package/dist/optimization/session-cache.d.ts +80 -0
  106. package/dist/optimization/session-cache.js +227 -0
  107. package/dist/optimization/token-optimizer-service.d.ts +10 -0
  108. package/dist/optimization/token-optimizer-service.js +51 -0
  109. package/dist/routing/economic-routing.d.ts +126 -0
  110. package/dist/routing/economic-routing.js +290 -0
  111. package/dist/routing/index.d.ts +2 -0
  112. package/dist/routing/index.js +2 -0
  113. package/dist/routing/routing-feedback.d.ts +29 -0
  114. package/dist/routing/routing-feedback.js +75 -0
  115. package/dist/shared/utils/index.d.ts +1 -0
  116. package/dist/shared/utils/index.js +1 -0
  117. package/dist/shared/utils/xorshift128.d.ts +24 -0
  118. package/dist/shared/utils/xorshift128.js +50 -0
  119. package/package.json +1 -1
@@ -0,0 +1,382 @@
1
+ /**
2
+ * Agentic QE v3 - Heartbeat Command Handler
3
+ * Imp-10: Token-Free Heartbeat Scheduler CLI Integration
4
+ *
5
+ * Handles the 'aqe heartbeat' command with subcommands:
6
+ * status, run-now, history, log, pause, resume
7
+ */
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ import chalk from 'chalk';
11
+ import { formatDuration } from './interfaces.js';
12
+ import { HeartbeatSchedulerWorker } from '../../workers/workers/heartbeat-scheduler.js';
13
+ import { toErrorMessage } from '../../shared/error-utils.js';
14
+ import { findProjectRoot } from '../../kernel/unified-memory.js';
15
+ // ============================================================================
16
+ // Heartbeat Handler
17
+ // ============================================================================
18
+ export class HeartbeatHandler {
19
+ name = 'heartbeat';
20
+ description = 'Manage the token-free heartbeat scheduler';
21
+ cleanupAndExit;
22
+ worker;
23
+ constructor(cleanupAndExit) {
24
+ this.cleanupAndExit = cleanupAndExit;
25
+ this.worker = new HeartbeatSchedulerWorker();
26
+ }
27
+ register(program, _context) {
28
+ const heartbeat = program
29
+ .command('heartbeat')
30
+ .description(this.description);
31
+ heartbeat
32
+ .command('status')
33
+ .description('Show heartbeat worker status, health, and schedule')
34
+ .action(async () => {
35
+ await this.executeStatus();
36
+ });
37
+ heartbeat
38
+ .command('run-now')
39
+ .description('Trigger an immediate heartbeat cycle')
40
+ .option('-t, --timeout <ms>', 'Timeout in milliseconds (default: worker built-in 60s)')
41
+ .action(async (options) => {
42
+ const timeout = options.timeout ? parseInt(options.timeout, 10) : undefined;
43
+ await this.executeRunNow(timeout);
44
+ });
45
+ heartbeat
46
+ .command('history')
47
+ .description('Show recent heartbeat results')
48
+ .option('-n, --count <count>', 'Number of entries to show', '10')
49
+ .action(async (options) => {
50
+ await this.executeHistory(parseInt(options.count, 10) || 10);
51
+ });
52
+ heartbeat
53
+ .command('log')
54
+ .description("Show today's daily log entries")
55
+ .option('-d, --date <date>', 'Show log for specific date (YYYY-MM-DD)')
56
+ .action(async (options) => {
57
+ await this.executeLog(options.date);
58
+ });
59
+ heartbeat
60
+ .command('pause')
61
+ .description('Pause the heartbeat worker')
62
+ .action(async () => {
63
+ await this.executePause();
64
+ });
65
+ heartbeat
66
+ .command('resume')
67
+ .description('Resume the heartbeat worker')
68
+ .action(async () => {
69
+ await this.executeResume();
70
+ });
71
+ }
72
+ // --------------------------------------------------------------------------
73
+ // Subcommand Implementations
74
+ // --------------------------------------------------------------------------
75
+ async executeStatus() {
76
+ try {
77
+ await this.worker.initialize();
78
+ const health = this.worker.getHealth();
79
+ const lastResult = this.worker.lastResult;
80
+ console.log(chalk.blue('\n Heartbeat Scheduler Status'));
81
+ console.log(chalk.gray(' ' + '\u2500'.repeat(35)));
82
+ console.log(` Status: ${statusColor(health.status)}`);
83
+ console.log(` Health Score: ${scoreColor(health.healthScore)}${chalk.gray('/100')}`);
84
+ if (this.worker.lastRunAt) {
85
+ const ago = formatRelativeTime(this.worker.lastRunAt);
86
+ console.log(` Last Run: ${chalk.cyan(this.worker.lastRunAt.toISOString().replace('T', ' ').slice(0, 19))} ${chalk.gray(`(${ago})`)}`);
87
+ }
88
+ else {
89
+ console.log(` Last Run: ${chalk.gray('never')}`);
90
+ }
91
+ if (this.worker.nextRunAt) {
92
+ const until = formatRelativeTime(this.worker.nextRunAt, true);
93
+ console.log(` Next Run: ${chalk.cyan(this.worker.nextRunAt.toISOString().replace('T', ' ').slice(0, 19))} ${chalk.gray(`(${until})`)}`);
94
+ }
95
+ console.log(` Total Runs: ${chalk.cyan(String(health.totalExecutions))}`);
96
+ const successRate = health.totalExecutions > 0
97
+ ? ((health.successfulExecutions / health.totalExecutions) * 100).toFixed(1)
98
+ : '100.0';
99
+ console.log(` Success Rate: ${chalk.cyan(successRate + '%')}`);
100
+ if (lastResult?.metrics?.domainMetrics) {
101
+ const dm = lastResult.metrics.domainMetrics;
102
+ console.log('');
103
+ console.log(chalk.blue(' Last Result:'));
104
+ console.log(` Promoted: ${chalk.cyan(String(dm.promoted ?? 0))} patterns`);
105
+ console.log(` Deprecated: ${chalk.cyan(String(dm.deprecated ?? 0))} patterns`);
106
+ console.log(` Decayed: ${chalk.cyan(String(dm.decayed ?? 0))} patterns`);
107
+ console.log(` Pending Exp: ${chalk.cyan(String(dm.pendingExperiences ?? 0))}`);
108
+ console.log(` Avg Conf: ${chalk.cyan(String(dm.avgConfidence ?? 0))}`);
109
+ }
110
+ console.log('');
111
+ await this.cleanupAndExit(0);
112
+ }
113
+ catch (error) {
114
+ console.error(chalk.red('\n Failed to get heartbeat status:'), toErrorMessage(error));
115
+ await this.cleanupAndExit(1);
116
+ }
117
+ }
118
+ async executeRunNow(timeoutMs) {
119
+ try {
120
+ console.log(chalk.blue('\n Triggering heartbeat cycle...\n'));
121
+ await this.worker.initialize();
122
+ const abortController = new AbortController();
123
+ // Only add external timeout if the user explicitly requests one shorter
124
+ // than the worker's built-in 60s timeout.
125
+ const timeoutHandle = timeoutMs && timeoutMs > 0
126
+ ? setTimeout(() => abortController.abort(), timeoutMs)
127
+ : null;
128
+ // Use a real logger that outputs to the console, and a lightweight
129
+ // event bus / memory — the heartbeat worker only needs DB access
130
+ // (which it gets via getUnifiedMemory() internally).
131
+ const result = await this.worker.execute({
132
+ eventBus: { publish: async () => { } },
133
+ memory: {
134
+ get: async () => undefined,
135
+ set: async () => { },
136
+ search: async () => [],
137
+ },
138
+ logger: {
139
+ debug: () => { },
140
+ info: (...args) => console.log(chalk.gray(' [heartbeat]'), ...args),
141
+ warn: (...args) => console.warn(chalk.yellow(' [heartbeat]'), ...args),
142
+ error: (...args) => console.error(chalk.red(' [heartbeat]'), ...args),
143
+ },
144
+ domains: {
145
+ getDomainAPI: () => undefined,
146
+ getDomainHealth: () => ({ status: 'healthy', errors: [] }),
147
+ },
148
+ signal: abortController.signal,
149
+ });
150
+ if (timeoutHandle)
151
+ clearTimeout(timeoutHandle);
152
+ if (result.success) {
153
+ const dm = result.metrics.domainMetrics;
154
+ console.log(chalk.green(' Heartbeat cycle complete.'));
155
+ console.log(` Duration: ${chalk.cyan(formatDuration(result.durationMs))}`);
156
+ console.log(` Health Score: ${scoreColor(result.metrics.healthScore)}${chalk.gray('/100')}`);
157
+ console.log(` Trend: ${trendColor(result.metrics.trend)}`);
158
+ console.log(` Promoted: ${chalk.cyan(String(dm.promoted ?? 0))}`);
159
+ console.log(` Deprecated: ${chalk.cyan(String(dm.deprecated ?? 0))}`);
160
+ console.log(` Decayed: ${chalk.cyan(String(dm.decayed ?? 0))}`);
161
+ console.log(` Findings: ${chalk.cyan(String(result.findings.length))}`);
162
+ }
163
+ else {
164
+ console.error(chalk.red(' Heartbeat cycle failed:'), result.error);
165
+ }
166
+ // Store history entry
167
+ storeHistoryEntry(result);
168
+ console.log('');
169
+ await this.cleanupAndExit(result.success ? 0 : 1);
170
+ }
171
+ catch (error) {
172
+ console.error(chalk.red('\n Failed to run heartbeat:'), toErrorMessage(error));
173
+ await this.cleanupAndExit(1);
174
+ }
175
+ }
176
+ async executeHistory(count) {
177
+ try {
178
+ const entries = loadHistoryEntries(count);
179
+ if (entries.length === 0) {
180
+ console.log(chalk.yellow('\n No heartbeat history found. Run `aqe heartbeat run-now` first.\n'));
181
+ await this.cleanupAndExit(0);
182
+ return;
183
+ }
184
+ console.log(chalk.blue(`\n Heartbeat History (last ${entries.length})`));
185
+ console.log(chalk.gray(' ' + '\u2500'.repeat(60)));
186
+ for (const entry of entries) {
187
+ const status = entry.success ? chalk.green('OK') : chalk.red('FAIL');
188
+ const ts = entry.timestamp.slice(0, 19).replace('T', ' ');
189
+ const dm = entry.domainMetrics || {};
190
+ console.log(` ${chalk.gray(ts)} ${status} ` +
191
+ `score:${chalk.cyan(String(entry.healthScore))} ` +
192
+ `+${dm.promoted ?? 0}/-${dm.deprecated ?? 0} ` +
193
+ `${chalk.gray(formatDuration(entry.durationMs))}`);
194
+ }
195
+ console.log('');
196
+ await this.cleanupAndExit(0);
197
+ }
198
+ catch (error) {
199
+ console.error(chalk.red('\n Failed to load history:'), toErrorMessage(error));
200
+ await this.cleanupAndExit(1);
201
+ }
202
+ }
203
+ async executeLog(date) {
204
+ try {
205
+ const targetDate = date || new Date().toISOString().split('T')[0];
206
+ // Validate date format to prevent path traversal (CLI-MCP parity with heartbeat-handlers.ts)
207
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(targetDate)) {
208
+ console.error(chalk.red(`\n Invalid date format: "${targetDate}". Use YYYY-MM-DD.\n`));
209
+ await this.cleanupAndExit(1);
210
+ return;
211
+ }
212
+ const logDir = path.join(findProjectRoot(), '.agentic-qe', 'logs');
213
+ const logPath = path.join(logDir, `${targetDate}.md`);
214
+ if (!fs.existsSync(logPath)) {
215
+ console.log(chalk.yellow(`\n No daily log found for ${targetDate}.\n`));
216
+ await this.cleanupAndExit(0);
217
+ return;
218
+ }
219
+ const content = fs.readFileSync(logPath, 'utf-8');
220
+ console.log(chalk.blue(`\n Daily Log \u2014 ${targetDate}`));
221
+ console.log(chalk.gray(' ' + '\u2500'.repeat(40)));
222
+ // Indent and display each line
223
+ for (const line of content.split('\n')) {
224
+ if (line.trim()) {
225
+ console.log(` ${line}`);
226
+ }
227
+ }
228
+ console.log('');
229
+ await this.cleanupAndExit(0);
230
+ }
231
+ catch (error) {
232
+ console.error(chalk.red('\n Failed to read daily log:'), toErrorMessage(error));
233
+ await this.cleanupAndExit(1);
234
+ }
235
+ }
236
+ async executePause() {
237
+ try {
238
+ this.worker.pause();
239
+ console.log(chalk.yellow('\n Heartbeat worker paused.\n'));
240
+ await this.cleanupAndExit(0);
241
+ }
242
+ catch (error) {
243
+ console.error(chalk.red('\n Failed to pause heartbeat:'), toErrorMessage(error));
244
+ await this.cleanupAndExit(1);
245
+ }
246
+ }
247
+ async executeResume() {
248
+ try {
249
+ this.worker.resume();
250
+ console.log(chalk.green('\n Heartbeat worker resumed.\n'));
251
+ await this.cleanupAndExit(0);
252
+ }
253
+ catch (error) {
254
+ console.error(chalk.red('\n Failed to resume heartbeat:'), toErrorMessage(error));
255
+ await this.cleanupAndExit(1);
256
+ }
257
+ }
258
+ getHelp() {
259
+ return `
260
+ Manage the token-free heartbeat scheduler (Imp-10).
261
+
262
+ The heartbeat runs every 30 minutes performing SQL-only maintenance:
263
+ - Pattern promotion checks
264
+ - Stale pattern deprecation
265
+ - Confidence decay application
266
+ - Experience buffer monitoring
267
+ - Daily Markdown log entries
268
+
269
+ Subcommands:
270
+ status Show heartbeat worker status, health, and schedule
271
+ run-now Trigger an immediate heartbeat cycle
272
+ history Show recent heartbeat results (last 10)
273
+ log Show today's daily log entries
274
+ pause Pause the heartbeat worker
275
+ resume Resume the heartbeat worker
276
+
277
+ Examples:
278
+ aqe heartbeat status
279
+ aqe heartbeat run-now
280
+ aqe heartbeat history -n 5
281
+ aqe heartbeat log
282
+ aqe heartbeat log --date 2026-03-25
283
+ aqe heartbeat pause
284
+ aqe heartbeat resume
285
+ `;
286
+ }
287
+ }
288
+ const MAX_HISTORY_ENTRIES = 100;
289
+ function getHistoryPath() {
290
+ return path.join(findProjectRoot(), '.agentic-qe', 'heartbeat-history.json');
291
+ }
292
+ function storeHistoryEntry(result) {
293
+ try {
294
+ const historyPath = getHistoryPath();
295
+ const dir = path.dirname(historyPath);
296
+ if (!fs.existsSync(dir)) {
297
+ fs.mkdirSync(dir, { recursive: true });
298
+ }
299
+ let entries = [];
300
+ if (fs.existsSync(historyPath)) {
301
+ try {
302
+ entries = JSON.parse(fs.readFileSync(historyPath, 'utf-8'));
303
+ }
304
+ catch {
305
+ entries = [];
306
+ }
307
+ }
308
+ entries.unshift({
309
+ timestamp: result.timestamp.toISOString(),
310
+ success: result.success,
311
+ durationMs: result.durationMs,
312
+ healthScore: result.metrics.healthScore,
313
+ domainMetrics: result.metrics.domainMetrics,
314
+ });
315
+ // Prune to max entries
316
+ if (entries.length > MAX_HISTORY_ENTRIES) {
317
+ entries = entries.slice(0, MAX_HISTORY_ENTRIES);
318
+ }
319
+ fs.writeFileSync(historyPath, JSON.stringify(entries, null, 2));
320
+ }
321
+ catch {
322
+ // Non-critical: don't fail the command if history persistence fails
323
+ }
324
+ }
325
+ function loadHistoryEntries(count) {
326
+ try {
327
+ const historyPath = getHistoryPath();
328
+ if (!fs.existsSync(historyPath)) {
329
+ return [];
330
+ }
331
+ const entries = JSON.parse(fs.readFileSync(historyPath, 'utf-8'));
332
+ return entries.slice(0, count);
333
+ }
334
+ catch {
335
+ return [];
336
+ }
337
+ }
338
+ // ============================================================================
339
+ // Display Helpers
340
+ // ============================================================================
341
+ function statusColor(status) {
342
+ switch (status) {
343
+ case 'idle': return chalk.cyan(status);
344
+ case 'running': return chalk.yellow(status);
345
+ case 'paused': return chalk.yellow(status);
346
+ case 'stopped': return chalk.gray(status);
347
+ case 'error': return chalk.red(status);
348
+ default: return chalk.white(status);
349
+ }
350
+ }
351
+ function scoreColor(score) {
352
+ if (score >= 80)
353
+ return chalk.green(String(score));
354
+ if (score >= 50)
355
+ return chalk.yellow(String(score));
356
+ return chalk.red(String(score));
357
+ }
358
+ function trendColor(trend) {
359
+ switch (trend) {
360
+ case 'improving': return chalk.green(trend);
361
+ case 'stable': return chalk.cyan(trend);
362
+ case 'degrading': return chalk.red(trend);
363
+ default: return chalk.gray(trend);
364
+ }
365
+ }
366
+ function formatRelativeTime(date, future = false) {
367
+ const diffMs = future ? date.getTime() - Date.now() : Date.now() - date.getTime();
368
+ if (diffMs < 0)
369
+ return future ? 'now' : 'just now';
370
+ if (diffMs < 60_000)
371
+ return `${Math.floor(diffMs / 1000)}s ${future ? 'from now' : 'ago'}`;
372
+ if (diffMs < 3_600_000)
373
+ return `${Math.floor(diffMs / 60_000)}m ${future ? 'from now' : 'ago'}`;
374
+ return `${Math.floor(diffMs / 3_600_000)}h ${future ? 'from now' : 'ago'}`;
375
+ }
376
+ // ============================================================================
377
+ // Factory
378
+ // ============================================================================
379
+ export function createHeartbeatHandler(cleanupAndExit) {
380
+ return new HeartbeatHandler(cleanupAndExit);
381
+ }
382
+ //# sourceMappingURL=heartbeat-handler.js.map
@@ -12,4 +12,6 @@ export { DomainHandler, createDomainHandler } from './domain-handler.js';
12
12
  export { ProtocolHandler, createProtocolHandler } from './protocol-handler.js';
13
13
  export { BrainHandler, createBrainHandler } from './brain-handler.js';
14
14
  export { HypergraphHandler, createHypergraphHandler } from './hypergraph-handler.js';
15
+ export { HeartbeatHandler, createHeartbeatHandler } from './heartbeat-handler.js';
16
+ export { RoutingHandler, createRoutingHandler } from './routing-handler.js';
15
17
  //# sourceMappingURL=index.d.ts.map
@@ -14,4 +14,6 @@ export { DomainHandler, createDomainHandler } from './domain-handler.js';
14
14
  export { ProtocolHandler, createProtocolHandler } from './protocol-handler.js';
15
15
  export { BrainHandler, createBrainHandler } from './brain-handler.js';
16
16
  export { HypergraphHandler, createHypergraphHandler } from './hypergraph-handler.js';
17
+ export { HeartbeatHandler, createHeartbeatHandler } from './heartbeat-handler.js';
18
+ export { RoutingHandler, createRoutingHandler } from './routing-handler.js';
17
19
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Agentic QE v3 - Routing Command Handler
3
+ * Imp-18: Economic Routing Model CLI Integration
4
+ *
5
+ * Handles the 'aqe routing' command with subcommands:
6
+ * economics, accuracy, metrics
7
+ */
8
+ import { Command } from 'commander';
9
+ import { ICommandHandler, CLIContext } from './interfaces.js';
10
+ export declare class RoutingHandler implements ICommandHandler {
11
+ readonly name = "routing";
12
+ readonly description = "View routing performance, economics, and accuracy";
13
+ private cleanupAndExit;
14
+ constructor(cleanupAndExit: (code: number) => Promise<never>);
15
+ getHelp(): string;
16
+ register(program: Command, _context: CLIContext): void;
17
+ private executeEconomics;
18
+ private executeAccuracy;
19
+ private executeMetrics;
20
+ }
21
+ export declare function createRoutingHandler(cleanupAndExit: (code: number) => Promise<never>): RoutingHandler;
22
+ //# sourceMappingURL=routing-handler.d.ts.map
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Agentic QE v3 - Routing Command Handler
3
+ * Imp-18: Economic Routing Model CLI Integration
4
+ *
5
+ * Handles the 'aqe routing' command with subcommands:
6
+ * economics, accuracy, metrics
7
+ */
8
+ import chalk from 'chalk';
9
+ import { toErrorMessage } from '../../shared/error-utils.js';
10
+ import { createRoutingFeedbackCollector } from '../../routing/routing-feedback.js';
11
+ import { getGlobalCostTracker } from '../../shared/llm/cost-tracker.js';
12
+ // ============================================================================
13
+ // Routing Handler
14
+ // ============================================================================
15
+ export class RoutingHandler {
16
+ name = 'routing';
17
+ description = 'View routing performance, economics, and accuracy';
18
+ cleanupAndExit;
19
+ constructor(cleanupAndExit) {
20
+ this.cleanupAndExit = cleanupAndExit;
21
+ }
22
+ getHelp() {
23
+ return [
24
+ 'aqe routing economics [--complexity <0-1>] [--json] Show tier efficiency & budget',
25
+ 'aqe routing accuracy [--json] Show routing accuracy analysis',
26
+ 'aqe routing metrics [--json] Show per-agent performance',
27
+ ].join('\n');
28
+ }
29
+ register(program, _context) {
30
+ const routing = program
31
+ .command('routing')
32
+ .description(this.description);
33
+ routing
34
+ .command('economics')
35
+ .description('Show economic routing report: tier efficiency, budget, savings')
36
+ .option('-c, --complexity <value>', 'Task complexity for scoring (0-1)', '0.5')
37
+ .option('--json', 'Output as JSON')
38
+ .action(async (options) => {
39
+ await this.executeEconomics(parseFloat(options.complexity) || 0.5, !!options.json);
40
+ });
41
+ routing
42
+ .command('accuracy')
43
+ .description('Show routing accuracy analysis')
44
+ .option('--json', 'Output as JSON')
45
+ .action(async (options) => {
46
+ await this.executeAccuracy(!!options.json);
47
+ });
48
+ routing
49
+ .command('metrics')
50
+ .description('Show per-agent performance metrics')
51
+ .option('--json', 'Output as JSON')
52
+ .action(async (options) => {
53
+ await this.executeMetrics(!!options.json);
54
+ });
55
+ }
56
+ // --------------------------------------------------------------------------
57
+ // Economics
58
+ // --------------------------------------------------------------------------
59
+ async executeEconomics(complexity, json) {
60
+ try {
61
+ const collector = createRoutingFeedbackCollector(100);
62
+ await collector.initialize();
63
+ collector.enableEconomicRouting({}, getGlobalCostTracker());
64
+ const report = collector.getEconomicReport();
65
+ if (!report) {
66
+ console.error(chalk.red('\n Economic routing is not available.\n'));
67
+ await this.cleanupAndExit(1);
68
+ return;
69
+ }
70
+ if (json) {
71
+ console.log(JSON.stringify(report, (_k, v) => (v === Infinity ? 'Infinity' : v), 2));
72
+ await this.cleanupAndExit(0);
73
+ return;
74
+ }
75
+ console.log(chalk.blue('\n Economic Routing Report'));
76
+ console.log(chalk.gray(' ' + '\u2500'.repeat(50)));
77
+ // Tier efficiency table
78
+ console.log(chalk.white('\n Tier Efficiency (complexity=' + complexity.toFixed(1) + '):\n'));
79
+ console.log(chalk.gray(' Tier Quality Cost/Task Q/$ Score'));
80
+ console.log(chalk.gray(' ' + '\u2500'.repeat(50)));
81
+ const scores = collector.getEconomicScore(complexity) ?? report.tierEfficiency;
82
+ for (const s of scores) {
83
+ const qpd = isFinite(s.qualityPerDollar) ? s.qualityPerDollar.toFixed(1) : '\u221E';
84
+ console.log(` ${padRight(s.tier, 10)}` +
85
+ `${chalk.cyan(s.qualityScore.toFixed(2))} ` +
86
+ `$${s.estimatedCostUsd.toFixed(4)} ` +
87
+ `${chalk.yellow(padLeft(qpd, 8))} ` +
88
+ `${scoreColor(s.economicScore)}`);
89
+ }
90
+ // Budget
91
+ console.log(chalk.white('\n Budget:'));
92
+ console.log(` Hourly cost: $${report.currentHourlyCostUsd.toFixed(4)}`);
93
+ console.log(` Daily cost: $${report.currentDailyCostUsd.toFixed(4)}`);
94
+ if (report.budgetRemaining.hourly !== null) {
95
+ console.log(` Hourly left: $${report.budgetRemaining.hourly.toFixed(4)}`);
96
+ }
97
+ if (report.budgetRemaining.daily !== null) {
98
+ console.log(` Daily left: $${report.budgetRemaining.daily.toFixed(4)}`);
99
+ }
100
+ // Recommendation
101
+ console.log(chalk.white('\n Recommendation:'));
102
+ console.log(` ${chalk.green(report.recommendation)}`);
103
+ if (report.savingsOpportunity) {
104
+ console.log(chalk.white('\n Savings Opportunity:'));
105
+ console.log(` ${chalk.yellow(report.savingsOpportunity.description)}`);
106
+ }
107
+ console.log('');
108
+ await this.cleanupAndExit(0);
109
+ }
110
+ catch (error) {
111
+ console.error(chalk.red('\n Failed to get economic report:'), toErrorMessage(error));
112
+ await this.cleanupAndExit(1);
113
+ }
114
+ }
115
+ // --------------------------------------------------------------------------
116
+ // Accuracy
117
+ // --------------------------------------------------------------------------
118
+ async executeAccuracy(json) {
119
+ try {
120
+ const collector = createRoutingFeedbackCollector(10000);
121
+ await collector.initialize();
122
+ const accuracy = collector.analyzeRoutingAccuracy();
123
+ if (json) {
124
+ console.log(JSON.stringify(accuracy, null, 2));
125
+ await this.cleanupAndExit(0);
126
+ return;
127
+ }
128
+ console.log(chalk.blue('\n Routing Accuracy Analysis'));
129
+ console.log(chalk.gray(' ' + '\u2500'.repeat(40)));
130
+ console.log(` Total outcomes: ${chalk.cyan(String(accuracy.totalOutcomes))}`);
131
+ console.log(` Followed recs: ${chalk.cyan(String(accuracy.followedRecommendations))}`);
132
+ console.log(` Override rate: ${chalk.yellow((accuracy.overrideRate * 100).toFixed(1) + '%')}`);
133
+ console.log(` Rec success rate: ${scoreColor100(accuracy.recommendationSuccessRate * 100)}`);
134
+ console.log(` Override success rate: ${scoreColor100(accuracy.overrideSuccessRate * 100)}`);
135
+ console.log(` Confidence correlation: ${chalk.cyan(accuracy.confidenceCorrelation.toFixed(3))}`);
136
+ // Recommendations
137
+ const recs = collector.getImprovementRecommendations();
138
+ if (recs.length > 0) {
139
+ console.log(chalk.white('\n Recommendations:'));
140
+ for (const rec of recs) {
141
+ console.log(` ${chalk.gray('\u2022')} ${rec}`);
142
+ }
143
+ }
144
+ console.log('');
145
+ await this.cleanupAndExit(0);
146
+ }
147
+ catch (error) {
148
+ console.error(chalk.red('\n Failed to analyze routing accuracy:'), toErrorMessage(error));
149
+ await this.cleanupAndExit(1);
150
+ }
151
+ }
152
+ // --------------------------------------------------------------------------
153
+ // Metrics
154
+ // --------------------------------------------------------------------------
155
+ async executeMetrics(json) {
156
+ try {
157
+ const collector = createRoutingFeedbackCollector(10000);
158
+ await collector.initialize();
159
+ const metrics = collector.getAllAgentMetrics();
160
+ if (json) {
161
+ console.log(JSON.stringify(metrics, null, 2));
162
+ await this.cleanupAndExit(0);
163
+ return;
164
+ }
165
+ if (metrics.length === 0) {
166
+ console.log(chalk.yellow('\n No routing metrics available yet. Run some QE tasks first.\n'));
167
+ await this.cleanupAndExit(0);
168
+ return;
169
+ }
170
+ console.log(chalk.blue('\n Agent Routing Metrics'));
171
+ console.log(chalk.gray(' ' + '\u2500'.repeat(60)));
172
+ console.log(chalk.gray(' Agent Tasks Success Quality Trend'));
173
+ console.log(chalk.gray(' ' + '\u2500'.repeat(60)));
174
+ for (const m of metrics.slice(0, 20)) {
175
+ const trend = m.trend === 'improving' ? chalk.green('\u2191')
176
+ : m.trend === 'declining' ? chalk.red('\u2193')
177
+ : chalk.gray('\u2192');
178
+ console.log(` ${padRight(m.agentId, 24)}` +
179
+ `${padLeft(String(m.totalTasks), 5)} ` +
180
+ `${scoreColor100(m.successRate * 100)} ` +
181
+ `${chalk.cyan(m.avgQualityScore.toFixed(2))} ` +
182
+ `${trend} ${m.trend}`);
183
+ }
184
+ const stats = collector.getStats();
185
+ console.log(chalk.gray(`\n ${stats.totalOutcomes} total outcomes, ${stats.uniqueAgentsUsed} agents`));
186
+ console.log('');
187
+ await this.cleanupAndExit(0);
188
+ }
189
+ catch (error) {
190
+ console.error(chalk.red('\n Failed to get agent metrics:'), toErrorMessage(error));
191
+ await this.cleanupAndExit(1);
192
+ }
193
+ }
194
+ }
195
+ // ============================================================================
196
+ // Helpers
197
+ // ============================================================================
198
+ function padRight(str, len) {
199
+ return str.length >= len ? str : str + ' '.repeat(len - str.length);
200
+ }
201
+ function padLeft(str, len) {
202
+ return str.length >= len ? str : ' '.repeat(len - str.length) + str;
203
+ }
204
+ function scoreColor(score) {
205
+ const pct = score * 100;
206
+ const str = score.toFixed(3);
207
+ if (pct >= 70)
208
+ return chalk.green(str);
209
+ if (pct >= 40)
210
+ return chalk.yellow(str);
211
+ return chalk.red(str);
212
+ }
213
+ function scoreColor100(pct) {
214
+ const str = pct.toFixed(1) + '%';
215
+ if (pct >= 70)
216
+ return chalk.green(str);
217
+ if (pct >= 40)
218
+ return chalk.yellow(str);
219
+ return chalk.red(str);
220
+ }
221
+ // ============================================================================
222
+ // Factory
223
+ // ============================================================================
224
+ export function createRoutingHandler(cleanupAndExit) {
225
+ return new RoutingHandler(cleanupAndExit);
226
+ }
227
+ //# sourceMappingURL=routing-handler.js.map
package/dist/cli/index.js CHANGED
@@ -822,6 +822,7 @@ import { createPlatformCommand } from './commands/platform.js';
822
822
  import { createProveCommand } from './commands/prove.js';
823
823
  import { createRuVectorCommand } from './commands/ruvector-commands.js';
824
824
  import { createAuditCommand } from './commands/audit.js';
825
+ import { createPipelineCommand } from './commands/pipeline.js';
825
826
  program.addCommand(createTokenUsageCommand());
826
827
  program.addCommand(createLLMRouterCommand());
827
828
  program.addCommand(createSyncCommands());
@@ -833,6 +834,7 @@ program.addCommand(createPlatformCommand());
833
834
  program.addCommand(createProveCommand(context, cleanupAndExit, ensureInitialized));
834
835
  program.addCommand(createRuVectorCommand());
835
836
  program.addCommand(createAuditCommand(context, cleanupAndExit, ensureInitialized));
837
+ program.addCommand(createPipelineCommand(context, cleanupAndExit, ensureInitialized));
836
838
  // ============================================================================
837
839
  // Shutdown Handlers
838
840
  // ============================================================================