agentic-qe 3.7.21 → 3.7.22
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/helpers/brain-checkpoint.cjs +4 -1
- package/.claude/helpers/statusline-v3.cjs +3 -1
- package/.claude/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +14 -0
- package/README.md +0 -12
- package/assets/helpers/statusline-v3.cjs +3 -1
- package/dist/cli/brain-commands.js +6 -10
- package/dist/cli/bundle.js +2049 -3380
- package/dist/cli/commands/hooks.js +29 -6
- package/dist/cli/commands/init.js +1 -73
- package/dist/cli/commands/learning.js +164 -12
- package/dist/cli/handlers/init-handler.d.ts +0 -1
- package/dist/cli/handlers/init-handler.js +0 -6
- package/dist/cli/index.js +0 -2
- package/dist/context/sources/defect-source.js +2 -2
- package/dist/context/sources/memory-source.js +2 -2
- package/dist/context/sources/requirements-source.js +2 -2
- package/dist/init/index.d.ts +0 -2
- package/dist/init/index.js +0 -1
- package/dist/init/init-wizard-steps.d.ts +10 -0
- package/dist/init/init-wizard-steps.js +87 -1
- package/dist/init/init-wizard.d.ts +1 -9
- package/dist/init/init-wizard.js +3 -69
- package/dist/init/orchestrator.js +0 -1
- package/dist/init/phases/01-detection.js +0 -27
- package/dist/init/phases/07-hooks.js +6 -4
- package/dist/init/phases/phase-interface.d.ts +0 -1
- package/dist/init/settings-merge.js +1 -1
- package/dist/kernel/unified-memory.js +5 -6
- package/dist/learning/experience-capture-middleware.js +20 -0
- package/dist/learning/index.d.ts +0 -2
- package/dist/learning/index.js +0 -4
- package/dist/learning/metrics-tracker.js +15 -13
- package/dist/learning/pattern-lifecycle.d.ts +1 -1
- package/dist/learning/pattern-lifecycle.js +18 -20
- package/dist/learning/qe-unified-memory.js +1 -28
- package/dist/mcp/bundle.js +180 -175
- package/package.json +1 -1
- package/dist/cli/commands/migrate.d.ts +0 -9
- package/dist/cli/commands/migrate.js +0 -566
- package/dist/init/init-wizard-migration.d.ts +0 -52
- package/dist/init/init-wizard-migration.js +0 -345
- package/dist/init/migration/config-migrator.d.ts +0 -31
- package/dist/init/migration/config-migrator.js +0 -149
- package/dist/init/migration/data-migrator.d.ts +0 -72
- package/dist/init/migration/data-migrator.js +0 -232
- package/dist/init/migration/detector.d.ts +0 -44
- package/dist/init/migration/detector.js +0 -105
- package/dist/init/migration/index.d.ts +0 -8
- package/dist/init/migration/index.js +0 -8
- package/dist/learning/v2-to-v3-migration.d.ts +0 -86
- package/dist/learning/v2-to-v3-migration.js +0 -529
|
@@ -358,7 +358,24 @@ async function consolidateExperiencesToPatterns() {
|
|
|
358
358
|
await um.initialize();
|
|
359
359
|
}
|
|
360
360
|
const db = um.getDatabase();
|
|
361
|
-
//
|
|
361
|
+
// Ensure consolidation columns exist (may be missing on older DBs)
|
|
362
|
+
const existingCols = new Set(db.prepare('PRAGMA table_info(captured_experiences)').all().map(c => c.name));
|
|
363
|
+
const migrations = [
|
|
364
|
+
['consolidated_into', 'TEXT DEFAULT NULL'],
|
|
365
|
+
['consolidation_count', 'INTEGER DEFAULT 1'],
|
|
366
|
+
['quality_updated_at', 'TEXT DEFAULT NULL'],
|
|
367
|
+
['reuse_success_count', 'INTEGER DEFAULT 0'],
|
|
368
|
+
['reuse_failure_count', 'INTEGER DEFAULT 0'],
|
|
369
|
+
];
|
|
370
|
+
for (const [col, def] of migrations) {
|
|
371
|
+
if (!existingCols.has(col)) {
|
|
372
|
+
db.exec(`ALTER TABLE captured_experiences ADD COLUMN ${col} ${def}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// Aggregate unprocessed experiences by domain+agent with quality thresholds.
|
|
376
|
+
// Exclude 'cli-hook' agent — these are low-quality hook telemetry events
|
|
377
|
+
// (quality ~0.40, success_rate ~0.24) that flood the pipeline and block
|
|
378
|
+
// real pattern creation. See issue #348.
|
|
362
379
|
const aggregates = db.prepare(`
|
|
363
380
|
SELECT
|
|
364
381
|
domain,
|
|
@@ -371,6 +388,7 @@ async function consolidateExperiencesToPatterns() {
|
|
|
371
388
|
GROUP_CONCAT(DISTINCT source) as sources
|
|
372
389
|
FROM captured_experiences
|
|
373
390
|
WHERE application_count = 0
|
|
391
|
+
AND agent != 'cli-hook'
|
|
374
392
|
GROUP BY domain, agent
|
|
375
393
|
HAVING cnt >= 3 AND avg_quality >= 0.5 AND success_rate >= 0.6
|
|
376
394
|
ORDER BY avg_quality DESC
|
|
@@ -382,20 +400,25 @@ async function consolidateExperiencesToPatterns() {
|
|
|
382
400
|
let created = 0;
|
|
383
401
|
for (const agg of aggregates) {
|
|
384
402
|
try {
|
|
385
|
-
//
|
|
403
|
+
// Use date-bucketed names so new patterns emerge as usage evolves,
|
|
404
|
+
// instead of silently reinforcing one static pattern forever.
|
|
405
|
+
const dateBucket = new Date().toISOString().slice(0, 7); // YYYY-MM
|
|
406
|
+
const patternName = `${agg.agent}-${agg.domain}-${dateBucket}`;
|
|
407
|
+
// Check for existing pattern with same name this month
|
|
386
408
|
const existing = db.prepare(`
|
|
387
409
|
SELECT id FROM qe_patterns
|
|
388
410
|
WHERE qe_domain = ? AND name = ?
|
|
389
411
|
LIMIT 1
|
|
390
|
-
`).get(agg.domain,
|
|
412
|
+
`).get(agg.domain, patternName);
|
|
391
413
|
if (existing) {
|
|
392
|
-
// Reinforce existing pattern
|
|
414
|
+
// Reinforce existing monthly pattern
|
|
393
415
|
db.prepare(`
|
|
394
416
|
UPDATE qe_patterns
|
|
395
417
|
SET usage_count = usage_count + ?,
|
|
396
418
|
successful_uses = successful_uses + ?,
|
|
397
419
|
confidence = MIN(0.99, confidence + 0.01),
|
|
398
|
-
quality_score = MIN(0.99, quality_score + 0.005)
|
|
420
|
+
quality_score = MIN(0.99, quality_score + 0.005),
|
|
421
|
+
updated_at = datetime('now')
|
|
399
422
|
WHERE id = ?
|
|
400
423
|
`).run(agg.cnt, agg.successes, existing.id);
|
|
401
424
|
}
|
|
@@ -409,7 +432,7 @@ async function consolidateExperiencesToPatterns() {
|
|
|
409
432
|
confidence, usage_count, success_rate, quality_score, tier,
|
|
410
433
|
template_json, context_json, created_at, successful_uses
|
|
411
434
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), ?)
|
|
412
|
-
`).run(patternId, 'workflow', agg.domain, agg.domain,
|
|
435
|
+
`).run(patternId, 'workflow', agg.domain, agg.domain, patternName, `Auto-consolidated from ${agg.cnt} experiences. Agent: ${agg.agent}, success rate: ${(agg.success_rate * 100).toFixed(0)}%`, confidence, agg.cnt, agg.success_rate, qualityScore, 'short-term', JSON.stringify({ type: 'workflow', content: `${agg.agent} pattern for ${agg.domain}`, variables: [] }), JSON.stringify({ tags: (agg.sources || '').split(','), sourceType: 'session-consolidation', extractedAt: new Date().toISOString() }), agg.successes);
|
|
413
436
|
created++;
|
|
414
437
|
}
|
|
415
438
|
// Mark experiences as processed
|
|
@@ -28,7 +28,6 @@ export function createInitCommand() {
|
|
|
28
28
|
.description('Initialize Agentic QE v3 in your project')
|
|
29
29
|
.option('-a, --auto', 'Auto-configure without prompts')
|
|
30
30
|
.option('-u, --upgrade', 'Upgrade existing installation (overwrites skills, agents, validation)')
|
|
31
|
-
.option('--auto-migrate', 'Automatically migrate from v2 if detected')
|
|
32
31
|
.option('--minimal', 'Minimal installation (no skills, patterns, or workers)')
|
|
33
32
|
.option('--skip-patterns', 'Skip pattern loading')
|
|
34
33
|
.option('--with-n8n', 'Include n8n workflow testing platform')
|
|
@@ -57,14 +56,6 @@ export function createInitCommand() {
|
|
|
57
56
|
.action(async () => {
|
|
58
57
|
await checkStatus();
|
|
59
58
|
});
|
|
60
|
-
initCmd
|
|
61
|
-
.command('migrate')
|
|
62
|
-
.description('Migrate from v2 to v3')
|
|
63
|
-
.option('--dry-run', 'Show what would be migrated without making changes')
|
|
64
|
-
.option('--force', 'Force migration even if v3 already exists')
|
|
65
|
-
.action(async (migrateOptions) => {
|
|
66
|
-
await runMigration(migrateOptions);
|
|
67
|
-
});
|
|
68
59
|
initCmd
|
|
69
60
|
.command('reset')
|
|
70
61
|
.description('Reset AQE configuration (keeps data)')
|
|
@@ -87,11 +78,10 @@ async function runInit(options) {
|
|
|
87
78
|
// Check if already initialized
|
|
88
79
|
const aqeDir = path.join(projectRoot, '.agentic-qe');
|
|
89
80
|
const isExisting = existsSync(aqeDir);
|
|
90
|
-
if (isExisting && !options.auto && !options.
|
|
81
|
+
if (isExisting && !options.auto && !options.upgrade) {
|
|
91
82
|
console.log(chalk.yellow(' ⚠ AQE directory already exists at:'), aqeDir);
|
|
92
83
|
console.log(chalk.gray(' Use --auto to update configuration (keeps existing skills)'));
|
|
93
84
|
console.log(chalk.gray(' Use --upgrade to update all skills, agents, and validation'));
|
|
94
|
-
console.log(chalk.gray(' Use --auto-migrate to migrate from v2'));
|
|
95
85
|
console.log('');
|
|
96
86
|
}
|
|
97
87
|
// Expand --with-all-platforms into individual flags
|
|
@@ -110,7 +100,6 @@ async function runInit(options) {
|
|
|
110
100
|
projectRoot,
|
|
111
101
|
autoMode: options.auto,
|
|
112
102
|
upgrade: options.upgrade,
|
|
113
|
-
autoMigrate: options.autoMigrate,
|
|
114
103
|
minimal: options.minimal,
|
|
115
104
|
skipPatterns: options.skipPatterns,
|
|
116
105
|
withN8n: options.withN8n,
|
|
@@ -223,67 +212,6 @@ async function checkStatus() {
|
|
|
223
212
|
}
|
|
224
213
|
console.log('');
|
|
225
214
|
}
|
|
226
|
-
/**
|
|
227
|
-
* Run v2 to v3 migration
|
|
228
|
-
*/
|
|
229
|
-
async function runMigration(options) {
|
|
230
|
-
const projectRoot = process.cwd();
|
|
231
|
-
const aqeDir = path.join(projectRoot, '.agentic-qe');
|
|
232
|
-
console.log('');
|
|
233
|
-
console.log(chalk.bold.blue(' AQE v2 to v3 Migration'));
|
|
234
|
-
console.log(chalk.gray(' ──────────────────────────'));
|
|
235
|
-
console.log('');
|
|
236
|
-
// Import migration modules
|
|
237
|
-
const { createV2Detector, createV2DataMigrator, createV2ConfigMigrator } = await import('../../init/migration/index.js');
|
|
238
|
-
// Detect v2
|
|
239
|
-
const detector = createV2Detector(projectRoot);
|
|
240
|
-
const v2Info = await detector.detect();
|
|
241
|
-
if (!v2Info.detected) {
|
|
242
|
-
console.log(chalk.yellow(' ⚠ No v2 installation detected'));
|
|
243
|
-
console.log(chalk.gray(' Run "aqe init" to create a new installation'));
|
|
244
|
-
console.log('');
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
console.log(chalk.green(' ✓ Found v2 installation'));
|
|
248
|
-
console.log(chalk.gray(` Database: ${v2Info.paths.memoryDb || 'not found'}`));
|
|
249
|
-
console.log(chalk.gray(` Config: ${v2Info.paths.configDir || 'not found'}`));
|
|
250
|
-
console.log(chalk.gray(` Version: ${v2Info.version || 'unknown'}`));
|
|
251
|
-
console.log('');
|
|
252
|
-
if (options.dryRun) {
|
|
253
|
-
console.log(chalk.yellow(' Dry run - no changes will be made'));
|
|
254
|
-
console.log('');
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
// Run migration
|
|
258
|
-
console.log(chalk.blue(' Migrating data...'));
|
|
259
|
-
if (v2Info.paths.memoryDb) {
|
|
260
|
-
const dataMigrator = createV2DataMigrator({
|
|
261
|
-
v2DbPath: v2Info.paths.memoryDb,
|
|
262
|
-
v3PatternsDbPath: path.join(aqeDir, 'patterns.db'),
|
|
263
|
-
onProgress: (p) => console.log(chalk.gray(` ${p.message}`)),
|
|
264
|
-
});
|
|
265
|
-
const dataResult = await dataMigrator.migrate();
|
|
266
|
-
if (dataResult.success) {
|
|
267
|
-
console.log(chalk.green(` ✓ Migrated ${dataResult.counts.patterns || 0} patterns`));
|
|
268
|
-
console.log(chalk.green(` ✓ Migrated ${dataResult.counts.experiences || 0} experiences`));
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
console.log(chalk.red(` ✗ Data migration failed: ${dataResult.errors.join(', ')}`));
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
const configMigrator = createV2ConfigMigrator(projectRoot);
|
|
275
|
-
const configResult = await configMigrator.migrate();
|
|
276
|
-
if (configResult.success) {
|
|
277
|
-
console.log(chalk.green(' ✓ Config migrated'));
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
console.log(chalk.yellow(' ⚠ Config migration skipped (no v2 config found)'));
|
|
281
|
-
}
|
|
282
|
-
console.log('');
|
|
283
|
-
console.log(chalk.green(' Migration complete!'));
|
|
284
|
-
console.log(chalk.gray(' Run "aqe init" to complete setup'));
|
|
285
|
-
console.log('');
|
|
286
|
-
}
|
|
287
215
|
/**
|
|
288
216
|
* Reset AQE configuration
|
|
289
217
|
*/
|
|
@@ -13,7 +13,7 @@ import { Command } from 'commander';
|
|
|
13
13
|
import chalk from 'chalk';
|
|
14
14
|
import path from 'node:path';
|
|
15
15
|
import { findProjectRoot } from '../../kernel/unified-memory.js';
|
|
16
|
-
import { existsSync, writeFileSync, readFileSync, mkdirSync, copyFileSync } from 'node:fs';
|
|
16
|
+
import { existsSync, writeFileSync, readFileSync, mkdirSync, copyFileSync, renameSync } from 'node:fs';
|
|
17
17
|
import { safeJsonParse } from '../../shared/safe-json.js';
|
|
18
18
|
import { stat, unlink } from 'node:fs/promises';
|
|
19
19
|
import { QE_DOMAIN_LIST } from '../../learning/qe-patterns.js';
|
|
@@ -62,6 +62,7 @@ Examples:
|
|
|
62
62
|
registerExportFullCommand(learning);
|
|
63
63
|
registerImportMergeCommand(learning);
|
|
64
64
|
registerDreamCommand(learning);
|
|
65
|
+
registerRepairCommand(learning);
|
|
65
66
|
return learning;
|
|
66
67
|
}
|
|
67
68
|
// ============================================================================
|
|
@@ -372,15 +373,19 @@ function registerExtractCommand(learning) {
|
|
|
372
373
|
console.log(` Min occurrences: ${minCount}\n`);
|
|
373
374
|
const db = openDatabase(dbPath, { readonly: true });
|
|
374
375
|
const experiences = db.prepare(`
|
|
375
|
-
SELECT task_type, COUNT(*) as count, AVG(
|
|
376
|
-
MIN(
|
|
377
|
-
FROM
|
|
376
|
+
SELECT domain as task_type, COUNT(*) as count, AVG(quality) as avg_reward, MAX(quality) as max_reward,
|
|
377
|
+
MIN(quality) as min_reward, GROUP_CONCAT(DISTINCT agent) as actions
|
|
378
|
+
FROM captured_experiences WHERE quality >= ? AND agent != 'cli-hook' GROUP BY domain HAVING COUNT(*) >= ? ORDER BY avg_reward DESC
|
|
378
379
|
`).all(minReward, minCount);
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
380
|
+
let memoryPatterns = [];
|
|
381
|
+
try {
|
|
382
|
+
memoryPatterns = db.prepare(`
|
|
383
|
+
SELECT substr(key, 1, 40) as key_prefix, COUNT(*) as count
|
|
384
|
+
FROM memory_entries WHERE key LIKE 'phase2/learning/%'
|
|
385
|
+
GROUP BY substr(key, 1, 40) HAVING COUNT(*) >= ? ORDER BY COUNT(*) DESC LIMIT 20
|
|
386
|
+
`).all(minCount);
|
|
387
|
+
}
|
|
388
|
+
catch { /* memory_entries table may not exist */ }
|
|
384
389
|
db.close();
|
|
385
390
|
const extractedPatterns = [];
|
|
386
391
|
for (const exp of experiences) {
|
|
@@ -689,7 +694,7 @@ function registerVerifyCommand(learning) {
|
|
|
689
694
|
let tableCounts = {};
|
|
690
695
|
try {
|
|
691
696
|
const db = openDatabase(dbPath, { readonly: true });
|
|
692
|
-
for (const table of ['qe_patterns', 'qe_trajectories', '
|
|
697
|
+
for (const table of ['qe_patterns', 'qe_trajectories', 'captured_experiences', 'kv_store', 'vectors']) {
|
|
693
698
|
try {
|
|
694
699
|
const r = db.prepare(`SELECT COUNT(*) as count FROM ${table}`).get();
|
|
695
700
|
tableCounts[table] = r.count;
|
|
@@ -770,9 +775,9 @@ function registerExportFullCommand(learning) {
|
|
|
770
775
|
if (options.includeExperiences) {
|
|
771
776
|
try {
|
|
772
777
|
const db = openDatabase(dbPath, { readonly: true });
|
|
773
|
-
const experiences = db.prepare(`SELECT task_type, action, AVG(
|
|
778
|
+
const experiences = db.prepare(`SELECT domain as task_type, agent as action, AVG(quality) as avg_reward, COUNT(*) as count FROM captured_experiences WHERE agent != 'cli-hook' GROUP BY domain, agent ORDER BY count DESC LIMIT 500`).all();
|
|
774
779
|
exportData.experiences = experiences.map(e => ({ taskType: e.task_type, action: e.action, reward: e.avg_reward, count: e.count }));
|
|
775
|
-
const metaRow = db.prepare(`SELECT COUNT(*) as total, AVG(
|
|
780
|
+
const metaRow = db.prepare(`SELECT COUNT(*) as total, AVG(quality) as avg_reward FROM captured_experiences WHERE agent != 'cli-hook'`).get();
|
|
776
781
|
exportData.metadata = { totalExperiences: metaRow.total, avgReward: metaRow.avg_reward };
|
|
777
782
|
db.close();
|
|
778
783
|
}
|
|
@@ -1136,6 +1141,153 @@ function registerDreamCommand(learning) {
|
|
|
1136
1141
|
});
|
|
1137
1142
|
}
|
|
1138
1143
|
// ============================================================================
|
|
1144
|
+
// Subcommand: repair
|
|
1145
|
+
// ============================================================================
|
|
1146
|
+
function registerRepairCommand(learning) {
|
|
1147
|
+
learning
|
|
1148
|
+
.command('repair')
|
|
1149
|
+
.description('Repair a corrupted learning database (dump + reimport to rebuild indexes)')
|
|
1150
|
+
.option('-f, --file <path>', 'Database file to repair (defaults to current project)')
|
|
1151
|
+
.option('--dry-run', 'Show what would be done without modifying files')
|
|
1152
|
+
.option('--json', 'Output as JSON')
|
|
1153
|
+
.action(async (options) => {
|
|
1154
|
+
try {
|
|
1155
|
+
const dbPath = options.file ? path.resolve(options.file) : getDbPath();
|
|
1156
|
+
if (!existsSync(dbPath))
|
|
1157
|
+
throw new Error(`Database file not found: ${dbPath}`);
|
|
1158
|
+
// Step 1: Check integrity first
|
|
1159
|
+
const integrityResult = await verifyDatabaseIntegrity(dbPath);
|
|
1160
|
+
if (integrityResult.valid) {
|
|
1161
|
+
if (options.json) {
|
|
1162
|
+
printJson({ status: 'healthy', message: 'Database is already healthy, no repair needed', path: dbPath });
|
|
1163
|
+
}
|
|
1164
|
+
else {
|
|
1165
|
+
printSuccess('Database integrity check passed — no repair needed.');
|
|
1166
|
+
}
|
|
1167
|
+
process.exit(0);
|
|
1168
|
+
}
|
|
1169
|
+
printInfo(`Corruption detected: ${integrityResult.message}`);
|
|
1170
|
+
// Step 2: Count rows before repair
|
|
1171
|
+
let rowCountsBefore = {};
|
|
1172
|
+
try {
|
|
1173
|
+
const db = openDatabase(dbPath, { readonly: true });
|
|
1174
|
+
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
|
|
1175
|
+
for (const t of tables) {
|
|
1176
|
+
try {
|
|
1177
|
+
const r = db.prepare(`SELECT COUNT(*) as count FROM "${t.name}"`).get();
|
|
1178
|
+
rowCountsBefore[t.name] = r.count;
|
|
1179
|
+
}
|
|
1180
|
+
catch { /* corrupted table, count what we can */ }
|
|
1181
|
+
}
|
|
1182
|
+
db.close();
|
|
1183
|
+
}
|
|
1184
|
+
catch { /* ignore count failures */ }
|
|
1185
|
+
const totalBefore = Object.values(rowCountsBefore).reduce((a, b) => a + b, 0);
|
|
1186
|
+
printInfo(`Found ${Object.keys(rowCountsBefore).length} tables, ~${totalBefore} total rows`);
|
|
1187
|
+
if (options.dryRun) {
|
|
1188
|
+
if (options.json) {
|
|
1189
|
+
printJson({ status: 'dry-run', corruptionDetected: true, message: integrityResult.message, tables: rowCountsBefore, totalRows: totalBefore });
|
|
1190
|
+
}
|
|
1191
|
+
else {
|
|
1192
|
+
console.log(chalk.bold('\nDry run — would perform these steps:'));
|
|
1193
|
+
console.log(' 1. Backup corrupted DB');
|
|
1194
|
+
console.log(' 2. Dump all data via .dump');
|
|
1195
|
+
console.log(' 3. Reimport into fresh DB (rebuilds all indexes)');
|
|
1196
|
+
console.log(' 4. Verify integrity of repaired DB');
|
|
1197
|
+
console.log(' 5. Verify row counts match');
|
|
1198
|
+
console.log(' 6. Swap repaired DB into place');
|
|
1199
|
+
console.log('');
|
|
1200
|
+
}
|
|
1201
|
+
process.exit(0);
|
|
1202
|
+
}
|
|
1203
|
+
// Step 3: Backup
|
|
1204
|
+
const backupPath = `${dbPath}.bak-${Date.now()}`;
|
|
1205
|
+
copyFileSync(dbPath, backupPath);
|
|
1206
|
+
printInfo(`Backup created: ${backupPath}`);
|
|
1207
|
+
// Step 4: Remove stale WAL/SHM
|
|
1208
|
+
for (const suffix of ['-wal', '-shm']) {
|
|
1209
|
+
const walFile = dbPath + suffix;
|
|
1210
|
+
if (existsSync(walFile)) {
|
|
1211
|
+
await unlink(walFile);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
// Step 5: Dump and reimport using sqlite3 CLI
|
|
1215
|
+
const { execSync } = await import('node:child_process');
|
|
1216
|
+
const repairedPath = `${dbPath}.repaired`;
|
|
1217
|
+
const dumpPath = `${dbPath}.dump.sql`;
|
|
1218
|
+
try {
|
|
1219
|
+
// Dump all data
|
|
1220
|
+
execSync(`sqlite3 "${dbPath}" ".dump" > "${dumpPath}"`, { stdio: 'pipe', timeout: 120000 });
|
|
1221
|
+
// Reimport into fresh DB
|
|
1222
|
+
execSync(`sqlite3 "${repairedPath}" < "${dumpPath}"`, { stdio: 'pipe', timeout: 120000 });
|
|
1223
|
+
// Enable WAL on repaired DB
|
|
1224
|
+
execSync(`sqlite3 "${repairedPath}" "PRAGMA journal_mode=WAL;"`, { stdio: 'pipe' });
|
|
1225
|
+
}
|
|
1226
|
+
catch (dumpError) {
|
|
1227
|
+
// Clean up partial files
|
|
1228
|
+
if (existsSync(repairedPath))
|
|
1229
|
+
await unlink(repairedPath);
|
|
1230
|
+
if (existsSync(dumpPath))
|
|
1231
|
+
await unlink(dumpPath);
|
|
1232
|
+
throw new Error(`sqlite3 dump/reimport failed: ${dumpError instanceof Error ? dumpError.message : 'unknown'}. Is sqlite3 installed?`);
|
|
1233
|
+
}
|
|
1234
|
+
// Step 6: Verify repaired DB
|
|
1235
|
+
const repairedIntegrity = await verifyDatabaseIntegrity(repairedPath);
|
|
1236
|
+
if (!repairedIntegrity.valid) {
|
|
1237
|
+
if (existsSync(dumpPath))
|
|
1238
|
+
await unlink(dumpPath);
|
|
1239
|
+
throw new Error(`Repaired DB still has integrity issues: ${repairedIntegrity.message}. Backup preserved at ${backupPath}`);
|
|
1240
|
+
}
|
|
1241
|
+
// Step 7: Verify row counts
|
|
1242
|
+
let rowCountsAfter = {};
|
|
1243
|
+
const repairedDb = openDatabase(repairedPath, { readonly: true });
|
|
1244
|
+
const repairedTables = repairedDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
|
|
1245
|
+
for (const t of repairedTables) {
|
|
1246
|
+
try {
|
|
1247
|
+
const r = repairedDb.prepare(`SELECT COUNT(*) as count FROM "${t.name}"`).get();
|
|
1248
|
+
rowCountsAfter[t.name] = r.count;
|
|
1249
|
+
}
|
|
1250
|
+
catch { /* skip */ }
|
|
1251
|
+
}
|
|
1252
|
+
repairedDb.close();
|
|
1253
|
+
const totalAfter = Object.values(rowCountsAfter).reduce((a, b) => a + b, 0);
|
|
1254
|
+
// Step 8: Swap
|
|
1255
|
+
renameSync(dbPath, `${dbPath}.pre-repair`);
|
|
1256
|
+
renameSync(repairedPath, dbPath);
|
|
1257
|
+
// Clean up dump file
|
|
1258
|
+
if (existsSync(dumpPath))
|
|
1259
|
+
await unlink(dumpPath);
|
|
1260
|
+
if (options.json) {
|
|
1261
|
+
printJson({
|
|
1262
|
+
status: 'repaired',
|
|
1263
|
+
backupPath,
|
|
1264
|
+
rowsBefore: totalBefore,
|
|
1265
|
+
rowsAfter: totalAfter,
|
|
1266
|
+
tablesRepaired: Object.keys(rowCountsAfter).length,
|
|
1267
|
+
rowCounts: rowCountsAfter,
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
else {
|
|
1271
|
+
printSuccess('Database repaired successfully!');
|
|
1272
|
+
console.log(` Backup: ${backupPath}`);
|
|
1273
|
+
console.log(` Rows before: ${totalBefore}`);
|
|
1274
|
+
console.log(` Rows after: ${totalAfter}`);
|
|
1275
|
+
if (totalAfter < totalBefore) {
|
|
1276
|
+
console.log(chalk.yellow(` Warning: ${totalBefore - totalAfter} rows may have been lost due to data page corruption`));
|
|
1277
|
+
}
|
|
1278
|
+
console.log(` Tables: ${Object.keys(rowCountsAfter).length}`);
|
|
1279
|
+
console.log(` Integrity: ${chalk.green('OK')}`);
|
|
1280
|
+
console.log('');
|
|
1281
|
+
}
|
|
1282
|
+
process.exit(0);
|
|
1283
|
+
}
|
|
1284
|
+
catch (error) {
|
|
1285
|
+
printError(`repair failed: ${error instanceof Error ? error.message : 'unknown'}`);
|
|
1286
|
+
process.exit(1);
|
|
1287
|
+
}
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
// ============================================================================
|
|
1139
1291
|
// Exports
|
|
1140
1292
|
// ============================================================================
|
|
1141
1293
|
export { initializeLearningSystem } from './learning-helpers.js';
|
|
@@ -72,10 +72,6 @@ export class InitHandler {
|
|
|
72
72
|
options.withWindsurf = true;
|
|
73
73
|
options.withContinuedev = true;
|
|
74
74
|
}
|
|
75
|
-
// --auto-migrate implies --auto (must use orchestrator for migration)
|
|
76
|
-
if (options.autoMigrate && !options.auto && !options.wizard) {
|
|
77
|
-
options.auto = true;
|
|
78
|
-
}
|
|
79
75
|
// --upgrade implies --auto (must use modular orchestrator to overwrite files)
|
|
80
76
|
if (options.upgrade && !options.auto && !options.wizard) {
|
|
81
77
|
options.auto = true;
|
|
@@ -118,7 +114,6 @@ export class InitHandler {
|
|
|
118
114
|
withCodex: options.withCodex,
|
|
119
115
|
withWindsurf: options.withWindsurf,
|
|
120
116
|
withContinueDev: options.withContinuedev,
|
|
121
|
-
autoMigrate: options.autoMigrate,
|
|
122
117
|
noGovernance: options.noGovernance,
|
|
123
118
|
});
|
|
124
119
|
console.log(chalk.white(' Analyzing project...\n'));
|
|
@@ -192,7 +187,6 @@ export class InitHandler {
|
|
|
192
187
|
minimal: options.minimal,
|
|
193
188
|
skipPatterns: options.skipPatterns,
|
|
194
189
|
withN8n: options.withN8n,
|
|
195
|
-
autoMigrate: options.autoMigrate,
|
|
196
190
|
};
|
|
197
191
|
const orchestrator = new InitOrchestrator(orchestratorOptions);
|
|
198
192
|
if (options.wizard) {
|
package/dist/cli/index.js
CHANGED
|
@@ -790,7 +790,6 @@ import { createCoverageCommand } from './commands/coverage.js';
|
|
|
790
790
|
import { createQualityCommand } from './commands/quality.js';
|
|
791
791
|
import { createSecurityCommand } from './commands/security.js';
|
|
792
792
|
import { createCodeCommand } from './commands/code.js';
|
|
793
|
-
import { createMigrateCommand } from './commands/migrate.js';
|
|
794
793
|
import { createCompletionsCommand } from './commands/completions.js';
|
|
795
794
|
import { createFleetCommand } from './commands/fleet.js';
|
|
796
795
|
import { createValidateSwarmCommand } from './commands/validate-swarm.js';
|
|
@@ -803,7 +802,6 @@ program.addCommand(createCoverageCommand(context, cleanupAndExit, ensureInitiali
|
|
|
803
802
|
program.addCommand(createQualityCommand(context, cleanupAndExit, ensureInitialized));
|
|
804
803
|
program.addCommand(createSecurityCommand(context, cleanupAndExit, ensureInitialized));
|
|
805
804
|
program.addCommand(createCodeCommand(context, cleanupAndExit, ensureInitialized));
|
|
806
|
-
program.addCommand(createMigrateCommand(context, cleanupAndExit, ensureInitialized));
|
|
807
805
|
program.addCommand(createCompletionsCommand(cleanupAndExit));
|
|
808
806
|
program.addCommand(createFleetCommand(context, cleanupAndExit, ensureInitialized, registerDomainWorkflowActions));
|
|
809
807
|
program.addCommand(createValidateSwarmCommand(context, cleanupAndExit, ensureInitialized));
|
|
@@ -19,8 +19,8 @@ export class DefectContextSource {
|
|
|
19
19
|
if (!existsSync(dbPath)) {
|
|
20
20
|
return [];
|
|
21
21
|
}
|
|
22
|
-
const
|
|
23
|
-
const db =
|
|
22
|
+
const { openDatabase } = await import('../../shared/safe-db.js');
|
|
23
|
+
const db = openDatabase(dbPath, { readonly: true });
|
|
24
24
|
try {
|
|
25
25
|
// Check if patterns table exists
|
|
26
26
|
const tableCheck = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='qe_patterns'").get();
|
|
@@ -20,8 +20,8 @@ export class MemoryContextSource {
|
|
|
20
20
|
return this.fallbackGather(request);
|
|
21
21
|
}
|
|
22
22
|
// Dynamic import to avoid hard dependency
|
|
23
|
-
const
|
|
24
|
-
const db =
|
|
23
|
+
const { openDatabase } = await import('../../shared/safe-db.js');
|
|
24
|
+
const db = openDatabase(dbPath, { readonly: true });
|
|
25
25
|
try {
|
|
26
26
|
// Query patterns relevant to the task domain
|
|
27
27
|
const domainKeywords = this.extractDomainKeywords(request.taskDescription);
|
|
@@ -18,8 +18,8 @@ export class RequirementsContextSource {
|
|
|
18
18
|
if (!existsSync(dbPath)) {
|
|
19
19
|
return this.fallbackGather(request);
|
|
20
20
|
}
|
|
21
|
-
const
|
|
22
|
-
const db =
|
|
21
|
+
const { openDatabase } = await import('../../shared/safe-db.js');
|
|
22
|
+
const db = openDatabase(dbPath, { readonly: true });
|
|
23
23
|
try {
|
|
24
24
|
// Check if requirements table exists
|
|
25
25
|
const tableCheck = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='qe_patterns'").get();
|
package/dist/init/index.d.ts
CHANGED
|
@@ -41,6 +41,4 @@ export type { WindsurfInstallerOptions, WindsurfInstallResult } from './windsurf
|
|
|
41
41
|
export { WindsurfInstaller, createWindsurfInstaller } from './windsurf-installer.js';
|
|
42
42
|
export type { ContinueDevInstallerOptions, ContinueDevInstallResult } from './continuedev-installer.js';
|
|
43
43
|
export { ContinueDevInstaller, createContinueDevInstaller } from './continuedev-installer.js';
|
|
44
|
-
export type { V2DetectionInfo, MigrationResult, } from './migration/index.js';
|
|
45
|
-
export { V2Detector, createV2Detector, V2DataMigrator, createV2DataMigrator, V2ConfigMigrator, createV2ConfigMigrator, } from './migration/index.js';
|
|
46
44
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/init/index.js
CHANGED
|
@@ -24,5 +24,4 @@ export { RooCodeInstaller, createRooCodeInstaller } from './roocode-installer.js
|
|
|
24
24
|
export { CodexInstaller, createCodexInstaller } from './codex-installer.js';
|
|
25
25
|
export { WindsurfInstaller, createWindsurfInstaller } from './windsurf-installer.js';
|
|
26
26
|
export { ContinueDevInstaller, createContinueDevInstaller } from './continuedev-installer.js';
|
|
27
|
-
export { V2Detector, createV2Detector, V2DataMigrator, createV2DataMigrator, V2ConfigMigrator, createV2ConfigMigrator, } from './migration/index.js';
|
|
28
27
|
//# sourceMappingURL=index.js.map
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
* Extracted from init-wizard.ts.
|
|
7
7
|
*/
|
|
8
8
|
import type { AQEInitConfig, PretrainedLibrary } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Read AQE version directly from memory.db without full initialization.
|
|
11
|
+
* Returns undefined if no version is stored.
|
|
12
|
+
*/
|
|
13
|
+
export declare function readVersionFromDb(dbPath: string): string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Write AQE version to memory.db in _system namespace.
|
|
16
|
+
* Used by init wizard to mark installation version.
|
|
17
|
+
*/
|
|
18
|
+
export declare function writeVersionToDb(projectRoot: string, version: string): Promise<boolean>;
|
|
9
19
|
/**
|
|
10
20
|
* Initialize the persistence database (REQUIRED).
|
|
11
21
|
* Creates the SQLite database file with proper schema.
|
|
@@ -6,12 +6,98 @@
|
|
|
6
6
|
* Extracted from init-wizard.ts.
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
9
|
-
import { join } from 'path';
|
|
9
|
+
import { join, dirname } from 'path';
|
|
10
10
|
import { createSkillsInstaller } from './skills-installer.js';
|
|
11
11
|
import { createAgentsInstaller } from './agents-installer.js';
|
|
12
12
|
import { createN8nInstaller } from './n8n-installer.js';
|
|
13
13
|
import { toErrorMessage } from '../shared/error-utils.js';
|
|
14
14
|
import { openDatabase } from '../shared/safe-db.js';
|
|
15
|
+
import { safeJsonParse } from '../shared/safe-json.js';
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Version Management
|
|
18
|
+
// ============================================================================
|
|
19
|
+
/**
|
|
20
|
+
* Read AQE version directly from memory.db without full initialization.
|
|
21
|
+
* Returns undefined if no version is stored.
|
|
22
|
+
*/
|
|
23
|
+
export function readVersionFromDb(dbPath) {
|
|
24
|
+
try {
|
|
25
|
+
const db = openDatabase(dbPath, { readonly: true, fileMustExist: true });
|
|
26
|
+
try {
|
|
27
|
+
const tableExists = db.prepare(`
|
|
28
|
+
SELECT name FROM sqlite_master
|
|
29
|
+
WHERE type='table' AND name='kv_store'
|
|
30
|
+
`).get();
|
|
31
|
+
if (!tableExists) {
|
|
32
|
+
db.close();
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const row = db.prepare(`
|
|
36
|
+
SELECT value FROM kv_store
|
|
37
|
+
WHERE key = 'aqe_version' AND namespace = '_system'
|
|
38
|
+
`).get();
|
|
39
|
+
db.close();
|
|
40
|
+
if (row) {
|
|
41
|
+
return safeJsonParse(row.value);
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
db.close();
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Write AQE version to memory.db in _system namespace.
|
|
56
|
+
* Used by init wizard to mark installation version.
|
|
57
|
+
*/
|
|
58
|
+
export async function writeVersionToDb(projectRoot, version) {
|
|
59
|
+
const memoryDbPath = join(projectRoot, '.agentic-qe', 'memory.db');
|
|
60
|
+
try {
|
|
61
|
+
const dir = dirname(memoryDbPath);
|
|
62
|
+
if (!existsSync(dir)) {
|
|
63
|
+
mkdirSync(dir, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
const db = openDatabase(memoryDbPath);
|
|
66
|
+
try {
|
|
67
|
+
db.exec(`
|
|
68
|
+
CREATE TABLE IF NOT EXISTS kv_store (
|
|
69
|
+
key TEXT NOT NULL,
|
|
70
|
+
namespace TEXT NOT NULL,
|
|
71
|
+
value TEXT NOT NULL,
|
|
72
|
+
expires_at INTEGER,
|
|
73
|
+
created_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),
|
|
74
|
+
PRIMARY KEY (namespace, key)
|
|
75
|
+
);
|
|
76
|
+
`);
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
db.prepare(`
|
|
79
|
+
INSERT OR REPLACE INTO kv_store (key, namespace, value, created_at)
|
|
80
|
+
VALUES (?, '_system', ?, ?)
|
|
81
|
+
`).run('aqe_version', JSON.stringify(version), now);
|
|
82
|
+
db.prepare(`
|
|
83
|
+
INSERT OR REPLACE INTO kv_store (key, namespace, value, created_at)
|
|
84
|
+
VALUES (?, '_system', ?, ?)
|
|
85
|
+
`).run('init_timestamp', JSON.stringify(new Date().toISOString()), now);
|
|
86
|
+
db.close();
|
|
87
|
+
console.log(` ✓ Version ${version} written to memory.db`);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
db.close();
|
|
92
|
+
console.warn(` ⚠ Could not write version: ${toErrorMessage(err)}`);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
console.warn(` ⚠ Could not open memory.db: ${toErrorMessage(err)}`);
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
15
101
|
// ============================================================================
|
|
16
102
|
// Persistence Database
|
|
17
103
|
// ============================================================================
|