plumbbob 0.8.14 → 0.8.18

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.
Files changed (85) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +6 -0
  3. package/dist/lib/agents.js +1 -1
  4. package/dist/lib/check.js +9 -6
  5. package/dist/verbs/finish.js +4 -2
  6. package/node_modules/checkride/CHANGELOG.md +234 -0
  7. package/node_modules/checkride/README.md +134 -31
  8. package/node_modules/checkride/dist/adapters.d.ts +50 -1
  9. package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
  10. package/node_modules/checkride/dist/adapters.js +123 -7
  11. package/node_modules/checkride/dist/adapters.js.map +1 -1
  12. package/node_modules/checkride/dist/agent-setup/hook.d.ts.map +1 -1
  13. package/node_modules/checkride/dist/agent-setup/hook.js +15 -1
  14. package/node_modules/checkride/dist/agent-setup/hook.js.map +1 -1
  15. package/node_modules/checkride/dist/agent-setup/index.d.ts +0 -1
  16. package/node_modules/checkride/dist/agent-setup/index.d.ts.map +1 -1
  17. package/node_modules/checkride/dist/agent-setup/index.js.map +1 -1
  18. package/node_modules/checkride/dist/baseline/fallow.d.ts +64 -0
  19. package/node_modules/checkride/dist/baseline/fallow.d.ts.map +1 -0
  20. package/node_modules/checkride/dist/baseline/fallow.js +256 -0
  21. package/node_modules/checkride/dist/baseline/fallow.js.map +1 -0
  22. package/node_modules/checkride/dist/baseline/fingerprint.d.ts +4 -6
  23. package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
  24. package/node_modules/checkride/dist/baseline/fingerprint.js +9 -4
  25. package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
  26. package/node_modules/checkride/dist/baseline/index.d.ts +4 -5
  27. package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
  28. package/node_modules/checkride/dist/baseline/index.js +2 -2
  29. package/node_modules/checkride/dist/baseline/index.js.map +1 -1
  30. package/node_modules/checkride/dist/{baseline/command.d.ts → baseline-command.d.ts} +11 -9
  31. package/node_modules/checkride/dist/baseline-command.d.ts.map +1 -0
  32. package/node_modules/checkride/dist/{baseline/command.js → baseline-command.js} +9 -7
  33. package/node_modules/checkride/dist/baseline-command.js.map +1 -0
  34. package/node_modules/checkride/dist/cli.d.ts.map +1 -1
  35. package/node_modules/checkride/dist/cli.js +95 -8
  36. package/node_modules/checkride/dist/cli.js.map +1 -1
  37. package/node_modules/checkride/dist/config.d.ts +37 -12
  38. package/node_modules/checkride/dist/config.d.ts.map +1 -1
  39. package/node_modules/checkride/dist/config.js +268 -60
  40. package/node_modules/checkride/dist/config.js.map +1 -1
  41. package/node_modules/checkride/dist/digest/digest.d.ts +0 -6
  42. package/node_modules/checkride/dist/digest/digest.d.ts.map +1 -1
  43. package/node_modules/checkride/dist/digest/digest.js +3 -3
  44. package/node_modules/checkride/dist/digest/digest.js.map +1 -1
  45. package/node_modules/checkride/dist/digest/index.d.ts +1 -2
  46. package/node_modules/checkride/dist/digest/index.d.ts.map +1 -1
  47. package/node_modules/checkride/dist/digest/index.js +1 -1
  48. package/node_modules/checkride/dist/digest/index.js.map +1 -1
  49. package/node_modules/checkride/dist/doctor.d.ts +14 -1
  50. package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
  51. package/node_modules/checkride/dist/doctor.js +124 -55
  52. package/node_modules/checkride/dist/doctor.js.map +1 -1
  53. package/node_modules/checkride/dist/init.d.ts +6 -0
  54. package/node_modules/checkride/dist/init.d.ts.map +1 -1
  55. package/node_modules/checkride/dist/init.js +347 -121
  56. package/node_modules/checkride/dist/init.js.map +1 -1
  57. package/node_modules/checkride/dist/links.d.ts.map +1 -1
  58. package/node_modules/checkride/dist/links.js +8 -9
  59. package/node_modules/checkride/dist/links.js.map +1 -1
  60. package/node_modules/checkride/dist/orchestrator.d.ts +56 -1
  61. package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
  62. package/node_modules/checkride/dist/orchestrator.js +506 -138
  63. package/node_modules/checkride/dist/orchestrator.js.map +1 -1
  64. package/node_modules/checkride/dist/pack.d.ts +76 -0
  65. package/node_modules/checkride/dist/pack.d.ts.map +1 -0
  66. package/node_modules/checkride/dist/pack.js +217 -0
  67. package/node_modules/checkride/dist/pack.js.map +1 -0
  68. package/node_modules/checkride/dist/pm/translate.d.ts +18 -10
  69. package/node_modules/checkride/dist/pm/translate.d.ts.map +1 -1
  70. package/node_modules/checkride/dist/pm/translate.js +27 -13
  71. package/node_modules/checkride/dist/pm/translate.js.map +1 -1
  72. package/node_modules/checkride/dist/smoke.d.ts +86 -0
  73. package/node_modules/checkride/dist/smoke.d.ts.map +1 -0
  74. package/node_modules/checkride/dist/smoke.js +346 -0
  75. package/node_modules/checkride/dist/smoke.js.map +1 -0
  76. package/node_modules/checkride/dist/snippets.d.ts +215 -0
  77. package/node_modules/checkride/dist/snippets.d.ts.map +1 -0
  78. package/node_modules/checkride/dist/snippets.js +360 -0
  79. package/node_modules/checkride/dist/snippets.js.map +1 -0
  80. package/node_modules/checkride/package.json +4 -2
  81. package/node_modules/checkride/schema/checkride.config.schema.json +11 -6
  82. package/package.json +4 -3
  83. package/skills/pb-finish/SKILL.md +15 -2
  84. package/node_modules/checkride/dist/baseline/command.d.ts.map +0 -1
  85. package/node_modules/checkride/dist/baseline/command.js.map +0 -1
