mirror of
https://github.com/ruvnet/RuView
synced 2026-07-30 18:41:42 +00:00
90b29595fb
Adds the accepted ADR-285 Homecore metaharness, WASM-first kernel, read-only MCP guidance, guarded local host adapters, reviewed memory, and provenance-only npm release gates.
81 lines
2.2 KiB
JavaScript
81 lines
2.2 KiB
JavaScript
#!/usr/bin/env node
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
import { createHash } from 'node:crypto';
|
|
import {
|
|
existsSync,
|
|
readdirSync,
|
|
readFileSync,
|
|
statSync,
|
|
} from 'node:fs';
|
|
import { dirname, join, relative } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
const quiet = process.argv.includes('--quiet');
|
|
const INCLUDE = [
|
|
'package.json',
|
|
'bin',
|
|
'src',
|
|
'skills',
|
|
'.claude/settings.json',
|
|
'.claude/skills',
|
|
'.codex',
|
|
'.mcp',
|
|
'.harness/claims.json',
|
|
'.harness/mcp-policy.json',
|
|
'brain/corpus/core.jsonl',
|
|
'scripts',
|
|
'AGENTS.md',
|
|
'CLAUDE.md',
|
|
'README.md',
|
|
'LICENSE',
|
|
];
|
|
|
|
const sha = (value) => createHash('sha256').update(value).digest('hex');
|
|
const canonicalFile = (path) => readFileSync(path, 'utf8').replace(/\r\n/g, '\n');
|
|
const path = join(ROOT, '.harness', 'manifest.json');
|
|
const raw = readFileSync(path);
|
|
const manifest = JSON.parse(raw);
|
|
const findings = [];
|
|
const actualFiles = [];
|
|
|
|
function walk(target) {
|
|
const stat = statSync(target);
|
|
if (stat.isDirectory()) {
|
|
for (const name of readdirSync(target).sort()) walk(join(target, name));
|
|
} else {
|
|
actualFiles.push(relative(ROOT, target).replaceAll('\\', '/'));
|
|
}
|
|
}
|
|
|
|
for (const entry of INCLUDE) walk(join(ROOT, entry));
|
|
const actualSet = new Set(actualFiles);
|
|
const expectedSet = new Set(Object.keys(manifest.files || {}));
|
|
|
|
for (const [name, expected] of Object.entries(manifest.files || {})) {
|
|
const target = join(ROOT, name);
|
|
if (!existsSync(target)) findings.push(`${name}:missing`);
|
|
else if (sha(canonicalFile(target)) !== expected) findings.push(`${name}:hash-mismatch`);
|
|
}
|
|
for (const name of actualSet) {
|
|
if (!expectedSet.has(name)) findings.push(`${name}:untracked-by-manifest`);
|
|
}
|
|
for (const name of expectedSet) {
|
|
if (!actualSet.has(name)) findings.push(`${name}:not-in-package-surface`);
|
|
}
|
|
|
|
const expectedOuter = readFileSync(join(ROOT, '.harness', 'manifest.sha256'), 'utf8')
|
|
.trim()
|
|
.split(/\s+/)[0];
|
|
if (sha(raw) !== expectedOuter) findings.push('manifest.sha256:mismatch');
|
|
|
|
if (!quiet) {
|
|
console.log(JSON.stringify({
|
|
ok: findings.length === 0,
|
|
files: expectedSet.size,
|
|
findings,
|
|
}, null, 2));
|
|
}
|
|
process.exit(findings.length ? 1 : 0);
|