agentic-qe 3.5.1 → 3.5.2

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 (199) hide show
  1. package/.claude/agents/n8n/n8n-base-agent.md +3 -3
  2. package/.claude/agents/n8n/n8n-bdd-scenario-tester.md +2 -2
  3. package/.claude/agents/n8n/n8n-chaos-tester.md +2 -2
  4. package/.claude/agents/n8n/n8n-ci-orchestrator.md +2 -2
  5. package/.claude/agents/n8n/n8n-compliance-validator.md +2 -2
  6. package/.claude/agents/n8n/n8n-expression-validator.md +2 -2
  7. package/.claude/agents/n8n/n8n-integration-test.md +2 -2
  8. package/.claude/agents/n8n/n8n-monitoring-validator.md +2 -2
  9. package/.claude/agents/n8n/n8n-node-validator.md +2 -2
  10. package/.claude/agents/n8n/n8n-performance-tester.md +2 -2
  11. package/.claude/agents/n8n/n8n-security-auditor.md +2 -2
  12. package/.claude/agents/n8n/n8n-trigger-test.md +2 -2
  13. package/.claude/agents/n8n/n8n-unit-tester.md +2 -2
  14. package/.claude/agents/n8n/n8n-version-comparator.md +2 -2
  15. package/.claude/agents/n8n/n8n-workflow-executor.md +2 -2
  16. package/.claude/agents/subagents/qe-code-reviewer.md +2 -2
  17. package/.claude/agents/subagents/qe-coverage-gap-analyzer.md +2 -2
  18. package/.claude/agents/subagents/qe-data-generator.md +2 -2
  19. package/.claude/agents/subagents/qe-flaky-investigator.md +2 -2
  20. package/.claude/agents/subagents/qe-integration-tester.md +2 -2
  21. package/.claude/agents/subagents/qe-performance-validator.md +2 -2
  22. package/.claude/agents/subagents/qe-security-auditor.md +2 -2
  23. package/.claude/agents/subagents/qe-test-data-architect-sub.md +2 -2
  24. package/.claude/agents/subagents/qe-test-implementer.md +2 -2
  25. package/.claude/agents/subagents/qe-test-refactorer.md +2 -2
  26. package/.claude/agents/subagents/qe-test-writer.md +2 -2
  27. package/.claude/agents/v3/qe-accessibility-auditor.md +3 -3
  28. package/.claude/agents/v3/qe-bdd-generator.md +4 -4
  29. package/.claude/agents/v3/qe-chaos-engineer.md +3 -3
  30. package/.claude/agents/v3/qe-code-complexity.md +3 -3
  31. package/.claude/agents/v3/qe-code-intelligence.md +3 -3
  32. package/.claude/agents/v3/qe-contract-validator.md +3 -3
  33. package/.claude/agents/v3/qe-coverage-specialist.md +4 -4
  34. package/.claude/agents/v3/qe-defect-predictor.md +4 -4
  35. package/.claude/agents/v3/qe-dependency-mapper.md +3 -3
  36. package/.claude/agents/v3/qe-deployment-advisor.md +3 -3
  37. package/.claude/agents/v3/qe-flaky-hunter.md +3 -3
  38. package/.claude/agents/v3/qe-fleet-commander.md +3 -3
  39. package/.claude/agents/v3/qe-gap-detector.md +4 -4
  40. package/.claude/agents/v3/qe-graphql-tester.md +3 -3
  41. package/.claude/agents/v3/qe-impact-analyzer.md +3 -3
  42. package/.claude/agents/v3/qe-integration-architect.md +2 -2
  43. package/.claude/agents/v3/qe-integration-tester.md +3 -3
  44. package/.claude/agents/v3/qe-kg-builder.md +3 -3
  45. package/.claude/agents/v3/qe-learning-coordinator.md +24 -15
  46. package/.claude/agents/v3/qe-load-tester.md +3 -3
  47. package/.claude/agents/v3/qe-metrics-optimizer.md +3 -3
  48. package/.claude/agents/v3/qe-mutation-tester.md +3 -3
  49. package/.claude/agents/v3/qe-parallel-executor.md +4 -4
  50. package/.claude/agents/v3/qe-pattern-learner.md +16 -12
  51. package/.claude/agents/v3/qe-performance-tester.md +3 -3
  52. package/.claude/agents/v3/qe-product-factors-assessor.md +4 -4
  53. package/.claude/agents/v3/qe-property-tester.md +3 -3
  54. package/.claude/agents/v3/qe-quality-criteria-recommender.md +4 -4
  55. package/.claude/agents/v3/qe-quality-gate.md +4 -4
  56. package/.claude/agents/v3/qe-qx-partner.md +3 -3
  57. package/.claude/agents/v3/qe-regression-analyzer.md +3 -3
  58. package/.claude/agents/v3/qe-requirements-validator.md +4 -4
  59. package/.claude/agents/v3/qe-responsive-tester.md +3 -3
  60. package/.claude/agents/v3/qe-retry-handler.md +3 -3
  61. package/.claude/agents/v3/qe-risk-assessor.md +4 -4
  62. package/.claude/agents/v3/qe-root-cause-analyzer.md +3 -3
  63. package/.claude/agents/v3/qe-security-auditor.md +3 -3
  64. package/.claude/agents/v3/qe-security-scanner.md +3 -3
  65. package/.claude/agents/v3/qe-tdd-specialist.md +4 -4
  66. package/.claude/agents/v3/qe-test-architect.md +4 -4
  67. package/.claude/agents/v3/qe-test-idea-rewriter.md +3 -3
  68. package/.claude/agents/v3/qe-transfer-specialist.md +3 -3
  69. package/.claude/agents/v3/qe-visual-tester.md +3 -3
  70. package/.claude/agents/v3/reasoningbank-learner.md +4 -4
  71. package/.claude/agents/v3/subagents/qe-code-reviewer.md +3 -3
  72. package/.claude/agents/v3/subagents/qe-integration-reviewer.md +3 -3
  73. package/.claude/agents/v3/subagents/qe-performance-reviewer.md +3 -3
  74. package/.claude/agents/v3/subagents/qe-security-reviewer.md +3 -3
  75. package/.claude/agents/v3/subagents/qe-tdd-green.md +3 -3
  76. package/.claude/agents/v3/subagents/qe-tdd-red.md +3 -3
  77. package/.claude/agents/v3/subagents/qe-tdd-refactor.md +3 -3
  78. package/.claude/helpers/daemon-manager.sh +10 -10
  79. package/.claude/helpers/github-safe.js +10 -10
  80. package/.claude/helpers/statusline-v3.cjs +9 -3
  81. package/.claude/helpers/statusline.cjs +891 -59
  82. package/.claude/skills/agentic-quality-engineering/SKILL.md +6 -6
  83. package/.claude/skills/qcsd-cicd-swarm/SKILL.md +14 -14
  84. package/.claude/skills/qcsd-development-swarm/SKILL.md +14 -14
  85. package/.claude/skills/qcsd-ideation-swarm/SKILL.md +21 -21
  86. package/.claude/skills/qcsd-refinement-swarm/SKILL.md +22 -22
  87. package/.claude/skills/qe-iterative-loop/SKILL.md +4 -4
  88. package/.claude/skills/verification-quality/SKILL.md +0 -6
  89. package/package.json +1 -1
  90. package/v3/CHANGELOG.md +34 -0
  91. package/v3/assets/agents/v3/qe-accessibility-auditor.md +12 -8
  92. package/v3/assets/agents/v3/qe-bdd-generator.md +13 -9
  93. package/v3/assets/agents/v3/qe-chaos-engineer.md +12 -8
  94. package/v3/assets/agents/v3/qe-code-complexity.md +12 -8
  95. package/v3/assets/agents/v3/qe-code-intelligence.md +12 -8
  96. package/v3/assets/agents/v3/qe-contract-validator.md +12 -8
  97. package/v3/assets/agents/v3/qe-coverage-specialist.md +13 -9
  98. package/v3/assets/agents/v3/qe-defect-predictor.md +13 -9
  99. package/v3/assets/agents/v3/qe-dependency-mapper.md +12 -8
  100. package/v3/assets/agents/v3/qe-deployment-advisor.md +12 -8
  101. package/v3/assets/agents/v3/qe-flaky-hunter.md +12 -8
  102. package/v3/assets/agents/v3/qe-fleet-commander.md +12 -8
  103. package/v3/assets/agents/v3/qe-gap-detector.md +13 -9
  104. package/v3/assets/agents/v3/qe-graphql-tester.md +12 -8
  105. package/v3/assets/agents/v3/qe-impact-analyzer.md +12 -8
  106. package/v3/assets/agents/v3/qe-integration-architect.md +2 -2
  107. package/v3/assets/agents/v3/qe-integration-tester.md +3 -3
  108. package/v3/assets/agents/v3/qe-kg-builder.md +12 -8
  109. package/v3/assets/agents/v3/qe-learning-coordinator.md +24 -15
  110. package/v3/assets/agents/v3/qe-load-tester.md +12 -8
  111. package/v3/assets/agents/v3/qe-metrics-optimizer.md +12 -8
  112. package/v3/assets/agents/v3/qe-mutation-tester.md +12 -8
  113. package/v3/assets/agents/v3/qe-parallel-executor.md +4 -4
  114. package/v3/assets/agents/v3/qe-pattern-learner.md +16 -12
  115. package/v3/assets/agents/v3/qe-performance-tester.md +12 -8
  116. package/v3/assets/agents/v3/qe-product-factors-assessor.md +4 -4
  117. package/v3/assets/agents/v3/qe-property-tester.md +12 -8
  118. package/v3/assets/agents/v3/qe-quality-criteria-recommender.md +4 -4
  119. package/v3/assets/agents/v3/qe-quality-gate.md +4 -4
  120. package/v3/assets/agents/v3/qe-qx-partner.md +12 -8
  121. package/v3/assets/agents/v3/qe-regression-analyzer.md +12 -8
  122. package/v3/assets/agents/v3/qe-requirements-validator.md +13 -9
  123. package/v3/assets/agents/v3/qe-responsive-tester.md +12 -8
  124. package/v3/assets/agents/v3/qe-retry-handler.md +12 -8
  125. package/v3/assets/agents/v3/qe-risk-assessor.md +13 -9
  126. package/v3/assets/agents/v3/qe-root-cause-analyzer.md +12 -8
  127. package/v3/assets/agents/v3/qe-security-auditor.md +12 -8
  128. package/v3/assets/agents/v3/qe-security-scanner.md +12 -8
  129. package/v3/assets/agents/v3/qe-tdd-specialist.md +4 -4
  130. package/v3/assets/agents/v3/qe-test-architect.md +13 -9
  131. package/v3/assets/agents/v3/qe-test-idea-rewriter.md +3 -3
  132. package/v3/assets/agents/v3/qe-transfer-specialist.md +12 -8
  133. package/v3/assets/agents/v3/qe-visual-tester.md +3 -3
  134. package/v3/assets/agents/v3/subagents/qe-code-reviewer.md +12 -8
  135. package/v3/assets/agents/v3/subagents/qe-integration-reviewer.md +12 -8
  136. package/v3/assets/agents/v3/subagents/qe-performance-reviewer.md +12 -8
  137. package/v3/assets/agents/v3/subagents/qe-security-reviewer.md +12 -8
  138. package/v3/assets/agents/v3/subagents/qe-tdd-green.md +12 -8
  139. package/v3/assets/agents/v3/subagents/qe-tdd-red.md +12 -8
  140. package/v3/assets/agents/v3/subagents/qe-tdd-refactor.md +12 -8
  141. package/v3/assets/skills/agentic-quality-engineering/SKILL.md +6 -6
  142. package/v3/assets/skills/qcsd-ideation-swarm/SKILL.md +21 -21
  143. package/v3/assets/skills/qe-iterative-loop/SKILL.md +4 -4
  144. package/v3/dist/cli/bundle.js +2508 -816
  145. package/v3/dist/cli/commands/hooks.d.ts.map +1 -1
  146. package/v3/dist/cli/commands/hooks.js +34 -21
  147. package/v3/dist/cli/commands/hooks.js.map +1 -1
  148. package/v3/dist/cli/commands/learning.d.ts +23 -0
  149. package/v3/dist/cli/commands/learning.d.ts.map +1 -0
  150. package/v3/dist/cli/commands/learning.js +1448 -0
  151. package/v3/dist/cli/commands/learning.js.map +1 -0
  152. package/v3/dist/cli/index.js +2 -0
  153. package/v3/dist/cli/index.js.map +1 -1
  154. package/v3/dist/init/init-wizard.d.ts +2 -1
  155. package/v3/dist/init/init-wizard.d.ts.map +1 -1
  156. package/v3/dist/init/init-wizard.js +25 -23
  157. package/v3/dist/init/init-wizard.js.map +1 -1
  158. package/v3/dist/init/phases/07-hooks.d.ts +3 -0
  159. package/v3/dist/init/phases/07-hooks.d.ts.map +1 -1
  160. package/v3/dist/init/phases/07-hooks.js +12 -9
  161. package/v3/dist/init/phases/07-hooks.js.map +1 -1
  162. package/v3/dist/kernel/unified-memory.d.ts +8 -3
  163. package/v3/dist/kernel/unified-memory.d.ts.map +1 -1
  164. package/v3/dist/kernel/unified-memory.js +39 -9
  165. package/v3/dist/kernel/unified-memory.js.map +1 -1
  166. package/v3/dist/learning/aqe-learning-engine.d.ts +26 -0
  167. package/v3/dist/learning/aqe-learning-engine.d.ts.map +1 -1
  168. package/v3/dist/learning/aqe-learning-engine.js +116 -2
  169. package/v3/dist/learning/aqe-learning-engine.js.map +1 -1
  170. package/v3/dist/learning/index.d.ts +4 -0
  171. package/v3/dist/learning/index.d.ts.map +1 -1
  172. package/v3/dist/learning/index.js +8 -0
  173. package/v3/dist/learning/index.js.map +1 -1
  174. package/v3/dist/learning/metrics-tracker.d.ts +133 -0
  175. package/v3/dist/learning/metrics-tracker.d.ts.map +1 -0
  176. package/v3/dist/learning/metrics-tracker.js +396 -0
  177. package/v3/dist/learning/metrics-tracker.js.map +1 -0
  178. package/v3/dist/learning/pattern-lifecycle.d.ts +203 -0
  179. package/v3/dist/learning/pattern-lifecycle.d.ts.map +1 -0
  180. package/v3/dist/learning/pattern-lifecycle.js +614 -0
  181. package/v3/dist/learning/pattern-lifecycle.js.map +1 -0
  182. package/v3/dist/learning/sqlite-persistence.d.ts +30 -0
  183. package/v3/dist/learning/sqlite-persistence.d.ts.map +1 -1
  184. package/v3/dist/learning/sqlite-persistence.js +137 -0
  185. package/v3/dist/learning/sqlite-persistence.js.map +1 -1
  186. package/v3/dist/mcp/bundle.js +104568 -102038
  187. package/v3/dist/mcp/handlers/handler-factory.d.ts +5 -0
  188. package/v3/dist/mcp/handlers/handler-factory.d.ts.map +1 -1
  189. package/v3/dist/mcp/handlers/handler-factory.js +84 -0
  190. package/v3/dist/mcp/handlers/handler-factory.js.map +1 -1
  191. package/v3/dist/mcp/services/task-router.d.ts +32 -0
  192. package/v3/dist/mcp/services/task-router.d.ts.map +1 -1
  193. package/v3/dist/mcp/services/task-router.js +28 -0
  194. package/v3/dist/mcp/services/task-router.js.map +1 -1
  195. package/v3/dist/workers/workers/learning-consolidation.d.ts +29 -0
  196. package/v3/dist/workers/workers/learning-consolidation.d.ts.map +1 -1
  197. package/v3/dist/workers/workers/learning-consolidation.js +294 -3
  198. package/v3/dist/workers/workers/learning-consolidation.js.map +1 -1
  199. package/v3/package.json +1 -1