@@ -0,0 +1,360 @@
1
+ /**
2
+ * Built-in `snippets` check.
3
+ *
4
+ * A doc snippet typecheck ported to match fascicle's `check-doc-snippets.mjs`
5
+ * byte-for-byte so the origin repo can adopt the slot verbatim (D11). It pulls
6
+ * fenced ```ts / ```typescript blocks out of `README.md` and the non-recursive
7
+ * `docs/*.md`, and typechecks only the ones explicitly opted in with an HTML
8
+ * comment on the line immediately above the fence:
9
+ *
10
+ * <!-- snippet: check -->
11
+ *
12
+ * Opt-in (rather than checking every fence) keeps the check honest: many doc
13
+ * fences are deliberately partial fragments that assert nothing about the API.
14
+ * A tagged snippet that fails to compile is documentation that lies — this
15
+ * turns that into a build failure. Opting the slot in but tagging *nothing* is
16
+ * a misconfiguration, not a vacuous pass, so zero tagged snippets is a hard
17
+ * error ({@link vacuousOptInError}; fascicle exits 2).
18
+ *
19
+ * The pure machinery — no filesystem, no spawning:
20
+ *
21
+ * - {@link extractSnippets} parses one doc's markdown into its ts/typescript
22
+ * fenced blocks, recording each block's start line and whether it is tagged;
23
+ * - {@link slugForDoc} / {@link snippetFileName} name the emitted scratch file
24
+ * for a block (`<slug>__<n>.ts`);
25
+ * - {@link planSnippets} folds a set of docs into the emission plan (the
26
+ * checked blocks to write, each mapped back to `<doc>:<line>`) plus the
27
+ * checked/skipped counts;
28
+ * - {@link generateSnippetTsconfig} builds the generated tsconfig (extends the
29
+ * repo's own, relaxes the three emit/style flags plus the composite-build
30
+ * fields a `tsc --build` project carries, embeds the caller's mode-specific
31
+ * `paths`).
32
+ *
33
+ * Execution (D12/Q1), consuming the primitives above:
34
+ *
35
+ * - {@link deriveSrcPaths} resolves **src mode**'s `paths` mapping: the repo's
36
+ * own tsconfig `paths` when present, else the `src/index.ts` convention,
37
+ * else null (the caller fails, recommending `snippets-dist`);
38
+ * - {@link checkSnippets} discovers the doc set, plans it, derives the mode's
39
+ * `paths`, emits the scratch files + generated tsconfig under
40
+ * `.check/doc-snippets/`, and typechecks them via a spawned
41
+ * `<pm> exec tsc --noEmit -p`, returning a {@link CheckOutcome} whose
42
+ * failure `stderr` carries tsc's raw output plus the `snippet -> source
43
+ * map:` legend.
44
+ */
45
+ import { existsSync } from 'node:fs';
46
+ import { mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises';
47
+ import { join } from 'node:path';
48
+ import { translateExec } from './pm/index.js';
49
+ /**
50
+ * The opt-in marker: an HTML comment `<!-- snippet: check -->` that must sit on
51
+ * the line immediately above a fence for its snippet to be typechecked. Exact
52
+ * fascicle regex (D11).
53
+ */
54
+ export const CHECK_MARKER_RE = /<!--\s*snippet:\s*check\s*-->/;
55
+ /** An opening fence — `ts` or `typescript` only, nothing else on the line (D11). */
56
+ export const FENCE_OPEN_RE = /^```(ts|typescript)\s*$/;
57
+ /** A closing fence: three backticks alone on the line. */
58
+ export const FENCE_CLOSE_RE = /^```\s*$/;
59
+ /**
60
+ * Select the doc files to check from a `docs/` directory listing (D11): always
61
+ * `README.md`, then every non-recursive `docs/*.md` entry, README first and the
62
+ * `docs/` entries in the order given. Pure — the caller does the (non-recursive)
63
+ * `readdir` and passes its entries; a plain entry list means a nested
64
+ * `docs/sub/x.md` never appears (its listing entry `sub` fails `.md`), matching
65
+ * fascicle's non-recursive `doc_files`.
66
+ */
67
+ export function selectDocFiles(docsEntries) {
68
+ const files = ['README.md'];
69
+ for (const name of docsEntries) {
70
+ if (name.endsWith('.md'))
71
+ files.push(`docs/${name}`);
72
+ }
73
+ return files;
74
+ }
75
+ /**
76
+ * Parse a doc's markdown into its `ts`/`typescript` fenced blocks (D11). A block
77
+ * is `checked` when a {@link CHECK_MARKER_RE} marker sits on the line immediately
78
+ * above its opening fence — a marker anywhere else does not count. Untagged
79
+ * blocks are still returned (the caller counts them as skipped). Semantics match
80
+ * fascicle's `extract_blocks` exactly.
81
+ */
82
+ export function extractSnippets(text) {
83
+ const lines = text.split('\n');
84
+ const blocks = [];
85
+ let i = 0;
86
+ while (i < lines.length) {
87
+ if (FENCE_OPEN_RE.test(lines[i] ?? '')) {
88
+ const startLine = i + 1;
89
+ const checked = i > 0 && CHECK_MARKER_RE.test(lines[i - 1] ?? '');
90
+ const body = [];
91
+ i += 1;
92
+ while (i < lines.length && !FENCE_CLOSE_RE.test(lines[i] ?? '')) {
93
+ body.push(lines[i] ?? '');
94
+ i += 1;
95
+ }
96
+ blocks.push({ code: body.join('\n'), startLine, checked });
97
+ }
98
+ i += 1;
99
+ }
100
+ return blocks;
101
+ }
102
+ /**
103
+ * The scratch-file slug for a doc's repo-relative path: every non-alphanumeric
104
+ * run collapses to a single `_`, with leading/trailing `_` trimmed
105
+ * (`README.md` → `README_md`, `docs/guide.md` → `docs_guide_md`). Exact fascicle
106
+ * algorithm. Distinct docs in the discovered namespace (`README.md` plus the
107
+ * flat `docs/*.md`) map to distinct slugs, so their emitted files never collide.
108
+ */
109
+ export function slugForDoc(relPath) {
110
+ return relPath.replace(/[^a-zA-Z0-9]+/g, '_').replace(/^_+|_+$/g, '');
111
+ }
112
+ /**
113
+ * The scratch-file name for a block: `<slug>__<n>.ts`, where `n` is the block's
114
+ * 1-based position among *all* ts/typescript fences in its doc (checked or not) —
115
+ * so a skipped fence still advances the number, matching fascicle.
116
+ */
117
+ export function snippetFileName(relPath, blockIndex) {
118
+ return `${slugForDoc(relPath)}__${blockIndex}.ts`;
119
+ }
120
+ /**
121
+ * Fold a set of docs into the emission plan: every checked block becomes a
122
+ * {@link SnippetPlanEntry} (named, mapped back to `<doc>:<line>`), untagged
123
+ * blocks bump `skipped`. The block index for naming counts all fences in a doc,
124
+ * so a checked block keeps its position even after skipped ones (fascicle
125
+ * parity). Pure — the caller reads the docs and writes the files.
126
+ */
127
+ export function planSnippets(docs) {
128
+ const entries = [];
129
+ let checked = 0;
130
+ let skipped = 0;
131
+ for (const doc of docs) {
132
+ extractSnippets(doc.text).forEach((block, index) => {
133
+ if (!block.checked) {
134
+ skipped += 1;
135
+ return;
136
+ }
137
+ entries.push({
138
+ name: snippetFileName(doc.relPath, index + 1),
139
+ file: doc.relPath,
140
+ line: block.startLine,
141
+ code: block.code,
142
+ });
143
+ checked += 1;
144
+ });
145
+ }
146
+ return { entries, checked, skipped };
147
+ }
148
+ /**
149
+ * The vacuous-opt-in guard (D11): a plan with no checked snippets is a hard
150
+ * error, not a vacuous green — opting the slot in obligates at least one tagged
151
+ * fence. Returns the error message for step 8 to surface (fascicle exits 2), or
152
+ * null when the plan has something to check.
153
+ */
154
+ export function vacuousOptInError(plan) {
155
+ if (plan.checked > 0)
156
+ return null;
157
+ return 'no tagged snippets found — refusing to pass vacuously (add a `<!-- snippet: check -->` marker above a ts/typescript fence)';
158
+ }
159
+ /**
160
+ * Build the generated tsconfig (D11): extend the repo's own config so the
161
+ * snippets inherit its strictness, then relax exactly the three flags that
162
+ * false-positive on illustrative snippets (`verbatimModuleSyntax`,
163
+ * `isolatedModules`, `noPropertyAccessFromIndexSignature`) while keeping the
164
+ * type-correctness flags that catch real API drift. `include` clears the parent's
165
+ * `.check` exclusion so the emitted `./*.ts` are seen; `paths` is the caller's
166
+ * mode-specific module resolution (src vs dist — Q1).
167
+ *
168
+ * A `tsc --build` project (composite, `rootDir`/`outDir` pinned to `src`/`dist`
169
+ * for project-reference emit) fails TS6059 on any *source* file outside its
170
+ * `rootDir` — and the generated snippets live under `.check/doc-snippets/`,
171
+ * while src mode's `paths` mapping pulls in a real source file elsewhere in the
172
+ * repo (dist mode's self-referenced `.d.ts` is exempt: a declaration file isn't
173
+ * subject to the same check). `rootDir: '../..'` re-scopes to `cwd` — the
174
+ * common ancestor of both — so either file satisfies it; `composite`/
175
+ * `declaration`/`declarationMap`/`incremental` are turned off since this is a
176
+ * one-shot typecheck, never a build. All four are no-ops on a plain
177
+ * (non-composite) project's tsconfig, so the override is harmless there too.
178
+ */
179
+ export function generateSnippetTsconfig(opts) {
180
+ return {
181
+ extends: opts.extendsPath,
182
+ compilerOptions: {
183
+ paths: opts.paths,
184
+ noEmit: true,
185
+ verbatimModuleSyntax: false,
186
+ isolatedModules: false,
187
+ noPropertyAccessFromIndexSignature: false,
188
+ rootDir: '../..',
189
+ composite: false,
190
+ declaration: false,
191
+ declarationMap: false,
192
+ incremental: false,
193
+ },
194
+ include: ['./*.ts'],
195
+ exclude: [],
196
+ };
197
+ }
198
+ /** Where the generated snippet files + tsconfig land, relative to `cwd`. */
199
+ const OUT_DIR_REL = join('.check', 'doc-snippets');
200
+ function isRecord(value) {
201
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
202
+ }
203
+ /**
204
+ * Read the doc set (D11): `README.md` plus non-recursive `docs/*.md`, skipping
205
+ * any that don't exist — a repo with no `docs/` directory (or no `README.md`)
206
+ * still runs on what it has; an empty result is caught by
207
+ * {@link vacuousOptInError}.
208
+ */
209
+ async function discoverDocs(cwd) {
210
+ let docsEntries = [];
211
+ try {
212
+ docsEntries = await readdir(join(cwd, 'docs'));
213
+ }
214
+ catch {
215
+ // No docs/ directory — README.md alone still gets checked.
216
+ }
217
+ const found = await Promise.all(selectDocFiles(docsEntries).map(async (relPath) => {
218
+ try {
219
+ return { relPath, text: await readFile(join(cwd, relPath), 'utf8') };
220
+ }
221
+ catch {
222
+ return null;
223
+ }
224
+ }));
225
+ return found.filter((d) => d !== null);
226
+ }
227
+ /** The package name from `package.json`, or null on any read/parse failure or a missing `name`. */
228
+ async function readManifestName(cwd) {
229
+ try {
230
+ const parsed = JSON.parse(await readFile(join(cwd, 'package.json'), 'utf8'));
231
+ const name = isRecord(parsed) ? parsed['name'] : undefined;
232
+ return typeof name === 'string' && name ? name : null;
233
+ }
234
+ catch {
235
+ return null;
236
+ }
237
+ }
238
+ /**
239
+ * The repo's own `tsconfig.json`'s **own** `compilerOptions.paths` (not resolved
240
+ * through its `extends` chain — a repo that hand-declares self-referencing
241
+ * paths, as fascicle does, puts them directly on the root file), or null when
242
+ * absent, unreadable, or empty.
243
+ */
244
+ async function readOwnTsconfigPaths(cwd) {
245
+ try {
246
+ const parsed = JSON.parse(await readFile(join(cwd, 'tsconfig.json'), 'utf8'));
247
+ const compilerOptions = isRecord(parsed) ? parsed['compilerOptions'] : undefined;
248
+ const paths = isRecord(compilerOptions) ? compilerOptions['paths'] : undefined;
249
+ if (!isRecord(paths))
250
+ return null;
251
+ const out = {};
252
+ for (const [key, value] of Object.entries(paths)) {
253
+ if (Array.isArray(value) && value.every((v) => typeof v === 'string'))
254
+ out[key] = value;
255
+ }
256
+ return Object.keys(out).length > 0 ? out : null;
257
+ }
258
+ catch {
259
+ return null;
260
+ }
261
+ }
262
+ /** Rewrite a tsconfig-root-relative target (`./src/x.ts` or `src/x.ts`) to resolve from `.check/doc-snippets/`. */
263
+ function remapRootRelative(target) {
264
+ const stripped = target.startsWith('./') ? target.slice(2) : target;
265
+ return `../../${stripped}`;
266
+ }
267
+ /**
268
+ * Derive the `paths` mapping **src mode** needs so `import { x } from
269
+ * '<pkg-name>'` resolves against source (Q1): the repo's own tsconfig `paths`
270
+ * when present (remapped two directories deeper, to where the generated
271
+ * tsconfig lives), else the `src/index.ts` convention (`<pkg-name>` maps to its
272
+ * root `src/index.ts`) when the manifest names the package and that file
273
+ * exists, else null — the caller turns a null into a failing outcome that
274
+ * recommends `snippets-dist` (whose dist-mode compilation needs no mapping at
275
+ * all: package self-reference through `exports` resolves the built types).
276
+ */
277
+ export function deriveSrcPaths(opts) {
278
+ if (opts.tsconfigPaths && Object.keys(opts.tsconfigPaths).length > 0) {
279
+ const remapped = {};
280
+ for (const [key, targets] of Object.entries(opts.tsconfigPaths)) {
281
+ remapped[key] = targets.map(remapRootRelative);
282
+ }
283
+ return remapped;
284
+ }
285
+ if (opts.manifestName !== null && opts.hasSrcIndex) {
286
+ return { [opts.manifestName]: ['../../src/index.ts'] };
287
+ }
288
+ return null;
289
+ }
290
+ function failOutcome(message) {
291
+ return {
292
+ ok: false,
293
+ exit_code: 1,
294
+ stdout: `${JSON.stringify({ ok: false, error: message }, null, 2)}\n`,
295
+ stderr: `check-snippets: ${message}\n`,
296
+ };
297
+ }
298
+ /** Compile the generated tsconfig via `<pm> exec tsc --noEmit -p <path>`, translated per PM. */
299
+ function runTsc(cwd, tsconfigRelPath, pm, spawn, timeoutSec) {
300
+ const { command, args } = translateExec('pnpm', ['exec', 'tsc', '--noEmit', '-p', tsconfigRelPath], pm);
301
+ return spawn(command, args, cwd, timeoutSec);
302
+ }
303
+ /**
304
+ * Run the `snippets` check against `cwd` in `mode` (D12: the `snippets` src
305
+ * adapter passes `'src'`, `snippets-dist` passes `'dist'`). Discovers the doc
306
+ * set, plans the checked snippets ({@link vacuousOptInError} applies first —
307
+ * zero tagged snippets is a hard error before anything is written), derives
308
+ * the mode's `paths` mapping, emits the scratch files plus a generated
309
+ * tsconfig under `.check/doc-snippets/`, and typechecks them through the
310
+ * injected `spawn`. A compile failure's `stderr` carries tsc's raw output plus
311
+ * the `snippet -> source map:` legend mapping each generated file back to
312
+ * `<doc>:<line>` (D11).
313
+ */
314
+ export async function checkSnippets(opts) {
315
+ const { cwd, mode, pm, spawn, timeoutSec } = opts;
316
+ const plan = planSnippets(await discoverDocs(cwd));
317
+ const vacuous = vacuousOptInError(plan);
318
+ if (vacuous)
319
+ return failOutcome(vacuous);
320
+ let paths = {};
321
+ if (mode === 'src') {
322
+ const [manifestName, tsconfigPaths] = await Promise.all([readManifestName(cwd), readOwnTsconfigPaths(cwd)]);
323
+ const derived = deriveSrcPaths({
324
+ manifestName,
325
+ tsconfigPaths,
326
+ hasSrcIndex: existsSync(join(cwd, 'src', 'index.ts')),
327
+ });
328
+ if (derived === null) {
329
+ return failOutcome("cannot map snippet imports to source: tsconfig.json has no self-referencing 'paths' and there is no " +
330
+ "src/index.ts — add a 'paths' entry for the package name, or opt into the 'snippets-dist' adapter " +
331
+ 'instead (dist mode needs no mapping)');
332
+ }
333
+ paths = derived;
334
+ }
335
+ const outDir = join(cwd, OUT_DIR_REL);
336
+ await rm(outDir, { recursive: true, force: true });
337
+ await mkdir(outDir, { recursive: true });
338
+ await Promise.all(plan.entries.map((e) => writeFile(join(outDir, e.name), `${e.code}\n`, 'utf8')));
339
+ const tsconfig = generateSnippetTsconfig({ extendsPath: '../../tsconfig.json', paths });
340
+ await writeFile(join(outDir, 'tsconfig.json'), `${JSON.stringify(tsconfig, null, 2)}\n`, 'utf8');
341
+ const outcome = await runTsc(cwd, join(OUT_DIR_REL, 'tsconfig.json'), pm, spawn, timeoutSec);
342
+ const status = { checked: plan.checked, skipped: plan.skipped, mode };
343
+ if (outcome.ok) {
344
+ return {
345
+ ok: true,
346
+ exit_code: 0,
347
+ stdout: `${JSON.stringify({ ok: true, ...status }, null, 2)}\n`,
348
+ stderr: `check-snippets: ${plan.checked} snippet(s) compile against ${mode} (${plan.skipped} skipped)\n`,
349
+ };
350
+ }
351
+ const legend = plan.entries.map((e) => ` ${e.name} <- ${e.file}:${e.line}`).join('\n');
352
+ const tscOutput = `${outcome.stdout}${outcome.stderr}`.trim();
353
+ return {
354
+ ok: false,
355
+ exit_code: 1,
356
+ stdout: `${JSON.stringify({ ok: false, ...status }, null, 2)}\n`,
357
+ stderr: `check-snippets: snippet(s) failed to compile:\n\n${tscOutput}\n\nsnippet -> source map:\n${legend}\n`,
358
+ };
359
+ }
360
+ //# sourceMappingURL=snippets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snippets.js","sourceRoot":"","sources":["../src/snippets.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "checkride",
3
- "version": "0.3.0",
3
+ "version": "0.5.2",
4
4
  "description": "An agent harness for TypeScript repositories: one command that is the definition of done, plus mechanically enforced module boundaries.",
5
5
  "license": "MIT",
6
6
  "author": "Rob McLarty",
@@ -62,6 +62,7 @@
62
62
  "prepublishOnly": "tsc --build && vitest run"
63
63
  },
