@lannguyensi/harness 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +552 -0
- package/LICENSE +21 -0
- package/README.md +154 -0
- package/dist/cli/add/index.d.ts +14 -0
- package/dist/cli/add/index.js +71 -0
- package/dist/cli/add/index.js.map +1 -0
- package/dist/cli/add/mutate.d.ts +39 -0
- package/dist/cli/add/mutate.js +36 -0
- package/dist/cli/add/mutate.js.map +1 -0
- package/dist/cli/adopt/derive.d.ts +38 -0
- package/dist/cli/adopt/derive.js +94 -0
- package/dist/cli/adopt/derive.js.map +1 -0
- package/dist/cli/adopt/index.d.ts +20 -0
- package/dist/cli/adopt/index.js +156 -0
- package/dist/cli/adopt/index.js.map +1 -0
- package/dist/cli/apply/apply.d.ts +49 -0
- package/dist/cli/apply/apply.js +333 -0
- package/dist/cli/apply/apply.js.map +1 -0
- package/dist/cli/apply/generate-memory-index.d.ts +17 -0
- package/dist/cli/apply/generate-memory-index.js +167 -0
- package/dist/cli/apply/generate-memory-index.js.map +1 -0
- package/dist/cli/apply/generate-settings.d.ts +15 -0
- package/dist/cli/apply/generate-settings.js +87 -0
- package/dist/cli/apply/generate-settings.js.map +1 -0
- package/dist/cli/apply/index.d.ts +1 -0
- package/dist/cli/apply/index.js +2 -0
- package/dist/cli/apply/index.js.map +1 -0
- package/dist/cli/audit.d.ts +36 -0
- package/dist/cli/audit.js +121 -0
- package/dist/cli/audit.js.map +1 -0
- package/dist/cli/describe.d.ts +13 -0
- package/dist/cli/describe.js +26 -0
- package/dist/cli/describe.js.map +1 -0
- package/dist/cli/diff/engine.d.ts +21 -0
- package/dist/cli/diff/engine.js +161 -0
- package/dist/cli/diff/engine.js.map +1 -0
- package/dist/cli/diff/git.d.ts +6 -0
- package/dist/cli/diff/git.js +32 -0
- package/dist/cli/diff/git.js.map +1 -0
- package/dist/cli/diff/index.d.ts +15 -0
- package/dist/cli/diff/index.js +39 -0
- package/dist/cli/diff/index.js.map +1 -0
- package/dist/cli/diff/since-apply.d.ts +57 -0
- package/dist/cli/diff/since-apply.js +255 -0
- package/dist/cli/diff/since-apply.js.map +1 -0
- package/dist/cli/doctor/format.d.ts +2 -0
- package/dist/cli/doctor/format.js +126 -0
- package/dist/cli/doctor/format.js.map +1 -0
- package/dist/cli/doctor/index.d.ts +14 -0
- package/dist/cli/doctor/index.js +281 -0
- package/dist/cli/doctor/index.js.map +1 -0
- package/dist/cli/doctor/types.d.ts +46 -0
- package/dist/cli/doctor/types.js +2 -0
- package/dist/cli/doctor/types.js.map +1 -0
- package/dist/cli/dry-run.d.ts +46 -0
- package/dist/cli/dry-run.js +168 -0
- package/dist/cli/dry-run.js.map +1 -0
- package/dist/cli/exit-codes.d.ts +10 -0
- package/dist/cli/exit-codes.js +15 -0
- package/dist/cli/exit-codes.js.map +1 -0
- package/dist/cli/explain.d.ts +14 -0
- package/dist/cli/explain.js +97 -0
- package/dist/cli/explain.js.map +1 -0
- package/dist/cli/export.d.ts +31 -0
- package/dist/cli/export.js +84 -0
- package/dist/cli/export.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +549 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init/index.d.ts +17 -0
- package/dist/cli/init/index.js +57 -0
- package/dist/cli/init/index.js.map +1 -0
- package/dist/cli/init/templates.d.ts +4 -0
- package/dist/cli/init/templates.js +175 -0
- package/dist/cli/init/templates.js.map +1 -0
- package/dist/cli/list.d.ts +12 -0
- package/dist/cli/list.js +118 -0
- package/dist/cli/list.js.map +1 -0
- package/dist/cli/loader.d.ts +24 -0
- package/dist/cli/loader.js +74 -0
- package/dist/cli/loader.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +6 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/policy/intercept.d.ts +34 -0
- package/dist/cli/policy/intercept.js +172 -0
- package/dist/cli/policy/intercept.js.map +1 -0
- package/dist/cli/remove/index.d.ts +18 -0
- package/dist/cli/remove/index.js +95 -0
- package/dist/cli/remove/index.js.map +1 -0
- package/dist/cli/remove/mutate.d.ts +9 -0
- package/dist/cli/remove/mutate.js +68 -0
- package/dist/cli/remove/mutate.js.map +1 -0
- package/dist/cli/validate/checks.d.ts +23 -0
- package/dist/cli/validate/checks.js +253 -0
- package/dist/cli/validate/checks.js.map +1 -0
- package/dist/cli/validate/index.d.ts +18 -0
- package/dist/cli/validate/index.js +50 -0
- package/dist/cli/validate/index.js.map +1 -0
- package/dist/cli/validate/types.d.ts +7 -0
- package/dist/cli/validate/types.js +5 -0
- package/dist/cli/validate/types.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/io/atomic-write.d.ts +8 -0
- package/dist/io/atomic-write.js +30 -0
- package/dist/io/atomic-write.js.map +1 -0
- package/dist/io/harness-lock.d.ts +33 -0
- package/dist/io/harness-lock.js +260 -0
- package/dist/io/harness-lock.js.map +1 -0
- package/dist/io/last-apply.d.ts +20 -0
- package/dist/io/last-apply.js +123 -0
- package/dist/io/last-apply.js.map +1 -0
- package/dist/io/lock.d.ts +11 -0
- package/dist/io/lock.js +33 -0
- package/dist/io/lock.js.map +1 -0
- package/dist/io/patch.d.ts +10 -0
- package/dist/io/patch.js +8 -0
- package/dist/io/patch.js.map +1 -0
- package/dist/io/restart-hints.d.ts +5 -0
- package/dist/io/restart-hints.js +59 -0
- package/dist/io/restart-hints.js.map +1 -0
- package/dist/io/three-state.d.ts +7 -0
- package/dist/io/three-state.js +20 -0
- package/dist/io/three-state.js.map +1 -0
- package/dist/io/validate-before-write.d.ts +12 -0
- package/dist/io/validate-before-write.js +23 -0
- package/dist/io/validate-before-write.js.map +1 -0
- package/dist/overrides/index.d.ts +2 -0
- package/dist/overrides/index.js +3 -0
- package/dist/overrides/index.js.map +1 -0
- package/dist/overrides/machines.d.ts +12 -0
- package/dist/overrides/machines.js +46 -0
- package/dist/overrides/machines.js.map +1 -0
- package/dist/overrides/merge.d.ts +6 -0
- package/dist/overrides/merge.js +173 -0
- package/dist/overrides/merge.js.map +1 -0
- package/dist/policies/duration.d.ts +5 -0
- package/dist/policies/duration.js +50 -0
- package/dist/policies/duration.js.map +1 -0
- package/dist/policies/extract.d.ts +50 -0
- package/dist/policies/extract.js +190 -0
- package/dist/policies/extract.js.map +1 -0
- package/dist/policies/index.d.ts +5 -0
- package/dist/policies/index.js +6 -0
- package/dist/policies/index.js.map +1 -0
- package/dist/policies/ledger-client.d.ts +39 -0
- package/dist/policies/ledger-client.js +378 -0
- package/dist/policies/ledger-client.js.map +1 -0
- package/dist/policies/requires.d.ts +44 -0
- package/dist/policies/requires.js +146 -0
- package/dist/policies/requires.js.map +1 -0
- package/dist/policies/timestamp.d.ts +14 -0
- package/dist/policies/timestamp.js +36 -0
- package/dist/policies/timestamp.js.map +1 -0
- package/dist/probes/mcp.d.ts +29 -0
- package/dist/probes/mcp.js +226 -0
- package/dist/probes/mcp.js.map +1 -0
- package/dist/probes/memory.d.ts +24 -0
- package/dist/probes/memory.js +89 -0
- package/dist/probes/memory.js.map +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/intercept.d.ts +53 -0
- package/dist/runtime/intercept.js +181 -0
- package/dist/runtime/intercept.js.map +1 -0
- package/dist/runtime/ledger-record.d.ts +43 -0
- package/dist/runtime/ledger-record.js +239 -0
- package/dist/runtime/ledger-record.js.map +1 -0
- package/dist/runtime/session-id.d.ts +10 -0
- package/dist/runtime/session-id.js +37 -0
- package/dist/runtime/session-id.js.map +1 -0
- package/dist/schema/extract.d.ts +5 -0
- package/dist/schema/extract.js +23 -0
- package/dist/schema/extract.js.map +1 -0
- package/dist/schema/grounding.d.ts +65 -0
- package/dist/schema/grounding.js +21 -0
- package/dist/schema/grounding.js.map +1 -0
- package/dist/schema/hooks.d.ts +86 -0
- package/dist/schema/hooks.js +42 -0
- package/dist/schema/hooks.js.map +1 -0
- package/dist/schema/index.d.ts +961 -0
- package/dist/schema/index.js +55 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/memory.d.ts +131 -0
- package/dist/schema/memory.js +38 -0
- package/dist/schema/memory.js.map +1 -0
- package/dist/schema/policies.d.ts +412 -0
- package/dist/schema/policies.js +53 -0
- package/dist/schema/policies.js.map +1 -0
- package/dist/schema/requires.d.ts +115 -0
- package/dist/schema/requires.js +57 -0
- package/dist/schema/requires.js.map +1 -0
- package/dist/schema/tools.d.ts +283 -0
- package/dist/schema/tools.js +66 -0
- package/dist/schema/tools.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Inverse of `src/cli/adopt/derive.ts#parseSettingsHooks`: turn the manifest's
|
|
2
|
+
// `hooks[]` into the nested Claude Code settings.json shape that `harness apply`
|
|
3
|
+
// writes to `harness.generated/settings.json`. Pure function, no I/O.
|
|
4
|
+
//
|
|
5
|
+
// Output shape (matches Claude Code's settings.json `hooks` section):
|
|
6
|
+
//
|
|
7
|
+
// { hooks: { <EventName>: [ { matcher?, hooks: [{ type, command, timeout? }] } ] } }
|
|
8
|
+
//
|
|
9
|
+
// Field decisions for v1 (per ARCHITECTURE §5 + Phase 3 #2 task scope):
|
|
10
|
+
// - `type: "command"` is always emitted explicitly. Claude Code accepts
|
|
11
|
+
// missing `type` (it defaults to "command") but explicit is friendlier
|
|
12
|
+
// to humans diffing the file.
|
|
13
|
+
// - `matcher` is omitted when the manifest hook has no `match`. Hooks with
|
|
14
|
+
// a match string emit a `matcher` field with that exact string. Regex
|
|
15
|
+
// metacharacters are passed through unchanged.
|
|
16
|
+
// - `timeout` is always emitted (the manifest's `budget_ms` is always set
|
|
17
|
+
// after schema-defaulting). Always-emit is the conservative choice
|
|
18
|
+
// against an unknown runtime default; an explicit value is consumed
|
|
19
|
+
// identically whether or not the runtime would have defaulted to it.
|
|
20
|
+
// - `path_match` and `bash_match` are NOT projected. Claude Code's
|
|
21
|
+
// settings.json `matcher` filters only on the tool name, so there is
|
|
22
|
+
// no native settings.json field for "additional filter when this hook
|
|
23
|
+
// fires". Per the canonical example in ARCHITECTURE.md Appendix A
|
|
24
|
+
// (e.g. `require-preflight-evidence` with `bash_match: "^git ..."`),
|
|
25
|
+
// these filters are enforced inside the referenced hook script, not by
|
|
26
|
+
// harness's wiring. The manifest fields are documentation: they tell
|
|
27
|
+
// `harness validate`/`doctor` what the script claims to filter on, so
|
|
28
|
+
// the script's behaviour can be cross-checked, but `harness apply`
|
|
29
|
+
// does not synthesise wrapper shell to enforce them. Wrapping was
|
|
30
|
+
// considered and rejected: it (a) doubles the surface for hook bugs,
|
|
31
|
+
// (b) hard-codes shell semantics into the manifest projection, and
|
|
32
|
+
// (c) the canonical hook scripts already enforce these filters.
|
|
33
|
+
// - `blocking` is harness-internal: it tells `validate`/`doctor` how to
|
|
34
|
+
// classify a hook for soft/hard-failure reporting. Claude Code's
|
|
35
|
+
// settings.json has no documented equivalent (verified against the
|
|
36
|
+
// ecosystem as of 2026-04 — revisit if a runtime field appears), so
|
|
37
|
+
// it does NOT survive the projection.
|
|
38
|
+
//
|
|
39
|
+
// Stable-output rules (load-bearing for byte-equivalent regeneration on a
|
|
40
|
+
// no-op apply):
|
|
41
|
+
// - Event keys sorted ascending by name (insertion order in the JSON
|
|
42
|
+
// object reflects this; JSON.stringify preserves it).
|
|
43
|
+
// - Within an event, groups sorted by (matcher, command).
|
|
44
|
+
// - Within a group, the inner hooks[] preserves matcher-grouping order
|
|
45
|
+
// and is sorted by command for the same reason.
|
|
46
|
+
export const DEFAULT_BUDGET_MS = 30_000;
|
|
47
|
+
export function generateSettings(manifest) {
|
|
48
|
+
const byEvent = new Map();
|
|
49
|
+
for (const h of manifest.hooks) {
|
|
50
|
+
const list = byEvent.get(h.event) ?? [];
|
|
51
|
+
list.push(h);
|
|
52
|
+
byEvent.set(h.event, list);
|
|
53
|
+
}
|
|
54
|
+
const out = { hooks: {} };
|
|
55
|
+
const eventNames = [...byEvent.keys()].sort();
|
|
56
|
+
for (const event of eventNames) {
|
|
57
|
+
const hooks = byEvent.get(event) ?? [];
|
|
58
|
+
out.hooks[event] = buildGroups(hooks);
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
function buildGroups(hooks) {
|
|
63
|
+
// Group by exact `match` value. Unmatched hooks share the empty-string
|
|
64
|
+
// bucket and emit a group without a `matcher` field.
|
|
65
|
+
const byMatcher = new Map();
|
|
66
|
+
for (const h of hooks) {
|
|
67
|
+
const key = h.match ?? "";
|
|
68
|
+
const list = byMatcher.get(key) ?? [];
|
|
69
|
+
list.push(h);
|
|
70
|
+
byMatcher.set(key, list);
|
|
71
|
+
}
|
|
72
|
+
const matcherKeys = [...byMatcher.keys()].sort();
|
|
73
|
+
const groups = [];
|
|
74
|
+
for (const key of matcherKeys) {
|
|
75
|
+
const groupHooks = byMatcher.get(key) ?? [];
|
|
76
|
+
const inner = [...groupHooks]
|
|
77
|
+
.sort((a, b) => (a.command < b.command ? -1 : a.command > b.command ? 1 : 0))
|
|
78
|
+
.map(toSettingsCommand);
|
|
79
|
+
const group = key === "" ? { hooks: inner } : { matcher: key, hooks: inner };
|
|
80
|
+
groups.push(group);
|
|
81
|
+
}
|
|
82
|
+
return groups;
|
|
83
|
+
}
|
|
84
|
+
function toSettingsCommand(h) {
|
|
85
|
+
return { type: "command", command: h.command, timeout: h.budget_ms };
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=generate-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-settings.js","sourceRoot":"","sources":["../../../src/cli/apply/generate-settings.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,iFAAiF;AACjF,sEAAsE;AACtE,EAAE;AACF,sEAAsE;AACtE,EAAE;AACF,uFAAuF;AACvF,EAAE;AACF,wEAAwE;AACxE,0EAA0E;AAC1E,2EAA2E;AAC3E,kCAAkC;AAClC,6EAA6E;AAC7E,0EAA0E;AAC1E,mDAAmD;AACnD,4EAA4E;AAC5E,uEAAuE;AACvE,wEAAwE;AACxE,yEAAyE;AACzE,qEAAqE;AACrE,yEAAyE;AACzE,0EAA0E;AAC1E,sEAAsE;AACtE,yEAAyE;AACzE,2EAA2E;AAC3E,yEAAyE;AACzE,0EAA0E;AAC1E,uEAAuE;AACvE,sEAAsE;AACtE,yEAAyE;AACzE,uEAAuE;AACvE,oEAAoE;AACpE,0EAA0E;AAC1E,qEAAqE;AACrE,uEAAuE;AACvE,wEAAwE;AACxE,0CAA0C;AAC1C,EAAE;AACF,0EAA0E;AAC1E,gBAAgB;AAChB,uEAAuE;AACvE,0DAA0D;AAC1D,4DAA4D;AAC5D,yEAAyE;AACzE,oDAAoD;AAIpD,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAiBxC,MAAM,UAAU,gBAAgB,CAAC,QAAkB;IACjD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM,GAAG,GAAiB,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACxC,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAE9C,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACvC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,uEAAuE;IACvE,qDAAqD;IACrD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACb,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,MAAM,WAAW,GAAG,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACjD,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,KAAK,GAA0B,CAAC,GAAG,UAAU,CAAC;aACjD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aAC5E,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAC1B,MAAM,KAAK,GACT,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,iBAAiB,CAAC,CAAO;IAChC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { apply, DRIFT_HINT_MESSAGE, GENERATED_DIRNAME, MANIFEST_BASENAME, MEMORY_BASENAME, SETTINGS_BASENAME, type ApplyOptions, type ApplyOutcome, type ApplyResult, type FileApplyOutcome, } from "./apply.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/apply/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,GAKlB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type LedgerQueryResult } from "../policies/index.js";
|
|
2
|
+
import { type PolicyDecisionPayload } from "../runtime/ledger-record.js";
|
|
3
|
+
import { type LoaderOptions } from "./loader.js";
|
|
4
|
+
export type AuditOutcome = PolicyDecisionPayload["outcome"];
|
|
5
|
+
export interface AuditOptions extends LoaderOptions {
|
|
6
|
+
json?: boolean;
|
|
7
|
+
since?: string;
|
|
8
|
+
policy?: string;
|
|
9
|
+
outcome?: AuditOutcome;
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Override the ledger fetcher (tests). The optional `filters` arg
|
|
13
|
+
* carries the Phase 5 #5 server-side narrowing hints (sinceIso,
|
|
14
|
+
* contentPrefix); test fakes can ignore it without consequence.
|
|
15
|
+
*/
|
|
16
|
+
fetchLedger?: (sessionId: string, filters?: {
|
|
17
|
+
sinceIso?: string;
|
|
18
|
+
contentPrefix?: string;
|
|
19
|
+
}) => Promise<LedgerQueryResult>;
|
|
20
|
+
/** Override "now" (tests). */
|
|
21
|
+
now?: Date;
|
|
22
|
+
}
|
|
23
|
+
export interface AuditDecisionRow {
|
|
24
|
+
timestamp: string;
|
|
25
|
+
name: string;
|
|
26
|
+
outcome: AuditOutcome;
|
|
27
|
+
enforcement: PolicyDecisionPayload["enforcement"];
|
|
28
|
+
reason: string;
|
|
29
|
+
ledgerTag: string;
|
|
30
|
+
extractValues: Record<string, string>;
|
|
31
|
+
}
|
|
32
|
+
export interface AuditResult {
|
|
33
|
+
output: string;
|
|
34
|
+
decisions: AuditDecisionRow[];
|
|
35
|
+
}
|
|
36
|
+
export declare function audit(opts?: AuditOptions): Promise<AuditResult>;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { parseDurationSeconds, parseLedgerTimestamp, queryLedgerByTag, } from "../policies/index.js";
|
|
2
|
+
import { decisionSortKey, decodeLedgerContent, } from "../runtime/ledger-record.js";
|
|
3
|
+
import { resolveSessionId } from "../runtime/session-id.js";
|
|
4
|
+
import { EX_UNAVAILABLE, EX_USAGE, HarnessExitError } from "./exit-codes.js";
|
|
5
|
+
import { loadManifest } from "./loader.js";
|
|
6
|
+
const DEFAULT_SINCE = "24h";
|
|
7
|
+
const VALID_OUTCOMES = ["allow", "deny", "warn-degraded"];
|
|
8
|
+
function isValidOutcome(v) {
|
|
9
|
+
return VALID_OUTCOMES.includes(v);
|
|
10
|
+
}
|
|
11
|
+
function defaultFetcher(opts) {
|
|
12
|
+
return async (sessionId, filters) => {
|
|
13
|
+
const { manifest } = loadManifest(opts);
|
|
14
|
+
const server = manifest.tools.mcp.find((m) => m.name === "grounding-mcp");
|
|
15
|
+
if (!server) {
|
|
16
|
+
return { kind: "degraded", reason: "grounding-mcp not declared in manifest" };
|
|
17
|
+
}
|
|
18
|
+
const command = Array.isArray(server.command)
|
|
19
|
+
? server.command
|
|
20
|
+
: server.command.trim().split(/\s+/);
|
|
21
|
+
return queryLedgerByTag({
|
|
22
|
+
mcpCommand: command,
|
|
23
|
+
...(server.env && { mcpEnv: server.env }),
|
|
24
|
+
sessionId,
|
|
25
|
+
timeoutMs: server.health?.timeout_ms ?? 5_000,
|
|
26
|
+
...(filters?.sinceIso !== undefined && { sinceIso: filters.sinceIso }),
|
|
27
|
+
...(filters?.contentPrefix !== undefined && { contentPrefix: filters.contentPrefix }),
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function rowsFromEntries(entries) {
|
|
32
|
+
// Decode + retain the entry/payload pair so we can sort by Phase 5 #9's
|
|
33
|
+
// ms-precision `evaluatedAt` before flattening to the display row. The
|
|
34
|
+
// displayed `timestamp` field stays the ledger's `createdAt` (which is
|
|
35
|
+
// what users see in the table); only the sort key changes.
|
|
36
|
+
const decoded = [];
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const payload = decodeLedgerContent(entry.content);
|
|
39
|
+
if (!payload)
|
|
40
|
+
continue;
|
|
41
|
+
decoded.push({ entry, payload });
|
|
42
|
+
}
|
|
43
|
+
decoded.sort((a, b) => decisionSortKey(a.entry, a.payload) - decisionSortKey(b.entry, b.payload));
|
|
44
|
+
return decoded.map(({ entry, payload }) => ({
|
|
45
|
+
timestamp: typeof entry.createdAt === "string"
|
|
46
|
+
? entry.createdAt
|
|
47
|
+
: entry.createdAt.toISOString(),
|
|
48
|
+
name: payload.name,
|
|
49
|
+
outcome: payload.outcome,
|
|
50
|
+
enforcement: payload.enforcement,
|
|
51
|
+
reason: payload.reason,
|
|
52
|
+
ledgerTag: payload.ledgerTag,
|
|
53
|
+
extractValues: payload.extractValues,
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
function formatTable(rows) {
|
|
57
|
+
if (rows.length === 0)
|
|
58
|
+
return "";
|
|
59
|
+
const header = ["timestamp", "policy", "outcome", "reason"];
|
|
60
|
+
// Reasons are normally single-line; flatten just in case so the column
|
|
61
|
+
// alignment doesn't break on a newline buried in a future policy's reason.
|
|
62
|
+
const flatten = (s) => s.replace(/\s+/g, " ").trim();
|
|
63
|
+
const data = rows.map((r) => [r.timestamp, r.name, r.outcome, flatten(r.reason)]);
|
|
64
|
+
const widths = header.map((h, i) => Math.max(h.length, ...data.map((row) => row[i].length)));
|
|
65
|
+
const fmt = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
|
|
66
|
+
const lines = [];
|
|
67
|
+
lines.push(fmt(header));
|
|
68
|
+
lines.push(widths.map((w) => "-".repeat(w)).join(" ").trimEnd());
|
|
69
|
+
for (const row of data)
|
|
70
|
+
lines.push(fmt(row));
|
|
71
|
+
return `${lines.join("\n")}\n`;
|
|
72
|
+
}
|
|
73
|
+
export async function audit(opts = {}) {
|
|
74
|
+
const since = opts.since ?? DEFAULT_SINCE;
|
|
75
|
+
let windowSeconds;
|
|
76
|
+
try {
|
|
77
|
+
windowSeconds = parseDurationSeconds(since);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
throw new HarnessExitError(`--since: ${err.message}`, EX_USAGE);
|
|
81
|
+
}
|
|
82
|
+
if (opts.outcome !== undefined && !isValidOutcome(opts.outcome)) {
|
|
83
|
+
throw new HarnessExitError(`--outcome: must be one of allow, deny, warn-degraded (got "${opts.outcome}")`, EX_USAGE);
|
|
84
|
+
}
|
|
85
|
+
const sessionId = resolveSessionId(opts.sessionId);
|
|
86
|
+
const fetch = opts.fetchLedger ?? defaultFetcher(opts);
|
|
87
|
+
// Phase 5 #5 — push filters server-side when the connected
|
|
88
|
+
// grounding-mcp supports them (capability-detected by
|
|
89
|
+
// queryLedgerByTag via tools/list). The audit's own client-side
|
|
90
|
+
// filter math at line ~165 is preserved verbatim, so an old server
|
|
91
|
+
// ignoring these args still produces the correct table.
|
|
92
|
+
const now = opts.now ?? new Date();
|
|
93
|
+
const cutoffMs = now.getTime() - windowSeconds * 1000;
|
|
94
|
+
const sinceIso = new Date(cutoffMs).toISOString();
|
|
95
|
+
const result = await fetch(sessionId, {
|
|
96
|
+
sinceIso,
|
|
97
|
+
contentPrefix: "policy_decision:",
|
|
98
|
+
});
|
|
99
|
+
if (result.kind === "degraded") {
|
|
100
|
+
throw new HarnessExitError(`ledger unreachable: ${result.reason}`, EX_UNAVAILABLE);
|
|
101
|
+
}
|
|
102
|
+
const all = rowsFromEntries(result.entries);
|
|
103
|
+
let filtered = all.filter((r) => parseLedgerTimestamp(r.timestamp) >= cutoffMs);
|
|
104
|
+
if (opts.policy) {
|
|
105
|
+
filtered = filtered.filter((r) => r.name === opts.policy);
|
|
106
|
+
}
|
|
107
|
+
if (opts.outcome) {
|
|
108
|
+
filtered = filtered.filter((r) => r.outcome === opts.outcome);
|
|
109
|
+
}
|
|
110
|
+
if (filtered.length === 0) {
|
|
111
|
+
const output = opts.json
|
|
112
|
+
? `${JSON.stringify({ decisions: [] }, null, 2)}\n`
|
|
113
|
+
: `no policy decisions in the last ${since}\n`;
|
|
114
|
+
return { output, decisions: [] };
|
|
115
|
+
}
|
|
116
|
+
const output = opts.json
|
|
117
|
+
? `${JSON.stringify({ decisions: filtered }, null, 2)}\n`
|
|
118
|
+
: formatTable(filtered);
|
|
119
|
+
return { output, decisions: filtered };
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/cli/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Manifest } from "../schema/index.js";
|
|
2
|
+
import { type LoaderOptions } from "./loader.js";
|
|
3
|
+
export type Pillar = "grounding" | "tools" | "memory" | "hooks" | "policies";
|
|
4
|
+
export declare function isPillar(value: string): value is Pillar;
|
|
5
|
+
export interface DescribeOptions extends LoaderOptions {
|
|
6
|
+
pillar?: Pillar;
|
|
7
|
+
json?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface DescribeResult {
|
|
10
|
+
output: string;
|
|
11
|
+
manifest: Manifest;
|
|
12
|
+
}
|
|
13
|
+
export declare function describe(opts?: DescribeOptions): DescribeResult;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { stringify as stringifyYaml } from "yaml";
|
|
2
|
+
import { loadManifest } from "./loader.js";
|
|
3
|
+
const PILLARS = [
|
|
4
|
+
"grounding",
|
|
5
|
+
"tools",
|
|
6
|
+
"memory",
|
|
7
|
+
"hooks",
|
|
8
|
+
"policies",
|
|
9
|
+
];
|
|
10
|
+
export function isPillar(value) {
|
|
11
|
+
return PILLARS.includes(value);
|
|
12
|
+
}
|
|
13
|
+
function projectPillar(manifest, pillar) {
|
|
14
|
+
return { version: manifest.version, [pillar]: manifest[pillar] };
|
|
15
|
+
}
|
|
16
|
+
export function describe(opts = {}) {
|
|
17
|
+
const { manifest } = loadManifest(opts);
|
|
18
|
+
const projection = opts.pillar
|
|
19
|
+
? projectPillar(manifest, opts.pillar)
|
|
20
|
+
: manifest;
|
|
21
|
+
const output = opts.json
|
|
22
|
+
? JSON.stringify(projection, null, 2)
|
|
23
|
+
: stringifyYaml(projection, { lineWidth: 0 });
|
|
24
|
+
return { output, manifest };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=describe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../../src/cli/describe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,MAAM,CAAC;AAElD,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAI/D,MAAM,OAAO,GAAsB;IACjC,WAAW;IACX,OAAO;IACP,QAAQ;IACR,OAAO;IACP,UAAU;CACX,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,OAAQ,OAA6B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACxD,CAAC;AAYD,SAAS,aAAa,CAAC,QAAkB,EAAE,MAAc;IACvD,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAwB,EAAE;IACjD,MAAM,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,UAAU,GAA4B,IAAI,CAAC,MAAM;QACrD,CAAC,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC;QACtC,CAAC,CAAE,QAA+C,CAAC;IAErD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI;QACtB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QACrC,CAAC,CAAC,aAAa,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;IAEhD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type ChangeKind = "added" | "removed" | "modified";
|
|
2
|
+
export interface Change {
|
|
3
|
+
kind: ChangeKind;
|
|
4
|
+
path: string;
|
|
5
|
+
before?: unknown;
|
|
6
|
+
after?: unknown;
|
|
7
|
+
}
|
|
8
|
+
declare function isNameKeyedList(arr: unknown[]): arr is Array<Record<string, unknown> & {
|
|
9
|
+
name: string;
|
|
10
|
+
}>;
|
|
11
|
+
declare function fmtPath(parts: Array<string | number>): string;
|
|
12
|
+
export declare function diffManifests(before: unknown, after: unknown): Change[];
|
|
13
|
+
declare function pillarOf(changePath: string): string;
|
|
14
|
+
export declare function groupByPillar(changes: Change[]): Map<string, Change[]>;
|
|
15
|
+
export declare function formatDiff(changes: Change[]): string;
|
|
16
|
+
export declare const __testables: {
|
|
17
|
+
isNameKeyedList: typeof isNameKeyedList;
|
|
18
|
+
fmtPath: typeof fmtPath;
|
|
19
|
+
pillarOf: typeof pillarOf;
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const PILLAR_ORDER = ["version", "grounding", "tools", "memory", "hooks", "policies"];
|
|
2
|
+
function isPlainObject(v) {
|
|
3
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4
|
+
}
|
|
5
|
+
function isNameKeyedList(arr) {
|
|
6
|
+
if (arr.length === 0)
|
|
7
|
+
return false;
|
|
8
|
+
return arr.every((item) => isPlainObject(item) && typeof item.name === "string");
|
|
9
|
+
}
|
|
10
|
+
function fmtPath(parts) {
|
|
11
|
+
let out = "";
|
|
12
|
+
for (const p of parts) {
|
|
13
|
+
if (typeof p === "number") {
|
|
14
|
+
out += `[${p}]`;
|
|
15
|
+
}
|
|
16
|
+
else if (out === "") {
|
|
17
|
+
out = p;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
out += `.${p}`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
function fmtPathWithName(parts) {
|
|
26
|
+
let out = "";
|
|
27
|
+
for (const p of parts) {
|
|
28
|
+
if (typeof p === "number") {
|
|
29
|
+
out += `[${p}]`;
|
|
30
|
+
}
|
|
31
|
+
else if (typeof p === "object") {
|
|
32
|
+
out += `[${p.name}]`;
|
|
33
|
+
}
|
|
34
|
+
else if (out === "") {
|
|
35
|
+
out = p;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
out += `.${p}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
function diffArrays(before, after, pathParts, out) {
|
|
44
|
+
const beforeNamed = isNameKeyedList(before);
|
|
45
|
+
const afterNamed = isNameKeyedList(after);
|
|
46
|
+
if (beforeNamed && afterNamed) {
|
|
47
|
+
const byName = new Map();
|
|
48
|
+
for (const item of before)
|
|
49
|
+
byName.set(item.name, item);
|
|
50
|
+
const seen = new Set();
|
|
51
|
+
for (const item of after) {
|
|
52
|
+
const prev = byName.get(item.name);
|
|
53
|
+
seen.add(item.name);
|
|
54
|
+
if (!prev) {
|
|
55
|
+
out.push({ kind: "added", path: fmtPathWithName([...pathParts, { name: item.name }]), after: item });
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
diffValue(prev, item, [...pathParts, { name: item.name }], out);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const [name, item] of byName) {
|
|
62
|
+
if (!seen.has(name)) {
|
|
63
|
+
out.push({ kind: "removed", path: fmtPathWithName([...pathParts, { name }]), before: item });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (JSON.stringify(before) !== JSON.stringify(after)) {
|
|
69
|
+
out.push({ kind: "modified", path: fmtPathWithName(pathParts), before, after });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function diffObjects(before, after, pathParts, out) {
|
|
73
|
+
const keys = new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
74
|
+
for (const key of keys) {
|
|
75
|
+
const a = before[key];
|
|
76
|
+
const b = after[key];
|
|
77
|
+
if (a === undefined && b !== undefined) {
|
|
78
|
+
out.push({ kind: "added", path: fmtPathWithName([...pathParts, key]), after: b });
|
|
79
|
+
}
|
|
80
|
+
else if (b === undefined && a !== undefined) {
|
|
81
|
+
out.push({ kind: "removed", path: fmtPathWithName([...pathParts, key]), before: a });
|
|
82
|
+
}
|
|
83
|
+
else if (!equal(a, b)) {
|
|
84
|
+
diffValue(a, b, [...pathParts, key], out);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function equal(a, b) {
|
|
89
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
90
|
+
}
|
|
91
|
+
function diffValue(before, after, pathParts, out) {
|
|
92
|
+
if (equal(before, after))
|
|
93
|
+
return;
|
|
94
|
+
if (Array.isArray(before) && Array.isArray(after)) {
|
|
95
|
+
diffArrays(before, after, pathParts, out);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (isPlainObject(before) && isPlainObject(after)) {
|
|
99
|
+
diffObjects(before, after, pathParts, out);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
out.push({ kind: "modified", path: fmtPathWithName(pathParts), before, after });
|
|
103
|
+
}
|
|
104
|
+
export function diffManifests(before, after) {
|
|
105
|
+
const out = [];
|
|
106
|
+
diffValue(before, after, [], out);
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
function pillarOf(changePath) {
|
|
110
|
+
for (const pillar of PILLAR_ORDER) {
|
|
111
|
+
if (changePath === pillar || changePath.startsWith(`${pillar}.`) || changePath.startsWith(`${pillar}[`)) {
|
|
112
|
+
return pillar;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return "(other)";
|
|
116
|
+
}
|
|
117
|
+
export function groupByPillar(changes) {
|
|
118
|
+
const out = new Map();
|
|
119
|
+
for (const pillar of PILLAR_ORDER)
|
|
120
|
+
out.set(pillar, []);
|
|
121
|
+
out.set("(other)", []);
|
|
122
|
+
for (const c of changes) {
|
|
123
|
+
const p = pillarOf(c.path);
|
|
124
|
+
out.get(p).push(c);
|
|
125
|
+
}
|
|
126
|
+
for (const [k, v] of out)
|
|
127
|
+
if (v.length === 0)
|
|
128
|
+
out.delete(k);
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
function renderValue(v) {
|
|
132
|
+
if (typeof v === "string")
|
|
133
|
+
return v;
|
|
134
|
+
return JSON.stringify(v);
|
|
135
|
+
}
|
|
136
|
+
export function formatDiff(changes) {
|
|
137
|
+
if (changes.length === 0)
|
|
138
|
+
return "no changes\n";
|
|
139
|
+
const lines = [];
|
|
140
|
+
const grouped = groupByPillar(changes);
|
|
141
|
+
for (const [pillar, group] of grouped) {
|
|
142
|
+
lines.push(`## ${pillar}`);
|
|
143
|
+
for (const c of group) {
|
|
144
|
+
switch (c.kind) {
|
|
145
|
+
case "added":
|
|
146
|
+
lines.push(`+ ${c.path}: ${renderValue(c.after)}`);
|
|
147
|
+
break;
|
|
148
|
+
case "removed":
|
|
149
|
+
lines.push(`- ${c.path}: ${renderValue(c.before)}`);
|
|
150
|
+
break;
|
|
151
|
+
case "modified":
|
|
152
|
+
lines.push(`~ ${c.path}: ${renderValue(c.before)} → ${renderValue(c.after)}`);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
lines.push("");
|
|
157
|
+
}
|
|
158
|
+
return `${lines.join("\n")}`;
|
|
159
|
+
}
|
|
160
|
+
export const __testables = { isNameKeyedList, fmtPath, pillarOf };
|
|
161
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/cli/diff/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { EX_UNAVAILABLE, EX_USAGE, HarnessExitError } from "../exit-codes.js";
|
|
4
|
+
function runGit(args, cwd) {
|
|
5
|
+
return execFileSync("git", args, {
|
|
6
|
+
cwd,
|
|
7
|
+
encoding: "utf8",
|
|
8
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export function locateGitContext(manifestPath) {
|
|
12
|
+
const dir = path.dirname(path.resolve(manifestPath));
|
|
13
|
+
let root;
|
|
14
|
+
try {
|
|
15
|
+
root = runGit(["rev-parse", "--show-toplevel"], dir).trim();
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
throw new HarnessExitError(`git is not available or ${dir} is not inside a git work tree: ${err.message.trim()}`, EX_UNAVAILABLE);
|
|
19
|
+
}
|
|
20
|
+
const relPath = path.relative(root, path.resolve(manifestPath));
|
|
21
|
+
return { root, manifestRelPath: relPath };
|
|
22
|
+
}
|
|
23
|
+
export function readManifestAtRef(ctx, ref) {
|
|
24
|
+
try {
|
|
25
|
+
return runGit(["show", `${ref}:${ctx.manifestRelPath}`], ctx.root);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
const message = err.stderr?.trim() || err.message;
|
|
29
|
+
throw new HarnessExitError(`cannot read ${ctx.manifestRelPath} at git ref "${ref}": ${message}`, EX_USAGE);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../../src/cli/diff/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAO9E,SAAS,MAAM,CAAC,IAAc,EAAE,GAAW;IACzC,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE;QAC/B,GAAG;QACH,QAAQ,EAAE,MAAM;QAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;KAClC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,YAAoB;IACnD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;IACrD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,gBAAgB,CACxB,2BAA2B,GAAG,mCAAoC,GAAa,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAChG,cAAc,CACf,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;IAChE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAe,EAAE,GAAW;IAC5D,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,GAAG,CAAC,eAAe,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAI,GAAmC,CAAC,MAAM,EAAE,IAAI,EAAE,IAAK,GAAa,CAAC,OAAO,CAAC;QAC9F,MAAM,IAAI,gBAAgB,CACxB,eAAe,GAAG,CAAC,eAAe,gBAAgB,GAAG,MAAM,OAAO,EAAE,EACpE,QAAQ,CACT,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Manifest } from "../../schema/index.js";
|
|
2
|
+
import { type LoaderOptions } from "../loader.js";
|
|
3
|
+
import { type Change } from "./engine.js";
|
|
4
|
+
export interface DiffOptions extends LoaderOptions {
|
|
5
|
+
since?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface DiffResult {
|
|
8
|
+
changes: Change[];
|
|
9
|
+
output: string;
|
|
10
|
+
before: Manifest;
|
|
11
|
+
after: Manifest;
|
|
12
|
+
}
|
|
13
|
+
export declare function diff(opts: DiffOptions): DiffResult;
|
|
14
|
+
export { diffManifests, formatDiff } from "./engine.js";
|
|
15
|
+
export type { Change, ChangeKind } from "./engine.js";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { parse as parseYaml } from "yaml";
|
|
2
|
+
import { ManifestParseError, parseManifest } from "../../schema/index.js";
|
|
3
|
+
import { EX_NOINPUT, EX_USAGE, HarnessExitError } from "../exit-codes.js";
|
|
4
|
+
import { loadManifest, resolvePaths } from "../loader.js";
|
|
5
|
+
import { diffManifests, formatDiff } from "./engine.js";
|
|
6
|
+
import { locateGitContext, readManifestAtRef } from "./git.js";
|
|
7
|
+
function parseRefManifest(raw) {
|
|
8
|
+
let parsed;
|
|
9
|
+
try {
|
|
10
|
+
parsed = parseYaml(raw);
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
throw new HarnessExitError(`manifest at git ref is not valid YAML: ${err.message}`, EX_NOINPUT);
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
return parseManifest(parsed);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
if (err instanceof ManifestParseError) {
|
|
20
|
+
throw new HarnessExitError(`manifest at git ref failed schema validation:\n${err.message}`, EX_NOINPUT);
|
|
21
|
+
}
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function diff(opts) {
|
|
26
|
+
if (!opts.since) {
|
|
27
|
+
throw new HarnessExitError("harness diff requires --since <ref>; --since-apply lands in Phase 3", EX_USAGE);
|
|
28
|
+
}
|
|
29
|
+
const resolved = resolvePaths(opts);
|
|
30
|
+
const ctx = locateGitContext(resolved.base);
|
|
31
|
+
const refRaw = readManifestAtRef(ctx, opts.since);
|
|
32
|
+
const before = parseRefManifest(refRaw);
|
|
33
|
+
const { manifest: after } = loadManifest(opts);
|
|
34
|
+
const changes = diffManifests(before, after);
|
|
35
|
+
const output = formatDiff(changes);
|
|
36
|
+
return { changes, output, before, after };
|
|
37
|
+
}
|
|
38
|
+
export { diffManifests, formatDiff } from "./engine.js";
|
|
39
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/diff/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAiB,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAsB,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,UAAU,EAAe,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAa/D,SAAS,gBAAgB,CAAC,GAAW;IACnC,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,gBAAgB,CACxB,0CAA2C,GAAa,CAAC,OAAO,EAAE,EAClE,UAAU,CACX,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,kBAAkB,EAAE,CAAC;YACtC,MAAM,IAAI,gBAAgB,CACxB,kDAAkD,GAAG,CAAC,OAAO,EAAE,EAC/D,UAAU,CACX,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAiB;IACpC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,gBAAgB,CACxB,qEAAqE,EACrE,QAAQ,CACT,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAC5C,CAAC;AAED,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
|