@lannguyensi/harness 0.11.0 → 0.12.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 +82 -0
- package/README.md +99 -58
- package/dist/cli/apply/apply.js +19 -3
- package/dist/cli/apply/apply.js.map +1 -1
- package/dist/cli/approve/understanding.js +98 -8
- package/dist/cli/approve/understanding.js.map +1 -1
- package/dist/cli/doctor/codex.d.ts +6 -1
- package/dist/cli/doctor/codex.js +10 -6
- package/dist/cli/doctor/codex.js.map +1 -1
- package/dist/cli/gate/disable.d.ts +42 -0
- package/dist/cli/gate/disable.js +199 -0
- package/dist/cli/gate/disable.js.map +1 -0
- package/dist/cli/gate/enable.d.ts +33 -0
- package/dist/cli/gate/enable.js +127 -0
- package/dist/cli/gate/enable.js.map +1 -0
- package/dist/cli/gate/snapshot.d.ts +65 -0
- package/dist/cli/gate/snapshot.js +119 -0
- package/dist/cli/gate/snapshot.js.map +1 -0
- package/dist/cli/index.js +101 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/session-start/index.d.ts +23 -0
- package/dist/cli/session-start/index.js +48 -11
- package/dist/cli/session-start/index.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.d.ts +27 -0
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.js +32 -0
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution.d.ts +16 -1
- package/dist/policy-packs/builtin/understanding-before-execution.js +35 -7
- package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
- package/dist/policy-packs/expand.d.ts +3 -1
- package/dist/policy-packs/expand.js +2 -2
- package/dist/policy-packs/expand.js.map +1 -1
- package/dist/policy-packs/index.d.ts +1 -1
- package/dist/policy-packs/index.js.map +1 -1
- package/dist/policy-packs/registry.d.ts +2 -1
- package/dist/policy-packs/registry.js +2 -2
- package/dist/policy-packs/registry.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../../src/cli/doctor/codex.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,EAAE;AACF,wEAAwE;AACxE,sEAAsE;AACtE,sEAAsE;AACtE,mEAAmE;AACnE,oEAAoE;AACpE,EAAE;AACF,qEAAqE;AACrE,oEAAoE;AACpE,sEAAsE;AACtE,sEAAsE;AACtE,qDAAqD;AAErD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../../src/cli/doctor/codex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type RemovedGroup } from "./snapshot.js";
|
|
2
|
+
export interface GateDisableOptions {
|
|
3
|
+
/** `~/.claude/settings.json` override (test injection / non-default install). */
|
|
4
|
+
settingsPath?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Test-injectable home dir; falls back to `os.homedir()` when computing
|
|
7
|
+
* the default settings.json path. Ignored when `settingsPath` is set.
|
|
8
|
+
*/
|
|
9
|
+
homeDir?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Matcher pattern. When present, hook groups whose `matcher` field
|
|
12
|
+
* substring-matches this string are removed (literal substring, not
|
|
13
|
+
* regex — operators block-paste the exact matcher they see in the
|
|
14
|
+
* gate's deny message, which is what we want to match against). When
|
|
15
|
+
* absent, the call is a dry-run listing.
|
|
16
|
+
*/
|
|
17
|
+
matcher?: string;
|
|
18
|
+
/** Override "now" for deterministic timestamps in tests. */
|
|
19
|
+
now?: Date;
|
|
20
|
+
}
|
|
21
|
+
export interface GateDisableCandidate {
|
|
22
|
+
event: string;
|
|
23
|
+
index: number;
|
|
24
|
+
matcher: string | null;
|
|
25
|
+
/** Compact one-line description of the group's commands, for the listing UI. */
|
|
26
|
+
description: string;
|
|
27
|
+
}
|
|
28
|
+
export type GateDisableResult = {
|
|
29
|
+
mode: "list";
|
|
30
|
+
settingsPath: string;
|
|
31
|
+
candidates: GateDisableCandidate[];
|
|
32
|
+
} | {
|
|
33
|
+
mode: "remove";
|
|
34
|
+
settingsPath: string;
|
|
35
|
+
backupPath: string;
|
|
36
|
+
snapshotPath: string;
|
|
37
|
+
removed: RemovedGroup[];
|
|
38
|
+
};
|
|
39
|
+
export declare class GateDisableError extends Error {
|
|
40
|
+
constructor(message: string);
|
|
41
|
+
}
|
|
42
|
+
export declare function gateDisable(opts?: GateDisableOptions): GateDisableResult;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// `harness gate disable` — operator escape hatch for hard-blocking hooks.
|
|
2
|
+
//
|
|
3
|
+
// Reads the operator's `~/.claude/settings.json` (or `--settings <path>`),
|
|
4
|
+
// finds hook groups whose `matcher` substring-matches the supplied
|
|
5
|
+
// `--matcher`, removes them, writes a snapshot of the removed groups to
|
|
6
|
+
// `<settings-dir>/harness.gate-disable.<ISO>.json`, backs up the original
|
|
7
|
+
// to `<settings.json>.bak.<ISO>`, and atomically rewrites settings.json
|
|
8
|
+
// without the matching groups.
|
|
9
|
+
//
|
|
10
|
+
// With NO `--matcher`, this verb is a pure listing — it prints the
|
|
11
|
+
// candidate groups and exits without writing. That's the no-args
|
|
12
|
+
// "what would be removed" probe the task spec calls out.
|
|
13
|
+
//
|
|
14
|
+
// Refuses to operate on a settings.json that isn't a JSON object so
|
|
15
|
+
// a malformed file is surfaced rather than silently overwritten with
|
|
16
|
+
// something "cleaner".
|
|
17
|
+
import * as fs from "node:fs";
|
|
18
|
+
import * as os from "node:os";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
import { atomicWriteFile } from "../../io/atomic-write.js";
|
|
21
|
+
import { backupPath, sha256Hex, SNAPSHOT_VERSION, snapshotPath, } from "./snapshot.js";
|
|
22
|
+
const DEFAULT_SETTINGS_REL = path.join(".claude", "settings.json");
|
|
23
|
+
export class GateDisableError extends Error {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "GateDisableError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function resolveSettingsPath(opts) {
|
|
30
|
+
if (typeof opts.settingsPath === "string" && opts.settingsPath.length > 0) {
|
|
31
|
+
return opts.settingsPath;
|
|
32
|
+
}
|
|
33
|
+
const home = opts.homeDir ?? os.homedir();
|
|
34
|
+
return path.join(home, DEFAULT_SETTINGS_REL);
|
|
35
|
+
}
|
|
36
|
+
function readSettings(settingsPath) {
|
|
37
|
+
let raw;
|
|
38
|
+
try {
|
|
39
|
+
raw = fs.readFileSync(settingsPath, "utf8");
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
if (err.code === "ENOENT") {
|
|
43
|
+
throw new GateDisableError(`settings file not found: ${settingsPath} — nothing to disable`);
|
|
44
|
+
}
|
|
45
|
+
throw new GateDisableError(`cannot read ${settingsPath}: ${err.message}`);
|
|
46
|
+
}
|
|
47
|
+
let parsed;
|
|
48
|
+
try {
|
|
49
|
+
parsed = JSON.parse(raw);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
throw new GateDisableError(`${settingsPath} is not valid JSON (${err.message}); refusing to operate. ` +
|
|
53
|
+
`Fix the file by hand or restore from a backup before re-running.`);
|
|
54
|
+
}
|
|
55
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
56
|
+
throw new GateDisableError(`${settingsPath} is not a JSON object; refusing to operate.`);
|
|
57
|
+
}
|
|
58
|
+
const obj = parsed;
|
|
59
|
+
const rawHooks = obj["hooks"];
|
|
60
|
+
if (rawHooks === undefined) {
|
|
61
|
+
return { raw, obj, hooks: null };
|
|
62
|
+
}
|
|
63
|
+
if (rawHooks === null || typeof rawHooks !== "object" || Array.isArray(rawHooks)) {
|
|
64
|
+
throw new GateDisableError(`${settingsPath} \`hooks\` field is not an object; refusing to operate.`);
|
|
65
|
+
}
|
|
66
|
+
const hooks = {};
|
|
67
|
+
for (const [event, groups] of Object.entries(rawHooks)) {
|
|
68
|
+
if (!Array.isArray(groups)) {
|
|
69
|
+
throw new GateDisableError(`${settingsPath} \`hooks.${event}\` is not an array; refusing to operate.`);
|
|
70
|
+
}
|
|
71
|
+
hooks[event] = groups;
|
|
72
|
+
}
|
|
73
|
+
return { raw, obj, hooks };
|
|
74
|
+
}
|
|
75
|
+
function summarizeGroup(group) {
|
|
76
|
+
if (group === null || typeof group !== "object" || Array.isArray(group)) {
|
|
77
|
+
return "<malformed group>";
|
|
78
|
+
}
|
|
79
|
+
const obj = group;
|
|
80
|
+
const inner = Array.isArray(obj["hooks"]) ? obj["hooks"] : [];
|
|
81
|
+
const cmds = [];
|
|
82
|
+
for (const h of inner) {
|
|
83
|
+
if (h !== null && typeof h === "object" && !Array.isArray(h)) {
|
|
84
|
+
const cmd = h["command"];
|
|
85
|
+
if (typeof cmd === "string")
|
|
86
|
+
cmds.push(cmd);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (cmds.length === 0)
|
|
90
|
+
return "<no commands>";
|
|
91
|
+
// Truncate aggressively — operators reading the list want to scan
|
|
92
|
+
// matchers, not full command paths.
|
|
93
|
+
return cmds.map((c) => (c.length > 80 ? `${c.slice(0, 77)}...` : c)).join("; ");
|
|
94
|
+
}
|
|
95
|
+
function groupMatcher(group) {
|
|
96
|
+
if (group === null || typeof group !== "object" || Array.isArray(group))
|
|
97
|
+
return null;
|
|
98
|
+
const m = group["matcher"];
|
|
99
|
+
return typeof m === "string" ? m : null;
|
|
100
|
+
}
|
|
101
|
+
function collectCandidates(hooks) {
|
|
102
|
+
const out = [];
|
|
103
|
+
for (const event of Object.keys(hooks).sort()) {
|
|
104
|
+
const groups = hooks[event] ?? [];
|
|
105
|
+
groups.forEach((group, index) => {
|
|
106
|
+
out.push({
|
|
107
|
+
event,
|
|
108
|
+
index,
|
|
109
|
+
matcher: groupMatcher(group),
|
|
110
|
+
description: summarizeGroup(group),
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
function matcherMatches(group, pattern) {
|
|
117
|
+
const m = groupMatcher(group);
|
|
118
|
+
if (m === null)
|
|
119
|
+
return false;
|
|
120
|
+
// Substring match. Operators copy/paste the exact `matcher` string from
|
|
121
|
+
// settings.json or from the gate's deny output (e.g. "Edit|Write|Bash"),
|
|
122
|
+
// and a literal substring match is the principle of least surprise.
|
|
123
|
+
// A future iteration can add a `--regex` mode if real-world usage shows
|
|
124
|
+
// demand for it.
|
|
125
|
+
return m.includes(pattern);
|
|
126
|
+
}
|
|
127
|
+
export function gateDisable(opts = {}) {
|
|
128
|
+
const settingsPath = resolveSettingsPath(opts);
|
|
129
|
+
const parsed = readSettings(settingsPath);
|
|
130
|
+
const hooks = parsed.hooks ?? {};
|
|
131
|
+
const allCandidates = collectCandidates(hooks);
|
|
132
|
+
if (typeof opts.matcher !== "string" || opts.matcher.length === 0) {
|
|
133
|
+
return { mode: "list", settingsPath, candidates: allCandidates };
|
|
134
|
+
}
|
|
135
|
+
const matcherPattern = opts.matcher;
|
|
136
|
+
const removed = [];
|
|
137
|
+
const keptHooks = {};
|
|
138
|
+
for (const event of Object.keys(hooks).sort()) {
|
|
139
|
+
const groups = hooks[event] ?? [];
|
|
140
|
+
const kept = [];
|
|
141
|
+
groups.forEach((group, index) => {
|
|
142
|
+
if (matcherMatches(group, matcherPattern)) {
|
|
143
|
+
removed.push({ event, index, group });
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
kept.push(group);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
if (kept.length > 0)
|
|
150
|
+
keptHooks[event] = kept;
|
|
151
|
+
}
|
|
152
|
+
if (removed.length === 0) {
|
|
153
|
+
throw new GateDisableError(`no hook groups matched matcher substring ${JSON.stringify(matcherPattern)} in ${settingsPath}. ` +
|
|
154
|
+
`Run \`harness gate disable\` (no args) to list the available candidates.`);
|
|
155
|
+
}
|
|
156
|
+
const beforeSha = sha256Hex(parsed.raw);
|
|
157
|
+
const now = opts.now ?? new Date();
|
|
158
|
+
const backup = backupPath(settingsPath, now);
|
|
159
|
+
const snapPath = snapshotPath(settingsPath, now);
|
|
160
|
+
// Backup BEFORE we touch the live file. If the backup write fails we
|
|
161
|
+
// bail without having mutated anything.
|
|
162
|
+
atomicWriteFile(backup, parsed.raw);
|
|
163
|
+
const newObj = { ...parsed.obj };
|
|
164
|
+
if (Object.keys(keptHooks).length === 0) {
|
|
165
|
+
// Preserve the operator's choice to omit the key when there are no
|
|
166
|
+
// kept hooks: dropping it entirely keeps re-applies clean rather
|
|
167
|
+
// than emitting `"hooks": {}`.
|
|
168
|
+
delete newObj["hooks"];
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
newObj["hooks"] = keptHooks;
|
|
172
|
+
}
|
|
173
|
+
const newSettings = `${JSON.stringify(newObj, null, 2)}\n`;
|
|
174
|
+
const afterSha = sha256Hex(newSettings);
|
|
175
|
+
const snapshot = {
|
|
176
|
+
version: SNAPSHOT_VERSION,
|
|
177
|
+
createdAt: now.toISOString(),
|
|
178
|
+
settingsPath,
|
|
179
|
+
settingsBackupPath: backup,
|
|
180
|
+
settingsBeforeSha256: beforeSha,
|
|
181
|
+
settingsAfterSha256: afterSha,
|
|
182
|
+
filter: { matcher: matcherPattern },
|
|
183
|
+
removed,
|
|
184
|
+
};
|
|
185
|
+
// Snapshot lands BEFORE the live settings.json rewrite: the snapshot
|
|
186
|
+
// is the operator's reversibility insurance, so we don't want to be
|
|
187
|
+
// left in a "settings.json mutated but snapshot missing" state if the
|
|
188
|
+
// process dies between the two writes.
|
|
189
|
+
atomicWriteFile(snapPath, `${JSON.stringify(snapshot, null, 2)}\n`);
|
|
190
|
+
atomicWriteFile(settingsPath, newSettings);
|
|
191
|
+
return {
|
|
192
|
+
mode: "remove",
|
|
193
|
+
settingsPath,
|
|
194
|
+
backupPath: backup,
|
|
195
|
+
snapshotPath: snapPath,
|
|
196
|
+
removed,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=disable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disable.js","sourceRoot":"","sources":["../../../src/cli/gate/disable.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { listSnapshots } from "./snapshot.js";
|
|
2
|
+
export interface GateEnableOptions {
|
|
3
|
+
/** `~/.claude/settings.json` override. */
|
|
4
|
+
settingsPath?: string;
|
|
5
|
+
/** Override the home dir used to compute the default settings path. */
|
|
6
|
+
homeDir?: string;
|
|
7
|
+
/**
|
|
8
|
+
* `--force`: restore even when the on-disk settings.json sha differs
|
|
9
|
+
* from the snapshot's recorded post-disable sha. Without it, a
|
|
10
|
+
* mismatch refuses (the operator edited the file between disable and
|
|
11
|
+
* enable; silent overwrite would lose their changes).
|
|
12
|
+
*/
|
|
13
|
+
force?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export type GateEnableResult = {
|
|
16
|
+
mode: "no-snapshots";
|
|
17
|
+
settingsPath: string;
|
|
18
|
+
} | {
|
|
19
|
+
mode: "already-restored";
|
|
20
|
+
settingsPath: string;
|
|
21
|
+
snapshotPath: string;
|
|
22
|
+
} | {
|
|
23
|
+
mode: "restored";
|
|
24
|
+
settingsPath: string;
|
|
25
|
+
snapshotPath: string;
|
|
26
|
+
restoredCount: number;
|
|
27
|
+
};
|
|
28
|
+
export declare class GateEnableError extends Error {
|
|
29
|
+
constructor(message: string);
|
|
30
|
+
}
|
|
31
|
+
export declare function gateEnable(opts?: GateEnableOptions): GateEnableResult;
|
|
32
|
+
/** Test-helper: re-export for the test suite to inspect snapshot pickup. */
|
|
33
|
+
export { listSnapshots };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// `harness gate enable` — restore the most recent `gate disable` snapshot.
|
|
2
|
+
//
|
|
3
|
+
// Reads the newest `harness.gate-disable.*.json` next to settings.json,
|
|
4
|
+
// merges its `removed[]` groups back into `hooks[event][]` at their
|
|
5
|
+
// original indices, and atomically rewrites settings.json. Refuses if
|
|
6
|
+
// settings.json has been edited since the snapshot was taken (the
|
|
7
|
+
// recorded `settingsAfterSha256` no longer matches the on-disk content)
|
|
8
|
+
// unless `--force` is passed, so a hand-edit between disable and enable
|
|
9
|
+
// is not silently overwritten. Idempotent on an already-restored file
|
|
10
|
+
// (the snapshot's `settingsBeforeSha256` matches the current sha →
|
|
11
|
+
// nothing to do).
|
|
12
|
+
import * as fs from "node:fs";
|
|
13
|
+
import * as os from "node:os";
|
|
14
|
+
import * as path from "node:path";
|
|
15
|
+
import { atomicWriteFile } from "../../io/atomic-write.js";
|
|
16
|
+
import { listSnapshots, readSnapshot, sha256Hex, } from "./snapshot.js";
|
|
17
|
+
const DEFAULT_SETTINGS_REL = path.join(".claude", "settings.json");
|
|
18
|
+
export class GateEnableError extends Error {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "GateEnableError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function resolveSettingsPath(opts) {
|
|
25
|
+
if (typeof opts.settingsPath === "string" && opts.settingsPath.length > 0) {
|
|
26
|
+
return opts.settingsPath;
|
|
27
|
+
}
|
|
28
|
+
const home = opts.homeDir ?? os.homedir();
|
|
29
|
+
return path.join(home, DEFAULT_SETTINGS_REL);
|
|
30
|
+
}
|
|
31
|
+
function readCurrentSettings(settingsPath) {
|
|
32
|
+
let raw;
|
|
33
|
+
try {
|
|
34
|
+
raw = fs.readFileSync(settingsPath, "utf8");
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
if (err.code === "ENOENT") {
|
|
38
|
+
throw new GateEnableError(`settings file not found: ${settingsPath} — cannot restore into a missing target`);
|
|
39
|
+
}
|
|
40
|
+
throw new GateEnableError(`cannot read ${settingsPath}: ${err.message}`);
|
|
41
|
+
}
|
|
42
|
+
let parsed;
|
|
43
|
+
try {
|
|
44
|
+
parsed = JSON.parse(raw);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
throw new GateEnableError(`${settingsPath} is not valid JSON (${err.message}); refusing to operate.`);
|
|
48
|
+
}
|
|
49
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
50
|
+
throw new GateEnableError(`${settingsPath} is not a JSON object; refusing to operate.`);
|
|
51
|
+
}
|
|
52
|
+
const obj = parsed;
|
|
53
|
+
const rawHooks = obj["hooks"];
|
|
54
|
+
let hooks = {};
|
|
55
|
+
if (rawHooks !== undefined) {
|
|
56
|
+
if (rawHooks === null || typeof rawHooks !== "object" || Array.isArray(rawHooks)) {
|
|
57
|
+
throw new GateEnableError(`${settingsPath} \`hooks\` field is not an object.`);
|
|
58
|
+
}
|
|
59
|
+
for (const [event, groups] of Object.entries(rawHooks)) {
|
|
60
|
+
if (!Array.isArray(groups)) {
|
|
61
|
+
throw new GateEnableError(`${settingsPath} \`hooks.${event}\` is not an array; refusing to operate.`);
|
|
62
|
+
}
|
|
63
|
+
hooks[event] = groups.slice();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { raw, obj, hooks };
|
|
67
|
+
}
|
|
68
|
+
function reinsertRemoved(hooks, snapshot) {
|
|
69
|
+
// Sort removed entries by event, then by ascending index, so we splice
|
|
70
|
+
// them back in growing order — that way an earlier insertion never
|
|
71
|
+
// shifts a later insertion's target index past the array end.
|
|
72
|
+
const sorted = [...snapshot.removed].sort((a, b) => {
|
|
73
|
+
if (a.event !== b.event)
|
|
74
|
+
return a.event < b.event ? -1 : 1;
|
|
75
|
+
return a.index - b.index;
|
|
76
|
+
});
|
|
77
|
+
let inserted = 0;
|
|
78
|
+
for (const entry of sorted) {
|
|
79
|
+
const arr = hooks[entry.event] ?? [];
|
|
80
|
+
const idx = Math.min(entry.index, arr.length);
|
|
81
|
+
arr.splice(idx, 0, entry.group);
|
|
82
|
+
hooks[entry.event] = arr;
|
|
83
|
+
inserted += 1;
|
|
84
|
+
}
|
|
85
|
+
return inserted;
|
|
86
|
+
}
|
|
87
|
+
export function gateEnable(opts = {}) {
|
|
88
|
+
const settingsPath = resolveSettingsPath(opts);
|
|
89
|
+
const settingsDir = path.dirname(settingsPath);
|
|
90
|
+
const snapshotPaths = listSnapshots(settingsDir);
|
|
91
|
+
if (snapshotPaths.length === 0) {
|
|
92
|
+
return { mode: "no-snapshots", settingsPath };
|
|
93
|
+
}
|
|
94
|
+
const latest = snapshotPaths[0];
|
|
95
|
+
const readResult = readSnapshot(latest);
|
|
96
|
+
if (!readResult.ok) {
|
|
97
|
+
throw new GateEnableError(readResult.reason);
|
|
98
|
+
}
|
|
99
|
+
const snapshot = readResult.snapshot;
|
|
100
|
+
const current = readCurrentSettings(settingsPath);
|
|
101
|
+
const currentSha = sha256Hex(current.raw);
|
|
102
|
+
if (currentSha === snapshot.settingsBeforeSha256) {
|
|
103
|
+
// The current file already matches the pre-disable state: nothing
|
|
104
|
+
// to restore. Idempotent return.
|
|
105
|
+
return { mode: "already-restored", settingsPath, snapshotPath: latest };
|
|
106
|
+
}
|
|
107
|
+
if (currentSha !== snapshot.settingsAfterSha256 && !opts.force) {
|
|
108
|
+
throw new GateEnableError(`settings.json has been edited since the snapshot was taken (current sha ${currentSha.slice(0, 12)}…, ` +
|
|
109
|
+
`snapshot's post-disable sha ${snapshot.settingsAfterSha256.slice(0, 12)}…). ` +
|
|
110
|
+
`Re-running would overwrite those edits. Inspect the diff against ` +
|
|
111
|
+
`${snapshot.settingsBackupPath}, then pass --force to restore anyway.`);
|
|
112
|
+
}
|
|
113
|
+
const restoredCount = reinsertRemoved(current.hooks, snapshot);
|
|
114
|
+
const newObj = { ...current.obj };
|
|
115
|
+
if (Object.keys(current.hooks).length === 0) {
|
|
116
|
+
delete newObj["hooks"];
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
newObj["hooks"] = current.hooks;
|
|
120
|
+
}
|
|
121
|
+
const newSettings = `${JSON.stringify(newObj, null, 2)}\n`;
|
|
122
|
+
atomicWriteFile(settingsPath, newSettings);
|
|
123
|
+
return { mode: "restored", settingsPath, snapshotPath: latest, restoredCount };
|
|
124
|
+
}
|
|
125
|
+
/** Test-helper: re-export for the test suite to inspect snapshot pickup. */
|
|
126
|
+
export { listSnapshots };
|
|
127
|
+
//# sourceMappingURL=enable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enable.js","sourceRoot":"","sources":["../../../src/cli/gate/enable.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export declare const SNAPSHOT_BASENAME_PREFIX = "harness.gate-disable.";
|
|
2
|
+
export declare const SNAPSHOT_BASENAME_SUFFIX = ".json";
|
|
3
|
+
export declare const SNAPSHOT_VERSION: 1;
|
|
4
|
+
export interface RemovedGroup {
|
|
5
|
+
/** Hook event the group lived under, e.g. "PreToolUse". */
|
|
6
|
+
event: string;
|
|
7
|
+
/** Position of the group inside `hooks[event][]` at the time of removal. */
|
|
8
|
+
index: number;
|
|
9
|
+
/** The literal JSON value of the removed group; preserved verbatim. */
|
|
10
|
+
group: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface GateDisableSnapshot {
|
|
13
|
+
version: typeof SNAPSHOT_VERSION;
|
|
14
|
+
/** ISO-8601 UTC timestamp of the disable call. */
|
|
15
|
+
createdAt: string;
|
|
16
|
+
/** Absolute path to the settings.json that was mutated. */
|
|
17
|
+
settingsPath: string;
|
|
18
|
+
/** Absolute path the pre-mutation copy was written to. */
|
|
19
|
+
settingsBackupPath: string;
|
|
20
|
+
/**
|
|
21
|
+
* SHA-256 of `settings.json` content BEFORE the removal. `gate enable`
|
|
22
|
+
* compares the live settings.json sha against this to detect operator
|
|
23
|
+
* edits since the disable; a mismatch refuses unless `--force`.
|
|
24
|
+
*/
|
|
25
|
+
settingsBeforeSha256: string;
|
|
26
|
+
/** SHA-256 of `settings.json` content AFTER the removal. */
|
|
27
|
+
settingsAfterSha256: string;
|
|
28
|
+
/** The filter the operator applied — recorded for the audit trail. */
|
|
29
|
+
filter: {
|
|
30
|
+
matcher?: string;
|
|
31
|
+
event?: string;
|
|
32
|
+
};
|
|
33
|
+
/** Groups removed, in stable order (sorted by event then ascending index). */
|
|
34
|
+
removed: RemovedGroup[];
|
|
35
|
+
}
|
|
36
|
+
export declare function sha256Hex(input: string | Buffer): string;
|
|
37
|
+
/**
|
|
38
|
+
* Build the snapshot file path for `settingsPath` at `now`. ISO timestamps
|
|
39
|
+
* use `-` in place of `:` so the filename is portable across filesystems
|
|
40
|
+
* (Windows reject `:` in basenames).
|
|
41
|
+
*/
|
|
42
|
+
export declare function snapshotPath(settingsPath: string, now: Date): string;
|
|
43
|
+
/** Build the settings-backup file path for `settingsPath` at `now`. */
|
|
44
|
+
export declare function backupPath(settingsPath: string, now: Date): string;
|
|
45
|
+
/**
|
|
46
|
+
* Return all gate-disable snapshot paths in `settingsDir`, newest-first
|
|
47
|
+
* by mtime. Missing directory returns []. Any file that isn't a valid
|
|
48
|
+
* snapshot is silently skipped — `gate enable` re-parses the candidates
|
|
49
|
+
* and surfaces format errors at that point.
|
|
50
|
+
*/
|
|
51
|
+
export declare function listSnapshots(settingsDir: string): string[];
|
|
52
|
+
export interface SnapshotReadOk {
|
|
53
|
+
ok: true;
|
|
54
|
+
snapshot: GateDisableSnapshot;
|
|
55
|
+
}
|
|
56
|
+
export interface SnapshotReadErr {
|
|
57
|
+
ok: false;
|
|
58
|
+
reason: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Parse a snapshot file. Validates structural shape only — the body's
|
|
62
|
+
* `removed[].group` payload is opaque (preserved verbatim from
|
|
63
|
+
* settings.json) and not re-validated against a hook schema.
|
|
64
|
+
*/
|
|
65
|
+
export declare function readSnapshot(filePath: string): SnapshotReadOk | SnapshotReadErr;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Snapshot file format + I/O for `harness gate disable` / `harness gate enable`.
|
|
2
|
+
//
|
|
3
|
+
// A snapshot is the on-disk record of one `gate disable` invocation. It
|
|
4
|
+
// captures (a) which hook groups were removed and where they came from,
|
|
5
|
+
// (b) the SHA-256 of `settings.json` immediately before and after the
|
|
6
|
+
// removal, and (c) the path the original was backed up to. `gate enable`
|
|
7
|
+
// reads the newest snapshot in the settings directory, refuses to restore
|
|
8
|
+
// over a settings.json the operator has edited since (`--force` overrides),
|
|
9
|
+
// and writes the removed groups back into place.
|
|
10
|
+
//
|
|
11
|
+
// The format is intentionally narrow: no compression, no transform, just
|
|
12
|
+
// the literal JSON groups as they were read out of settings.json.
|
|
13
|
+
import * as crypto from "node:crypto";
|
|
14
|
+
import * as fs from "node:fs";
|
|
15
|
+
import * as path from "node:path";
|
|
16
|
+
export const SNAPSHOT_BASENAME_PREFIX = "harness.gate-disable.";
|
|
17
|
+
export const SNAPSHOT_BASENAME_SUFFIX = ".json";
|
|
18
|
+
export const SNAPSHOT_VERSION = 1;
|
|
19
|
+
export function sha256Hex(input) {
|
|
20
|
+
return crypto.createHash("sha256").update(input).digest("hex");
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build the snapshot file path for `settingsPath` at `now`. ISO timestamps
|
|
24
|
+
* use `-` in place of `:` so the filename is portable across filesystems
|
|
25
|
+
* (Windows reject `:` in basenames).
|
|
26
|
+
*/
|
|
27
|
+
export function snapshotPath(settingsPath, now) {
|
|
28
|
+
const stamp = now.toISOString().replace(/:/g, "-");
|
|
29
|
+
return path.join(path.dirname(settingsPath), `${SNAPSHOT_BASENAME_PREFIX}${stamp}${SNAPSHOT_BASENAME_SUFFIX}`);
|
|
30
|
+
}
|
|
31
|
+
/** Build the settings-backup file path for `settingsPath` at `now`. */
|
|
32
|
+
export function backupPath(settingsPath, now) {
|
|
33
|
+
const stamp = now.toISOString().replace(/:/g, "-");
|
|
34
|
+
return `${settingsPath}.bak.${stamp}`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Return all gate-disable snapshot paths in `settingsDir`, newest-first
|
|
38
|
+
* by mtime. Missing directory returns []. Any file that isn't a valid
|
|
39
|
+
* snapshot is silently skipped — `gate enable` re-parses the candidates
|
|
40
|
+
* and surfaces format errors at that point.
|
|
41
|
+
*/
|
|
42
|
+
export function listSnapshots(settingsDir) {
|
|
43
|
+
let names;
|
|
44
|
+
try {
|
|
45
|
+
names = fs.readdirSync(settingsDir);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
const candidates = [];
|
|
51
|
+
for (const name of names) {
|
|
52
|
+
if (!name.startsWith(SNAPSHOT_BASENAME_PREFIX))
|
|
53
|
+
continue;
|
|
54
|
+
if (!name.endsWith(SNAPSHOT_BASENAME_SUFFIX))
|
|
55
|
+
continue;
|
|
56
|
+
const full = path.join(settingsDir, name);
|
|
57
|
+
try {
|
|
58
|
+
const stat = fs.statSync(full);
|
|
59
|
+
if (!stat.isFile())
|
|
60
|
+
continue;
|
|
61
|
+
candidates.push({ filePath: full, mtimeMs: stat.mtimeMs });
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
68
|
+
return candidates.map((c) => c.filePath);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Parse a snapshot file. Validates structural shape only — the body's
|
|
72
|
+
* `removed[].group` payload is opaque (preserved verbatim from
|
|
73
|
+
* settings.json) and not re-validated against a hook schema.
|
|
74
|
+
*/
|
|
75
|
+
export function readSnapshot(filePath) {
|
|
76
|
+
let raw;
|
|
77
|
+
try {
|
|
78
|
+
raw = fs.readFileSync(filePath, "utf8");
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
return { ok: false, reason: `cannot read snapshot ${filePath}: ${err.message}` };
|
|
82
|
+
}
|
|
83
|
+
let parsed;
|
|
84
|
+
try {
|
|
85
|
+
parsed = JSON.parse(raw);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
return { ok: false, reason: `snapshot ${filePath} is not valid JSON: ${err.message}` };
|
|
89
|
+
}
|
|
90
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
91
|
+
return { ok: false, reason: `snapshot ${filePath} is not a JSON object` };
|
|
92
|
+
}
|
|
93
|
+
const obj = parsed;
|
|
94
|
+
if (obj["version"] !== SNAPSHOT_VERSION) {
|
|
95
|
+
return {
|
|
96
|
+
ok: false,
|
|
97
|
+
reason: `snapshot ${filePath} has unsupported version ${JSON.stringify(obj["version"])}; expected ${SNAPSHOT_VERSION}`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
for (const field of [
|
|
101
|
+
"createdAt",
|
|
102
|
+
"settingsPath",
|
|
103
|
+
"settingsBackupPath",
|
|
104
|
+
"settingsBeforeSha256",
|
|
105
|
+
"settingsAfterSha256",
|
|
106
|
+
]) {
|
|
107
|
+
if (typeof obj[field] !== "string") {
|
|
108
|
+
return { ok: false, reason: `snapshot ${filePath} is missing string field "${field}"` };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (typeof obj["filter"] !== "object" || obj["filter"] === null) {
|
|
112
|
+
return { ok: false, reason: `snapshot ${filePath} is missing object field "filter"` };
|
|
113
|
+
}
|
|
114
|
+
if (!Array.isArray(obj["removed"])) {
|
|
115
|
+
return { ok: false, reason: `snapshot ${filePath} is missing array field "removed"` };
|
|
116
|
+
}
|
|
117
|
+
return { ok: true, snapshot: obj };
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=snapshot.js.map
|