64
64
  "devDependencies": {
65
+ "@arethetypeswrong/cli": "0.18.4",
65
66
  "@ast-grep/cli": "0.42.1",
66
67
  "@stryker-mutator/core": "9.6.1",
67
68
  "@stryker-mutator/vitest-runner": "9.6.1",
@@ -69,10 +70,11 @@
69
70
  "@vitest/coverage-v8": "4.1.5",
70
71
  "ajv": "8.20.0",
71
72
  "cspell": "10.0.0",
72
- "fallow": "2.48.0",
73
+ "fallow": "3.5.0",
73
74
  "markdownlint-cli2": "0.22.1",
74
75
  "oxlint": "1.61.0",
75
76
  "oxlint-tsgolint": "0.21.1",
77
+ "publint": "0.3.21",
76
78
  "typescript": "6.0.3",
77
79
  "vitest": "4.1.5"
78
80
  }
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "checks": {
26
26
  "type": "object",
27
- "description": "Per-slot configuration. A key that matches a catalogue slot (types, format, lint, struct, dead, test, docs, links, spell, mutation, security, publint, attw) configures that slot; any other key is a custom check that runs alongside the catalogue. Naming an opt-in slot (format, mutation, security, publint, attw) here also opts it into the default run.",
27
+ "description": "Per-slot configuration. A key that matches a catalogue slot (types, format, lint, struct, dead, dupes, health, test, docs, links, spell, mutation, security, publint, attw) configures that slot; any other key is a custom check that runs alongside the catalogue. Naming an opt-in slot (format, dupes, health, mutation, security, publint, attw) here also opts it into the default run.",
28
28
  "additionalProperties": {
29
29
  "$ref": "#/definitions/slotConfig"
30
30
  }
@@ -62,9 +62,17 @@
62
62
  "outputFile": { "type": ["string", "null"] },
63
63
  "changedArgs": { "type": "array", "items": { "type": "string" } },
64
64
  "fixArgs": { "type": "array", "items": { "type": "string" } },
65
- "timeout": { "type": "number", "minimum": 0 }
65
+ "timeout": { "type": "number", "minimum": 0 },
66
+ "order": { "$ref": "#/definitions/order" }
66
67
  }