@@ -0,0 +1,1448 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Agentic QE v3 - Learning Commands
4
+ * ADR-021: Standalone Learning System (No claude-flow dependency)
5
+ *
6
+ * Provides CLI commands for the AQE self-learning system:
7
+ * - Pattern consolidation and promotion
8
+ * - Learning statistics and metrics
9
+ * - Pattern export/import for sharing
10
+ * - Background learning worker management
11
+ */
12
+ import { Command } from 'commander';
13
+ import chalk from 'chalk';
14
+ import path from 'node:path';
15
+ import { existsSync, writeFileSync, readFileSync, mkdirSync, copyFileSync } from 'node:fs';
16
+ import { createReadStream, createWriteStream } from 'node:fs';
17
+ import { stat, unlink } from 'node:fs/promises';
18
+ import { createGzip, createGunzip } from 'node:zlib';
19
+ import { pipeline } from 'node:stream/promises';
20
+ import { createQEReasoningBank, } from '../../learning/qe-reasoning-bank.js';
21
+ import { HybridMemoryBackend } from '../../kernel/hybrid-backend.js';
22
+ import { QE_DOMAIN_LIST } from '../../learning/qe-patterns.js';
23
+ import { createLearningMetricsTracker, } from '../../learning/metrics-tracker.js';
24
+ const state = {
25
+ reasoningBank: null,
26
+ initialized: false,
27
+ };
28
+ /**
29
+ * Initialize the learning system
30
+ */
31
+ async function initializeLearningSystem() {
32
+ if (state.initialized && state.reasoningBank) {
33
+ return state.reasoningBank;
34
+ }
35
+ const cwd = process.cwd();
36
+ const dataDir = path.join(cwd, '.agentic-qe');
37
+ // Create hybrid backend
38
+ const backend = new HybridMemoryBackend({
39
+ sqlite: {
40
+ path: path.join(dataDir, 'memory.db'),
41
+ walMode: true,
42
+ poolSize: 3,
43
+ busyTimeout: 5000,
44
+ },
45
+ enableFallback: true,
46
+ defaultNamespace: 'qe-patterns',
47
+ });
48
+ await backend.initialize();
49
+ // Create reasoning bank
50
+ state.reasoningBank = createQEReasoningBank(backend, undefined, {
51
+ enableLearning: true,
52
+ enableGuidance: true,
53
+ enableRouting: true,
54
+ embeddingDimension: 128,
55
+ useONNXEmbeddings: false,
56
+ });
57
+ await state.reasoningBank.initialize();
58
+ state.initialized = true;
59
+ return state.reasoningBank;
60
+ }
61
+ // ============================================================================
62
+ // Helper Functions
63
+ // ============================================================================
64
+ function printJson(data) {
65
+ console.log(JSON.stringify(data, null, 2));
66
+ }
67
+ function printSuccess(message) {
68
+ console.log(chalk.green('āœ“'), message);
69
+ }
70
+ function printError(message) {
71
+ console.error(chalk.red('āœ—'), message);
72
+ }
73
+ function printInfo(message) {
74
+ console.log(chalk.blue('ℹ'), message);
75
+ }
76
+ /**
77
+ * Display the learning dashboard
78
+ */
79
+ function displayDashboard(dashboard) {
80
+ const { current, topDomains } = dashboard;
81
+ // Box drawing characters
82
+ const BOX = {
83
+ tl: 'ā”Œ', tr: '┐', bl: 'ā””', br: 'ā”˜',
84
+ h: '─', v: '│', ml: 'ā”œ', mr: '┤',
85
+ };
86
+ const WIDTH = 55;
87
+ const HR = BOX.h.repeat(WIDTH - 2);
88
+ console.log('');
89
+ console.log(`${BOX.tl}${HR}${BOX.tr}`);
90
+ console.log(`${BOX.v}${centerText('AQE LEARNING DASHBOARD', WIDTH - 2)}${BOX.v}`);
91
+ console.log(`${BOX.ml}${HR}${BOX.mr}`);
92
+ // Pattern stats
93
+ const patternToday = current.patternsCreatedToday > 0
94
+ ? chalk.green(` (+${current.patternsCreatedToday} today)`)
95
+ : '';
96
+ console.log(`${BOX.v} Patterns: ${padRight(String(current.totalPatterns) + patternToday, 32)}${BOX.v}`);
97
+ // Experience stats
98
+ const expToday = current.experiencesToday > 0
99
+ ? chalk.green(` (+${current.experiencesToday} today)`)
100
+ : '';
101
+ console.log(`${BOX.v} Experiences: ${padRight(String(current.totalExperiences) + expToday, 32)}${BOX.v}`);
102
+ // Q-Values
103
+ console.log(`${BOX.v} Q-Values: ${padRight(String(current.totalQValues), 32)}${BOX.v}`);
104
+ // Average Reward with trend
105
+ const rewardStr = current.avgReward.toFixed(2);
106
+ const rewardTrend = current.avgRewardDelta >= 0
107
+ ? chalk.green(`(↑ ${Math.abs(current.avgRewardDelta).toFixed(2)} from last week)`)
108
+ : chalk.red(`(↓ ${Math.abs(current.avgRewardDelta).toFixed(2)} from last week)`);
109
+ console.log(`${BOX.v} Avg Reward: ${padRight(`${rewardStr} ${rewardTrend}`, 32)}${BOX.v}`);
110
+ // Success rate
111
+ const successPct = (current.successRate * 100).toFixed(1);
112
+ console.log(`${BOX.v} Success Rate: ${padRight(`${successPct}%`, 32)}${BOX.v}`);
113
+ // Pattern tiers
114
+ console.log(`${BOX.v} Short-term: ${padRight(String(current.shortTermPatterns), 32)}${BOX.v}`);
115
+ console.log(`${BOX.v} Long-term: ${padRight(String(current.longTermPatterns), 32)}${BOX.v}`);
116
+ console.log(`${BOX.v}${' '.repeat(WIDTH - 2)}${BOX.v}`);
117
+ // Domain Coverage
118
+ console.log(`${BOX.v} ${chalk.bold('Domain Coverage:')}${' '.repeat(WIDTH - 19)}${BOX.v}`);
119
+ if (topDomains.length === 0) {
120
+ console.log(`${BOX.v} ${chalk.dim('No patterns yet')}${' '.repeat(WIDTH - 19)}${BOX.v}`);
121
+ }
122
+ else {
123
+ const maxCount = Math.max(...topDomains.map(d => d.count), 1);
124
+ const barWidth = 14;
125
+ for (const { domain, count } of topDomains) {
126
+ const filledBars = Math.round((count / maxCount) * barWidth);
127
+ const emptyBars = barWidth - filledBars;
128
+ const bar = chalk.green('ā–ˆ'.repeat(filledBars)) + chalk.dim('ā–‘'.repeat(emptyBars));
129
+ const domainName = padRight(domain, 20);
130
+ const countStr = padLeft(String(count), 3);
131
+ console.log(`${BOX.v} ${domainName} ${bar} ${countStr} patterns ${BOX.v}`);
132
+ }
133
+ // Show remaining domains with 0 patterns
134
+ const shownDomains = new Set(topDomains.map(d => d.domain));
135
+ const zeroDomains = QE_DOMAIN_LIST.filter(d => !shownDomains.has(d)).slice(0, 3);
136
+ for (const domain of zeroDomains) {
137
+ const bar = chalk.dim('ā–‘'.repeat(barWidth));
138
+ const domainName = padRight(domain, 20);
139
+ console.log(`${BOX.v} ${domainName} ${bar} 0 patterns ${BOX.v}`);
140
+ }
141
+ }
142
+ console.log(`${BOX.bl}${HR}${BOX.br}`);
143
+ console.log('');
144
+ }
145
+ /**
146
+ * Center text within a given width
147
+ */
148
+ function centerText(text, width) {
149
+ const stripped = stripAnsi(text);
150
+ const padding = Math.max(0, width - stripped.length);
151
+ const leftPad = Math.floor(padding / 2);
152
+ const rightPad = padding - leftPad;
153
+ return ' '.repeat(leftPad) + text + ' '.repeat(rightPad);
154
+ }
155
+ /**
156
+ * Pad string to the right
157
+ */
158
+ function padRight(text, width) {
159
+ const stripped = stripAnsi(text);
160
+ const padding = Math.max(0, width - stripped.length);
161
+ return text + ' '.repeat(padding);
162
+ }
163
+ /**
164
+ * Pad string to the left
165
+ */
166
+ function padLeft(text, width) {
167
+ const stripped = stripAnsi(text);
168
+ const padding = Math.max(0, width - stripped.length);
169
+ return ' '.repeat(padding) + text;
170
+ }
171
+ /**
172
+ * Strip ANSI escape codes from a string
173
+ */
174
+ function stripAnsi(text) {
175
+ // eslint-disable-next-line no-control-regex
176
+ return text.replace(/\x1b\[[0-9;]*m/g, '');
177
+ }
178
+ /**
179
+ * Get the learning database path
180
+ */
181
+ function getDbPath() {
182
+ const cwd = process.cwd();
183
+ return path.join(cwd, '.agentic-qe', 'memory.db');
184
+ }
185
+ /**
186
+ * Compress a file using gzip
187
+ */
188
+ async function compressFile(inputPath, outputPath) {
189
+ const gzPath = outputPath || `${inputPath}.gz`;
190
+ await pipeline(createReadStream(inputPath), createGzip(), createWriteStream(gzPath));
191
+ return gzPath;
192
+ }
193
+ /**
194
+ * Decompress a gzipped file
195
+ */
196
+ async function decompressFile(gzPath, outputPath) {
197
+ await pipeline(createReadStream(gzPath), createGunzip(), createWriteStream(outputPath));
198
+ }
199
+ /**
200
+ * Verify database integrity using SQLite's built-in check
201
+ */
202
+ async function verifyDatabaseIntegrity(dbPath) {
203
+ try {
204
+ const Database = (await import('better-sqlite3')).default;
205
+ const db = new Database(dbPath, { readonly: true });
206
+ // Run SQLite integrity check
207
+ const result = db.prepare('PRAGMA integrity_check').get();
208
+ db.close();
209
+ if (result.integrity_check === 'ok') {
210
+ return { valid: true, message: 'Database integrity verified' };
211
+ }
212
+ else {
213
+ return { valid: false, message: `Integrity check failed: ${result.integrity_check}` };
214
+ }
215
+ }
216
+ catch (error) {
217
+ return {
218
+ valid: false,
219
+ message: `Failed to verify: ${error instanceof Error ? error.message : 'unknown error'}`
220
+ };
221
+ }
222
+ }
223
+ /**
224
+ * Get database schema version
225
+ */
226
+ async function getSchemaVersion(dbPath) {
227
+ try {
228
+ const Database = (await import('better-sqlite3')).default;
229
+ const db = new Database(dbPath, { readonly: true });
230
+ // Check if schema_version table exists
231
+ const tableExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'").get();
232
+ if (!tableExists) {
233
+ db.close();
234
+ return 0;
235
+ }
236
+ const result = db.prepare('SELECT version FROM schema_version WHERE id = 1').get();
237
+ db.close();
238
+ return result?.version ?? 0;
239
+ }
240
+ catch {
241
+ return 0;
242
+ }
243
+ }
244
+ /**
245
+ * Export format version for compatibility tracking
246
+ */
247
+ const EXPORT_FORMAT_VERSION = '3.1.0';
248
+ // ============================================================================
249
+ // Learning Command
250
+ // ============================================================================
251
+ /**
252
+ * Create the learning command with all subcommands
253
+ */
254
+ export function createLearningCommand() {
255
+ const learning = new Command('learning')
256
+ .description('AQE self-learning system management (standalone, no claude-flow required)')
257
+ .addHelpText('after', `
258
+ Examples:
259
+ # Check learning system status
260
+ aqe learning stats
261
+
262
+ # Consolidate patterns (promote successful patterns to long-term memory)
263
+ aqe learning consolidate
264
+
265
+ # Export learned patterns for sharing
266
+ aqe learning export --output patterns.json
267
+
268
+ # Import patterns from another project
269
+ aqe learning import --input patterns.json
270
+
271
+ # Run background learning consolidation
272
+ aqe learning daemon --interval 3600
273
+ `);
274
+ // -------------------------------------------------------------------------
275
+ // stats: Display learning statistics
276
+ // -------------------------------------------------------------------------
277
+ learning
278
+ .command('stats')
279
+ .description('Display learning system statistics')
280
+ .option('--json', 'Output as JSON')
281
+ .option('--detailed', 'Show detailed breakdown')
282
+ .action(async (options) => {
283
+ try {
284
+ const reasoningBank = await initializeLearningSystem();
285
+ const stats = await reasoningBank.getStats();
286
+ if (options.json) {
287
+ printJson(stats);
288
+ }
289
+ else {
290
+ console.log(chalk.bold('\nšŸ“Š AQE Learning System Statistics\n'));
291
+ // Overview
292
+ console.log(chalk.bold('Patterns:'));
293
+ console.log(` Total: ${chalk.cyan(stats.totalPatterns)}`);
294
+ console.log(` Short-term: ${stats.patternStoreStats.byTier.shortTerm}`);
295
+ console.log(` Long-term: ${stats.patternStoreStats.byTier.longTerm}`);
296
+ console.log(` Avg Confidence: ${(stats.patternStoreStats.avgConfidence * 100).toFixed(1)}%`);
297
+ console.log(` Avg Quality: ${(stats.patternStoreStats.avgQualityScore * 100).toFixed(1)}%`);
298
+ // By Domain
299
+ console.log(chalk.bold('\nBy Domain:'));
300
+ const domainsWithPatterns = Object.entries(stats.byDomain)
301
+ .filter(([_, count]) => count > 0)
302
+ .sort((a, b) => b[1] - a[1]);
303
+ if (domainsWithPatterns.length === 0) {
304
+ console.log(chalk.dim(' No patterns yet'));
305
+ }
306
+ else {
307
+ for (const [domain, count] of domainsWithPatterns) {
308
+ console.log(` ${domain}: ${count}`);
309
+ }
310
+ }
311
+ // Learning Metrics
312
+ console.log(chalk.bold('\nLearning:'));
313
+ console.log(` Routing Requests: ${stats.routingRequests}`);
314
+ console.log(` Avg Routing Confidence: ${(stats.avgRoutingConfidence * 100).toFixed(1)}%`);
315
+ console.log(` Learning Outcomes: ${stats.learningOutcomes}`);
316
+ console.log(` Pattern Success Rate: ${(stats.patternSuccessRate * 100).toFixed(1)}%`);
317
+ // Search Performance
318
+ console.log(chalk.bold('\nSearch Performance:'));
319
+ console.log(` Operations: ${stats.patternStoreStats.searchOperations}`);
320
+ console.log(` Avg Latency: ${stats.patternStoreStats.avgSearchLatencyMs.toFixed(2)}ms`);
321
+ console.log(` HNSW Native: ${stats.patternStoreStats.hnswStats.nativeAvailable ? chalk.green('āœ“') : chalk.dim('ā—‹')}`);
322
+ console.log(` Vector Count: ${stats.patternStoreStats.hnswStats.vectorCount}`);
323
+ if (options.detailed) {
324
+ console.log(chalk.bold('\nBy Pattern Type:'));
325
+ for (const [type, count] of Object.entries(stats.patternStoreStats.byType)) {
326
+ if (count > 0) {
327
+ console.log(` ${type}: ${count}`);
328
+ }
329
+ }
330
+ }
331
+ console.log('');
332
+ }
333
+ process.exit(0);
334
+ }
335
+ catch (error) {
336
+ printError(`stats failed: ${error instanceof Error ? error.message : 'unknown'}`);
337
+ process.exit(1);
338
+ }
339
+ });
340
+ // -------------------------------------------------------------------------
341
+ // consolidate: Promote successful patterns to long-term memory
342
+ // -------------------------------------------------------------------------
343
+ learning
344
+ .command('consolidate')
345
+ .description('Consolidate patterns - promote successful patterns to long-term memory')
346
+ .option('--dry-run', 'Show what would be promoted without making changes')
347
+ .option('--threshold <n>', 'Minimum successful uses for promotion', '3')
348
+ .option('--success-rate <n>', 'Minimum success rate for promotion (0-1)', '0.7')
349
+ .option('--json', 'Output as JSON')
350
+ .action(async (options) => {
351
+ try {
352
+ const reasoningBank = await initializeLearningSystem();
353
+ const stats = await reasoningBank.getStats();
354
+ const threshold = parseInt(options.threshold, 10);
355
+ const successRateMin = parseFloat(options.successRate);
356
+ // Get patterns eligible for promotion
357
+ const eligiblePatterns = [];
358
+ // Search for patterns that meet promotion criteria
359
+ // This would be enhanced with actual pattern store access
360
+ const searchResult = await reasoningBank.searchPatterns('*', { limit: 1000 });
361
+ if (searchResult.success) {
362
+ for (const match of searchResult.value) {
363
+ const pattern = match.pattern;
364
+ if (pattern.tier === 'short-term' &&
365
+ pattern.successfulUses >= threshold &&
366
+ pattern.successRate >= successRateMin) {
367
+ eligiblePatterns.push({
368
+ id: pattern.id,
369
+ name: pattern.name,
370
+ domain: pattern.qeDomain,
371
+ successfulUses: pattern.successfulUses,
372
+ successRate: pattern.successRate,
373
+ currentTier: pattern.tier,
374
+ });
375
+ }
376
+ }
377
+ }
378
+ if (options.json) {
379
+ printJson({
380
+ dryRun: options.dryRun || false,
381
+ eligibleCount: eligiblePatterns.length,
382
+ threshold,
383
+ successRateMin,
384
+ patterns: eligiblePatterns,
385
+ });
386
+ }
387
+ else {
388
+ console.log(chalk.bold('\nšŸ”„ Pattern Consolidation\n'));
389
+ console.log(` Promotion threshold: ${threshold} successful uses`);
390
+ console.log(` Minimum success rate: ${(successRateMin * 100).toFixed(0)}%`);
391
+ console.log(` Eligible patterns: ${eligiblePatterns.length}`);
392
+ console.log('');
393
+ if (eligiblePatterns.length === 0) {
394
+ console.log(chalk.dim(' No patterns eligible for promotion yet.'));
395
+ console.log(chalk.dim(' Patterns need more successful uses to be promoted.'));
396
+ }
397
+ else {
398
+ console.log(chalk.bold(' Patterns to promote:'));
399
+ for (const p of eligiblePatterns) {
400
+ console.log(` ${chalk.cyan(p.name)}`);
401
+ console.log(chalk.dim(` Domain: ${p.domain}, Uses: ${p.successfulUses}, Rate: ${(p.successRate * 100).toFixed(0)}%`));
402
+ }
403
+ if (!options.dryRun) {
404
+ // TODO: Implement actual promotion via reasoningBank
405
+ console.log(chalk.yellow('\n Promotion would happen here (not yet implemented)'));
406
+ }
407
+ else {
408
+ console.log(chalk.yellow('\n Dry run - no changes made'));
409
+ }
410
+ }
411
+ console.log('');
412
+ }
413
+ process.exit(0);
414
+ }
415
+ catch (error) {
416
+ printError(`consolidate failed: ${error instanceof Error ? error.message : 'unknown'}`);
417
+ process.exit(1);
418
+ }
419
+ });
420
+ // -------------------------------------------------------------------------
421
+ // export: Export learned patterns
422
+ // -------------------------------------------------------------------------
423
+ learning
424
+ .command('export')
425
+ .description('Export learned patterns for sharing')
426
+ .option('-o, --output <file>', 'Output file path', 'aqe-patterns.json')
427
+ .option('-d, --domain <domain>', 'Filter by domain')
428
+ .option('--long-term-only', 'Only export long-term patterns')
429
+ .option('--json', 'Output as JSON (to stdout)')
430
+ .action(async (options) => {
431
+ try {
432
+ const reasoningBank = await initializeLearningSystem();
433
+ // Search all patterns
434
+ const searchResult = await reasoningBank.searchPatterns('*', {
435
+ limit: 10000,
436
+ domain: options.domain,
437
+ });
438
+ if (!searchResult.success) {
439
+ throw new Error(searchResult.error.message);
440
+ }
441
+ const patterns = searchResult.value
442
+ .map(m => m.pattern)
443
+ .filter(p => !options.longTermOnly || p.tier === 'long-term');
444
+ const exportData = {
445
+ version: '3.0.0',
446
+ exportedAt: new Date().toISOString(),
447
+ source: process.cwd(),
448
+ patternCount: patterns.length,
449
+ patterns: patterns.map(p => ({
450
+ id: p.id,
451
+ name: p.name,
452
+ description: p.description,
453
+ patternType: p.patternType,
454
+ qeDomain: p.qeDomain,
455
+ tier: p.tier,
456
+ confidence: p.confidence,
457
+ successRate: p.successRate,
458
+ successfulUses: p.successfulUses,
459
+ qualityScore: p.qualityScore,
460
+ template: p.template,
461
+ context: p.context,
462
+ })),
463
+ };
464
+ if (options.json) {
465
+ printJson(exportData);
466
+ }
467
+ else {
468
+ const outputPath = path.resolve(options.output);
469
+ writeFileSync(outputPath, JSON.stringify(exportData, null, 2), 'utf-8');
470
+ printSuccess(`Exported ${patterns.length} patterns to ${outputPath}`);
471
+ }
472
+ process.exit(0);
473
+ }
474
+ catch (error) {
475
+ printError(`export failed: ${error instanceof Error ? error.message : 'unknown'}`);
476
+ process.exit(1);
477
+ }
478
+ });
479
+ // -------------------------------------------------------------------------
480
+ // import: Import patterns from file
481
+ // -------------------------------------------------------------------------
482
+ learning
483
+ .command('import')
484
+ .description('Import patterns from file')
485
+ .requiredOption('-i, --input <file>', 'Input file path')
486
+ .option('--dry-run', 'Show what would be imported without making changes')
487
+ .option('--json', 'Output as JSON')
488
+ .action(async (options) => {
489
+ try {
490
+ const inputPath = path.resolve(options.input);
491
+ if (!existsSync(inputPath)) {
492
+ throw new Error(`File not found: ${inputPath}`);
493
+ }
494
+ const content = readFileSync(inputPath, 'utf-8');
495
+ const importData = JSON.parse(content);
496
+ if (!importData.patterns || !Array.isArray(importData.patterns)) {
497
+ throw new Error('Invalid pattern file format');
498
+ }
499
+ const reasoningBank = await initializeLearningSystem();
500
+ let imported = 0;
501
+ let skipped = 0;
502
+ const errors = [];
503
+ if (!options.dryRun) {
504
+ for (const pattern of importData.patterns) {
505
+ try {
506
+ const result = await reasoningBank.storePattern({
507
+ patternType: pattern.patternType,
508
+ name: pattern.name,
509
+ description: pattern.description,
510
+ template: pattern.template,
511
+ context: pattern.context,
512
+ });
513
+ if (result.success) {
514
+ imported++;
515
+ }
516
+ else {
517
+ skipped++;
518
+ errors.push(`${pattern.name}: ${result.error.message}`);
519
+ }
520
+ }
521
+ catch (e) {
522
+ skipped++;
523
+ errors.push(`${pattern.name}: ${e instanceof Error ? e.message : 'unknown'}`);
524
+ }
525
+ }
526
+ }
527
+ if (options.json) {
528
+ printJson({
529
+ dryRun: options.dryRun || false,
530
+ totalPatterns: importData.patterns.length,
531
+ imported,
532
+ skipped,
533
+ errors,
534
+ });
535
+ }
536
+ else {
537
+ console.log(chalk.bold('\nšŸ“„ Pattern Import\n'));
538
+ console.log(` Source: ${inputPath}`);
539
+ console.log(` Total patterns: ${importData.patterns.length}`);
540
+ if (options.dryRun) {
541
+ console.log(chalk.yellow('\n Dry run - no changes made'));
542
+ console.log(` Would import: ${importData.patterns.length} patterns`);
543
+ }
544
+ else {
545
+ console.log(chalk.green(`\n Imported: ${imported}`));
546
+ if (skipped > 0) {
547
+ console.log(chalk.yellow(` Skipped: ${skipped}`));
548
+ }
549
+ }
550
+ console.log('');
551
+ }
552
+ process.exit(0);
553
+ }
554
+ catch (error) {
555
+ printError(`import failed: ${error instanceof Error ? error.message : 'unknown'}`);
556
+ process.exit(1);
557
+ }
558
+ });
559
+ // -------------------------------------------------------------------------
560
+ // reset: Reset learning data
561
+ // -------------------------------------------------------------------------
562
+ learning
563
+ .command('reset')
564
+ .description('Reset learning data (patterns and metrics)')
565
+ .option('--confirm', 'Confirm reset without prompting')
566
+ .option('--patterns-only', 'Only reset patterns, keep metrics')
567
+ .action(async (options) => {
568
+ try {
569
+ if (!options.confirm) {
570
+ console.log(chalk.yellow('\nāš ļø This will reset your learning data.'));
571
+ console.log(chalk.gray(' Use --confirm to proceed'));
572
+ console.log('');
573
+ process.exit(0);
574
+ }
575
+ const cwd = process.cwd();
576
+ const dataDir = path.join(cwd, '.agentic-qe');
577
+ // List files that would be affected
578
+ const filesToReset = [
579
+ path.join(dataDir, 'data', 'patterns'),
580
+ path.join(dataDir, 'data', 'hnsw'),
581
+ ];
582
+ if (!options.patternsOnly) {
583
+ filesToReset.push(path.join(dataDir, 'data', 'learning-config.json'));
584
+ }
585
+ console.log(chalk.bold('\nšŸ—‘ļø Resetting Learning Data\n'));
586
+ for (const file of filesToReset) {
587
+ if (existsSync(file)) {
588
+ console.log(chalk.dim(` Removing: ${path.relative(cwd, file)}`));
589
+ // Actual deletion would happen here
590
+ }
591
+ }
592
+ printSuccess('Learning data reset. Run "aqe init --auto" to reinitialize.');
593
+ console.log('');
594
+ process.exit(0);
595
+ }
596
+ catch (error) {
597
+ printError(`reset failed: ${error instanceof Error ? error.message : 'unknown'}`);
598
+ process.exit(1);
599
+ }
600
+ });
601
+ // -------------------------------------------------------------------------
602
+ // extract: Extract patterns from learning experiences
603
+ // -------------------------------------------------------------------------
604
+ learning
605
+ .command('extract')
606
+ .description('Extract QE patterns from existing learning experiences')
607
+ .option('--min-reward <n>', 'Minimum reward threshold for pattern extraction', '0.7')
608
+ .option('--min-count <n>', 'Minimum occurrences to form a pattern', '3')
609
+ .option('--dry-run', 'Show what would be extracted without saving')
610
+ .option('--json', 'Output as JSON')
611
+ .action(async (options) => {
612
+ try {
613
+ const cwd = process.cwd();
614
+ const dbPath = path.join(cwd, '.agentic-qe', 'memory.db');
615
+ if (!existsSync(dbPath)) {
616
+ throw new Error('No memory database found. Run "aqe init --auto" first.');
617
+ }
618
+ const minReward = parseFloat(options.minReward);
619
+ const minCount = parseInt(options.minCount, 10);
620
+ console.log(chalk.bold('\nšŸ”¬ Pattern Extraction from Learning Experiences\n'));
621
+ console.log(` Min reward threshold: ${minReward}`);
622
+ console.log(` Min occurrences: ${minCount}`);
623
+ console.log('');
624
+ // Dynamic import for better-sqlite3
625
+ const Database = (await import('better-sqlite3')).default;
626
+ const db = new Database(dbPath, { readonly: true });
627
+ // Query learning experiences grouped by task_type
628
+ const experiences = db.prepare(`
629
+ SELECT
630
+ task_type,
631
+ COUNT(*) as count,
632
+ AVG(reward) as avg_reward,
633
+ MAX(reward) as max_reward,
634
+ MIN(reward) as min_reward,
635
+ GROUP_CONCAT(DISTINCT action) as actions
636
+ FROM learning_experiences
637
+ WHERE reward >= ?
638
+ GROUP BY task_type
639
+ HAVING COUNT(*) >= ?
640
+ ORDER BY avg_reward DESC
641
+ `).all(minReward, minCount);
642
+ // Query memory entries for additional context
643
+ const memoryPatterns = db.prepare(`
644
+ SELECT
645
+ substr(key, 1, 40) as key_prefix,
646
+ COUNT(*) as count
647
+ FROM memory_entries
648
+ WHERE key LIKE 'phase2/learning/%'
649
+ GROUP BY substr(key, 1, 40)
650
+ HAVING COUNT(*) >= ?
651
+ ORDER BY COUNT(*) DESC
652
+ LIMIT 20
653
+ `).all(minCount);
654
+ db.close();
655
+ // Map task types to QE domains
656
+ const domainMapping = {
657
+ 'generate': 'test-generation',
658
+ 'test-generation': 'test-generation',
659
+ 'analyze': 'coverage-analysis',
660
+ 'coverage': 'coverage-analysis',
661
+ 'coverage-analysis': 'coverage-analysis',
662
+ 'run': 'test-execution',
663
+ 'test-execution': 'test-execution',
664
+ 'report': 'quality-assessment',
665
+ 'quality': 'quality-assessment',
666
+ 'quality-analysis': 'quality-assessment',
667
+ 'security': 'security-compliance',
668
+ 'sast': 'security-compliance',
669
+ 'owasp': 'security-compliance',
670
+ 'secrets': 'security-compliance',
671
+ 'audit': 'security-compliance',
672
+ 'recommend': 'defect-intelligence',
673
+ 'predict': 'defect-intelligence',
674
+ 'complexity-analysis': 'code-intelligence',
675
+ 'code-analysis': 'code-intelligence',
676
+ 'stabilize': 'chaos-resilience',
677
+ 'flaky': 'chaos-resilience',
678
+ 'quarantine': 'chaos-resilience',
679
+ 'retry': 'chaos-resilience',
680
+ 'stress': 'chaos-resilience',
681
+ 'load': 'chaos-resilience',
682
+ 'endurance': 'chaos-resilience',
683
+ 'baseline': 'chaos-resilience',
684
+ };
685
+ // Map task types to valid QE pattern types
686
+ const patternTypeMapping = {
687
+ 'generate': 'test-template',
688
+ 'test-generation': 'test-template',
689
+ 'analyze': 'coverage-strategy',
690
+ 'coverage': 'coverage-strategy',
691
+ 'coverage-analysis': 'coverage-strategy',
692
+ 'run': 'test-template',
693
+ 'test-execution': 'test-template',
694
+ 'report': 'assertion-pattern',
695
+ 'quality': 'assertion-pattern',
696
+ 'quality-analysis': 'assertion-pattern',
697
+ 'security': 'assertion-pattern',
698
+ 'sast': 'assertion-pattern',
699
+ 'owasp': 'assertion-pattern',
700
+ 'secrets': 'assertion-pattern',
701
+ 'audit': 'assertion-pattern',
702
+ 'recommend': 'assertion-pattern',
703
+ 'predict': 'assertion-pattern',
704
+ 'complexity-analysis': 'assertion-pattern',
705
+ 'code-analysis': 'assertion-pattern',
706
+ 'stabilize': 'flaky-fix',
707
+ 'flaky': 'flaky-fix',
708
+ 'quarantine': 'flaky-fix',
709
+ 'retry': 'flaky-fix',
710
+ 'stress': 'perf-benchmark',
711
+ 'load': 'perf-benchmark',
712
+ 'endurance': 'perf-benchmark',
713
+ 'baseline': 'perf-benchmark',
714
+ 'mock': 'mock-pattern',
715
+ 'dependency': 'mock-pattern',
716
+ };
717
+ // Extract patterns
718
+ const extractedPatterns = [];
719
+ for (const exp of experiences) {
720
+ const domain = domainMapping[exp.task_type] || 'code-intelligence';
721
+ const patternType = patternTypeMapping[exp.task_type] || 'test-template';
722
+ const actions = exp.actions ? exp.actions.split(',').slice(0, 5) : [];
723
+ extractedPatterns.push({
724
+ name: `${exp.task_type}-success-pattern`,
725
+ domain,
726
+ patternType,
727
+ confidence: Math.min(0.95, exp.avg_reward / 2),
728
+ sourceCount: exp.count,
729
+ avgReward: exp.avg_reward,
730
+ actions,
731
+ });
732
+ }
733
+ if (options.json) {
734
+ printJson({
735
+ minReward,
736
+ minCount,
737
+ dryRun: options.dryRun || false,
738
+ experienceGroups: experiences.length,
739
+ memoryPatterns: memoryPatterns.length,
740
+ extractedPatterns,
741
+ });
742
+ }
743
+ else {
744
+ console.log(chalk.bold('Learning Experience Groups:'));
745
+ if (experiences.length === 0) {
746
+ console.log(chalk.dim(' No qualifying experience groups found'));
747
+ }
748
+ else {
749
+ for (const exp of experiences) {
750
+ const rewardColor = exp.avg_reward >= 1.0 ? chalk.green :
751
+ exp.avg_reward >= 0.5 ? chalk.yellow : chalk.red;
752
+ console.log(` ${chalk.cyan(exp.task_type)}`);
753
+ console.log(` Count: ${exp.count}, Avg Reward: ${rewardColor(exp.avg_reward.toFixed(2))}`);
754
+ }
755
+ }
756
+ console.log(chalk.bold('\nMemory Pattern Sources:'));
757
+ for (const mp of memoryPatterns.slice(0, 10)) {
758
+ console.log(` ${mp.key_prefix}... (${mp.count})`);
759
+ }
760
+ console.log(chalk.bold('\nExtracted Patterns:'));
761
+ if (extractedPatterns.length === 0) {
762
+ console.log(chalk.dim(' No patterns extracted'));
763
+ }
764
+ else {
765
+ for (const p of extractedPatterns) {
766
+ console.log(` ${chalk.green('+')} ${chalk.cyan(p.name)}`);
767
+ console.log(chalk.dim(` Domain: ${p.domain}, Type: ${p.patternType}`));
768
+ console.log(chalk.dim(` Confidence: ${(p.confidence * 100).toFixed(0)}%, Sources: ${p.sourceCount}`));
769
+ }
770
+ }
771
+ if (!options.dryRun && extractedPatterns.length > 0) {
772
+ // Store extracted patterns
773
+ const reasoningBank = await initializeLearningSystem();
774
+ let stored = 0;
775
+ const errors = [];
776
+ for (const p of extractedPatterns) {
777
+ try {
778
+ // Build template content from actions
779
+ const templateContent = p.actions.length > 0
780
+ ? `// ${p.name}\n// Steps: ${p.actions.join(' -> ')}\n\n{{implementation}}`
781
+ : `// ${p.name}\n// Extracted pattern with ${p.sourceCount} successful uses\n\n{{implementation}}`;
782
+ const result = await reasoningBank.storePattern({
783
+ patternType: p.patternType,
784
+ name: p.name,
785
+ description: `Extracted from ${p.sourceCount} learning experiences with avg reward ${p.avgReward.toFixed(2)}`,
786
+ template: {
787
+ type: 'code',
788
+ content: templateContent,
789
+ variables: [
790
+ {
791
+ name: 'implementation',
792
+ type: 'code',
793
+ required: true,
794
+ description: 'Implementation code for this pattern',
795
+ },
796
+ ],
797
+ },
798
+ context: {
799
+ tags: [p.domain, p.patternType, `sources:${p.sourceCount}`, `reward:${p.avgReward.toFixed(2)}`],
800
+ },
801
+ });
802
+ if (result.success) {
803
+ stored++;
804
+ }
805
+ else {
806
+ errors.push(`${p.name}: ${result.error.message}`);
807
+ }
808
+ }
809
+ catch (e) {
810
+ errors.push(`${p.name}: ${e instanceof Error ? e.message : 'unknown'}`);
811
+ }
812
+ }
813
+ console.log(chalk.green(`\nāœ“ Stored ${stored} new patterns`));
814
+ if (errors.length > 0 && errors.length <= 5) {
815
+ console.log(chalk.yellow(` Errors: ${errors.slice(0, 3).join(', ')}${errors.length > 3 ? '...' : ''}`));
816
+ }
817
+ }
818
+ else if (options.dryRun) {
819
+ console.log(chalk.yellow('\n Dry run - no patterns stored'));
820
+ }
821
+ console.log('');
822
+ }
823
+ process.exit(0);
824
+ }
825
+ catch (error) {
826
+ printError(`extract failed: ${error instanceof Error ? error.message : 'unknown'}`);
827
+ process.exit(1);
828
+ }
829
+ });
830
+ // -------------------------------------------------------------------------
831
+ // dashboard: Display learning system dashboard
832
+ // -------------------------------------------------------------------------
833
+ learning
834
+ .command('dashboard')
835
+ .description('Display learning system dashboard with metrics and trends')
836
+ .option('--json', 'Output as JSON')
837
+ .option('--save-snapshot', 'Save a daily metrics snapshot')
838
+ .action(async (options) => {
839
+ try {
840
+ const cwd = process.cwd();
841
+ const tracker = createLearningMetricsTracker(cwd);
842
+ await tracker.initialize();
843
+ if (options.saveSnapshot) {
844
+ await tracker.saveSnapshot();
845
+ printSuccess('Daily snapshot saved');
846
+ }
847
+ const dashboard = await tracker.getDashboardData();
848
+ tracker.close();
849
+ if (options.json) {
850
+ printJson(dashboard);
851
+ }
852
+ else {
853
+ displayDashboard(dashboard);
854
+ }
855
+ process.exit(0);
856
+ }
857
+ catch (error) {
858
+ printError(`dashboard failed: ${error instanceof Error ? error.message : 'unknown'}`);
859
+ process.exit(1);
860
+ }
861
+ });
862
+ // -------------------------------------------------------------------------
863
+ // info: Show learning system information
864
+ // -------------------------------------------------------------------------
865
+ learning
866
+ .command('info')
867
+ .description('Show learning system configuration and paths')
868
+ .action(async () => {
869
+ try {
870
+ const cwd = process.cwd();
871
+ const dataDir = path.join(cwd, '.agentic-qe');
872
+ console.log(chalk.bold('\nšŸ“‹ AQE Learning System Info\n'));
873
+ console.log(chalk.bold('Paths:'));
874
+ console.log(` Data directory: ${dataDir}`);
875
+ console.log(` Memory database: ${path.join(dataDir, 'memory.db')}`);
876
+ console.log(` HNSW index: ${path.join(dataDir, 'data', 'hnsw')}`);
877
+ console.log(` Patterns: ${path.join(dataDir, 'data', 'patterns')}`);
878
+ console.log(chalk.bold('\nConfiguration:'));
879
+ console.log(` Learning enabled: ${process.env.AQE_LEARNING_ENABLED !== 'false' ? chalk.green('yes') : chalk.red('no')}`);
880
+ console.log(` V3 mode: ${process.env.AQE_V3_MODE === 'true' ? chalk.green('yes') : chalk.dim('no')}`);
881
+ console.log(` Promotion threshold: ${process.env.AQE_V3_PATTERN_PROMOTION_THRESHOLD || '3'} uses`);
882
+ console.log(` Success rate threshold: ${process.env.AQE_V3_SUCCESS_RATE_THRESHOLD || '0.7'}`);
883
+ console.log(chalk.bold('\nDomains:'));
884
+ console.log(` ${QE_DOMAIN_LIST.join(', ')}`);
885
+ console.log(chalk.bold('\nHooks:'));
886
+ console.log(' Configure in .claude/settings.json');
887
+ console.log(' Events: pre-edit, post-edit, pre-task, post-task, route');
888
+ console.log('');
889
+ process.exit(0);
890
+ }
891
+ catch (error) {
892
+ printError(`info failed: ${error instanceof Error ? error.message : 'unknown'}`);
893
+ process.exit(1);
894
+ }
895
+ });
896
+ // -------------------------------------------------------------------------
897
+ // backup: Backup learning database
898
+ // -------------------------------------------------------------------------
899
+ learning
900
+ .command('backup')
901
+ .description('Backup learning database to a file')
902
+ .option('-o, --output <path>', 'Output file path')
903
+ .option('--compress', 'Compress backup with gzip')
904
+ .option('--verify', 'Verify backup integrity after creation')
905
+ .option('--json', 'Output as JSON')
906
+ .action(async (options) => {
907
+ try {
908
+ const dbPath = getDbPath();
909
+ if (!existsSync(dbPath)) {
910
+ throw new Error(`No learning database found at: ${dbPath}`);
911
+ }
912
+ // Generate default output path with timestamp
913
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
914
+ const defaultOutput = path.join(process.cwd(), 'backups', `learning-${timestamp}.db`);
915
+ let outputPath = options.output ? path.resolve(options.output) : defaultOutput;
916
+ // Ensure backup directory exists
917
+ const backupDir = path.dirname(outputPath);
918
+ if (!existsSync(backupDir)) {
919
+ mkdirSync(backupDir, { recursive: true });
920
+ }
921
+ // Get source file size
922
+ const sourceStats = await stat(dbPath);
923
+ const sourceSizeKB = (sourceStats.size / 1024).toFixed(2);
924
+ // Copy the database file
925
+ copyFileSync(dbPath, outputPath);
926
+ // Also copy WAL file if it exists (for consistency)
927
+ const walPath = `${dbPath}-wal`;
928
+ if (existsSync(walPath)) {
929
+ copyFileSync(walPath, `${outputPath}-wal`);
930
+ }
931
+ // Compress if requested
932
+ let finalPath = outputPath;
933
+ if (options.compress) {
934
+ finalPath = await compressFile(outputPath);
935
+ // Remove uncompressed version
936
+ await unlink(outputPath);
937
+ if (existsSync(`${outputPath}-wal`)) {
938
+ await unlink(`${outputPath}-wal`);
939
+ }
940
+ }
941
+ // Get final file size
942
+ const finalStats = await stat(finalPath);
943
+ const finalSizeKB = (finalStats.size / 1024).toFixed(2);
944
+ // Verify if requested
945
+ let verificationResult;
946
+ if (options.verify && !options.compress) {
947
+ verificationResult = await verifyDatabaseIntegrity(outputPath);
948
+ }
949
+ // Get schema version
950
+ const schemaVersion = await getSchemaVersion(dbPath);
951
+ if (options.json) {
952
+ printJson({
953
+ success: true,
954
+ sourcePath: dbPath,
955
+ backupPath: finalPath,
956
+ sourceSizeKB: parseFloat(sourceSizeKB),
957
+ backupSizeKB: parseFloat(finalSizeKB),
958
+ compressed: options.compress || false,
959
+ schemaVersion,
960
+ verification: verificationResult,
961
+ timestamp: new Date().toISOString(),
962
+ });
963
+ }
964
+ else {
965
+ console.log(chalk.bold('\nšŸ’¾ Learning Database Backup\n'));
966
+ console.log(` Source: ${dbPath}`);
967
+ console.log(` Source size: ${sourceSizeKB} KB`);
968
+ console.log(` Backup: ${finalPath}`);
969
+ console.log(` Backup size: ${finalSizeKB} KB`);
970
+ console.log(` Schema version: ${schemaVersion}`);
971
+ if (options.compress) {
972
+ const compressionRatio = ((1 - finalStats.size / sourceStats.size) * 100).toFixed(1);
973
+ console.log(` Compression: ${compressionRatio}% reduction`);
974
+ }
975
+ if (verificationResult) {
976
+ if (verificationResult.valid) {
977
+ console.log(chalk.green(` Verification: ${verificationResult.message}`));
978
+ }
979
+ else {
980
+ console.log(chalk.red(` Verification: ${verificationResult.message}`));
981
+ }
982
+ }
983
+ printSuccess(`Backup saved to: ${finalPath}`);
984
+ console.log('');
985
+ }
986
+ process.exit(0);
987
+ }
988
+ catch (error) {
989
+ printError(`backup failed: ${error instanceof Error ? error.message : 'unknown'}`);
990
+ process.exit(1);
991
+ }
992
+ });
993
+ // -------------------------------------------------------------------------
994
+ // restore: Restore learning database from backup
995
+ // -------------------------------------------------------------------------
996
+ learning
997
+ .command('restore')
998
+ .description('Restore learning database from backup')
999
+ .requiredOption('-i, --input <path>', 'Backup file path to restore from')
1000
+ .option('--verify', 'Verify backup integrity before restore')
1001
+ .option('--force', 'Overwrite existing database without confirmation')
1002
+ .option('--json', 'Output as JSON')
1003
+ .action(async (options) => {
1004
+ try {
1005
+ const inputPath = path.resolve(options.input);
1006
+ const dbPath = getDbPath();
1007
+ if (!existsSync(inputPath)) {
1008
+ throw new Error(`Backup file not found: ${inputPath}`);
1009
+ }
1010
+ // Determine if compressed
1011
+ const isCompressed = inputPath.endsWith('.gz');
1012
+ let restorePath = inputPath;
1013
+ // Decompress if needed
1014
+ if (isCompressed) {
1015
+ const tempPath = inputPath.replace('.gz', '.tmp');
1016
+ await decompressFile(inputPath, tempPath);
1017
+ restorePath = tempPath;
1018
+ }
1019
+ // Verify if requested
1020
+ if (options.verify) {
1021
+ const verificationResult = await verifyDatabaseIntegrity(restorePath);
1022
+ if (!verificationResult.valid) {
1023
+ if (isCompressed && existsSync(restorePath)) {
1024
+ await unlink(restorePath);
1025
+ }
1026
+ throw new Error(`Backup verification failed: ${verificationResult.message}`);
1027
+ }
1028
+ }
1029
+ // Check if target exists
1030
+ if (existsSync(dbPath) && !options.force) {
1031
+ printError(`Database already exists at: ${dbPath}`);
1032
+ console.log(chalk.yellow(' Use --force to overwrite'));
1033
+ if (isCompressed && existsSync(restorePath)) {
1034
+ await unlink(restorePath);
1035
+ }
1036
+ process.exit(1);
1037
+ }
1038
+ // Ensure target directory exists
1039
+ const targetDir = path.dirname(dbPath);
1040
+ if (!existsSync(targetDir)) {
1041
+ mkdirSync(targetDir, { recursive: true });
1042
+ }
1043
+ // Remove existing database files
1044
+ if (existsSync(dbPath)) {
1045
+ await unlink(dbPath);
1046
+ }
1047
+ if (existsSync(`${dbPath}-wal`)) {
1048
+ await unlink(`${dbPath}-wal`);
1049
+ }
1050
+ if (existsSync(`${dbPath}-shm`)) {
1051
+ await unlink(`${dbPath}-shm`);
1052
+ }
1053
+ // Copy restored file to target
1054
+ copyFileSync(restorePath, dbPath);
1055
+ // Clean up temp file if we decompressed
1056
+ if (isCompressed && existsSync(restorePath)) {
1057
+ await unlink(restorePath);
1058
+ }
1059
+ // Get restored database info
1060
+ const restoredStats = await stat(dbPath);
1061
+ const schemaVersion = await getSchemaVersion(dbPath);
1062
+ if (options.json) {
1063
+ printJson({
1064
+ success: true,
1065
+ backupPath: inputPath,
1066
+ restoredPath: dbPath,
1067
+ sizeKB: parseFloat((restoredStats.size / 1024).toFixed(2)),
1068
+ schemaVersion,
1069
+ wasCompressed: isCompressed,
1070
+ timestamp: new Date().toISOString(),
1071
+ });
1072
+ }
1073
+ else {
1074
+ console.log(chalk.bold('\nšŸ”„ Learning Database Restore\n'));
1075
+ console.log(` Backup: ${inputPath}`);
1076
+ console.log(` Restored to: ${dbPath}`);
1077
+ console.log(` Size: ${(restoredStats.size / 1024).toFixed(2)} KB`);
1078
+ console.log(` Schema version: ${schemaVersion}`);
1079
+ printSuccess('Database restored successfully');
1080
+ console.log('');
1081
+ }
1082
+ process.exit(0);
1083
+ }
1084
+ catch (error) {
1085
+ printError(`restore failed: ${error instanceof Error ? error.message : 'unknown'}`);
1086
+ process.exit(1);
1087
+ }
1088
+ });
1089
+ // -------------------------------------------------------------------------
1090
+ // verify: Verify database integrity
1091
+ // -------------------------------------------------------------------------
1092
+ learning
1093
+ .command('verify')
1094
+ .description('Verify learning database integrity')
1095
+ .option('-f, --file <path>', 'Database file to verify (defaults to current)')
1096
+ .option('--json', 'Output as JSON')
1097
+ .action(async (options) => {
1098
+ try {
1099
+ const dbPath = options.file ? path.resolve(options.file) : getDbPath();
1100
+ if (!existsSync(dbPath)) {
1101
+ throw new Error(`Database file not found: ${dbPath}`);
1102
+ }
1103
+ const verificationResult = await verifyDatabaseIntegrity(dbPath);
1104
+ const schemaVersion = await getSchemaVersion(dbPath);
1105
+ const fileStats = await stat(dbPath);
1106
+ // Get table counts
1107
+ let tableCounts = {};
1108
+ try {
1109
+ const Database = (await import('better-sqlite3')).default;
1110
+ const db = new Database(dbPath, { readonly: true });
1111
+ const tables = ['qe_patterns', 'qe_trajectories', 'learning_experiences', 'kv_store', 'vectors'];
1112
+ for (const table of tables) {
1113
+ try {
1114
+ const result = db.prepare(`SELECT COUNT(*) as count FROM ${table}`).get();
1115
+ tableCounts[table] = result.count;
1116
+ }
1117
+ catch {
1118
+ // Table may not exist
1119
+ }
1120
+ }
1121
+ db.close();
1122
+ }
1123
+ catch {
1124
+ // Ignore table count errors
1125
+ }
1126
+ if (options.json) {
1127
+ printJson({
1128
+ valid: verificationResult.valid,
1129
+ message: verificationResult.message,
1130
+ path: dbPath,
1131
+ sizeKB: parseFloat((fileStats.size / 1024).toFixed(2)),
1132
+ schemaVersion,
1133
+ tableCounts,
1134
+ timestamp: new Date().toISOString(),
1135
+ });
1136
+ }
1137
+ else {
1138
+ console.log(chalk.bold('\nšŸ” Database Verification\n'));
1139
+ console.log(` Path: ${dbPath}`);
1140
+ console.log(` Size: ${(fileStats.size / 1024).toFixed(2)} KB`);
1141
+ console.log(` Schema version: ${schemaVersion}`);
1142
+ if (verificationResult.valid) {
1143
+ console.log(chalk.green(` Status: ${verificationResult.message}`));
1144
+ }
1145
+ else {
1146
+ console.log(chalk.red(` Status: ${verificationResult.message}`));
1147
+ }
1148
+ if (Object.keys(tableCounts).length > 0) {
1149
+ console.log(chalk.bold('\nTable Counts:'));
1150
+ for (const [table, count] of Object.entries(tableCounts)) {
1151
+ console.log(` ${table}: ${count}`);
1152
+ }
1153
+ }
1154
+ console.log('');
1155
+ }
1156
+ process.exit(verificationResult.valid ? 0 : 1);
1157
+ }
1158
+ catch (error) {
1159
+ printError(`verify failed: ${error instanceof Error ? error.message : 'unknown'}`);
1160
+ process.exit(1);
1161
+ }
1162
+ });
1163
+ // -------------------------------------------------------------------------
1164
+ // export-full: Enhanced export with all learning data
1165
+ // -------------------------------------------------------------------------
1166
+ learning
1167
+ .command('export-full')
1168
+ .description('Export all learning data including patterns, trajectories, and experiences')
1169
+ .option('-o, --output <file>', 'Output file path', 'aqe-learning-export.json')
1170
+ .option('--compress', 'Compress output with gzip')
1171
+ .option('--include-trajectories', 'Include learning trajectories')
1172
+ .option('--include-experiences', 'Include learning experiences')
1173
+ .option('--json', 'Output as JSON (to stdout)')
1174
+ .action(async (options) => {
1175
+ try {
1176
+ const dbPath = getDbPath();
1177
+ if (!existsSync(dbPath)) {
1178
+ throw new Error('No learning database found. Run "aqe init --auto" first.');
1179
+ }
1180
+ const reasoningBank = await initializeLearningSystem();
1181
+ const schemaVersion = await getSchemaVersion(dbPath);
1182
+ // Get all patterns
1183
+ const searchResult = await reasoningBank.searchPatterns('*', { limit: 10000 });
1184
+ if (!searchResult.success) {
1185
+ throw new Error(searchResult.error.message);
1186
+ }
1187
+ const patterns = searchResult.value.map(m => ({
1188
+ id: m.pattern.id,
1189
+ name: m.pattern.name,
1190
+ description: m.pattern.description,
1191
+ patternType: m.pattern.patternType,
1192
+ qeDomain: m.pattern.qeDomain,
1193
+ tier: m.pattern.tier,
1194
+ confidence: m.pattern.confidence,
1195
+ successRate: m.pattern.successRate,
1196
+ successfulUses: m.pattern.successfulUses,
1197
+ qualityScore: m.pattern.qualityScore,
1198
+ template: m.pattern.template,
1199
+ context: m.pattern.context,
1200
+ createdAt: m.pattern.createdAt instanceof Date
1201
+ ? m.pattern.createdAt.toISOString()
1202
+ : m.pattern.createdAt || undefined,
1203
+ lastUsedAt: m.pattern.lastUsedAt instanceof Date
1204
+ ? m.pattern.lastUsedAt.toISOString()
1205
+ : m.pattern.lastUsedAt || undefined,
1206
+ }));
1207
+ const exportData = {
1208
+ version: EXPORT_FORMAT_VERSION,
1209
+ exportedAt: new Date().toISOString(),
1210
+ source: process.cwd(),
1211
+ schemaVersion,
1212
+ patternCount: patterns.length,
1213
+ patterns,
1214
+ };
1215
+ // Include trajectories if requested
1216
+ if (options.includeTrajectories) {
1217
+ try {
1218
+ const Database = (await import('better-sqlite3')).default;
1219
+ const db = new Database(dbPath, { readonly: true });
1220
+ const trajectories = db.prepare(`
1221
+ SELECT id, task, agent, domain, success, steps_json
1222
+ FROM qe_trajectories
1223
+ ORDER BY started_at DESC
1224
+ LIMIT 1000
1225
+ `).all();
1226
+ exportData.trajectories = trajectories.map(t => ({
1227
+ id: t.id,
1228
+ task: t.task,
1229
+ agent: t.agent,
1230
+ domain: t.domain,
1231
+ success: t.success,
1232
+ stepsJson: t.steps_json,
1233
+ }));
1234
+ db.close();
1235
+ }
1236
+ catch {
1237
+ // Trajectories table may not exist
1238
+ }
1239
+ }
1240
+ // Include experiences if requested
1241
+ if (options.includeExperiences) {
1242
+ try {
1243
+ const Database = (await import('better-sqlite3')).default;
1244
+ const db = new Database(dbPath, { readonly: true });
1245
+ const experiences = db.prepare(`
1246
+ SELECT task_type, action, AVG(reward) as avg_reward, COUNT(*) as count
1247
+ FROM learning_experiences
1248
+ GROUP BY task_type, action
1249
+ ORDER BY count DESC
1250
+ LIMIT 500
1251
+ `).all();
1252
+ exportData.experiences = experiences.map(e => ({
1253
+ taskType: e.task_type,
1254
+ action: e.action,
1255
+ reward: e.avg_reward,
1256
+ count: e.count,
1257
+ }));
1258
+ // Add metadata
1259
+ const metaRow = db.prepare(`
1260
+ SELECT COUNT(*) as total, AVG(reward) as avg_reward FROM learning_experiences
1261
+ `).get();
1262
+ exportData.metadata = {
1263
+ totalExperiences: metaRow.total,
1264
+ avgReward: metaRow.avg_reward,
1265
+ };
1266
+ db.close();
1267
+ }
1268
+ catch {
1269
+ // Learning experiences table may not exist
1270
+ }
1271
+ }
1272
+ if (options.json) {
1273
+ printJson(exportData);
1274
+ }
1275
+ else {
1276
+ let outputPath = path.resolve(options.output);
1277
+ writeFileSync(outputPath, JSON.stringify(exportData, null, 2), 'utf-8');
1278
+ if (options.compress) {
1279
+ const compressedPath = await compressFile(outputPath);
1280
+ await unlink(outputPath);
1281
+ outputPath = compressedPath;
1282
+ }
1283
+ console.log(chalk.bold('\nšŸ“¤ Learning Data Export\n'));
1284
+ console.log(` Patterns: ${patterns.length}`);
1285
+ if (exportData.trajectories) {
1286
+ console.log(` Trajectories: ${exportData.trajectories.length}`);
1287
+ }
1288
+ if (exportData.experiences) {
1289
+ console.log(` Experience groups: ${exportData.experiences.length}`);
1290
+ }
1291
+ console.log(` Schema version: ${schemaVersion}`);
1292
+ console.log(` Export format: ${EXPORT_FORMAT_VERSION}`);
1293
+ printSuccess(`Exported to: ${outputPath}`);
1294
+ console.log('');
1295
+ }
1296
+ process.exit(0);
1297
+ }
1298
+ catch (error) {
1299
+ printError(`export-full failed: ${error instanceof Error ? error.message : 'unknown'}`);
1300
+ process.exit(1);
1301
+ }
1302
+ });
1303
+ // -------------------------------------------------------------------------
1304
+ // import-merge: Import and merge patterns from export file
1305
+ // -------------------------------------------------------------------------
1306
+ learning
1307
+ .command('import-merge')
1308
+ .description('Import and merge patterns from export file (preserves existing data)')
1309
+ .requiredOption('-i, --input <file>', 'Input file path')
1310
+ .option('--skip-duplicates', 'Skip patterns with matching names (default: update)')
1311
+ .option('--dry-run', 'Show what would be imported without making changes')
1312
+ .option('--json', 'Output as JSON')
1313
+ .action(async (options) => {
1314
+ try {
1315
+ let inputPath = path.resolve(options.input);
1316
+ if (!existsSync(inputPath)) {
1317
+ throw new Error(`File not found: ${inputPath}`);
1318
+ }
1319
+ // Handle compressed files
1320
+ let content;
1321
+ if (inputPath.endsWith('.gz')) {
1322
+ const tempPath = inputPath.replace('.gz', '.tmp.json');
1323
+ await decompressFile(inputPath, tempPath);
1324
+ content = readFileSync(tempPath, 'utf-8');
1325
+ await unlink(tempPath);
1326
+ }
1327
+ else {
1328
+ content = readFileSync(inputPath, 'utf-8');
1329
+ }
1330
+ const importData = JSON.parse(content);
1331
+ // Validate import data
1332
+ if (!importData.patterns || !Array.isArray(importData.patterns)) {
1333
+ throw new Error('Invalid import file format: missing patterns array');
1334
+ }
1335
+ const reasoningBank = await initializeLearningSystem();
1336
+ // Get existing pattern names for duplicate detection
1337
+ const existingResult = await reasoningBank.searchPatterns('*', { limit: 10000 });
1338
+ const existingNames = new Set();
1339
+ if (existingResult.success) {
1340
+ for (const m of existingResult.value) {
1341
+ existingNames.add(m.pattern.name);
1342
+ }
1343
+ }
1344
+ let imported = 0;
1345
+ let skipped = 0;
1346
+ let updated = 0;
1347
+ const errors = [];
1348
+ if (!options.dryRun) {
1349
+ for (const pattern of importData.patterns) {
1350
+ const isDuplicate = existingNames.has(pattern.name);
1351
+ if (isDuplicate && options.skipDuplicates) {
1352
+ skipped++;
1353
+ continue;
1354
+ }
1355
+ try {
1356
+ const result = await reasoningBank.storePattern({
1357
+ patternType: pattern.patternType,
1358
+ name: pattern.name,
1359
+ description: pattern.description,
1360
+ template: pattern.template,
1361
+ context: pattern.context,
1362
+ });
1363
+ if (result.success) {
1364
+ if (isDuplicate) {
1365
+ updated++;
1366
+ }
1367
+ else {
1368
+ imported++;
1369
+ }
1370
+ }
1371
+ else {
1372
+ skipped++;
1373
+ errors.push(`${pattern.name}: ${result.error.message}`);
1374
+ }
1375
+ }
1376
+ catch (e) {
1377
+ skipped++;
1378
+ errors.push(`${pattern.name}: ${e instanceof Error ? e.message : 'unknown'}`);
1379
+ }
1380
+ }
1381
+ }
1382
+ // Calculate what would happen in dry-run
1383
+ let wouldImport = 0;
1384
+ let wouldSkip = 0;
1385
+ let wouldUpdate = 0;
1386
+ if (options.dryRun) {
1387
+ for (const pattern of importData.patterns) {
1388
+ const isDuplicate = existingNames.has(pattern.name);
1389
+ if (isDuplicate && options.skipDuplicates) {
1390
+ wouldSkip++;
1391
+ }
1392
+ else if (isDuplicate) {
1393
+ wouldUpdate++;
1394
+ }
1395
+ else {
1396
+ wouldImport++;
1397
+ }
1398
+ }
1399
+ }
1400
+ if (options.json) {
1401
+ printJson({
1402
+ dryRun: options.dryRun || false,
1403
+ importVersion: importData.version,
1404
+ totalPatterns: importData.patterns.length,
1405
+ imported: options.dryRun ? wouldImport : imported,
1406
+ updated: options.dryRun ? wouldUpdate : updated,
1407
+ skipped: options.dryRun ? wouldSkip : skipped,
1408
+ errors: errors.slice(0, 10),
1409
+ });
1410
+ }
1411
+ else {
1412
+ console.log(chalk.bold('\nšŸ“„ Learning Data Import (Merge)\n'));
1413
+ console.log(` Source: ${inputPath}`);
1414
+ console.log(` Export version: ${importData.version || 'unknown'}`);
1415
+ console.log(` Total patterns: ${importData.patterns.length}`);
1416
+ console.log(` Existing patterns: ${existingNames.size}`);
1417
+ if (options.dryRun) {
1418
+ console.log(chalk.yellow('\n Dry run - no changes made'));
1419
+ console.log(` Would import: ${wouldImport}`);
1420
+ console.log(` Would update: ${wouldUpdate}`);
1421
+ console.log(` Would skip: ${wouldSkip}`);
1422
+ }
1423
+ else {
1424
+ console.log(chalk.green(`\n Imported: ${imported}`));
1425
+ console.log(chalk.blue(` Updated: ${updated}`));
1426
+ if (skipped > 0) {
1427
+ console.log(chalk.yellow(` Skipped: ${skipped}`));
1428
+ }
1429
+ if (errors.length > 0) {
1430
+ console.log(chalk.red(` Errors: ${errors.length}`));
1431
+ }
1432
+ }
1433
+ console.log('');
1434
+ }
1435
+ process.exit(0);
1436
+ }
1437
+ catch (error) {
1438
+ printError(`import-merge failed: ${error instanceof Error ? error.message : 'unknown'}`);
1439
+ process.exit(1);
1440
+ }
1441
+ });
1442
+ return learning;
1443
+ }
1444
+ // ============================================================================
1445
+ // Exports
1446
+ // ============================================================================
1447
+ export { initializeLearningSystem };
1448
+ //# sourceMappingURL=learning.js.map