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.
- package/.claude/agents/n8n/n8n-base-agent.md +3 -3
- package/.claude/agents/n8n/n8n-bdd-scenario-tester.md +2 -2
- package/.claude/agents/n8n/n8n-chaos-tester.md +2 -2
- package/.claude/agents/n8n/n8n-ci-orchestrator.md +2 -2
- package/.claude/agents/n8n/n8n-compliance-validator.md +2 -2
- package/.claude/agents/n8n/n8n-expression-validator.md +2 -2
- package/.claude/agents/n8n/n8n-integration-test.md +2 -2
- package/.claude/agents/n8n/n8n-monitoring-validator.md +2 -2
- package/.claude/agents/n8n/n8n-node-validator.md +2 -2
- package/.claude/agents/n8n/n8n-performance-tester.md +2 -2
- package/.claude/agents/n8n/n8n-security-auditor.md +2 -2
- package/.claude/agents/n8n/n8n-trigger-test.md +2 -2
- package/.claude/agents/n8n/n8n-unit-tester.md +2 -2
- package/.claude/agents/n8n/n8n-version-comparator.md +2 -2
- package/.claude/agents/n8n/n8n-workflow-executor.md +2 -2
- package/.claude/agents/subagents/qe-code-reviewer.md +2 -2
- package/.claude/agents/subagents/qe-coverage-gap-analyzer.md +2 -2
- package/.claude/agents/subagents/qe-data-generator.md +2 -2
- package/.claude/agents/subagents/qe-flaky-investigator.md +2 -2
- package/.claude/agents/subagents/qe-integration-tester.md +2 -2
- package/.claude/agents/subagents/qe-performance-validator.md +2 -2
- package/.claude/agents/subagents/qe-security-auditor.md +2 -2
- package/.claude/agents/subagents/qe-test-data-architect-sub.md +2 -2
- package/.claude/agents/subagents/qe-test-implementer.md +2 -2
- package/.claude/agents/subagents/qe-test-refactorer.md +2 -2
- package/.claude/agents/subagents/qe-test-writer.md +2 -2
- package/.claude/agents/v3/qe-accessibility-auditor.md +3 -3
- package/.claude/agents/v3/qe-bdd-generator.md +4 -4
- package/.claude/agents/v3/qe-chaos-engineer.md +3 -3
- package/.claude/agents/v3/qe-code-complexity.md +3 -3
- package/.claude/agents/v3/qe-code-intelligence.md +3 -3
- package/.claude/agents/v3/qe-contract-validator.md +3 -3
- package/.claude/agents/v3/qe-coverage-specialist.md +4 -4
- package/.claude/agents/v3/qe-defect-predictor.md +4 -4
- package/.claude/agents/v3/qe-dependency-mapper.md +3 -3
- package/.claude/agents/v3/qe-deployment-advisor.md +3 -3
- package/.claude/agents/v3/qe-flaky-hunter.md +3 -3
- package/.claude/agents/v3/qe-fleet-commander.md +3 -3
- package/.claude/agents/v3/qe-gap-detector.md +4 -4
- package/.claude/agents/v3/qe-graphql-tester.md +3 -3
- package/.claude/agents/v3/qe-impact-analyzer.md +3 -3
- package/.claude/agents/v3/qe-integration-architect.md +2 -2
- package/.claude/agents/v3/qe-integration-tester.md +3 -3
- package/.claude/agents/v3/qe-kg-builder.md +3 -3
- package/.claude/agents/v3/qe-learning-coordinator.md +24 -15
- package/.claude/agents/v3/qe-load-tester.md +3 -3
- package/.claude/agents/v3/qe-metrics-optimizer.md +3 -3
- package/.claude/agents/v3/qe-mutation-tester.md +3 -3
- package/.claude/agents/v3/qe-parallel-executor.md +4 -4
- package/.claude/agents/v3/qe-pattern-learner.md +16 -12
- package/.claude/agents/v3/qe-performance-tester.md +3 -3
- package/.claude/agents/v3/qe-product-factors-assessor.md +4 -4
- package/.claude/agents/v3/qe-property-tester.md +3 -3
- package/.claude/agents/v3/qe-quality-criteria-recommender.md +4 -4
- package/.claude/agents/v3/qe-quality-gate.md +4 -4
- package/.claude/agents/v3/qe-qx-partner.md +3 -3
- package/.claude/agents/v3/qe-regression-analyzer.md +3 -3
- package/.claude/agents/v3/qe-requirements-validator.md +4 -4
- package/.claude/agents/v3/qe-responsive-tester.md +3 -3
- package/.claude/agents/v3/qe-retry-handler.md +3 -3
- package/.claude/agents/v3/qe-risk-assessor.md +4 -4
- package/.claude/agents/v3/qe-root-cause-analyzer.md +3 -3
- package/.claude/agents/v3/qe-security-auditor.md +3 -3
- package/.claude/agents/v3/qe-security-scanner.md +3 -3
- package/.claude/agents/v3/qe-tdd-specialist.md +4 -4
- package/.claude/agents/v3/qe-test-architect.md +4 -4
- package/.claude/agents/v3/qe-test-idea-rewriter.md +3 -3
- package/.claude/agents/v3/qe-transfer-specialist.md +3 -3
- package/.claude/agents/v3/qe-visual-tester.md +3 -3
- package/.claude/agents/v3/reasoningbank-learner.md +4 -4
- package/.claude/agents/v3/subagents/qe-code-reviewer.md +3 -3
- package/.claude/agents/v3/subagents/qe-integration-reviewer.md +3 -3
- package/.claude/agents/v3/subagents/qe-performance-reviewer.md +3 -3
- package/.claude/agents/v3/subagents/qe-security-reviewer.md +3 -3
- package/.claude/agents/v3/subagents/qe-tdd-green.md +3 -3
- package/.claude/agents/v3/subagents/qe-tdd-red.md +3 -3
- package/.claude/agents/v3/subagents/qe-tdd-refactor.md +3 -3
- package/.claude/helpers/daemon-manager.sh +10 -10
- package/.claude/helpers/github-safe.js +10 -10
- package/.claude/helpers/statusline-v3.cjs +9 -3
- package/.claude/helpers/statusline.cjs +891 -59
- package/.claude/skills/agentic-quality-engineering/SKILL.md +6 -6
- package/.claude/skills/qcsd-cicd-swarm/SKILL.md +14 -14
- package/.claude/skills/qcsd-development-swarm/SKILL.md +14 -14
- package/.claude/skills/qcsd-ideation-swarm/SKILL.md +21 -21
- package/.claude/skills/qcsd-refinement-swarm/SKILL.md +22 -22
- package/.claude/skills/qe-iterative-loop/SKILL.md +4 -4
- package/.claude/skills/verification-quality/SKILL.md +0 -6
- package/package.json +1 -1
- package/v3/CHANGELOG.md +34 -0
- package/v3/assets/agents/v3/qe-accessibility-auditor.md +12 -8
- package/v3/assets/agents/v3/qe-bdd-generator.md +13 -9
- package/v3/assets/agents/v3/qe-chaos-engineer.md +12 -8
- package/v3/assets/agents/v3/qe-code-complexity.md +12 -8
- package/v3/assets/agents/v3/qe-code-intelligence.md +12 -8
- package/v3/assets/agents/v3/qe-contract-validator.md +12 -8
- package/v3/assets/agents/v3/qe-coverage-specialist.md +13 -9
- package/v3/assets/agents/v3/qe-defect-predictor.md +13 -9
- package/v3/assets/agents/v3/qe-dependency-mapper.md +12 -8
- package/v3/assets/agents/v3/qe-deployment-advisor.md +12 -8
- package/v3/assets/agents/v3/qe-flaky-hunter.md +12 -8
- package/v3/assets/agents/v3/qe-fleet-commander.md +12 -8
- package/v3/assets/agents/v3/qe-gap-detector.md +13 -9
- package/v3/assets/agents/v3/qe-graphql-tester.md +12 -8
- package/v3/assets/agents/v3/qe-impact-analyzer.md +12 -8
- package/v3/assets/agents/v3/qe-integration-architect.md +2 -2
- package/v3/assets/agents/v3/qe-integration-tester.md +3 -3
- package/v3/assets/agents/v3/qe-kg-builder.md +12 -8
- package/v3/assets/agents/v3/qe-learning-coordinator.md +24 -15
- package/v3/assets/agents/v3/qe-load-tester.md +12 -8
- package/v3/assets/agents/v3/qe-metrics-optimizer.md +12 -8
- package/v3/assets/agents/v3/qe-mutation-tester.md +12 -8
- package/v3/assets/agents/v3/qe-parallel-executor.md +4 -4
- package/v3/assets/agents/v3/qe-pattern-learner.md +16 -12
- package/v3/assets/agents/v3/qe-performance-tester.md +12 -8
- package/v3/assets/agents/v3/qe-product-factors-assessor.md +4 -4
- package/v3/assets/agents/v3/qe-property-tester.md +12 -8
- package/v3/assets/agents/v3/qe-quality-criteria-recommender.md +4 -4
- package/v3/assets/agents/v3/qe-quality-gate.md +4 -4
- package/v3/assets/agents/v3/qe-qx-partner.md +12 -8
- package/v3/assets/agents/v3/qe-regression-analyzer.md +12 -8
- package/v3/assets/agents/v3/qe-requirements-validator.md +13 -9
- package/v3/assets/agents/v3/qe-responsive-tester.md +12 -8
- package/v3/assets/agents/v3/qe-retry-handler.md +12 -8
- package/v3/assets/agents/v3/qe-risk-assessor.md +13 -9
- package/v3/assets/agents/v3/qe-root-cause-analyzer.md +12 -8
- package/v3/assets/agents/v3/qe-security-auditor.md +12 -8
- package/v3/assets/agents/v3/qe-security-scanner.md +12 -8
- package/v3/assets/agents/v3/qe-tdd-specialist.md +4 -4
- package/v3/assets/agents/v3/qe-test-architect.md +13 -9
- package/v3/assets/agents/v3/qe-test-idea-rewriter.md +3 -3
- package/v3/assets/agents/v3/qe-transfer-specialist.md +12 -8
- package/v3/assets/agents/v3/qe-visual-tester.md +3 -3
- package/v3/assets/agents/v3/subagents/qe-code-reviewer.md +12 -8
- package/v3/assets/agents/v3/subagents/qe-integration-reviewer.md +12 -8
- package/v3/assets/agents/v3/subagents/qe-performance-reviewer.md +12 -8
- package/v3/assets/agents/v3/subagents/qe-security-reviewer.md +12 -8
- package/v3/assets/agents/v3/subagents/qe-tdd-green.md +12 -8
- package/v3/assets/agents/v3/subagents/qe-tdd-red.md +12 -8
- package/v3/assets/agents/v3/subagents/qe-tdd-refactor.md +12 -8
- package/v3/assets/skills/agentic-quality-engineering/SKILL.md +6 -6
- package/v3/assets/skills/qcsd-ideation-swarm/SKILL.md +21 -21
- package/v3/assets/skills/qe-iterative-loop/SKILL.md +4 -4
- package/v3/dist/cli/bundle.js +2508 -816
- package/v3/dist/cli/commands/hooks.d.ts.map +1 -1
- package/v3/dist/cli/commands/hooks.js +34 -21
- package/v3/dist/cli/commands/hooks.js.map +1 -1
- package/v3/dist/cli/commands/learning.d.ts +23 -0
- package/v3/dist/cli/commands/learning.d.ts.map +1 -0
- package/v3/dist/cli/commands/learning.js +1448 -0
- package/v3/dist/cli/commands/learning.js.map +1 -0
- package/v3/dist/cli/index.js +2 -0
- package/v3/dist/cli/index.js.map +1 -1
- package/v3/dist/init/init-wizard.d.ts +2 -1
- package/v3/dist/init/init-wizard.d.ts.map +1 -1
- package/v3/dist/init/init-wizard.js +25 -23
- package/v3/dist/init/init-wizard.js.map +1 -1
- package/v3/dist/init/phases/07-hooks.d.ts +3 -0
- package/v3/dist/init/phases/07-hooks.d.ts.map +1 -1
- package/v3/dist/init/phases/07-hooks.js +12 -9
- package/v3/dist/init/phases/07-hooks.js.map +1 -1
- package/v3/dist/kernel/unified-memory.d.ts +8 -3
- package/v3/dist/kernel/unified-memory.d.ts.map +1 -1
- package/v3/dist/kernel/unified-memory.js +39 -9
- package/v3/dist/kernel/unified-memory.js.map +1 -1
- package/v3/dist/learning/aqe-learning-engine.d.ts +26 -0
- package/v3/dist/learning/aqe-learning-engine.d.ts.map +1 -1
- package/v3/dist/learning/aqe-learning-engine.js +116 -2
- package/v3/dist/learning/aqe-learning-engine.js.map +1 -1
- package/v3/dist/learning/index.d.ts +4 -0
- package/v3/dist/learning/index.d.ts.map +1 -1
- package/v3/dist/learning/index.js +8 -0
- package/v3/dist/learning/index.js.map +1 -1
- package/v3/dist/learning/metrics-tracker.d.ts +133 -0
- package/v3/dist/learning/metrics-tracker.d.ts.map +1 -0
- package/v3/dist/learning/metrics-tracker.js +396 -0
- package/v3/dist/learning/metrics-tracker.js.map +1 -0
- package/v3/dist/learning/pattern-lifecycle.d.ts +203 -0
- package/v3/dist/learning/pattern-lifecycle.d.ts.map +1 -0
- package/v3/dist/learning/pattern-lifecycle.js +614 -0
- package/v3/dist/learning/pattern-lifecycle.js.map +1 -0
- package/v3/dist/learning/sqlite-persistence.d.ts +30 -0
- package/v3/dist/learning/sqlite-persistence.d.ts.map +1 -1
- package/v3/dist/learning/sqlite-persistence.js +137 -0
- package/v3/dist/learning/sqlite-persistence.js.map +1 -1
- package/v3/dist/mcp/bundle.js +104568 -102038
- package/v3/dist/mcp/handlers/handler-factory.d.ts +5 -0
- package/v3/dist/mcp/handlers/handler-factory.d.ts.map +1 -1
- package/v3/dist/mcp/handlers/handler-factory.js +84 -0
- package/v3/dist/mcp/handlers/handler-factory.js.map +1 -1
- package/v3/dist/mcp/services/task-router.d.ts +32 -0
- package/v3/dist/mcp/services/task-router.d.ts.map +1 -1
- package/v3/dist/mcp/services/task-router.js +28 -0
- package/v3/dist/mcp/services/task-router.js.map +1 -1
- package/v3/dist/workers/workers/learning-consolidation.d.ts +29 -0
- package/v3/dist/workers/workers/learning-consolidation.d.ts.map +1 -1
- package/v3/dist/workers/workers/learning-consolidation.js +294 -3
- package/v3/dist/workers/workers/learning-consolidation.js.map +1 -1
- 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
|