67
68
  },
69
+ "order": {
70
+ "description": "Scheduling order. A number places the check on the ascending wave line (equal numbers run together as one wave; decimals sequence steps within a wave); the keywords place it relative to that line — first/last before/after everything, single exclusively on its own, middle/any unordered in the main group.",
71
+ "oneOf": [
72
+ { "enum": ["first", "last", "middle", "single", "any"] },
73
+ { "type": "number" }
74
+ ]
75
+ },
68
76
  "customCheck": {
69
77
  "type": "object",
70
78
  "description": "A bare command that needs no adapter.",
@@ -87,10 +95,7 @@
87
95
  "items": { "type": "string" },
88
96
  "description": "Marker files that gate a non-catalogue custom check: it runs only when at least one listed file exists, and is skipped otherwise. Ignored on an entry that fills a catalogue slot."
89
97
  },
90
- "order": {
91
- "enum": ["first", "last"],
92
- "description": "Where a non-catalogue custom check runs relative to the catalogue. Default 'last'."
93
- }
98
+ "order": { "$ref": "#/definitions/order" }
94
99
  }
95
100
  }
96
101
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plumbbob",
3
- "version": "0.8.14",
3
+ "version": "0.8.18",
4
4
  "description": "Attention-first build process: 12 skills + a CLI that keep you the decider — guidance on the work, a latch on the record.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -10,7 +10,7 @@
