paperlint 11.1.2 → 11.1.3

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.
@@ -1,59 +1,77 @@
1
1
  /**
2
- * The `submission-confirm` hook — no send to a venue's portal without the person's own yes.
2
+ * The `submission-confirm` hook — no send to a venue's portal without the person's own yes to it.
3
3
  * PreToolUse on `Bash` (`plugin/hooks/hooks.json`): a command that sends a submission — `paperlint
4
- * submission new|update` with `--save` or `--submit`, or a request straight at a HotCRP site — gets
5
- * `"permissionDecision": "ask"`, and Claude Code puts the question to the person, with the reason
6
- * below, in its permission prompt. Anything else: no output.
4
+ * submission new|update` with `--save` or `--submit` — runs only when the person has said yes, in an
5
+ * `AskUserQuestion` asked since the last send, to a question naming the paper folder (its full path)
6
+ * and the sha256 of the PDF it sends.
7
+ * A command that names a portal's host runs only after a yes to a question naming that host and the
8
+ * command's first 60 characters. Without the yes the command is refused (`"permissionDecision":
9
+ * "deny"`), and the reason tells the agent exactly what to ask. Anything else: no output.
7
10
  *
8
- * ── WHY "ask", AND WHY NOTHING TURNS IT OFF ──────────────────────────────────────
9
- * The hooks reference (code.claude.com/docs/en/hooks): "`ask` prompts the user to confirm"; "A
10
- * hook's `ask` also forces a permission prompt in auto mode"; the reason is "shown to the user in
11
- * the permission prompt"; and decisions combine deny > defer > ask > allow, so an `allow` rule in
12
- * the settings does not outvote it. An agent cannot answer the prompt, and no flag or variable of
13
- * this package skips it — that is the point: the send is the person's decision, taken on the dry
14
- * run's venue check (paperlint#264). The hook never denies (the person may send over a limit) and
15
- * never allows (that would skip the person).
11
+ * ── WHY A QUESTION, NOT CLAUDE CODE'S PERMISSION PROMPT ──────────────────────────
12
+ * It used to answer `ask` (paperlint#283). The Claude mobile app's permission card shows only the
13
+ * command line, not the hook's reason, so the person approved a shell line without seeing which
14
+ * paper or PDF it sends (paperlint#291). `AskUserQuestion` shows the whole question on every
15
+ * client, and a yes to a question that names the folder and the sha256 is a yes to those bytes in that
16
+ * folder: a stale build or a wrong PDF has another sha, and identical bytes in another folder (the
17
+ * same manuscript for another venue) have another path, so their yes does not count.
18
+ *
19
+ * ── WHERE THE YES IS READ ─────────────────────────────────────────────────────────
20
+ * The session's transcript (`transcript_path` in the hook's input, one JSON entry per line). A
21
+ * question is the assistant's `tool_use` named `AskUserQuestion`; its answer is the user entry that
22
+ * carries the matching `tool_result`, whose `toolUseResult.answers` maps each question's text to the
23
+ * option chosen. An answer is a yes when its first word is "yes". Every earlier `Bash` call that
24
+ * sends or names a portal — refused or not, but not this one (`tool_use_id`) — uses up the questions
25
+ * before it, so one yes lets one command through. Within one command each send takes an answer of
26
+ * its own: a yes that names two shas, or the same PDF sent twice, covers one send. When several
27
+ * questions name the same thing, the latest decides.
16
28
  *
17
29
  * ── WHAT IT SEES, AND WHAT IT IS NOT ──────────────────────────────────────────────
18
30
  * A send is judged on the command as the shell reads it (`shellLeaves`: each simple command across
19
31
  * `&&`, `;`, `|`, words unquoted, the commands inside a `bash -c "…"` string too), never on the raw
20
- * text: `submission`, `new|update` and `--save|--submit` in one command's words, or a portal's host
21
- * in any word (a HotCRP-hosted site, or a host a venue preset declares), whatever program it runs.
22
- * A false ask costs one click; a missed send cannot be taken back. NOT SEEN: an upload from a script
23
- * file that names the host only inside the file, or words built at run time (`$(…)`).
24
- *
25
- * The hook catches an agent that runs a send without asking; it is not a security boundary against
26
- * an agent that deliberately disguises a send.
32
+ * text: `submission`, `new|update` and `--save|--submit` in one command's words; or a portal's host
33
+ * in any word (a HotCRP-hosted site, or a host a venue preset declares), whatever program it runs —
34
+ * such a command may send around paperlint's venue check, and has no PDF to name, so the question
35
+ * names the host and the command instead. When the presets cannot be read, only HotCRP-hosted sites
36
+ * and paperlint sends are recognised, and a refusal says so: one bad preset does not stop the shell.
37
+ * NOT SEEN: an upload from a script file that names the host only inside the file, or words built
38
+ * at run time (`$(…)`).
27
39
  *
28
- * The reason is built from the same parse and the CLI's own argument parser (`readSend`), one block
29
- * per send; a send whose PDF cannot be told for certain is still asked about, and says so.
40
+ * The agent writes the question, so a deliberately misleading question still passes. The hook
41
+ * catches an agent that sends without asking, or asks about other bytes; it is not a security
42
+ * boundary against an agent that deliberately disguises a send.
30
43
  *
31
- * ── IT FAILS TOWARD THE QUESTION ─────────────────────────────────────────────────
32
- * The reason is built from files (the PDF, the build's facts); whatever goes wrong there, the ask
33
- * still goes out, saying what could not be read. A payload that is not JSON is asked about.
44
+ * ── IT FAILS CLOSED ───────────────────────────────────────────────────────────────
45
+ * A send whose PDF cannot be told for certain, a PDF that is not there, no transcript, a payload
46
+ * that is not JSON, a crash: refused, with what went wrong. A transcript line that is not JSON is
47
+ * not a reason to refuse every send for the rest of the session — real transcripts have them (an
48
+ * entry cut short and run into the next, measured in a live one) — so it counts as a send when its
49
+ * text may hold one, and a yes before it no longer counts. Only lines that may hold a question, an
50
+ * answer or a send are parsed, and the shell parser runs only on commands that say `submission` or
51
+ * a portal's host:
52
+ * a transcript of hundreds of megabytes is read in seconds.
34
53
  *
35
54
  * ── WHY IN-PROCESS, NOT A VIGILES PROGRAM ────────────────────────────────────────
36
55
  * It reads the build's facts with this package's own parser and hashes the PDF; a shipped vigiles
37
56
  * hook may import nothing but `vigiles/hook` (`src/folder-layout-hook.ts` says the rest). So
38
57
  * `paperlint hook submission-confirm` runs it in-process (`runHook`, src/cli.ts).
39
58
  */
