Expose historical source import through CLI

This commit is contained in:
morluto 2026-07-12 21:34:05 +00:00
parent 77db2ecc1c
commit fbe8ec1f13
4 changed files with 74 additions and 2 deletions

View File

@ -127,6 +127,7 @@ npx -y rea-agents doctor
| Ask an agent to investigate an app and build a feature | Install the skill, then talk to your agent |
| Inspect or decompile one part of an app from the Terminal | `rea analyze` or `rea decompile` |
| Validate or canonicalize an Evidence v2 bundle | `rea evidence-import` or `rea evidence-export` |
| Import source as historical reference | `rea import-reference-source` |
Filesystem evidence commands and MCP file tools are disabled until the operator approves absolute roots:
@ -136,6 +137,13 @@ rea evidence-import /absolute/path/to/evidence/bundle.json
rea evidence-export /absolute/path/to/evidence/bundle.json /absolute/path/to/evidence/canonical.json
```
Historical source import requires a separate allowlist and never treats source as current behavioral authority:
```bash
export REA_REFERENCE_ROOTS_JSON='["/absolute/path/to/source"]'
rea import-reference-source /absolute/path/to/source
```
Exports never replace an existing file unless `--overwrite` is explicit. Imports are size/depth bounded, validate every Evidence v2 ID and manifest, and never execute bundle content.
Completion metadata is verifier-owned. Run `npm run completion:generate` after changing contracts, schemas, providers, scenarios, package metadata, verifier inputs, or the managed skill. CI runs `npm run completion:check`; it detects stale or tampered hashes and never rewrites a branch. Unsupported and unknown outcomes remain separate from passes, and a pass or failure requires indexed Evidence v2 IDs.

View File

@ -16,7 +16,7 @@
},
{
"path": "scripts/verify-package.mjs",
"sha256": "ffad65be3d5dde87c1e2d7b8886949066531e5b82933596b947507aab5f45b58"
"sha256": "b3809e3b7593fcaf16c9f962fbfaea105caca168c2ed8fd07336804ebb581174"
},
{
"path": "scripts/verify-real-hopper.mjs",

View File

@ -24,6 +24,7 @@ const exec = promisify(execFile);
const root = process.cwd();
const workspace = await mkdtemp(join(tmpdir(), "rea-package-"));
const evidenceRoot = join(workspace, "evidence");
const referenceRoot = join(workspace, "reference-source");
let tarball;
try {
@ -44,7 +45,21 @@ try {
const home = join(workspace, "home");
const fakeBin = join(workspace, "bin");
await mkdir(fakeBin, { recursive: true });
await mkdir(evidenceRoot, { recursive: true });
await Promise.all([
mkdir(evidenceRoot, { recursive: true }),
mkdir(join(referenceRoot, "src"), { recursive: true }),
]);
await Promise.all([
writeFile(
join(referenceRoot, "src", "main.ts"),
'import "./dependency";\n',
),
writeFile(
join(referenceRoot, "src", "dependency.ts"),
"export const value = 1;\n",
),
writeFile(join(referenceRoot, ".env"), "PACKAGE_SECRET_SENTINEL=1\n"),
]);
const brew = join(fakeBin, "brew");
const swVers = join(fakeBin, "sw_vers");
const npx = join(fakeBin, "npx");
@ -84,6 +99,8 @@ try {
HOPPER_LAUNCHER_PATH: hopper,
REA_NPX_LOG: npxLog,
REA_EVIDENCE_ROOTS_JSON: JSON.stringify([evidenceRoot]),
REA_REFERENCE_ROOTS_JSON: JSON.stringify([referenceRoot]),
REA_REFERENCE_SECRET_PATTERNS_JSON: JSON.stringify([".env"]),
};
await exec(
"npm",
@ -99,6 +116,7 @@ try {
!help.includes("setup") ||
!help.includes("inventory-artifact") ||
!help.includes("extract-artifact") ||
!help.includes("import-reference-source") ||
!llms.includes("decompile") ||
!llms.includes("inventory-artifact") ||
doctor.healthy !== expectedDoctorHealth ||
@ -124,6 +142,20 @@ try {
artifactInventory.normalized_result?.manifest?.root_format !== "zip"
)
throw new Error("packaged artifact inventory CLI failed");
const referenceImport = json(
await run(
cli,
["import-reference-source", referenceRoot, "--json"],
environment,
),
);
if (
referenceImport.authority !== "historical-reference" ||
referenceImport.root_alias !== "$REFERENCE_ROOT" ||
referenceImport.relationships?.[0]?.resolution !== "internal" ||
JSON.stringify(referenceImport).includes("PACKAGE_SECRET_SENTINEL")
)
throw new Error("packaged historical reference import CLI failed");
const { createEvidence } = await import(
new URL("../dist/domain/evidence.js", import.meta.url)
);

View File

@ -14,6 +14,7 @@ import {
exportEvidenceBundleCommand,
importEvidenceBundleCommand,
} from "./application/EvidenceBundleCommands.js";
import { importReferenceSource } from "./application/ReferenceSourceImport.js";
/**
* Build the one-shot Incur CLI without starting Hopper at import time.
@ -96,9 +97,40 @@ export const createCli = (): ReturnType<typeof Cli.create> => {
registerNativeCommands(cli, logger);
registerArtifactCommands(cli, logger);
registerEvidenceCommands(cli, logger);
registerReferenceSourceCommand(cli, logger);
return cli;
};
const registerReferenceSourceCommand = (
cli: ReturnType<typeof Cli.create>,
logger: Logger,
): void => {
cli.command("import-reference-source", {
description: "Import a bounded source tree as historical reference only",
args: z.object({
root: z
.string()
.describe("Source root allowed by REA_REFERENCE_ROOTS_JSON"),
}),
run: ({ args }) =>
logCliCommand(logger, "import-reference-source", async () => {
const config = parseConfig(process.env);
if (!config.ok)
return { error: config.error._tag, message: config.error.message };
const imported = await importReferenceSource({
root: args.root,
caller: "rea-cli",
policy: config.value.referenceSourcePolicy,
importer: PRODUCT_IDENTITY.packageName,
importerVersion: null,
});
return imported.ok
? imported.value
: { error: imported.error.code, message: imported.error.message };
}),
});
};
const registerArtifactCommands = (
cli: ReturnType<typeof Cli.create>,
logger: Logger,