10
10
  "agent",
11
11
  "attention-first"
12
12
  ],
13
- "homepage": "https://github.com/robmclarty/plumbbob#readme",
13
+ "homepage": "https://robmclarty.github.io/plumbbob/",
14
14
  "bugs": "https://github.com/robmclarty/plumbbob/issues",
15
15
  "repository": {
16
16
  "type": "git",
@@ -46,6 +46,7 @@
46
46
  },
47
47
  "devDependencies": {
48
48
  "@ast-grep/cli": "0.43.0",
49
+ "@stryker-mutator/api": "9.6.1",
49
50
  "@stryker-mutator/core": "9.6.1",
50
51
  "@stryker-mutator/vitest-runner": "9.6.1",
51
52
  "@types/node": "24.13.1",
@@ -58,7 +59,7 @@
58
59
  "vitest": "4.1.9"
59
60
  },
60
61
  "dependencies": {
61
- "checkride": "0.3.0"
62
+ "checkride": "0.5.2"
62
63
  },
63
64
  "bundleDependencies": [
64
65
  "checkride"
@@ -30,11 +30,24 @@ record.
30
30
  This is the "yeah, I did that" artifact. Write it by default; the human may edit it.
31
31
  2. **Finish.** Run `plumbbob finish`, which appends the checkpoint SHAs to the
32
32
  report and makes the final commit — subject `chore(<scope>): finish`, its body
33
- led by a `plumbbob finish` marker plus an optional proportional `--body` (the
34
- stdin heredoc) — then
33
+ led by a `plumbbob finish` marker plus an optional proportional `--body` — then
35
34
  clears the control state (the in-flight markers, then `STATE` last — one delete
36
35
  drops both the session sentinel and the active-build cursor it carries). The
37
36
  build folder stays in place, committed, and rides the branch into the PR.
37
+
38
+ `--body` reads the close-out message from **stdin via a single-quoted heredoc
39
+ redirect** — the same form the other verbs use:
40
+
41
+ ```
42
+ plumbbob finish --body <<'BODY'
43
+ …proportional close-out prose…
44
+ BODY
45
+ ```
46
+
47
+ Redirect the heredoc *into* the command (`--body <<'BODY'`). Do **not** pass it as
48
+ an argument value (`--body "$(cat <<'BODY'…)"`): `--body` ignores an argument and
49
+ reads stdin, so that form leaves stdin open with no EOF and the command hangs. Omit
50
+ `--body` entirely and the commit carries the `plumbbob finish` marker subject only.
38
51
  3. **Point at the next goal** — `/pb-plan` to frame the next one.
39
52
 
40
53
  ## The hard contracts
@@ -1 +0,0 @@
1
- {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/baseline/command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,GAAG,EAAc,MAAM,oBAAoB,CAAC;AAEvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC9B,MAAM,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,sEAAsE;IACtE,EAAE,CAAC,EAAE,cAAc,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEnF,gFAAgF;AAChF,wBAAsB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAsCnF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/baseline/command.ts"],"names":[],"mappings":"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"}