be755dd8eb
Code-review fixes: guard pathToFileURL against undefined argv[1]; reject unrecognised flags with exit 2 before any filesystem access (prevents a --revert typo from silently applying the patch). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
123 lines
4.4 KiB
JavaScript
123 lines
4.4 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* ruflo H7 patch re-apply tool.
|
|
*
|
|
* H7: `ruflo memory store` routes through the AgentDB-v3 bridge
|
|
* (memory-bridge.js:bridgeStoreEntry) which inserts into an in-RAM sql.js DB
|
|
* and never flushes to disk — entries are lost between CLI invocations.
|
|
*
|
|
* Fix: force getBridge() in @claude-flow/cli's memory-initializer.js to
|
|
* return null, so memory ops fall through to the raw sql.js path that DOES
|
|
* persist (db.export() + writeFileRestricted).
|
|
*
|
|
* The patch lives in the GLOBAL npm install and is overwritten by
|
|
* `ruflo update` — re-run this script after any ruflo upgrade.
|
|
*
|
|
* Usage:
|
|
* node tools/ruflo-h7-patch.mjs apply the patch (idempotent)
|
|
* node tools/ruflo-h7-patch.mjs --check exit 0 if patched, 1 if not
|
|
* node tools/ruflo-h7-patch.mjs --revert remove the patch
|
|
*
|
|
* See docs/superpowers/specs/2026-05-15-ruflo-memory-h7-fix-and-advisory-hook-design.md §4
|
|
*/
|
|
import { execSync } from 'node:child_process';
|
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { pathToFileURL } from 'node:url';
|
|
|
|
export const PATCH_MARKER = 'LIDERRA-H7-PATCH';
|
|
export const PATCH_LINE =
|
|
' return null; /* LIDERRA-H7-PATCH: AgentDB bridge writes in-RAM sql.js, never flushes — force raw persisting path */';
|
|
const ANCHOR = 'async function getBridge() {';
|
|
|
|
/** True if the file content already contains the patch. */
|
|
export function isPatched(content) {
|
|
return content.includes(PATCH_MARKER);
|
|
}
|
|
|
|
/**
|
|
* Insert the patch line as the first statement of getBridge().
|
|
* Idempotent. Throws if the anchor is absent (ruflo upstream changed).
|
|
*/
|
|
export function applyPatch(content) {
|
|
if (isPatched(content)) return { content, changed: false };
|
|
const idx = content.indexOf(ANCHOR);
|
|
if (idx === -1) {
|
|
throw new Error(
|
|
`anchor "${ANCHOR}" not found — ruflo upstream changed getBridge(); revise this patch manually`,
|
|
);
|
|
}
|
|
const insertAt = idx + ANCHOR.length;
|
|
const patched = content.slice(0, insertAt) + '\n' + PATCH_LINE + content.slice(insertAt);
|
|
return { content: patched, changed: true };
|
|
}
|
|
|
|
/** Remove the patch line (the line carrying PATCH_MARKER). */
|
|
export function revertPatch(content) {
|
|
if (!isPatched(content)) return { content, changed: false };
|
|
const lines = content.split('\n');
|
|
const kept = lines.filter((l) => !l.includes(PATCH_MARKER));
|
|
return { content: kept.join('\n'), changed: true };
|
|
}
|
|
|
|
/**
|
|
* Resolve the path to @claude-flow/cli's memory-initializer.js inside the
|
|
* global npm tree. Handles both nested (under ruflo/) and hoisted layouts.
|
|
*/
|
|
export function resolveTargetPath(npmRoot) {
|
|
const tail = ['@claude-flow', 'cli', 'dist', 'src', 'memory', 'memory-initializer.js'];
|
|
const candidates = [
|
|
join(npmRoot, 'ruflo', 'node_modules', ...tail),
|
|
join(npmRoot, ...tail),
|
|
];
|
|
return candidates.find((c) => existsSync(c)) ?? null;
|
|
}
|
|
|
|
function main() {
|
|
const mode = process.argv[2];
|
|
const VALID_MODES = [undefined, '--check', '--revert'];
|
|
if (!VALID_MODES.includes(mode)) {
|
|
console.error(`[ruflo-h7-patch] unknown flag: ${mode}`);
|
|
console.error('Usage: node tools/ruflo-h7-patch.mjs [--check | --revert]');
|
|
process.exit(2);
|
|
}
|
|
let npmRoot;
|
|
try {
|
|
npmRoot = execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
} catch (e) {
|
|
console.error('[ruflo-h7-patch] failed to run `npm root -g`: ' + e.message);
|
|
process.exit(2);
|
|
}
|
|
const target = resolveTargetPath(npmRoot);
|
|
if (!target) {
|
|
console.error('[ruflo-h7-patch] memory-initializer.js not found under ' + npmRoot);
|
|
process.exit(2);
|
|
}
|
|
const content = readFileSync(target, 'utf8');
|
|
|
|
if (mode === '--check') {
|
|
const patched = isPatched(content);
|
|
console.log(`[ruflo-h7-patch] ${target}\n ${patched ? 'PATCHED' : 'UNPATCHED'}`);
|
|
process.exit(patched ? 0 : 1);
|
|
}
|
|
if (mode === '--revert') {
|
|
const { content: out, changed } = revertPatch(content);
|
|
if (changed) writeFileSync(target, out);
|
|
console.log(`[ruflo-h7-patch] revert: ${changed ? 'patch removed' : 'was not patched'}`);
|
|
process.exit(0);
|
|
}
|
|
try {
|
|
const { content: out, changed } = applyPatch(content);
|
|
if (changed) writeFileSync(target, out);
|
|
console.log(`[ruflo-h7-patch] apply: ${changed ? 'patched' : 'already patched'}`);
|
|
process.exit(0);
|
|
} catch (e) {
|
|
console.error('[ruflo-h7-patch] ' + e.message);
|
|
process.exit(3);
|
|
}
|
|
}
|
|
|
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
main();
|
|
}
|