69b17e15a2
Implements three-layer defense against native module version mismatches: Layer 1: Node.js Version Tracking - Track Node.js version alongside package version in .install-version marker - Auto-trigger npm install when Node.js version changes - Backward compatible with old plain-text version marker format Layer 2: Native Module Verification - Add verifyNativeModules() function to test better-sqlite3 loads correctly - Verify after install completes to catch corrupted builds - Retry with force flag if initial install verification fails Layer 3: Graceful Failure - Catch ERR_DLOPEN_FAILED in context-hook and delete version marker - Exit cleanly to avoid error spam in Claude Code UI - Auto-fix on next session start Changes: - scripts/smart-install.js: Add Node.js version tracking and verification - src/hooks/context-hook.ts: Add graceful failure handling for native module errors - tests/smart-install.test.js: Add tests for version marker format compatibility - plugin/scripts/context-hook.js: Built output from TypeScript source Fixes the issue where users see ERR_DLOPEN_FAILED errors after Node.js upgrades, requiring manual npm install. Now automatically detects and fixes the issue. Related design doc: docs/context/native-module-auto-fix-design.md Implementation plan: docs/context/native-module-auto-fix-implementation.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Alex Newman <thedotmack@gmail.com>
48 lines
1.3 KiB
JavaScript
48 lines
1.3 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert';
|
|
import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
|
|
import { join } from 'path';
|
|
|
|
const VERSION_MARKER_PATH = join(process.cwd(), '.install-version');
|
|
|
|
test('version marker - new JSON format', () => {
|
|
const marker = {
|
|
packageVersion: '6.3.2',
|
|
nodeVersion: 'v22.21.1',
|
|
installedAt: new Date().toISOString()
|
|
};
|
|
|
|
writeFileSync(VERSION_MARKER_PATH, JSON.stringify(marker, null, 2));
|
|
const content = JSON.parse(readFileSync(VERSION_MARKER_PATH, 'utf-8'));
|
|
|
|
assert.strictEqual(content.packageVersion, '6.3.2');
|
|
assert.strictEqual(content.nodeVersion, 'v22.21.1');
|
|
assert.ok(content.installedAt);
|
|
|
|
unlinkSync(VERSION_MARKER_PATH);
|
|
});
|
|
|
|
test('version marker - backward compatibility with old format', () => {
|
|
// Old format: plain text version string
|
|
writeFileSync(VERSION_MARKER_PATH, '6.3.2');
|
|
const content = readFileSync(VERSION_MARKER_PATH, 'utf-8').trim();
|
|
|
|
// Should be able to parse old format
|
|
let marker;
|
|
try {
|
|
marker = JSON.parse(content);
|
|
} catch {
|
|
// Old format - create compatible object
|
|
marker = {
|
|
packageVersion: content,
|
|
nodeVersion: null,
|
|
installedAt: null
|
|
};
|
|
}
|
|
|
|
assert.strictEqual(marker.packageVersion, '6.3.2');
|
|
assert.strictEqual(marker.nodeVersion, null);
|
|
|
|
unlinkSync(VERSION_MARKER_PATH);
|
|
});
|