lockwire 0.1.18 → 0.1.20
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/dist/actions.d.ts +21 -4
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +31 -11
- package/dist/actions.js.map +1 -1
- package/dist/cli.js +18 -1
- package/dist/cli.js.map +1 -1
- package/dist/docindex.d.ts +48 -0
- package/dist/docindex.d.ts.map +1 -0
- package/dist/docindex.js +311 -0
- package/dist/docindex.js.map +1 -0
- package/dist/hook-claude.d.ts.map +1 -1
- package/dist/hook-claude.js +8 -5
- package/dist/hook-claude.js.map +1 -1
- package/dist/hook-codex.d.ts.map +1 -1
- package/dist/hook-codex.js +6 -4
- package/dist/hook-codex.js.map +1 -1
- package/dist/hook-common.d.ts +10 -1
- package/dist/hook-common.d.ts.map +1 -1
- package/dist/hook-common.js +57 -17
- package/dist/hook-common.js.map +1 -1
- package/dist/repo.d.ts +12 -4
- package/dist/repo.d.ts.map +1 -1
- package/dist/repo.js +37 -9
- package/dist/repo.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/docindex.js
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A cache of which docs hold `<!-- lockwire … -->` markers, so a latency-sensitive caller (the
|
|
3
|
+
* PreToolUse hook) doesn't have to walk the tree and read every doc on every edit.
|
|
4
|
+
*
|
|
5
|
+
* The index stores, per doc, its markers and the mtime/size it had when read; and per directory, its
|
|
6
|
+
* mtime. Validating it is a `stat` of every recorded directory and doc — no tree walk, no reads:
|
|
7
|
+
*
|
|
8
|
+
* - a directory whose mtime moved had entries added, removed or renamed: it's re-listed;
|
|
9
|
+
* - a doc whose mtime or size moved was edited: it's re-read;
|
|
10
|
+
* - a recorded path that's gone is dropped.
|
|
11
|
+
*
|
|
12
|
+
* Staleness is the one way this can go wrong, so it fails towards re-reading:
|
|
13
|
+
* - Entries modified within RACY_MS of the index being written are re-checked every time (a second
|
|
14
|
+
* write in the same timestamp tick would otherwise be invisible) and the index is refreshed once
|
|
15
|
+
* it has aged past that window.
|
|
16
|
+
* - A changed `docs`/`exclude` config, a different schema version, a corrupt file, or an index older
|
|
17
|
+
* than MAX_AGE_MS all rebuild from scratch.
|
|
18
|
+
* - A build or refresh that ran out of its time budget is never persisted.
|
|
19
|
+
* - Writes are atomic (temp file + rename), and any failure to read or write the cache just means it
|
|
20
|
+
* isn't used.
|
|
21
|
+
*/
|
|
22
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
23
|
+
import { appendFile, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
import { scanMarkers } from "./markers.js";
|
|
26
|
+
import { isScannedDoc, isSkippedEntry, stripBom } from "./repo.js";
|
|
27
|
+
const INDEX_VERSION = 1;
|
|
28
|
+
/** Timestamp granularity we guard against: HFS+ is 1 s, FAT 2 s. */
|
|
29
|
+
export const RACY_MS = 2000;
|
|
30
|
+
/** Rebuild from scratch at least this often, in case a filesystem didn't keep its mtimes honest. */
|
|
31
|
+
export const MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
32
|
+
export const indexPath = (repoRoot) => join(repoRoot, ".lockwire", "cache", "doc-index.json");
|
|
33
|
+
const parentOf = (rel) => (rel.includes("/") ? rel.slice(0, rel.lastIndexOf("/")) : "");
|
|
34
|
+
const joinRel = (dir, name) => (dir === "" ? name : `${dir}/${name}`);
|
|
35
|
+
const configKeyOf = (config) => JSON.stringify([config.docs, config.exclude]);
|
|
36
|
+
function tryStat(path) {
|
|
37
|
+
try {
|
|
38
|
+
return statSync(path);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function readIndexFile(repoRoot) {
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(stripBom(await readFile(indexPath(repoRoot), "utf8")));
|
|
47
|
+
if (parsed?.v === INDEX_VERSION &&
|
|
48
|
+
typeof parsed.configKey === "string" &&
|
|
49
|
+
typeof parsed.builtAt === "number" &&
|
|
50
|
+
parsed.dirs &&
|
|
51
|
+
typeof parsed.dirs === "object" &&
|
|
52
|
+
parsed.docs &&
|
|
53
|
+
typeof parsed.docs === "object")
|
|
54
|
+
return parsed;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// missing or corrupt: rebuild
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
/** Keeps the cache (and hook.log) out of git: `.lockwire/` itself is committed. Never overwrites the user's. */
|
|
62
|
+
export async function ensureLockwireGitignore(repoRoot) {
|
|
63
|
+
const path = join(repoRoot, ".lockwire", ".gitignore");
|
|
64
|
+
const wanted = ["cache/", "hook.log"];
|
|
65
|
+
let current = "";
|
|
66
|
+
try {
|
|
67
|
+
current = await readFile(path, "utf8");
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// none yet
|
|
71
|
+
}
|
|
72
|
+
const have = new Set(current.split(/\r?\n/).map((l) => l.trim()));
|
|
73
|
+
const missing = wanted.filter((w) => !have.has(w));
|
|
74
|
+
if (missing.length === 0)
|
|
75
|
+
return;
|
|
76
|
+
await mkdir(join(repoRoot, ".lockwire"), { recursive: true });
|
|
77
|
+
const prefix = current === "" || current.endsWith("\n") ? "" : "\n";
|
|
78
|
+
if (current === "")
|
|
79
|
+
await writeFile(path, `# lockwire's derived, machine-local files\n${missing.join("\n")}\n`, "utf8");
|
|
80
|
+
else
|
|
81
|
+
await appendFile(path, `${prefix}${missing.join("\n")}\n`, "utf8");
|
|
82
|
+
}
|
|
83
|
+
async function writeIndexFile(repoRoot, data) {
|
|
84
|
+
const final = indexPath(repoRoot);
|
|
85
|
+
const tmp = `${final}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
86
|
+
try {
|
|
87
|
+
await ensureLockwireGitignore(repoRoot);
|
|
88
|
+
await mkdir(join(repoRoot, ".lockwire", "cache"), { recursive: true });
|
|
89
|
+
await writeFile(tmp, JSON.stringify(data), "utf8");
|
|
90
|
+
await rename(tmp, final);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
await rm(tmp, { force: true }).catch(() => undefined);
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** Reads (and, unless told not to, refreshes) the doc index for `repoRoot`. Never throws. */
|
|
99
|
+
export async function loadDocIndex(repoRoot, config, opts = {}) {
|
|
100
|
+
const now = opts.now ?? Date.now;
|
|
101
|
+
const deadline = now() + (opts.budgetMs ?? Number.POSITIVE_INFINITY);
|
|
102
|
+
const stats = {
|
|
103
|
+
dirsStatted: 0,
|
|
104
|
+
dirsRescanned: 0,
|
|
105
|
+
docsStatted: 0,
|
|
106
|
+
docsRead: 0,
|
|
107
|
+
rebuilt: false,
|
|
108
|
+
wrote: false,
|
|
109
|
+
};
|
|
110
|
+
const configKey = configKeyOf(config);
|
|
111
|
+
const abs = (rel) => (rel === "" ? repoRoot : join(repoRoot, rel));
|
|
112
|
+
const over = () => now() > deadline;
|
|
113
|
+
let data = opts.rebuild ? null : await readIndexFile(repoRoot);
|
|
114
|
+
if (data && (data.configKey !== configKey || now() - data.builtAt > MAX_AGE_MS))
|
|
115
|
+
data = null;
|
|
116
|
+
let complete = true;
|
|
117
|
+
let changed = false;
|
|
118
|
+
let sawRacy = false;
|
|
119
|
+
const readThisRun = new Set();
|
|
120
|
+
/** Stat, then read: an edit landing in between leaves a newer mtime than we recorded, so it's caught next time. */
|
|
121
|
+
function readDoc(rel) {
|
|
122
|
+
const st = tryStat(abs(rel));
|
|
123
|
+
if (!st?.isFile() || !data)
|
|
124
|
+
return;
|
|
125
|
+
let markers;
|
|
126
|
+
try {
|
|
127
|
+
markers = scanMarkers(readFileSync(abs(rel), "utf8")).map((m) => ({
|
|
128
|
+
line: m.line,
|
|
129
|
+
target: m.target,
|
|
130
|
+
id: m.id,
|
|
131
|
+
claimExcerpt: m.claimExcerpt,
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return; // unreadable now: leave it as it was; a later run will try again
|
|
136
|
+
}
|
|
137
|
+
stats.docsRead++;
|
|
138
|
+
const prev = data.docs[rel];
|
|
139
|
+
data.docs[rel] = { mtimeMs: st.mtimeMs, size: st.size, markers };
|
|
140
|
+
readThisRun.add(rel);
|
|
141
|
+
// Re-reading an unchanged (merely racy) doc isn't a change worth rewriting the index for.
|
|
142
|
+
if (!prev ||
|
|
143
|
+
prev.mtimeMs !== st.mtimeMs ||
|
|
144
|
+
prev.size !== st.size ||
|
|
145
|
+
JSON.stringify(prev.markers) !== JSON.stringify(markers))
|
|
146
|
+
changed = true;
|
|
147
|
+
}
|
|
148
|
+
function listDir(rel) {
|
|
149
|
+
let entries;
|
|
150
|
+
try {
|
|
151
|
+
entries = readdirSync(abs(rel), { withFileTypes: true });
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const dirs = [];
|
|
157
|
+
const docs = [];
|
|
158
|
+
for (const e of entries) {
|
|
159
|
+
const child = joinRel(rel, e.name);
|
|
160
|
+
if (isSkippedEntry(e.name, child, config.exclude))
|
|
161
|
+
continue;
|
|
162
|
+
if (e.isDirectory())
|
|
163
|
+
dirs.push(child);
|
|
164
|
+
else if (isScannedDoc(child, config))
|
|
165
|
+
docs.push(child);
|
|
166
|
+
}
|
|
167
|
+
return { dirs, docs };
|
|
168
|
+
}
|
|
169
|
+
function buildSubtree(rel) {
|
|
170
|
+
if (!data)
|
|
171
|
+
return;
|
|
172
|
+
if (over()) {
|
|
173
|
+
complete = false;
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const st = tryStat(abs(rel));
|
|
177
|
+
if (!st?.isDirectory())
|
|
178
|
+
return;
|
|
179
|
+
const listing = listDir(rel);
|
|
180
|
+
if (!listing)
|
|
181
|
+
return;
|
|
182
|
+
data.dirs[rel] = st.mtimeMs; // recorded from the stat taken before the listing
|
|
183
|
+
changed = true;
|
|
184
|
+
for (const doc of listing.docs) {
|
|
185
|
+
if (over()) {
|
|
186
|
+
complete = false;
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
readDoc(doc);
|
|
190
|
+
}
|
|
191
|
+
for (const dir of listing.dirs)
|
|
192
|
+
buildSubtree(dir);
|
|
193
|
+
}
|
|
194
|
+
function dropSubtree(rel) {
|
|
195
|
+
if (!data)
|
|
196
|
+
return;
|
|
197
|
+
const prefix = rel === "" ? "" : `${rel}/`;
|
|
198
|
+
for (const d of Object.keys(data.dirs))
|
|
199
|
+
if (d === rel || d.startsWith(prefix))
|
|
200
|
+
delete data.dirs[d];
|
|
201
|
+
for (const d of Object.keys(data.docs))
|
|
202
|
+
if (d.startsWith(prefix))
|
|
203
|
+
delete data.docs[d];
|
|
204
|
+
changed = true;
|
|
205
|
+
}
|
|
206
|
+
if (!data) {
|
|
207
|
+
stats.rebuilt = true;
|
|
208
|
+
// Create the cache directory first: doing it after the walk would bump the repo root's mtime past
|
|
209
|
+
// the one we recorded, and the next load would re-list the root for nothing.
|
|
210
|
+
if (opts.persist !== false)
|
|
211
|
+
await mkdir(join(repoRoot, ".lockwire", "cache"), { recursive: true }).catch(() => undefined);
|
|
212
|
+
data = { v: INDEX_VERSION, configKey, builtAt: now(), dirs: {}, docs: {} };
|
|
213
|
+
buildSubtree("");
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
const index = data;
|
|
217
|
+
const racy = (mtimeMs) => mtimeMs >= index.builtAt - RACY_MS;
|
|
218
|
+
// 1. Which recorded directories moved? Parents first, so dropping one covers its children.
|
|
219
|
+
const dirty = [];
|
|
220
|
+
for (const rel of Object.keys(index.dirs).sort((a, b) => a.length - b.length)) {
|
|
221
|
+
if (!(rel in index.dirs))
|
|
222
|
+
continue; // already dropped with its parent
|
|
223
|
+
if (over()) {
|
|
224
|
+
complete = false;
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
stats.dirsStatted++;
|
|
228
|
+
const st = tryStat(abs(rel));
|
|
229
|
+
if (!st?.isDirectory()) {
|
|
230
|
+
dropSubtree(rel);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (racy(st.mtimeMs))
|
|
234
|
+
sawRacy = true;
|
|
235
|
+
if (st.mtimeMs !== index.dirs[rel] || racy(st.mtimeMs))
|
|
236
|
+
dirty.push({ rel, mtimeMs: st.mtimeMs });
|
|
237
|
+
}
|
|
238
|
+
// 2. Re-list those, reconciling added and removed entries.
|
|
239
|
+
for (const { rel, mtimeMs } of dirty) {
|
|
240
|
+
if (!complete)
|
|
241
|
+
break;
|
|
242
|
+
if (!(rel in index.dirs))
|
|
243
|
+
continue;
|
|
244
|
+
stats.dirsRescanned++;
|
|
245
|
+
const listing = listDir(rel);
|
|
246
|
+
if (!listing) {
|
|
247
|
+
dropSubtree(rel);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const wantDirs = new Set(listing.dirs);
|
|
251
|
+
const wantDocs = new Set(listing.docs);
|
|
252
|
+
for (const d of Object.keys(index.dirs))
|
|
253
|
+
if (d !== rel && parentOf(d) === rel && !wantDirs.has(d))
|
|
254
|
+
dropSubtree(d);
|
|
255
|
+
for (const d of Object.keys(index.docs))
|
|
256
|
+
if (parentOf(d) === rel && !wantDocs.has(d)) {
|
|
257
|
+
delete index.docs[d];
|
|
258
|
+
changed = true;
|
|
259
|
+
}
|
|
260
|
+
for (const dir of listing.dirs)
|
|
261
|
+
if (!(dir in index.dirs))
|
|
262
|
+
buildSubtree(dir);
|
|
263
|
+
for (const doc of listing.docs)
|
|
264
|
+
if (!(doc in index.docs))
|
|
265
|
+
readDoc(doc);
|
|
266
|
+
if (index.dirs[rel] !== mtimeMs) {
|
|
267
|
+
index.dirs[rel] = mtimeMs;
|
|
268
|
+
changed = true;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// 3. Which recorded docs were edited or removed?
|
|
272
|
+
if (complete) {
|
|
273
|
+
for (const rel of Object.keys(index.docs)) {
|
|
274
|
+
if (readThisRun.has(rel))
|
|
275
|
+
continue;
|
|
276
|
+
if (over()) {
|
|
277
|
+
complete = false;
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
stats.docsStatted++;
|
|
281
|
+
const st = tryStat(abs(rel));
|
|
282
|
+
const entry = index.docs[rel];
|
|
283
|
+
if (!st?.isFile() || !entry) {
|
|
284
|
+
delete index.docs[rel];
|
|
285
|
+
changed = true;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (racy(st.mtimeMs))
|
|
289
|
+
sawRacy = true;
|
|
290
|
+
if (st.mtimeMs !== entry.mtimeMs || st.size !== entry.size || racy(st.mtimeMs))
|
|
291
|
+
readDoc(rel);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const index = data;
|
|
296
|
+
if (complete && opts.persist !== false) {
|
|
297
|
+
// Rewrite when something changed, or when racy entries have aged enough to be trusted.
|
|
298
|
+
const settled = sawRacy && now() - index.builtAt > RACY_MS;
|
|
299
|
+
if (changed || settled || stats.rebuilt) {
|
|
300
|
+
index.builtAt = now();
|
|
301
|
+
stats.wrote = await writeIndexFile(repoRoot, index);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
docs: Object.keys(index.docs).sort(),
|
|
306
|
+
markersOf: (doc) => index.docs[doc]?.markers,
|
|
307
|
+
complete,
|
|
308
|
+
stats,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=docindex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docindex.js","sourceRoot":"","sources":["../src/docindex.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-claude.d.ts","sourceRoot":"","sources":["../src/hook-claude.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hook-claude.d.ts","sourceRoot":"","sources":["../src/hook-claude.ts"],"names":[],"mappings":"AAcA;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,YAAY,GAAG,aAAa,EACrC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAiDf"}
|
package/dist/hook-claude.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readConfig } from "./config.js";
|
|
2
|
-
import { absoluteHookPath, buildAdvisoryText, claimsForPath, hookActor, logHookError, normalizeTouchedPath, readStdinJson, reportDriftFor, resolveHookRoot, } from "./hook-common.js";
|
|
2
|
+
import { absoluteHookPath, buildAdvisoryText, claimsForPath, hookActor, logHookError, normalizeTouchedPath, readStdinJson, reportDriftFor, resolveHookRoot, unlinkedClaimsFor, } from "./hook-common.js";
|
|
3
3
|
/**
|
|
4
4
|
* Claude Code PreToolUse/PostToolUse adapter. Fail-open by contract: any exception here must
|
|
5
5
|
* result in exit 0 with no output, never a broken tool call. See LOCKWIRE-SPEC.md §6.
|
|
@@ -19,18 +19,21 @@ export async function runClaudeHook(adapter, fallbackRoot) {
|
|
|
19
19
|
const config = await readConfig(repoRoot);
|
|
20
20
|
if (adapter === "claude-pre") {
|
|
21
21
|
const anchors = await claimsForPath(repoRoot, touched);
|
|
22
|
-
|
|
22
|
+
const unlinked = await unlinkedClaimsFor(repoRoot, config, touched, adapter);
|
|
23
|
+
if (anchors.length === 0 && unlinked.length === 0)
|
|
23
24
|
return;
|
|
24
|
-
const additionalContext = buildAdvisoryText(anchors, config);
|
|
25
|
+
const additionalContext = buildAdvisoryText(anchors, config, unlinked);
|
|
25
26
|
const output = {
|
|
26
27
|
hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext },
|
|
27
28
|
};
|
|
28
29
|
const spec = output.hookSpecificOutput;
|
|
29
|
-
|
|
30
|
+
// Only anchored claims can be acknowledged, so only they may ask or block; unlinked ones inform.
|
|
31
|
+
const mode = anchors.length > 0 ? config.hook.mode : "advisory";
|
|
32
|
+
if (mode === "ask") {
|
|
30
33
|
spec.permissionDecision = "ask";
|
|
31
34
|
spec.permissionDecisionReason = "lockwire: documented claims cover this code";
|
|
32
35
|
}
|
|
33
|
-
else if (
|
|
36
|
+
else if (mode === "deny") {
|
|
34
37
|
spec.permissionDecision = "deny";
|
|
35
38
|
spec.permissionDecisionReason =
|
|
36
39
|
"lockwire: acknowledge the affected claims first (lockwire_claims_for / lockwire ack)";
|
package/dist/hook-claude.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-claude.js","sourceRoot":"","sources":["../src/hook-claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,YAAY,EACZ,oBAAoB,EACpB,aAAa,EACb,cAAc,EACd,eAAe,
|
|
1
|
+
{"version":3,"file":"hook-claude.js","sourceRoot":"","sources":["../src/hook-claude.ts"],"names":[],"mappings":"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"}
|
package/dist/hook-codex.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-codex.d.ts","sourceRoot":"","sources":["../src/hook-codex.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hook-codex.d.ts","sourceRoot":"","sources":["../src/hook-codex.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,WAAW,GAAG,YAAY,EACnC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
|
package/dist/hook-codex.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readConfig } from "./config.js";
|
|
2
|
-
import { absoluteHookPath, buildAdvisoryText, claimsForPath, hookActor, logHookError, normalizeTouchedPath, readStdinJson, reportDriftFor, resolveHookRoot, } from "./hook-common.js";
|
|
2
|
+
import { absoluteHookPath, buildAdvisoryText, claimsForPath, hookActor, logHookError, normalizeTouchedPath, readStdinJson, reportDriftFor, resolveHookRoot, unlinkedClaimsFor, } from "./hook-common.js";
|
|
3
3
|
/**
|
|
4
4
|
* Codex PreToolUse/PostToolUse adapter. Codex can deny a tool call but cannot rewrite its input,
|
|
5
5
|
* so `ask` mode degrades to advisory here — see LOCKWIRE-SPEC.md §6 for the verified caveat on
|
|
@@ -26,13 +26,15 @@ export async function runCodexHook(adapter, fallbackRoot) {
|
|
|
26
26
|
const config = await readConfig(repoRoot);
|
|
27
27
|
if (adapter === "codex-pre") {
|
|
28
28
|
const anchors = await claimsForPath(repoRoot, touched);
|
|
29
|
-
|
|
29
|
+
const unlinked = await unlinkedClaimsFor(repoRoot, config, touched, adapter);
|
|
30
|
+
if (anchors.length === 0 && unlinked.length === 0)
|
|
30
31
|
return;
|
|
31
|
-
const additionalContext = buildAdvisoryText(anchors, config);
|
|
32
|
+
const additionalContext = buildAdvisoryText(anchors, config, unlinked);
|
|
32
33
|
const output = {
|
|
33
34
|
hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext },
|
|
34
35
|
};
|
|
35
|
-
|
|
36
|
+
// Only anchored claims can be acknowledged, so only they may block; unlinked ones inform.
|
|
37
|
+
if (anchors.length > 0 && config.hook.mode === "deny") {
|
|
36
38
|
output.hookSpecificOutput.permissionDecision = "deny";
|
|
37
39
|
output.hookSpecificOutput.permissionDecisionReason =
|
|
38
40
|
"lockwire: acknowledge the affected claims first";
|
package/dist/hook-codex.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-codex.js","sourceRoot":"","sources":["../src/hook-codex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,YAAY,EACZ,oBAAoB,EACpB,aAAa,EACb,cAAc,EACd,eAAe,
|
|
1
|
+
{"version":3,"file":"hook-codex.js","sourceRoot":"","sources":["../src/hook-codex.ts"],"names":[],"mappings":"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"}
|
package/dist/hook-common.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type UnlinkedMarker } from "./actions.js";
|
|
1
2
|
import type { Actor, Anchor, LockwireConfig } from "./types.js";
|
|
2
3
|
export interface HookInput {
|
|
3
4
|
tool_name?: string;
|
|
@@ -20,7 +21,15 @@ export declare function absoluteHookPath(input: HookInput, filePath: string): st
|
|
|
20
21
|
export declare function resolveHookRoot(fallbackRoot: string, input: HookInput, absPath: string): string;
|
|
21
22
|
/** Windows delivers `C:\project\src\index.ts`; hooks compare against posix, repo-relative anchor targets. */
|
|
22
23
|
export declare function normalizeTouchedPath(repoRoot: string, filePath: string): string;
|
|
23
|
-
export declare function buildAdvisoryText(anchors: Anchor[], config: LockwireConfig): string;
|
|
24
|
+
export declare function buildAdvisoryText(anchors: Anchor[], config: LockwireConfig, unlinked?: UnlinkedMarker[]): string;
|
|
25
|
+
/** How long the PreToolUse hook may spend looking for unlinked claims before giving up on them. */
|
|
26
|
+
export declare const UNLINKED_SCAN_BUDGET_MS = 750;
|
|
27
|
+
/**
|
|
28
|
+
* Claims in the docs about the file about to be edited that no anchor backs. Never throws and never
|
|
29
|
+
* blocks the edit: the anchored claims are the hook's job, this is a courtesy on top, so any failure
|
|
30
|
+
* or a blown time budget just means it's left out (and noted in `.lockwire/hook.log`).
|
|
31
|
+
*/
|
|
32
|
+
export declare function unlinkedClaimsFor(repoRoot: string, config: LockwireConfig, touchedPath: string, adapter: string, budgetMs?: number): Promise<UnlinkedMarker[]>;
|
|
24
33
|
export declare function claimsForPath(repoRoot: string, touchedPath: string): Promise<Anchor[]>;
|
|
25
34
|
/** The agent that just made the edit, for ledger attribution: the PostToolUse hook runs right after it. */
|
|
26
35
|
export declare function hookActor(tool: string, input: HookInput): Actor;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-common.d.ts","sourceRoot":"","sources":["../src/hook-common.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hook-common.d.ts","sourceRoot":"","sources":["../src/hook-common.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,KAAK,cAAc,EAAqB,MAAM,cAAc,CAAC;AAGnF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEhE,MAAM,WAAW,SAAS;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,SAAS,CAAC,CAMxD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI3E;AAED,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAI/F;AAED,6GAA6G;AAC7G,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO/E;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,cAAc,EACtB,QAAQ,GAAE,cAAc,EAAO,GAC9B,MAAM,CA6CR;AAED,mGAAmG;AACnG,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAgC,GACzC,OAAO,CAAC,cAAc,EAAE,CAAC,CAqB3B;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAE5F;AAED,2GAA2G;AAC3G,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,KAAK,CAM/D;AAED,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,KAAK,GACZ,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAoC9C;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAYjG"}
|
package/dist/hook-common.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { appendFile, mkdir } from "node:fs/promises";
|
|
2
2
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
3
|
-
import { check, refs } from "./actions.js";
|
|
3
|
+
import { check, refs, unlinkedForWithin } from "./actions.js";
|
|
4
4
|
import { readConfig } from "./config.js";
|
|
5
5
|
import { toPosix, toRepoRelative, tryFindRepoRoot } from "./repo.js";
|
|
6
6
|
export async function readStdinJson() {
|
|
@@ -34,22 +34,62 @@ export function normalizeTouchedPath(repoRoot, filePath) {
|
|
|
34
34
|
}
|
|
35
35
|
return posixAbs.replace(/^[A-Za-z]:/, "").replace(/^\//, "");
|
|
36
36
|
}
|
|
37
|
-
export function buildAdvisoryText(anchors, config) {
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
37
|
+
export function buildAdvisoryText(anchors, config, unlinked = []) {
|
|
38
|
+
const cap = config.hook.maxClaimsInContext;
|
|
39
|
+
const sections = [];
|
|
40
|
+
if (anchors.length > 0) {
|
|
41
|
+
const shown = anchors.slice(0, cap);
|
|
42
|
+
const lines = shown.map((a) => {
|
|
43
|
+
const target = `${a.target.path}${a.target.symbol ? `#${a.target.symbol}` : ""}`;
|
|
44
|
+
const claimText = a.claim ? `"${a.claim.excerpt}"` : "(whole-file binding, no inline claim)";
|
|
45
|
+
return `- ${a.doc ?? "(lockfile-only)"} asserts ${claimText} about ${target} [tiers: ${a.tiers.join(",")}]`;
|
|
46
|
+
});
|
|
47
|
+
const more = anchors.length > shown.length ? `\n…and ${anchors.length - shown.length} more.` : "";
|
|
48
|
+
sections.push([
|
|
49
|
+
`lockwire: documentation makes claims about this code:`,
|
|
50
|
+
...lines,
|
|
51
|
+
more,
|
|
52
|
+
`If this edit changes what any of these claims assert, update the doc and run \`lockwire link <doc>\`.`,
|
|
53
|
+
]
|
|
54
|
+
.filter(Boolean)
|
|
55
|
+
.join("\n"));
|
|
56
|
+
}
|
|
57
|
+
if (unlinked.length > 0) {
|
|
58
|
+
const shown = unlinked.slice(0, cap);
|
|
59
|
+
const lines = shown.map((u) => `- ${u.doc}:${u.line} asserts "${u.excerpt}" about ${u.target} (${u.reason})`);
|
|
60
|
+
const more = unlinked.length > shown.length ? `\n…and ${unlinked.length - shown.length} more.` : "";
|
|
61
|
+
const docs = [...new Set(shown.map((u) => u.doc))];
|
|
62
|
+
sections.push([
|
|
63
|
+
`lockwire: these docs also make claims about this code that nobody has linked, so nothing is checking them:`,
|
|
64
|
+
...lines,
|
|
65
|
+
more,
|
|
66
|
+
`Treat them as real claims. Run \`lockwire link ${docs.length === 1 ? docs[0] : "<doc>"}\` so lockwire checks them.`,
|
|
67
|
+
]
|
|
68
|
+
.filter(Boolean)
|
|
69
|
+
.join("\n"));
|
|
70
|
+
}
|
|
71
|
+
return sections.join("\n\n");
|
|
72
|
+
}
|
|
73
|
+
/** How long the PreToolUse hook may spend looking for unlinked claims before giving up on them. */
|
|
74
|
+
export const UNLINKED_SCAN_BUDGET_MS = 750;
|
|
75
|
+
/**
|
|
76
|
+
* Claims in the docs about the file about to be edited that no anchor backs. Never throws and never
|
|
77
|
+
* blocks the edit: the anchored claims are the hook's job, this is a courtesy on top, so any failure
|
|
78
|
+
* or a blown time budget just means it's left out (and noted in `.lockwire/hook.log`).
|
|
79
|
+
*/
|
|
80
|
+
export async function unlinkedClaimsFor(repoRoot, config, touchedPath, adapter, budgetMs = UNLINKED_SCAN_BUDGET_MS) {
|
|
81
|
+
if (!config.hook.unlinkedClaims)
|
|
82
|
+
return [];
|
|
83
|
+
try {
|
|
84
|
+
const { claims, complete } = await unlinkedForWithin(repoRoot, config, touchedPath, undefined, budgetMs);
|
|
85
|
+
if (!complete)
|
|
86
|
+
await logHookError(repoRoot, adapter, `unlinked-claims scan hit its ${budgetMs}ms budget before finishing; showing what was found. Run \`lockwire index\` once to build the doc index so later edits are fast, or set hook.unlinkedClaims to false in .lockwire/config.json to stop scanning.`);
|
|
87
|
+
return claims;
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
await logHookError(repoRoot, adapter, err);
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
53
93
|
}
|
|
54
94
|
export async function claimsForPath(repoRoot, touchedPath) {
|
|
55
95
|
return refs(repoRoot, touchedPath);
|
package/dist/hook-common.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-common.js","sourceRoot":"","sources":["../src/hook-common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"hook-common.js","sourceRoot":"","sources":["../src/hook-common.ts"],"names":[],"mappings":"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"}
|
package/dist/repo.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import type { LockwireConfig } from "./types.js";
|
|
2
|
+
/** Whether a directory entry is skipped by every tree walk: vendored/internal dirs, and anything `exclude` matches. */
|
|
3
|
+
export declare function isSkippedEntry(name: string, rel: string, exclude: readonly string[]): boolean;
|
|
4
|
+
/** Whether `path` is a doc the config selects for scanning (`docs`, minus `exclude`, never vendored dirs). */
|
|
5
|
+
export declare function isScannedDoc(path: string, config: LockwireConfig): boolean;
|
|
6
|
+
export interface WalkResult {
|
|
7
|
+
files: string[];
|
|
8
|
+
/** False when the walk stopped at its deadline, so `files` is only part of the tree. */
|
|
9
|
+
complete: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Like {@link walkFiles}, but stops at `deadline` (a `Date.now()` timestamp) and says so. */
|
|
12
|
+
export declare function walkFilesWithin(repoRoot: string, exclude: readonly string[], deadline: number): WalkResult;
|
|
1
13
|
/** Recursively lists repo-relative, posix paths, skipping `.git`/`node_modules`/`.lockwire` and anything matching `exclude`. */
|
|
2
14
|
export declare function walkFiles(repoRoot: string, exclude?: readonly string[]): string[];
|
|
3
15
|
/** Windows editors (and PowerShell's `Out-File`) prepend a UTF-8 BOM that `JSON.parse` rejects. */
|
|
@@ -15,10 +27,6 @@ export declare function toRepoPath(repoRoot: string, input: string, cwd?: string
|
|
|
15
27
|
export declare function tryFindRepoRoot(startDir: string): string | undefined;
|
|
16
28
|
/** The nearest `lockwire.lock`/`.git` ancestor of `startDir`, or `startDir` itself. */
|
|
17
29
|
export declare function findRepoRoot(startDir: string): string;
|
|
18
|
-
/**
|
|
19
|
-
* A small, dependency-free glob matcher: `**` matches across path separators, `*` matches within
|
|
20
|
-
* one segment. Enough for `config.docs`/`config.exclude` defaults; not a full minimatch replacement.
|
|
21
|
-
*/
|
|
22
30
|
export declare function globToRegExp(glob: string): RegExp;
|
|
23
31
|
export declare function matchesAny(path: string, globs: readonly string[]): boolean;
|
|
24
32
|
/**
|
package/dist/repo.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../src/repo.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../src/repo.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjD,uHAAuH;AACvH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAE7F;AAED,8GAA8G;AAC9G,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAM1E;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wFAAwF;IACxF,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,8FAA8F;AAC9F,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,QAAQ,EAAE,MAAM,GACf,UAAU,CAmBZ;AAED,gIAAgI;AAChI,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,EAAE,CAErF;AAED,mGAAmG;AACnG,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,yGAAyG;AACzG,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAExE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,MAAM,CAE/F;AAED,kMAAkM;AAClM,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQpE;AAED,uFAAuF;AACvF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErD;AAiCD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAc3E"}
|