diff --git a/README.md b/README.md index ed3336c..4d4c3fe 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,12 @@ Projects that need worker orchestration can import `createDifferentialTestingWor See the [Differential Testing data contract](skills/burnlist/references/differential-testing-data.md) and [adapter SDK reference](skills/burnlist/references/differential-testing-adapter-sdk.md) for scenario bundles, exact sessions, telemetry, and worker interfaces. +## Streaming Diff hooks + +`burnlist hooks install --agent codex,claude` merges local Streaming Diff commands into `.codex/hooks.json` and `.claude/settings.json`; it preserves existing hook entries. The agent remains responsible for any first-run hook trust/consent prompt—Burnlist only writes configuration and never bypasses that review. `burnlist hooks status` reports whether each config is tracked (and therefore shared) or local; an already tracked config cannot be hidden with `.git/info/exclude`. + +Hooks use the portable `burnlist` command from `PATH`; the host resolves the platform-specific launcher. + ## Command Line - `burnlist --plan --check` validates the active queue and completed ledger. diff --git a/bin/burnlist.mjs b/bin/burnlist.mjs index 1cbfc88..6aba10c 100755 --- a/bin/burnlist.mjs +++ b/bin/burnlist.mjs @@ -9,6 +9,8 @@ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const knownSubcommands = new Set([ "uninstall", "differential-testing", + "streaming-diff", + "hooks", "oven", "new", "show", @@ -42,6 +44,7 @@ function runNodeScript(path, scriptArgs) { }); } +async function main() { if (args[0] === "uninstall") { let prefix; try { @@ -64,17 +67,18 @@ if (args[0] === "uninstall") { console.error("Burnlist: npm uninstall failed; restoring agent skill registrations."); runNodeScript(resolve(packageRoot, "scripts", "register-skills.mjs"), ["--force-global"]); } - process.exit(removal.status || 0); + process.exitCode = removal.status || 0; + return; } if (args[0] === "differential-testing" && args[1] === "schema") { console.log(resolve(packageRoot, "ovens", "differential-testing", "schema", "differential-testing-data.schema.json")); - process.exit(0); + return; } if (args[0] === "differential-testing" && args[1] === "sdk") { console.log(resolve(packageRoot, "ovens", "differential-testing", "engine", "differential-testing-adapter-sdk.mjs")); - process.exit(0); + return; } if (args[0] === "differential-testing" && ["validate", "validate-bundle"].includes(args[1])) { @@ -95,7 +99,7 @@ if (args[0] === "differential-testing" && ["validate", "validate-bundle"].includ const sampleCount = document.fields.reduce((total, field) => total + field.sampleCount, 0); console.log(`Valid Differential Testing data: ${document.fields.length} fields, ${sampleCount} samples, ${document.summary.frames.uniqueTicks} aligned ticks.`); } - process.exit(0); + return; } catch (error) { console.error(error.message); process.exit(1); @@ -120,6 +124,8 @@ Usage: burnlist differential-testing validate-bundle burnlist differential-testing schema burnlist differential-testing sdk + burnlist streaming-diff ... + burnlist hooks [--agent codex,claude] [--untracked] burnlist oven ... burnlist new [--repo ] burnlist show [#] [--repo ] @@ -142,17 +148,21 @@ Options: --oven-data Bind one Oven to a read-only normalized JSON payload. --version, -v Print the installed Burnlist version. --help, -h Show this help.`); - process.exit(0); + return; } if (args[0] !== "oven" && (args.includes("--version") || args.includes("-v"))) { const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")); console.log(packageJson.version); - process.exit(0); + return; } if (args[0] === "oven") { await import("../src/cli/oven-cli.mjs"); +} else if (args[0] === "streaming-diff") { + await import("../src/cli/streaming-diff-cli.mjs"); +} else if (args[0] === "hooks") { + await import("../src/cli/hooks-cli.mjs"); } else if (["new", "show", "ready", "start", "close", "burn"].includes(args[0])) { await import("../src/cli/lifecycle-cli.mjs"); } else if (["register", "unregister", "roots", "init"].includes(args[0])) { @@ -160,3 +170,6 @@ if (args[0] === "oven") { } else { await import("../src/server/burnlist-dashboard-server.mjs"); } +} + +await main(); diff --git a/dashboard/src/App.tsx b/dashboard/src/App.tsx index a068380..e751ab7 100644 --- a/dashboard/src/App.tsx +++ b/dashboard/src/App.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from "react"; import { Clock3, ListChecks } from "lucide-react"; -import { AppHeader, ChecklistDashboard, DashboardError, DifferentialTestingPage, EmptyState, FILTERS, Filters, NewOvenPage, ProjectGroup, RunBurnPage } from "@components"; +import { AppHeader, ChecklistDashboard, DashboardError, DifferentialTestingPage, EmptyState, FILTERS, Filters, NewOvenPage, ProjectGroup, RunBurnPage, StreamingDiff } from "@components"; import { useDashboardData } from "@hooks"; import { currentSection, filterFromUrl, listHref, selectedBurnlist } from "@lib"; import type { Filter } from "@lib"; @@ -9,7 +9,8 @@ export function App() { const section = currentSection(); const selected = useMemo(selectedBurnlist, [window.location.pathname, window.location.search]); const [filter, setFilter] = useState(() => filterFromUrl(FILTERS)); - const { projects, progress, error, loading } = useDashboardData({ section, selected }); + const dashboardSection = section === "streaming-diff" ? "burnlists" : section; + const { projects, progress, error, loading } = useDashboardData({ section: dashboardSection, selected }); const updateFilter = (nextFilter: Filter) => { const url = new URL(window.location.href); @@ -23,8 +24,8 @@ export function App() { return (
-
- {section === "differential-testing" ? : section === "new-oven" ? : section === "run-burn" ? : selected ? ( +
+ {section === "differential-testing" ? : section === "streaming-diff" ? : section === "new-oven" ? : section === "run-burn" ? : selected ? ( error ? : loading && !progress ? : progress ? ( ) : diff --git a/dashboard/src/components/AppHeader/AppHeader.tsx b/dashboard/src/components/AppHeader/AppHeader.tsx index b0ee491..5a978fc 100644 --- a/dashboard/src/components/AppHeader/AppHeader.tsx +++ b/dashboard/src/components/AppHeader/AppHeader.tsx @@ -13,8 +13,9 @@ export function AppHeader({ section }: { section: string }) { Burnlist {section === "differential-testing" &&
Differential Testing
} + {section === "streaming-diff" &&
Streaming Diff
}