mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
5d187de2a1
Bundles fixes for issue #439 (Windows install) and Jordi RUFLO patches P020 (RVF init) + P220 (hypergraph blindness). Windows install (#439): - Move hnswlib-node from dependencies to optionalDependencies so installs no longer fail on Windows boxes without VS C++ Build Tools; HnswAdapter already routes around a missing native binary at runtime. - Replace HNSWIndex.ts top-level static `import hnswlib from 'hnswlib-node'` with a lazy require so package load survives the optional dep being absent. - README + preinstall.cjs: document toolchain requirement and disclose that the JS fallback degrades to O(N) brute-force when @ruvector/gnn is also unavailable (Windows default — no win32 prebuilds ship). - ADR-090 amendment reconciling the move with ADR-081's optional-dep + JS-fallback intent. RVF init (Jordi P020): - Root cause: shared-rvf-adapter and pattern-store factory always called RvfDatabase.create(), which throws `0x0303 FsyncFailed` when the .rvf file already exists from a prior init phase. Reproduced and confirmed against both 0.1.7 and 0.1.8 native binaries — not a version regression. - Fix: race-tolerant open-or-create ladder (try open → fall back to create → retry open on create-race) with dim() verification after open. Mismatched-dim files are closed and the caller degrades rather than corrupting silently. Hypergraph (Jordi P220): - Drop the original flushEdgesToHypergraph (wrote `call`/`extends` edges that no production query reads — devil's-advocate review caught it as dead infrastructure). - Add HypergraphEngine.synthesizeTestCoverage that re-tags test files type='test' and writes `covers` edges from each test file to functions in the source files it imports. This is the shape findUntestedFunctions / findImpactedTests actually filter on. - E2E verified: untested correctly excludes covered functions; impacted-tests returns the right test file. Verification: - `npm run build` clean - 276/276 tests pass across HNSW, RVF, KG, coordinator, hypergraph suites (5 new synthesizeTestCoverage tests; replaced 4 obsolete flush tests) - E2E on fresh project: aqe init clean, hooks stats shows nativeAvailable: true, code index logs `Test coverage synthesized`, hg untested + hg impacted return useful results Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
315 lines
10 KiB
JavaScript
315 lines
10 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Preinstall script for agentic-qe
|
|
* Detects and handles conflicts from:
|
|
* - agentic-qe v2 (old package)
|
|
* - @agentic-qe/v3 (alpha package)
|
|
*
|
|
* Uses CommonJS for maximum Node.js compatibility
|
|
*/
|
|
|
|
const { execSync, spawnSync } = require('child_process');
|
|
const { existsSync, unlinkSync, lstatSync } = require('fs');
|
|
const { join } = require('path');
|
|
|
|
const RESET = '\x1b[0m';
|
|
const BOLD = '\x1b[1m';
|
|
const YELLOW = '\x1b[33m';
|
|
const GREEN = '\x1b[32m';
|
|
const CYAN = '\x1b[36m';
|
|
const RED = '\x1b[31m';
|
|
|
|
function log(msg) {
|
|
console.log(`${CYAN}[agentic-qe]${RESET} ${msg}`);
|
|
}
|
|
|
|
function warn(msg) {
|
|
console.log(`${YELLOW}[agentic-qe]${RESET} ${msg}`);
|
|
}
|
|
|
|
function error(msg) {
|
|
console.log(`${RED}[agentic-qe]${RESET} ${msg}`);
|
|
}
|
|
|
|
function success(msg) {
|
|
console.log(`${GREEN}[agentic-qe]${RESET} ${msg}`);
|
|
}
|
|
|
|
function findExistingAqeBinary() {
|
|
// Try 'which' command first
|
|
try {
|
|
const result = spawnSync('which', ['aqe'], { encoding: 'utf-8' });
|
|
if (result.status === 0 && result.stdout.trim()) {
|
|
return result.stdout.trim();
|
|
}
|
|
} catch {
|
|
// which command failed
|
|
}
|
|
|
|
// Try to find via npm prefix
|
|
try {
|
|
const prefix = execSync('npm config get prefix', { encoding: 'utf-8' }).trim();
|
|
const binPath = join(prefix, 'bin', 'aqe');
|
|
if (existsSync(binPath)) {
|
|
return binPath;
|
|
}
|
|
} catch {
|
|
// Ignore
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function checkPackageInstalled(packageName) {
|
|
try {
|
|
const result = execSync(`npm list -g ${packageName} --depth=0 2>/dev/null`, {
|
|
encoding: 'utf-8',
|
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
});
|
|
return result.includes(`${packageName}@`);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function getPackageVersion(packageName) {
|
|
try {
|
|
const result = execSync(`npm list -g ${packageName} --depth=0 2>/dev/null`, {
|
|
encoding: 'utf-8',
|
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
});
|
|
const regex = new RegExp(`${packageName.replace('/', '\\/')}@([\\d\\.\\-a-z]+)`);
|
|
const match = result.match(regex);
|
|
return match ? match[1] : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function uninstallPackage(packageName) {
|
|
try {
|
|
log(`Uninstalling ${packageName}...`);
|
|
execSync(`npm uninstall -g ${packageName}`, {
|
|
encoding: 'utf-8',
|
|
stdio: 'inherit'
|
|
});
|
|
success(`Successfully uninstalled ${packageName}`);
|
|
return true;
|
|
} catch (err) {
|
|
error(`Failed to uninstall ${packageName}: ${err.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function removeStaleSymlink(binaryPath) {
|
|
try {
|
|
const stats = lstatSync(binaryPath);
|
|
if (stats.isSymbolicLink()) {
|
|
unlinkSync(binaryPath);
|
|
success(`Removed stale symlink: ${binaryPath}`);
|
|
return true;
|
|
}
|
|
} catch {
|
|
// Not a symlink or can't remove
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function emitWindowsToolchainNotice() {
|
|
// Only show on Windows. Optional native dep `hnswlib-node` compiles from
|
|
// source via node-gyp and needs a C++ toolchain to install. If it fails
|
|
// the install still succeeds (it's optional) — this notice just tells the
|
|
// user what to install if they want the faster native HNSW backend.
|
|
// See README "Windows install" section and ADR-090 amendment 2026-05-08.
|
|
if (process.platform !== 'win32') return;
|
|
if (process.env.AQE_SKIP_WINDOWS_NOTICE === 'true') return;
|
|
|
|
console.log('');
|
|
log(`${BOLD}Windows install notice${RESET}`);
|
|
console.log(
|
|
` agentic-qe will attempt to compile the optional native HNSW backend`
|
|
);
|
|
console.log(` (hnswlib-node) during install. This requires:`);
|
|
console.log('');
|
|
console.log(` - ${BOLD}Python 3${RESET} on PATH, and`);
|
|
console.log(
|
|
` - ${BOLD}Visual Studio 2022 Build Tools${RESET} with the` +
|
|
` 'Desktop development with C++' workload, OR`
|
|
);
|
|
console.log(
|
|
` - ${BOLD}Visual Studio 2026${RESET} with the same workload AND` +
|
|
` ${BOLD}npm >= 11.6.3${RESET}`
|
|
);
|
|
console.log(` (run: ${CYAN}npm install -g npm@latest${RESET})`);
|
|
console.log('');
|
|
console.log(
|
|
` ${YELLOW}If the native build fails, install still succeeds${RESET} —`
|
|
);
|
|
console.log(
|
|
` AQE falls back to a pure-JS HNSW backend at runtime. That fallback`
|
|
);
|
|
console.log(
|
|
` is correct but degrades to O(N) brute-force when @ruvector/gnn is`
|
|
);
|
|
console.log(
|
|
` also unavailable (the default on Windows — no win32 prebuilds`
|
|
);
|
|
console.log(
|
|
` ship). Fine for small projects; for codebases with tens of`
|
|
);
|
|
console.log(
|
|
` thousands of vectors or more, install the toolchain.`
|
|
);
|
|
console.log('');
|
|
console.log(
|
|
` Set ${CYAN}AQE_SKIP_WINDOWS_NOTICE=true${RESET} to suppress this notice.`
|
|
);
|
|
console.log('');
|
|
}
|
|
|
|
function main() {
|
|
// Skip in CI environments unless explicitly requested
|
|
if (process.env.CI && !process.env.AQE_PREINSTALL_CHECK) {
|
|
return;
|
|
}
|
|
|
|
emitWindowsToolchainNotice();
|
|
|
|
const aqeBinary = findExistingAqeBinary();
|
|
|
|
// Check for both old package names
|
|
const v2Installed = checkPackageInstalled('agentic-qe');
|
|
const alphaInstalled = checkPackageInstalled('@agentic-qe/v3');
|
|
|
|
const v2Version = v2Installed ? getPackageVersion('agentic-qe') : null;
|
|
const alphaVersion = alphaInstalled ? getPackageVersion('@agentic-qe/v3') : null;
|
|
|
|
// If the currently installed agentic-qe is version 3.x, we're good (user is upgrading within v3)
|
|
if (v2Version && v2Version.startsWith('3.')) {
|
|
// User is upgrading from 3.x to newer 3.x - this should work normally
|
|
return;
|
|
}
|
|
|
|
if (!aqeBinary && !v2Installed && !alphaInstalled) {
|
|
// Clean install, nothing to do
|
|
return;
|
|
}
|
|
|
|
console.log('');
|
|
log(`${BOLD}Checking for existing agentic-qe installation...${RESET}`);
|
|
|
|
// Handle alpha package conflict
|
|
if (alphaInstalled) {
|
|
warn(`Found @agentic-qe/v3@${alphaVersion || 'alpha'} installed globally`);
|
|
warn('The alpha package uses the same "aqe" binary name as the released version');
|
|
console.log('');
|
|
|
|
// Check if we can auto-migrate
|
|
const autoMigrate = process.env.AQE_AUTO_MIGRATE === 'true' ||
|
|
process.env.npm_config_yes === 'true';
|
|
|
|
if (autoMigrate) {
|
|
log('Auto-migrating from alpha to release...');
|
|
if (uninstallPackage('@agentic-qe/v3')) {
|
|
success('Migration complete. Continuing with install...');
|
|
return;
|
|
} else {
|
|
error('Auto-migration failed. Please run manually:');
|
|
console.log(` ${CYAN}npm uninstall -g @agentic-qe/v3 && npm install -g agentic-qe@latest${RESET}`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Interactive mode
|
|
console.log(`${YELLOW}To upgrade from alpha to release:${RESET}`);
|
|
console.log('');
|
|
console.log(` ${BOLD}Option 1: Auto-migrate (recommended)${RESET}`);
|
|
console.log(` ${CYAN}AQE_AUTO_MIGRATE=true npm install -g agentic-qe@latest${RESET}`);
|
|
console.log('');
|
|
console.log(` ${BOLD}Option 2: Manual uninstall first${RESET}`);
|
|
console.log(` ${CYAN}npm uninstall -g @agentic-qe/v3${RESET}`);
|
|
console.log(` ${CYAN}npm install -g agentic-qe@latest${RESET}`);
|
|
console.log('');
|
|
console.log(` ${BOLD}Option 3: Force overwrite${RESET}`);
|
|
console.log(` ${CYAN}npm install -g agentic-qe@latest --force${RESET}`);
|
|
console.log('');
|
|
|
|
process.exit(1);
|
|
}
|
|
|
|
// Handle v2 package conflict
|
|
if (v2Installed && !v2Version?.startsWith('3.')) {
|
|
warn(`Found agentic-qe v${v2Version || '2.x'} installed globally`);
|
|
warn('The v2 package uses the same "aqe" binary name as v3');
|
|
console.log('');
|
|
|
|
const autoMigrate = process.env.AQE_AUTO_MIGRATE === 'true' ||
|
|
process.env.npm_config_yes === 'true';
|
|
|
|
if (autoMigrate) {
|
|
log('Auto-migrating from v2 to v3...');
|
|
if (uninstallPackage('agentic-qe')) {
|
|
success('Migration complete. Continuing with v3 install...');
|
|
return;
|
|
} else {
|
|
error('Auto-migration failed. Please run manually:');
|
|
console.log(` ${CYAN}npm uninstall -g agentic-qe && npm install -g agentic-qe@latest${RESET}`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
console.log(`${YELLOW}To upgrade from v2 to v3:${RESET}`);
|
|
console.log('');
|
|
console.log(` ${BOLD}Option 1: Auto-migrate (recommended)${RESET}`);
|
|
console.log(` ${CYAN}AQE_AUTO_MIGRATE=true npm install -g agentic-qe@latest${RESET}`);
|
|
console.log('');
|
|
console.log(` ${BOLD}Option 2: Manual uninstall first${RESET}`);
|
|
console.log(` ${CYAN}npm uninstall -g agentic-qe${RESET}`);
|
|
console.log(` ${CYAN}npm install -g agentic-qe@latest${RESET}`);
|
|
console.log('');
|
|
console.log(` ${BOLD}Option 3: Force overwrite${RESET}`);
|
|
console.log(` ${CYAN}npm install -g agentic-qe@latest --force${RESET}`);
|
|
console.log('');
|
|
|
|
process.exit(1);
|
|
|
|
} else if (aqeBinary && !v2Installed && !alphaInstalled) {
|
|
// Binary exists but no known package found - stale symlink or different package
|
|
warn(`Found existing 'aqe' binary at: ${aqeBinary}`);
|
|
|
|
// Try to identify what it's from
|
|
try {
|
|
const realpath = require('fs').realpathSync(aqeBinary);
|
|
if (realpath.includes('agentic-qe')) {
|
|
warn('This appears to be from a previous agentic-qe installation.');
|
|
}
|
|
} catch {
|
|
// Can't resolve realpath
|
|
}
|
|
|
|
console.log('');
|
|
console.log(`${YELLOW}To resolve the conflict:${RESET}`);
|
|
console.log('');
|
|
console.log(` ${BOLD}Option 1: Remove existing binary${RESET}`);
|
|
console.log(` ${CYAN}rm ${aqeBinary}${RESET}`);
|
|
console.log(` ${CYAN}npm install -g agentic-qe@latest${RESET}`);
|
|
console.log('');
|
|
console.log(` ${BOLD}Option 2: Force overwrite${RESET}`);
|
|
console.log(` ${CYAN}npm install -g agentic-qe@latest --force${RESET}`);
|
|
console.log('');
|
|
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
try {
|
|
main();
|
|
} catch (err) {
|
|
// Don't let preinstall failures block installation
|
|
// Just log and continue
|
|
if (process.env.DEBUG) {
|
|
error(`Preinstall check error: ${err.message}`);
|
|
}
|
|
}
|