40
- import { basename, isAbsolute, join, relative, resolve } from "node:path";
59
+ import { isAbsolute, join, relative, resolve } from "node:path";
41
60
  import { callerPath } from "./caller-path.js";
42
61
  import { bodyEnd } from "./domain/body-pages.js";
43
- import { fieldOf } from "./domain/record.js";
62
+ import { fieldOf, isRecord } from "./domain/record.js";
44
63
  import { sha256Hex } from "./domain/sha256.js";
45
64
  import { messageOf } from "./domain/text.js";
46
65
  import { factsPath, parseFactsText } from "./facts-file.js";
47
66
  import { paperPreset } from "./presets.js";
48
67
  import { paperPdf } from "./submission.js";
49
- const ask = (reason) => ({
68
+ const deny = (reason) => ({
50
69
  hookSpecificOutput: {
51
70
  hookEventName: "PreToolUse",
52
- permissionDecision: "ask",
71
+ permissionDecision: "deny",
53
72
  permissionDecisionReason: reason,
54
73
  },
55
74
  });
56
- const LAST = "Allow only if you have read the dry run's venue check.";
57
75
  /** The domain of HotCRP's hosted sites; a self-hosted portal is known by the host its preset declares. */
58
76
  const HOTCRP_HOSTED = "hotcrp.com";
59
77
  /**
@@ -92,25 +110,21 @@ function sendsIn(leaves, readSend) {
92
110
  return [moved ? null : readSend(l.args.slice(at + 1))];
93
111
  });
94
112
  }
113
+ /** The words of the commands that may name a host: those with a dot, lowercased. */
114
+ const dottedWords = (leaves) => leaves
115
+ .flatMap((l) => [l.head, ...l.args])
116
+ .map((w) => w.toLowerCase())
117
+ .filter((w) => w.includes("."));
118
+ /** The portal hosts known: HotCRP's hosted sites always, then those `hosts` names; lowercased. */
119
+ const knownHosts = (hosts) => [HOTCRP_HOSTED, ...hosts].map((h) => h.toLowerCase()).filter((h) => h !== "");
95
120
  /**
96
- * What the commands send: a submission through paperlint, anything that names a venue's portal — a
97
- * HotCRP-hosted site, or a host a venue preset declares (`hosts`, read only when a word has a dot),
98
- * whatever program it runs, as a plain case-insensitive substring of a word — or nothing. Judged on
99
- * the shell's words only, never on the raw text.
121
+ * The portal host a command names — a HotCRP-hosted site, or a host a venue preset declares
122
+ * (`hosts`), whatever program it runs, as a plain case-insensitive substring of a word — or null.
123
+ * Judged on the shell's words only, never on the raw text.
100
124
  */
101
- export function sendKind(leaves, hosts) {
102
- if (leaves.some(isSend))
103
- return "paperlint";
104
- const words = leaves
105
- .flatMap((l) => [l.head, ...l.args])
106
- .map((w) => w.toLowerCase())
107
- .filter((w) => w.includes("."));
108
- if (words.length === 0)
109
- return null;
110
- const known = [HOTCRP_HOSTED, ...hosts()]
111
- .map((h) => h.toLowerCase())
112
- .filter((h) => h !== "");
113
- return words.some((w) => known.some((h) => w.includes(h))) ? "direct" : null;
125
+ function portalHost(leaves, hosts) {
126
+ const words = dottedWords(leaves);
127
+ return (knownHosts(hosts).find((h) => words.some((w) => w.includes(h))) ?? null);
114
128
  }
115
129
  /** The body pages the build counted: up to the references or appendix, or banal's count. */
116
130
  function bodyPages(f) {
@@ -144,43 +158,238 @@ function factsLines(paperDir, sha, files) {
144
158
  return [...stale, bodyPages(f.value)];
145
159
  }
146
160
  /** One send, described: the paper, the PDF as the command resolves it, what the build saw. */
147
- function sendLines(send, cwd, deps) {
161
+ function describe(send, cwd, deps) {
148
162
  const paperDir = resolve(cwd, send.paper ?? ".");
149
163
  const p = paperPreset(paperDir, deps);
150
164
  const own = paperPdf(paperDir, p.kind === "settings-problem" ? null : p.settings);
151
165
  const pdf = send.pdf === null ? own : resolve(cwd, send.pdf);
152
166
  const bytes = deps.files.readBytes(callerPath(pdf));
153
167
  const sha = bytes === null ? null : sha256Hex(bytes);
154
- return [
155
- `paper folder: ${send.paper ?? `${basename(paperDir)} (the current folder)`}`,
156
- `PDF: ${send.pdf ?? join(send.paper ?? ".", relative(paperDir, own))}${sha === null ? " — NOT FOUND" : `, sha256 ${sha.slice(0, 12)}`}`,
157
- ...factsLines(paperDir, sha, deps.files),
158
- ];
168
+ return {
169
+ named: sha === null ? null : { folder: paperDir, sha: sha.slice(0, SHA_SHOWN) },
170
+ lines: [
171
+ `paper folder: ${paperDir}`,
172
+ `PDF: ${send.pdf ?? join(send.paper ?? ".", relative(paperDir, own))}${sha === null ? " — NOT FOUND" : `, sha256 ${sha.slice(0, SHA_SHOWN)}`}`,
173
+ ...factsLines(paperDir, sha, deps.files),
174
+ ],
175
+ };
159
176
  }
177
+ /** How much of the sha256 the reason shows, and the question must contain. */
178
+ const SHA_SHOWN = 12;
160
179
  /** A send whose PDF cannot be told for certain. */
161
180
  const UNRESOLVED = "could not resolve which PDF this send uploads — check the dry run";
181
+ const NOT_SENT = "Not run: this sends the paper to the venue's portal";
182
+ const NAMES_PORTAL = "Not run: this command names a venue's submission portal";
183
+ /** What to ask, once every send's PDF is known. */
184
+ const HOW_TO_ASK = [
185
+ "Ask the person with AskUserQuestion first, then run this command again. In the question, give:",
186
+ "- the paper folder (the full path, as written above), the PDF and its sha256 as written above (the folder path and the sha256 must both be in the question text),",
187
+ "- what the lines above say about the build (the body pages, or that it is stale or missing),",
188
+ "- the dry run's venue check, quoted word for word (its `venue rules at their default options` line and every error under it).",
189
+ 'Options: "Yes, send it" and "No". One yes lets one send through: ask one question per send, each naming its own folder and sha256.',
190
+ ].join("\n");
191
+ /** How much of a command naming a portal the question must quote. */
192
+ const COMMAND_SHOWN = 60;
193
+ /** What to ask before a command that names a portal. */
194
+ const HOW_TO_ASK_PORTAL = [
195
+ "It may send to the portal around paperlint's venue check. Ask the person with AskUserQuestion first, then run this command again: the question must contain the portal host and the command start above, exactly as written, and say what the command does.",
196
+ 'Options: "Yes, run it" and "No". One yes lets one command through. To send a paper, use `paperlint submission new|update --save` instead.',
197
+ ].join("\n");
198
+ /** Each send's block: alone, or numbered when the command has several. */
199
+ function blocks(described) {
200
+ return described.length === 1
201
+ ? described.flatMap((d) => d.lines)
202
+ : described.flatMap((d, n) => [
203
+ `send ${String(n + 1)} of ${String(described.length)}:`,
204
+ ...d.lines,
205
+ ]);
206
+ }
207
+ /** A transcript line that may hold a question, an answer or a send; no other line is parsed. */
208
+ const MAY_MATTER = /"tool_use"|"toolUseResult"/u;
209
+ /** Nothing asked or answered: the start, and the state right after a send. */
210
+ const CLEARED = { asked: new Set(), answers: [] };
211
+ /** A transcript line that is not JSON. */
212
+ const BROKEN = Symbol("broken");
162
213
  /**
163
- * Why the person is asked, for `paperlint submission` sends: each send's paper, PDF and build — every
164
- * send in the command, one block each. Words the shell would expand (`$…`, backticks) are not
165
- * guessed at: such a command is described as unresolved.
214
+ * The person's answers to `AskUserQuestion` since the last send in the transcript, oldest first.
215
+ * A line that may matter and is not JSON (real transcripts have them: an entry cut short, run into
216
+ * the next) counts as a send when it may hold one, so a yes before it is used up — never wrongly
217
+ * kept; otherwise it is skipped, and a yes lost in it only means asking again.
166
218
  */
167
- export function paperlintReason(command, leaves, cwd, deps) {
168
- const sends = /[`$]/u.test(command) ? [] : sendsIn(leaves, deps.readSend);
169
- const one = (x) => x === null ? [UNRESOLVED] : sendLines(x, cwd, deps);
170
- const blocks = sends.length === 0
171
- ? [UNRESOLVED]
172
- : sends.length === 1
173
- ? sends.flatMap(one)
174
- : sends.flatMap((x, n) => [
175
- `send ${String(n + 1)} of ${String(sends.length)}:`,
176
- ...one(x),
177
- ]);
178
- return [SENDS, ...blocks, LAST].join("\n");
179
- }
180
- /** The ask when the hook's input cannot be read at all: it cannot tell, so the person decides. */
181
- export const UNREADABLE_INPUT = ask(`paperlint could not read this command, so it cannot tell whether it sends a paper to a venue's portal. ${LAST}`);
182
- const SENDS = "This sends the paper to the venue's portal (paperlint submission with --save or --submit).";
183
- const DIRECT = `This command names a venue's submission portal, so it may send to it outside paperlint and its venue check. ${LAST}`;
219
+ export function answersSinceLastSend(transcript, earlier) {
220
+ return transcript.split("\n").reduce((since, line) => {
221
+ if (!MAY_MATTER.test(line))
222
+ return since;
223
+ const entry = parsedLine(line);
224
+ if (entry === BROKEN)
225
+ return earlier.mayHold(line) ? CLEARED : since;
226
+ const content = fieldOf(fieldOf(entry, "message"), "content");
227
+ return (Array.isArray(content) ? content : []).reduce((s, block) => fieldOf(block, "type") === "tool_result"
228
+ ? toolResult(s, block, entry)
229
+ : toolUse(s, block, earlier), since);
230
+ }, CLEARED).answers;
231
+ }
232
+ /** One transcript line, parsed; BROKEN when it is not JSON. */
233
+ function parsedLine(line) {
234
+ try {
235
+ return JSON.parse(line);
236
+ }
237
+ catch {
238
+ return BROKEN;
239
+ }
240
+ }
241
+ /** A block the assistant wrote: a question is noted; an earlier send clears what was asked before it. */
242
+ function toolUse(s, block, earlier) {
243
+ const id = fieldOf(block, "id");
244
+ if (fieldOf(block, "type") !== "tool_use" || typeof id !== "string")
245
+ return s;
246
+ const name = fieldOf(block, "name");
247
+ if (name === "AskUserQuestion")
248
+ return { ...s, asked: new Set([...s.asked, id]) };
249
+ const command = fieldOf(fieldOf(block, "input"), "command");
250
+ const send = name === "Bash" &&
251
+ id !== earlier.current &&
252
+ typeof command === "string" &&
253
+ earlier.mayHold(command) &&
254
+ earlier.sends(command);
255
+ return send ? CLEARED : s;
256
+ }
257
+ /** The person's reply to a question asked since the last send: its answers, kept in order. */
258
+ function toolResult(s, block, entry) {
259
+ const id = fieldOf(block, "tool_use_id");
260
+ if (typeof id !== "string" || !s.asked.has(id))
261
+ return s;
262
+ const answers = fieldOf(fieldOf(entry, "toolUseResult"), "answers");
263
+ const given = Object.entries(isRecord(answers) ? answers : {}).flatMap(([question, answer]) => typeof answer === "string" ? [{ question, answer }] : []);
264
+ return { ...s, answers: [...s.answers, ...given] };
265
+ }
266
+ /** Whether an answer is a yes: its first word, in any case. */
267
+ const isYes = (a) => /^yes\b/iu.test(a.answer.trim());
268
+ /** Whether `text` contains every one of `keys`, in any case. */
269
+ const hasAll = (text, keys) => keys.every((k) => text.toLowerCase().includes(k.toLowerCase()));
270
+ /** `text` as a literal inside a regular expression. */
271
+ const literal = (text) => text.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
272
+ /**
273
+ * Whether a question names the folder `folder` and not a longer path that starts with it
274
+ * (`/w/paper` is not `/w/paper2` or `/w/paper.old`), nor one it ends: the whole path, case-sensitive.
275
+ */
276
+ const namesFolder = (question, folder) => new RegExp(`(?<![\\w.-])${literal(folder)}(?![\\w.-])`, "u").test(question);
277
+ /**
278
+ * The answers used up after one more need: it takes the latest answer not yet taken whose question
279
+ * `names` it, and that answer must be a yes; null when there is none, or it is not.
280
+ */
281
+ function takeYes(answers, taken, names) {
282
+ if (taken === null)
283
+ return null;
284
+ const at = answers.findLastIndex((a, n) => !taken.has(n) && names(a.question));
285
+ const found = answers[at];
286
+ return found !== undefined && isYes(found) ? new Set([...taken, at]) : null;
287
+ }
288
+ /** What a yes to one send must name: its sha256 (any case) and its folder (the whole path, as written). */
289
+ const namesSend = (s) => (question) => hasAll(question, [s.sha]) && namesFolder(question, s.folder);
290
+ /** What a yes to a command that names a portal must name: the host (any case) and the command's start, literally. */
291
+ const namesPortal = (host, start) => (question) => hasAll(question, [host]) && question.includes(start);
292
+ /**
293
+ * Whether every send and the portal command (null for none) has a yes of its own. Each takes the
294
+ * latest answer not yet taken, in turn, from one shared set: a yes used up by a send cannot also
295
+ * approve the portal, one that names two shas covers one send, and the same PDF sent twice needs
296
+ * two. (A yes that two needs could share is given to the first; the second is refused and the
297
+ * person is asked again: it fails closed.)
298
+ */
299
+ const everyNeedHasItsYes = (answers, sends, portal) => [...sends.map(namesSend), ...(portal === null ? [] : [portal])].reduce((taken, names) => takeYes(answers, taken, names), new Set()) !== null;
300
+ /** Every send in the command, described; an unresolved one has no sha. */
301
+ function describedSends(command, cwd, deps) {
302
+ const sends = /[`$]/u.test(command)
303
+ ? []
304
+ : sendsIn(deps.shellLeaves(command), deps.readSend);
305
+ return (sends.length === 0 ? [null] : sends).map((x) => x === null ? { named: null, lines: [UNRESOLVED] } : describe(x, cwd, deps));
306
+ }
307
+ /** The refusal of a send whose PDF is not known or not there: there is no sha256 to ask about. */
308
+ const noPdf = (described) => [
309
+ `${NOT_SENT}, and paperlint cannot find the PDF it uploads, so there is no sha256 to ask the person about.`,
310
+ ...blocks(described),
311
+ "Name the paper folder and the PDF plainly (no cd, no $…), build the PDF, run the dry run, and ask then.",
312
+ ];
313
+ /** The presets' hosts; when they cannot be read, none, and why. */
314
+ function readHosts(cwd, deps) {
315
+ try {
316
+ return { hosts: deps.portalHosts(cwd), failed: null };
317
+ }
318
+ catch (e) {
319
+ return { hosts: [], failed: messageOf(e) };
320
+ }
321
+ }
322
+ /** The line a refusal ends with when the presets could not be read; none when they could. */
323
+ const fallbackLines = (h) => h.failed === null
324
+ ? []
325
+ : [
326
+ `The venue presets could not be read (${h.failed}), so only HotCRP-hosted sites and paperlint sends are recognised until they are fixed.`,
327
+ ];
328
+ /**
329
+ * A command that sends: let through (null) when the person said yes to it since the last send —
330
+ * each paperlint send by its paper folder and its PDF's sha256, a yes of its own for each, a portal
331
+ * named by its host and the command's start —
332
+ * else the lines of the refusal, with what to ask.
333
+ */
334
+ function decide(c, payload, deps) {
335
+ const described = c.paperlint ? describedSends(c.text, c.cwd, deps) : [];
336
+ const named = described.flatMap((d) => (d.named === null ? [] : [d.named]));
337
+ if (named.length < described.length)
338
+ return noPdf(described);
339
+ const base = c.paperlint ? NOT_SENT : NAMES_PORTAL;
340
+ const read = transcriptOf(payload, deps.files);
341
+ if (typeof read !== "string")
342
+ return [
343
+ `${base}, and ${read.failed}, so paperlint cannot see the person's yes.`,
344
+ ];
345
+ const start = c.text.trim().slice(0, COMMAND_SHOWN);
346
+ const answers = answersSinceLastSend(read, earlierSend(c, payload, deps));
347
+ const portal = c.host === null ? null : namesPortal(c.host, start);
348
+ if (everyNeedHasItsYes(answers, named, portal))
349
+ return null;
350
+ return [
351
+ `${base}, and the person has not said yes to it since the last send.`,
352
+ ...blocks(described),
353
+ ...(c.paperlint ? [HOW_TO_ASK] : []),
354
+ ...portalLines(c.host, start),
355
+ ];
356
+ }
357
+ /** What a refusal says about a portal the command names: the host, the start, what to ask; none for none. */
358
+ const portalLines = (host, start) => host === null
359
+ ? []
360
+ : [`portal host: ${host}`, `command start: ${start}`, HOW_TO_ASK_PORTAL];
361
+ /** How an earlier send is told in the transcript: by the same matching as this command. */
362
+ function earlierSend(c, payload, deps) {
363
+ const known = knownHosts(c.hosts.hosts);
364
+ return {
365
+ current: stringField(payload, "tool_use_id"),
366
+ mayHold: (text) => {
367
+ const plain = text.replace(/["'\\]/gu, "").toLowerCase();
368
+ return (plain.includes("submission") || known.some((h) => plain.includes(h)));
369
+ },
370
+ sends: (command) => {
371
+ const leaves = allLeaves(command, deps.shellLeaves);
372
+ return leaves.some(isSend) || portalHost(leaves, known) !== null;
373
+ },
374
+ };
375
+ }
376
+ /** A string field of the payload; null for none. */
377
+ function stringField(payload, key) {
378
+ const v = fieldOf(payload, key);
379
+ return typeof v === "string" ? v : null;
380
+ }
381
+ /** The session's transcript as text, or why it could not be read. */
382
+ function transcriptOf(payload, files) {
383
+ const path = stringField(payload, "transcript_path");
384
+ if (path === null || !isAbsolute(path))
385
+ return { failed: "the hook was given no transcript_path" };
386
+ const bytes = files.readBytes(callerPath(path));
387
+ return bytes === null
388
+ ? { failed: `the transcript ${path} could not be read` }
389
+ : new TextDecoder().decode(bytes);
390
+ }
391
+ /** The refusal when the hook's input cannot be read at all: it cannot tell, so it does not let it run. */
392
+ export const UNREADABLE_INPUT = deny("Not run: paperlint could not read this command, so it cannot tell whether it sends a paper to a venue's portal.");
184
393
  /** The payload, parsed; null when it is not JSON. */
185
394
  function parsed(stdin) {
186
395
  try {
@@ -190,41 +399,39 @@ function parsed(stdin) {
190
399
  return null;
191
400
  }
192
401
  }
402
+ /** One command, judged: null to let it run, or the refusal. */
403
+ function judge(text, cwd, payload, deps) {
404
+ const leaves = allLeaves(text, deps.shellLeaves);
405
+ const paperlint = leaves.some(isSend);
406
+ // The presets are read only for a command that may send: anything else pays nothing.
407
+ if (!paperlint && dottedWords(leaves).length === 0)
408
+ return null;
409
+ const hosts = readHosts(cwd, deps);
410
+ const host = portalHost(leaves, hosts.hosts);
411
+ if (!paperlint && host === null)
412
+ return null;
413
+ const lines = decide({ text, cwd, paperlint, host, hosts }, payload, deps);
414
+ return lines === null
415
+ ? null
416
+ : deny([...lines, ...fallbackLines(hosts)].join("\n"));
417
+ }
193
418
  /**
194
- * The hook on one payload, as Claude Code sends it on stdin: the ask, or null for a command that
195
- * sends nothing. Never throws: what it cannot read, it asks about.
419
+ * The hook on one payload, as Claude Code sends it on stdin: a refusal, or null to let the command
420
+ * run (it sends nothing, or every send in it has the person's yes). Never throws: a crash refuses.
196
421
  */
197
422
  export function submissionConfirm(stdin, deps) {
198
423
  const payload = parsed(stdin);
199
424
  if (payload === null)
200
425
  return UNREADABLE_INPUT;
201
426
  const raw = fieldOf(fieldOf(payload, "tool_input"), "command");
202
- const command = typeof raw === "string" ? raw : "";
427
+ const text = typeof raw === "string" ? raw : "";
203
428
  const given = fieldOf(payload, "cwd");
204
429
  const cwd = typeof given === "string" && isAbsolute(given) ? given : resolve(".");
205
- const kind = kindOf(command, cwd, deps);
206
- if (kind === null)
207
- return null;
208
- if (kind !== "paperlint" && kind !== "direct")
209
- return ask(kind.failed);
210
- if (kind === "direct")
211
- return ask(DIRECT);
212
- try {
213
- return ask(paperlintReason(command, deps.shellLeaves(command), cwd, deps));
214
- }
215
- catch (e) {
216
- return ask(`${SENDS} paperlint could not say what will be sent: ${messageOf(e)}. ${LAST}`);
217
- }
218
- }
219
- /** What the command sends, or — when reading it failed — the reason to ask anyway. */
220
- function kindOf(command, cwd, deps) {
221
430
  try {
222
- return sendKind(allLeaves(command, deps.shellLeaves), () => deps.portalHosts(cwd));
431
+ return judge(text, cwd, payload, deps);
223
432
  }
224
433
  catch (e) {
225
- return {
226
- failed: `paperlint could not tell what this command sends (${messageOf(e)}). ${LAST}`,
227
- };
434
+ return deny(`Not run: paperlint could not check whether this command sends a paper: ${messageOf(e)}.`);
228
435
  }
229
436
  }
230
437
  //# sourceMappingURL=submission-confirm-hook.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"submission-confirm-hook.js","sourceRoot":"","sources":["../src/submission-confirm-hook.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"submission-confirm-hook.js","sourceRoot":"","sources":["../src/submission-confirm-hook.ts"],"names":[],"mappings":"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"}
package/docs/layout.md CHANGED
@@ -136,28 +136,28 @@ A check runs at one of four moments:
136
136
  The lint-only job skips what needs a build (pages, fonts, references).
137
137
  - **On demand** — a skill, when you ask for what it does. A skill stops nothing; it writes.
138
138
 
139
- | what | kind | when it runs | what it stops |
140
- | ------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
141
- | `paper-folder-layout` | hook | before an agent creates a file (Edit, Write) | a new entry the layout does not allow, at a paper's root or the top of its `repro/`; never an existing file |
142
- | `paper-edit-guard` | hook | before an agent's shell command | a shell command that writes or moves a paper's source, which would skip the hooks that run after an edit |
143
- | `submission-confirm` | hook | before an agent's shell command | nothing by itself: Claude Code asks the person before a submission is sent (`--save`, `--submit`, or straight to HotCRP) |
144
- | `paper-skills-nudge` | hook | after an agent edits a paper's source | nothing: it shows the agent the pre-submit checklist |
145
- | `paper-status-gates` | hook | after an agent edits a paper's source | nothing: it prints the paper's readiness and its unrun gates, for a person reading the transcript |
146
- | [`paper/allowed-files-only`](rules/paper/allowed-files-only.md) | rule | every `paperlint lint`, and CI | a stray file or folder at a paper's root (error) |
147
- | [`repro/allowed-files-only`](rules/repro/allowed-files-only.md) | rule | every `paperlint lint`, and CI | a loose entry at the top of `repro/`, a folder that is not a study's, a study folder without `README.md` (error) |
148
- | [`repro/study-registered`](rules/repro/study-registered.md) | rule | every `paperlint lint`, and CI | a study folder without its row in `repro/studies.json`, a row that does not match, a measurement or plan that does not hold, an index out of sync (error) |
149
- | [`repro/study-plan-name`](rules/repro/study-plan-name.md) | rule | every `paperlint lint`, and CI | nothing: it warns when a study's plan is still named `prereg.json` |
150
- | [`repro/outputs-before-decision`](rules/repro/outputs-before-decision.md) | rule | every `paperlint lint`, and CI (with the whole history) | an output of a study decided before its run that git shows was not made after the decision |
151
- | `paperlint studies gate` | command | before a decide-first study's run | the run, until the decision is committed |
152
- | [`study/declare-sample`](rules/study/declare-sample.md) | rule | every `paperlint lint`, and CI, once a paper turns it on | a study's `prereg.json` without its population, strata, screens, coverage or the author's check (experimental, off by default) |
153
- | [`paper/folder-venue-leftover`](rules/paper/folder-venue-leftover.md) | rule | every `paperlint lint`, and CI | nothing: it warns when the folder is named after another venue |
154
- | `review/frontmatter`, `sibling/frontmatter` | rule | every `paperlint lint`, and CI | a review whose findings break the schema (error); a sibling card that does not say how much was read (warning) |
155
- | `paper/stages`, `paper/source` | rule | every `paperlint lint`, and CI | a stage whose frozen PDF or source is missing or changed (error) |
156
- | the rules on the source (`tex/*`, `paper/*`, `bib/*`) | rule | every `paperlint lint`, and CI | what each one's [row](rules.md) says |
157
- | the rules on what `paperlint build` measured (`pdf/*`, `format/page-limit`, the references) | rule | every `paperlint lint` after a build; CI's full job | a PDF over the page limit, in the wrong font or format, or with a bad reference |
158
- | `study-accepted-papers` | skill | when you ask; stage 2 (modes A, B) and stage 5 (modes C, D) | nothing: it fills the venue's corpus, and writes the strong-accept scorecard in `PIPELINE-STATUS.md` |
159
- | `build-benchmark` | skill | when you ask; stage 3 | nothing: it registers the study with `paperlint studies new`, writes its design, fills its `plan.json`, and updates its row when it ends |
160
- | `build-label-page` | skill | when you ask; stage 3 | nothing: it builds the labelling page, and exports the answers into the study's `data/` |
139
+ | what | kind | when it runs | what it stops |
140
+ | ------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
141
+ | `paper-folder-layout` | hook | before an agent creates a file (Edit, Write) | a new entry the layout does not allow, at a paper's root or the top of its `repro/`; never an existing file |
142
+ | `paper-edit-guard` | hook | before an agent's shell command | a shell command that writes or moves a paper's source, which would skip the hooks that run after an edit |
143
+ | `submission-confirm` | hook | before an agent's shell command | a submission send (`--save`, `--submit`), or a command naming a portal's host, that the person has not said yes to in a question naming its PDF's sha256 (or the host and the command) |
144
+ | `paper-skills-nudge` | hook | after an agent edits a paper's source | nothing: it shows the agent the pre-submit checklist |
145
+ | `paper-status-gates` | hook | after an agent edits a paper's source | nothing: it prints the paper's readiness and its unrun gates, for a person reading the transcript |
146
+ | [`paper/allowed-files-only`](rules/paper/allowed-files-only.md) | rule | every `paperlint lint`, and CI | a stray file or folder at a paper's root (error) |
147
+ | [`repro/allowed-files-only`](rules/repro/allowed-files-only.md) | rule | every `paperlint lint`, and CI | a loose entry at the top of `repro/`, a folder that is not a study's, a study folder without `README.md` (error) |
148
+ | [`repro/study-registered`](rules/repro/study-registered.md) | rule | every `paperlint lint`, and CI | a study folder without its row in `repro/studies.json`, a row that does not match, a measurement or plan that does not hold, an index out of sync (error) |
149
+ | [`repro/study-plan-name`](rules/repro/study-plan-name.md) | rule | every `paperlint lint`, and CI | nothing: it warns when a study's plan is still named `prereg.json` |
150
+ | [`repro/outputs-before-decision`](rules/repro/outputs-before-decision.md) | rule | every `paperlint lint`, and CI (with the whole history) | an output of a study decided before its run that git shows was not made after the decision |
151
+ | `paperlint studies gate` | command | before a decide-first study's run | the run, until the decision is committed |
152
+ | [`study/declare-sample`](rules/study/declare-sample.md) | rule | every `paperlint lint`, and CI, once a paper turns it on | a study's `prereg.json` without its population, strata, screens, coverage or the author's check (experimental, off by default) |
153
+ | [`paper/folder-venue-leftover`](rules/paper/folder-venue-leftover.md) | rule | every `paperlint lint`, and CI | nothing: it warns when the folder is named after another venue |
154
+ | `review/frontmatter`, `sibling/frontmatter` | rule | every `paperlint lint`, and CI | a review whose findings break the schema (error); a sibling card that does not say how much was read (warning) |
155
+ | `paper/stages`, `paper/source` | rule | every `paperlint lint`, and CI | a stage whose frozen PDF or source is missing or changed (error) |
156
+ | the rules on the source (`tex/*`, `paper/*`, `bib/*`) | rule | every `paperlint lint`, and CI | what each one's [row](rules.md) says |
157
+ | the rules on what `paperlint build` measured (`pdf/*`, `format/page-limit`, the references) | rule | every `paperlint lint` after a build; CI's full job | a PDF over the page limit, in the wrong font or format, or with a bad reference |
158
+ | `study-accepted-papers` | skill | when you ask; stage 2 (modes A, B) and stage 5 (modes C, D) | nothing: it fills the venue's corpus, and writes the strong-accept scorecard in `PIPELINE-STATUS.md` |
159
+ | `build-benchmark` | skill | when you ask; stage 3 | nothing: it registers the study with `paperlint studies new`, writes its design, fills its `plan.json`, and updates its row when it ends |
160
+ | `build-label-page` | skill | when you ask; stage 3 | nothing: it builds the labelling page, and exports the answers into the study's `data/` |
161
161
 
162
162
  The hooks are wired by `paperlint init` ([`install.md`](install.md)). A rule's level and options:
163
163
  [`rules.md`](rules.md).