plumbbob 0.6.6 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +2 -2
- package/README.md +24 -7
- package/dist/cli-core.js +7 -2
- package/dist/lib/buildlog.js +5 -2
- package/dist/lib/check.js +33 -1
- package/dist/lib/git.js +17 -0
- package/dist/lib/latch.js +97 -0
- package/dist/lib/orient.js +24 -0
- package/dist/lib/sidecar.js +116 -0
- package/dist/verbs/build.js +5 -1
- package/dist/verbs/checkpoint.js +51 -2
- package/dist/verbs/doctor.js +33 -2
- package/dist/verbs/finish.js +57 -1
- package/dist/verbs/revert.js +19 -1
- package/dist/verbs/start.js +22 -2
- package/dist/verbs/status.js +10 -3
- package/dist/verbs/turn.js +106 -0
- package/hooks/hooks.json +15 -0
- package/hooks/pre-bash-commit.sh +69 -0
- package/node_modules/checkride/CHANGELOG.md +111 -0
- package/node_modules/checkride/README.md +185 -20
- package/node_modules/checkride/dist/adapters.d.ts +9 -4
- package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
- package/node_modules/checkride/dist/adapters.js +62 -2
- package/node_modules/checkride/dist/adapters.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/hook.d.ts +63 -0
- package/node_modules/checkride/dist/agent-setup/hook.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/hook.js +92 -0
- package/node_modules/checkride/dist/agent-setup/hook.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/index.d.ts +8 -0
- package/node_modules/checkride/dist/agent-setup/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/index.js +7 -0
- package/node_modules/checkride/dist/agent-setup/index.js.map +1 -0
- package/node_modules/checkride/dist/atomic.d.ts +13 -0
- package/node_modules/checkride/dist/atomic.d.ts.map +1 -0
- package/node_modules/checkride/dist/atomic.js +24 -0
- package/node_modules/checkride/dist/atomic.js.map +1 -0
- package/node_modules/checkride/dist/baseline/command.d.ts +42 -0
- package/node_modules/checkride/dist/baseline/command.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/command.js +58 -0
- package/node_modules/checkride/dist/baseline/command.js.map +1 -0
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts +41 -0
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/fingerprint.js +119 -0
- package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -0
- package/node_modules/checkride/dist/baseline/index.d.ts +13 -0
- package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/index.js +10 -0
- package/node_modules/checkride/dist/baseline/index.js.map +1 -0
- package/node_modules/checkride/dist/baseline/store.d.ts +67 -0
- package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/store.js +110 -0
- package/node_modules/checkride/dist/baseline/store.js.map +1 -0
- package/node_modules/checkride/dist/cli.d.ts.map +1 -1
- package/node_modules/checkride/dist/cli.js +37 -3
- package/node_modules/checkride/dist/cli.js.map +1 -1
- package/node_modules/checkride/dist/config.d.ts +43 -3
- package/node_modules/checkride/dist/config.d.ts.map +1 -1
- package/node_modules/checkride/dist/config.js +95 -22
- package/node_modules/checkride/dist/config.js.map +1 -1
- package/node_modules/checkride/dist/digest/digest.d.ts +52 -0
- package/node_modules/checkride/dist/digest/digest.d.ts.map +1 -0
- package/node_modules/checkride/dist/digest/digest.js +147 -0
- package/node_modules/checkride/dist/digest/digest.js.map +1 -0
- package/node_modules/checkride/dist/digest/index.d.ts +8 -0
- package/node_modules/checkride/dist/digest/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/digest/index.js +7 -0
- package/node_modules/checkride/dist/digest/index.js.map +1 -0
- package/node_modules/checkride/dist/doctor.d.ts +4 -0
- package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
- package/node_modules/checkride/dist/doctor.js +33 -10
- package/node_modules/checkride/dist/doctor.js.map +1 -1
- package/node_modules/checkride/dist/index.d.ts +1 -1
- package/node_modules/checkride/dist/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/index.js +1 -1
- package/node_modules/checkride/dist/index.js.map +1 -1
- package/node_modules/checkride/dist/init.d.ts +38 -0
- package/node_modules/checkride/dist/init.d.ts.map +1 -1
- package/node_modules/checkride/dist/init.js +97 -8
- package/node_modules/checkride/dist/init.js.map +1 -1
- package/node_modules/checkride/dist/orchestrator.d.ts +58 -1
- package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
- package/node_modules/checkride/dist/orchestrator.js +155 -27
- package/node_modules/checkride/dist/orchestrator.js.map +1 -1
- package/node_modules/checkride/dist/pm/detect.d.ts +21 -0
- package/node_modules/checkride/dist/pm/detect.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/detect.js +60 -0
- package/node_modules/checkride/dist/pm/detect.js.map +1 -0
- package/node_modules/checkride/dist/pm/index.d.ts +10 -0
- package/node_modules/checkride/dist/pm/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/index.js +9 -0
- package/node_modules/checkride/dist/pm/index.js.map +1 -0
- package/node_modules/checkride/dist/pm/translate.d.ts +28 -0
- package/node_modules/checkride/dist/pm/translate.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/translate.js +40 -0
- package/node_modules/checkride/dist/pm/translate.js.map +1 -0
- package/node_modules/checkride/package.json +20 -17
- package/node_modules/checkride/schema/checkride.config.schema.json +97 -0
- package/node_modules/checkride/schema/checkride.summary.schema.json +57 -0
- package/node_modules/checkride/templates/shared/cspell.json +1 -0
- package/node_modules/checkride/templates/shared/prettierrc.json +6 -0
- package/package.json +8 -4
- package/skills/pb-build/SKILL.md +14 -0
- package/skills/pb-plan/SKILL.md +17 -0
- package/skills/pb-verify/SKILL.md +18 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-manager detection.
|
|
3
|
+
*
|
|
4
|
+
* Resolves which package manager a repo uses so the orchestrator can translate
|
|
5
|
+
* each adapter's canonical `pnpm exec <tool>` invocation into that PM's form.
|
|
6
|
+
* Resolution order (D5): the declared `packageManager` field wins (it is the
|
|
7
|
+
* author's explicit intent), then a lockfile, then a `pnpm` default. Every fs
|
|
8
|
+
* touch is injectable so the logic is unit-testable without a real repo.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
const KNOWN = new Set(['pnpm', 'npm', 'yarn', 'bun']);
|
|
13
|
+
/** Narrow a raw name to a known package manager. */
|
|
14
|
+
function isPackageManager(name) {
|
|
15
|
+
return KNOWN.has(name);
|
|
16
|
+
}
|
|
17
|
+
/** Lockfile → package manager, in resolution priority when several coexist. */
|
|
18
|
+
const LOCKFILES = [
|
|
19
|
+
{ file: 'pnpm-lock.yaml', pm: 'pnpm' },
|
|
20
|
+
{ file: 'bun.lock', pm: 'bun' },
|
|
21
|
+
{ file: 'bun.lockb', pm: 'bun' },
|
|
22
|
+
{ file: 'yarn.lock', pm: 'yarn' },
|
|
23
|
+
{ file: 'package-lock.json', pm: 'npm' },
|
|
24
|
+
];
|
|
25
|
+
/** Parse the `packageManager` field value (`"yarn@3.6.1"` → `'yarn'`), if known. */
|
|
26
|
+
function parseField(value) {
|
|
27
|
+
if (!value)
|
|
28
|
+
return null;
|
|
29
|
+
const name = value.split('@')[0]?.trim() ?? '';
|
|
30
|
+
return isPackageManager(name) ? name : null;
|
|
31
|
+
}
|
|
32
|
+
/** Read the `packageManager` field from `cwd`'s package.json, or `undefined`. */
|
|
33
|
+
function readPackageManagerField(cwd) {
|
|
34
|
+
try {
|
|
35
|
+
const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8'));
|
|
36
|
+
return pkg.packageManager;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the package manager for `cwd`: the declared `packageManager` field,
|
|
44
|
+
* else the first matching lockfile, else `pnpm`. `fileExists` and
|
|
45
|
+
* `packageManagerField` are injectable for tests.
|
|
46
|
+
*/
|
|
47
|
+
export function detectPackageManager(input = {}) {
|
|
48
|
+
const cwd = input.cwd ?? process.cwd();
|
|
49
|
+
const fileExists = input.fileExists ?? ((file) => existsSync(join(cwd, file)));
|
|
50
|
+
const readField = input.packageManagerField ?? (() => readPackageManagerField(cwd));
|
|
51
|
+
const declared = parseField(readField());
|
|
52
|
+
if (declared)
|
|
53
|
+
return declared;
|
|
54
|
+
for (const { file, pm } of LOCKFILES) {
|
|
55
|
+
if (fileExists(file))
|
|
56
|
+
return pm;
|
|
57
|
+
}
|
|
58
|
+
return 'pnpm';
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/pm/detect.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,MAAM,KAAK,GAAwB,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AAE3E,oDAAoD;AACpD,SAAS,gBAAgB,CAAC,IAAY;IACpC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AAED,+EAA+E;AAC/E,MAAM,SAAS,GAAoD;IACjE,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,MAAM,EAAE;IACtC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE;IAC/B,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,KAAK,EAAE;IAChC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE;IACjC,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE,EAAE,KAAK,EAAE;CACzC,CAAC;AAEF,oFAAoF;AACpF,SAAS,UAAU,CAAC,KAAyB;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC/C,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9C,CAAC;AAED,iFAAiF;AACjF,SAAS,uBAAuB,CAAC,GAAW;IAC1C,IAAI,CAAC;QACH,MAAM,GAAG,GAAgC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACrG,OAAO,GAAG,CAAC,cAAc,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAII,EAAE;IAEN,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACvC,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/E,MAAM,SAAS,GAAG,KAAK,CAAC,mBAAmB,IAAI,CAAC,GAAG,EAAE,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC,CAAC;IAEpF,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC;IACzC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,KAAK,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,SAAS,EAAE,CAAC;QACrC,IAAI,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;IAClC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-manager module — the public surface for detecting a repo's package
|
|
3
|
+
* manager and translating each adapter's canonical `pnpm exec <tool>` prefix
|
|
4
|
+
* into that PM's form. Detection lives in `detect.ts`, translation in
|
|
5
|
+
* `translate.ts`; siblings import only from here.
|
|
6
|
+
*/
|
|
7
|
+
export type { PackageManager } from './detect.js';
|
|
8
|
+
export { detectPackageManager } from './detect.js';
|
|
9
|
+
export { isAvailableUnder, translateExec } from './translate.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pm/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-manager module — the public surface for detecting a repo's package
|
|
3
|
+
* manager and translating each adapter's canonical `pnpm exec <tool>` prefix
|
|
4
|
+
* into that PM's form. Detection lives in `detect.ts`, translation in
|
|
5
|
+
* `translate.ts`; siblings import only from here.
|
|
6
|
+
*/
|
|
7
|
+
export { detectPackageManager } from './detect.js';
|
|
8
|
+
export { isAvailableUnder, translateExec } from './translate.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pm/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exec-prefix translation.
|
|
3
|
+
*
|
|
4
|
+
* The adapter registry keeps its canonical `pnpm exec <tool>` form (D5); this
|
|
5
|
+
* is the single seam that rewrites that prefix for whichever package manager a
|
|
6
|
+
* repo actually uses. Only the `pnpm exec` prefix is translated — `pnpm audit`,
|
|
7
|
+
* custom-check commands, and built-ins pass through untouched, so the default
|
|
8
|
+
* pnpm run is byte-identical to before.
|
|
9
|
+
*/
|
|
10
|
+
import type { PackageManager } from './detect.js';
|
|
11
|
+
/**
|
|
12
|
+
* Rewrite a canonical `pnpm exec <tool> …` invocation for `pm`. Anything that
|
|
13
|
+
* is not a `pnpm exec` prefix (a `pnpm audit`, a custom check's own command, a
|
|
14
|
+
* built-in) is returned unchanged, and so is every invocation under `pnpm`
|
|
15
|
+
* itself — the default stays exactly as it was.
|
|
16
|
+
*/
|
|
17
|
+
export declare function translateExec(command: string, args: readonly string[], pm: PackageManager): {
|
|
18
|
+
command: string;
|
|
19
|
+
args: string[];
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Can this adapter run under `pm`? Audit is package-manager-specific — its
|
|
23
|
+
* flags and JSON shape don't port — so `pnpm audit` (the `security` slot) is
|
|
24
|
+
* unavailable on a non-pnpm PM until a per-PM audit adapter lands (b5).
|
|
25
|
+
* Everything else is PM-agnostic once its exec prefix is translated.
|
|
26
|
+
*/
|
|
27
|
+
export declare function isAvailableUnder(command: string, args: readonly string[], pm: PackageManager): boolean;
|
|
28
|
+
//# sourceMappingURL=translate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../src/pm/translate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AASlD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,cAAc,GACjB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAKrC;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAGtG"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exec-prefix translation.
|
|
3
|
+
*
|
|
4
|
+
* The adapter registry keeps its canonical `pnpm exec <tool>` form (D5); this
|
|
5
|
+
* is the single seam that rewrites that prefix for whichever package manager a
|
|
6
|
+
* repo actually uses. Only the `pnpm exec` prefix is translated — `pnpm audit`,
|
|
7
|
+
* custom-check commands, and built-ins pass through untouched, so the default
|
|
8
|
+
* pnpm run is byte-identical to before.
|
|
9
|
+
*/
|
|
10
|
+
/** The exec command each non-pnpm PM uses in place of `pnpm exec`. */
|
|
11
|
+
const EXEC_COMMAND = {
|
|
12
|
+
npm: 'npx',
|
|
13
|
+
yarn: 'yarn',
|
|
14
|
+
bun: 'bunx',
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Rewrite a canonical `pnpm exec <tool> …` invocation for `pm`. Anything that
|
|
18
|
+
* is not a `pnpm exec` prefix (a `pnpm audit`, a custom check's own command, a
|
|
19
|
+
* built-in) is returned unchanged, and so is every invocation under `pnpm`
|
|
20
|
+
* itself — the default stays exactly as it was.
|
|
21
|
+
*/
|
|
22
|
+
export function translateExec(command, args, pm) {
|
|
23
|
+
const isExec = command === 'pnpm' && args[0] === 'exec';
|
|
24
|
+
if (!isExec || pm === 'pnpm')
|
|
25
|
+
return { command, args: [...args] };
|
|
26
|
+
// Drop 'exec'; keep <tool> and its arguments, e.g. `npx oxlint --type-aware`.
|
|
27
|
+
return { command: EXEC_COMMAND[pm], args: args.slice(1) };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Can this adapter run under `pm`? Audit is package-manager-specific — its
|
|
31
|
+
* flags and JSON shape don't port — so `pnpm audit` (the `security` slot) is
|
|
32
|
+
* unavailable on a non-pnpm PM until a per-PM audit adapter lands (b5).
|
|
33
|
+
* Everything else is PM-agnostic once its exec prefix is translated.
|
|
34
|
+
*/
|
|
35
|
+
export function isAvailableUnder(command, args, pm) {
|
|
36
|
+
if (command === 'pnpm' && args[0] === 'audit')
|
|
37
|
+
return pm === 'pnpm';
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=translate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../src/pm/translate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,sEAAsE;AACtE,MAAM,YAAY,GAAoD;IACpE,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,MAAM;CACZ,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAe,EACf,IAAuB,EACvB,EAAkB;IAElB,MAAM,MAAM,GAAG,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;IACxD,IAAI,CAAC,MAAM,IAAI,EAAE,KAAK,MAAM;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;IAClE,8EAA8E;IAC9E,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe,EAAE,IAAuB,EAAE,EAAkB;IAC3F,IAAI,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO;QAAE,OAAO,EAAE,KAAK,MAAM,CAAC;IACpE,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "checkride",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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",
|
|
@@ -39,24 +39,11 @@
|
|
|
39
39
|
"files": [
|
|
40
40
|
"dist",
|
|
41
41
|
"templates",
|
|
42
|
+
"schema",
|
|
42
43
|
"README.md",
|
|
43
44
|
"CHANGELOG.md",
|
|
44
45
|
"LICENSE"
|
|
45
46
|
],
|
|
46
|
-
"devDependencies": {
|
|
47
|
-
"@ast-grep/cli": "0.42.1",
|
|
48
|
-
"@stryker-mutator/core": "9.6.1",
|
|
49
|
-
"@stryker-mutator/vitest-runner": "9.6.1",
|
|
50
|
-
"@types/node": "22.20.0",
|
|
51
|
-
"@vitest/coverage-v8": "4.1.5",
|
|
52
|
-
"cspell": "10.0.0",
|
|
53
|
-
"fallow": "2.48.0",
|
|
54
|
-
"markdownlint-cli2": "0.22.1",
|
|
55
|
-
"oxlint": "1.61.0",
|
|
56
|
-
"oxlint-tsgolint": "0.21.1",
|
|
57
|
-
"typescript": "6.0.3",
|
|
58
|
-
"vitest": "4.1.5"
|
|
59
|
-
},
|
|
60
47
|
"scripts": {
|
|
61
48
|
"check": "tsc --build && node dist/cli.js",
|
|
62
49
|
"check:all": "tsc --build && node dist/cli.js --all",
|
|
@@ -71,6 +58,22 @@
|
|
|
71
58
|
"mutation": "stryker run",
|
|
72
59
|
"build": "tsc --build",
|
|
73
60
|
"build:clean": "tsc --build --clean",
|
|
74
|
-
"types": "tsc --build"
|
|
61
|
+
"types": "tsc --build",
|
|
62
|
+
"prepublishOnly": "tsc --build && vitest run"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@ast-grep/cli": "0.42.1",
|
|
66
|
+
"@stryker-mutator/core": "9.6.1",
|
|
67
|
+
"@stryker-mutator/vitest-runner": "9.6.1",
|
|
68
|
+
"@types/node": "22.20.0",
|
|
69
|
+
"@vitest/coverage-v8": "4.1.5",
|
|
70
|
+
"ajv": "8.20.0",
|
|
71
|
+
"cspell": "10.0.0",
|
|
72
|
+
"fallow": "2.48.0",
|
|
73
|
+
"markdownlint-cli2": "0.22.1",
|
|
74
|
+
"oxlint": "1.61.0",
|
|
75
|
+
"oxlint-tsgolint": "0.21.1",
|
|
76
|
+
"typescript": "6.0.3",
|
|
77
|
+
"vitest": "4.1.5"
|
|
75
78
|
}
|
|
76
|
-
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/robmclarty/checkride/main/schema/checkride.config.schema.json",
|
|
4
|
+
"title": "checkride configuration",
|
|
5
|
+
"description": "Schema for checkride.config.json — add it only to deviate from the zero-config defaults.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"$schema": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "URL of this JSON Schema, for editor validation and autocompletion."
|
|
12
|
+
},
|
|
13
|
+
"extends": {
|
|
14
|
+
"description": "Preset(s) to inherit — a file path (./base.json) or package specifier (@acme/preset), or an array of them. Bases merge left-to-right and the local config wins over all; objects deep-merge, arrays and scalars replace (arrays are not concatenated).",
|
|
15
|
+
"oneOf": [
|
|
16
|
+
{ "type": "string" },
|
|
17
|
+
{ "type": "array", "items": { "type": "string" } }
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"timeout": {
|
|
21
|
+
"type": "number",
|
|
22
|
+
"minimum": 0,
|
|
23
|
+
"description": "Default per-check timeout in seconds. Unset, checks fall back to the built-in 600s cap; 0 here or on a check disables the cap."
|
|
24
|
+
},
|
|
25
|
+
"checks": {
|
|
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.",
|
|
28
|
+
"additionalProperties": {
|
|
29
|
+
"$ref": "#/definitions/slotConfig"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"definitions": {
|
|
34
|
+
"slotConfig": {
|
|
35
|
+
"description": "How to fill a slot: an adapter name, false to disable it, an adapter with overrides, or a custom check.",
|
|
36
|
+
"oneOf": [
|
|
37
|
+
{
|
|
38
|
+
"type": "string",
|
|
39
|
+
"description": "Adapter name that fills this slot, beating detection."
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"const": false,
|
|
43
|
+
"description": "Disable this slot (skipped, not failed)."
|
|
44
|
+
},
|
|
45
|
+
{ "$ref": "#/definitions/useConfig" },
|
|
46
|
+
{ "$ref": "#/definitions/customCheck" }
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"useConfig": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"description": "Pick an adapter by name and override individual fields.",
|
|
52
|
+
"required": ["use"],
|
|
53
|
+
"additionalProperties": false,
|
|
54
|
+
"properties": {
|
|
55
|
+
"use": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"description": "Adapter name to select from the registry."
|
|
58
|
+
},
|
|
59
|
+
"command": { "type": "string" },
|
|
60
|
+
"args": { "type": "array", "items": { "type": "string" } },
|
|
61
|
+
"description": { "type": "string" },
|
|
62
|
+
"outputFile": { "type": ["string", "null"] },
|
|
63
|
+
"changedArgs": { "type": "array", "items": { "type": "string" } },
|
|
64
|
+
"fixArgs": { "type": "array", "items": { "type": "string" } },
|
|
65
|
+
"timeout": { "type": "number", "minimum": 0 }
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"customCheck": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"description": "A bare command that needs no adapter.",
|
|
71
|
+
"required": ["command"],
|
|
72
|
+
"additionalProperties": false,
|
|
73
|
+
"properties": {
|
|
74
|
+
"command": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"description": "Executable to spawn."
|
|
77
|
+
},
|
|
78
|
+
"args": { "type": "array", "items": { "type": "string" } },
|
|
79
|
+
"description": { "type": "string" },
|
|
80
|
+
"outputFile": { "type": ["string", "null"] },
|
|
81
|
+
"changedArgs": { "type": "array", "items": { "type": "string" } },
|
|
82
|
+
"fixArgs": { "type": "array", "items": { "type": "string" } },
|
|
83
|
+
"timeout": { "type": "number", "minimum": 0 },
|
|
84
|
+
"name": { "type": "string" },
|
|
85
|
+
"detect": {
|
|
86
|
+
"type": "array",
|
|
87
|
+
"items": { "type": "string" },
|
|
88
|
+
"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
|
+
},
|
|
90
|
+
"order": {
|
|
91
|
+
"enum": ["first", "last"],
|
|
92
|
+
"description": "Where a non-catalogue custom check runs relative to the catalogue. Default 'last'."
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/robmclarty/checkride/main/schema/checkride.summary.schema.json",
|
|
4
|
+
"title": "checkride run summary",
|
|
5
|
+
"description": "Schema for .check/summary.json — the aggregate report every run writes. A public API for agents and gate consumers. Fields are additive under schema_version 1: they are added (in lockstep with this schema), never renamed, removed, or retyped without a version bump. See docs/contract.md.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["schema_version", "timestamp", "ok", "checks_run", "total_duration_ms", "checks"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": {
|
|
11
|
+
"const": 1,
|
|
12
|
+
"description": "Version of this report shape. Bumped only for a breaking (non-additive) change."
|
|
13
|
+
},
|
|
14
|
+
"timestamp": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "ISO 8601 time the report was written."
|
|
17
|
+
},
|
|
18
|
+
"ok": {
|
|
19
|
+
"type": "boolean",
|
|
20
|
+
"description": "True when every check passed (skipped checks count as ok). Can be vacuous — see checks_run."
|
|
21
|
+
},
|
|
22
|
+
"checks_run": {
|
|
23
|
+
"type": "integer",
|
|
24
|
+
"minimum": 0,
|
|
25
|
+
"description": "Count of checks that actually executed (skipped entries excluded). ok=true with checks_run=0 is a vacuous green: nothing was verified. Gate consumers should treat it as a failure (or run with --strict, which exits 2)."
|
|
26
|
+
},
|
|
27
|
+
"total_duration_ms": {
|
|
28
|
+
"type": "number",
|
|
29
|
+
"minimum": 0,
|
|
30
|
+
"description": "Sum of all check durations in milliseconds."
|
|
31
|
+
},
|
|
32
|
+
"checks": {
|
|
33
|
+
"type": "array",
|
|
34
|
+
"items": { "$ref": "#/definitions/check" },
|
|
35
|
+
"description": "One entry per selected slot, in pipeline order."
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"definitions": {
|
|
39
|
+
"check": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"additionalProperties": false,
|
|
42
|
+
"required": ["name", "adapter", "description", "ok", "exit_code", "duration_ms", "output_file"],
|
|
43
|
+
"properties": {
|
|
44
|
+
"name": { "type": "string", "description": "Slot name (e.g. lint), or a custom check's key." },
|
|
45
|
+
"adapter": { "type": ["string", "null"], "description": "Adapter that ran (e.g. oxlint), or null when the slot resolved to nothing." },
|
|
46
|
+
"description": { "type": "string", "description": "Human description of the check." },
|
|
47
|
+
"ok": { "type": "boolean", "description": "Pass/fail after any baseline masking; skipped checks are ok." },
|
|
48
|
+
"skipped": { "type": "boolean", "description": "Present (true) when the check did not execute; see reason." },
|
|
49
|
+
"reason": { "type": "string", "description": "Why a skipped check sat out (no tool detected, disabled in config, unavailable under this package manager, ...)." },
|
|
50
|
+
"exit_code": { "type": ["integer", "null"], "description": "The tool's exit code; -1 for spawn failure or timeout; null when skipped." },
|
|
51
|
+
"duration_ms": { "type": "number", "description": "Wall-clock duration in milliseconds (0 when skipped)." },
|
|
52
|
+
"output_file": { "type": ["string", "null"], "description": "The .check/ file holding the tool's raw JSON output, when it emits JSON on stdout." },
|
|
53
|
+
"baselined": { "type": "integer", "minimum": 1, "description": "Present only when a committed baseline masked diagnostics: how many current findings are grandfathered." }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plumbbob",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Attention-first build process: 12 skills + a CLI that keep you the decider — guidance,
|
|
3
|
+
"version": "0.8.0",
|
|
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",
|
|
7
7
|
"claude",
|
|
@@ -48,13 +48,14 @@
|
|
|
48
48
|
"@types/node": "^24.0.0",
|
|
49
49
|
"@vitest/coverage-v8": "^4.1.9",
|
|
50
50
|
"fallow": "^2.103.0",
|
|
51
|
+
"fascicle": "0.8.16",
|
|
51
52
|
"markdownlint-cli2": "^0.22.1",
|
|
52
53
|
"oxlint": "^1.69.0",
|
|
53
54
|
"typescript": "^6.0.3",
|
|
54
55
|
"vitest": "^4.1.8"
|
|
55
56
|
},
|
|
56
57
|
"dependencies": {
|
|
57
|
-
"checkride": "0.
|
|
58
|
+
"checkride": "0.3.0"
|
|
58
59
|
},
|
|
59
60
|
"bundleDependencies": [
|
|
60
61
|
"checkride"
|
|
@@ -63,6 +64,9 @@
|
|
|
63
64
|
"clean": "rm -rf dist",
|
|
64
65
|
"build": "tsc -p tsconfig.build.json",
|
|
65
66
|
"check": "checkride",
|
|
66
|
-
"test": "vitest run"
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"eval:baseline": "pnpm build && node test/evals/run.ts --sweep baseline",
|
|
69
|
+
"eval:latched": "pnpm build && node test/evals/run.ts --sweep latched",
|
|
70
|
+
"eval:report": "node test/evals/run.ts --report"
|
|
67
71
|
}
|
|
68
72
|
}
|
package/skills/pb-build/SKILL.md
CHANGED
|
@@ -83,6 +83,15 @@ differs from the model you're running as, say so before implementing — the hum
|
|
|
83
83
|
`--body` for the deterministic done-when + seam + diffstat fallback). Do **not** bump
|
|
84
84
|
the version or changelog — that is the human's `/version` call.
|
|
85
85
|
|
|
86
|
+
**The latch makes this pause real (D64).** On the default (non-`--auto`) path you
|
|
87
|
+
build the step and reach the pause *in one turn* — so under plumbbob's turn hook the
|
|
88
|
+
checkpoint at the end of that turn is **refused** ("no human turn since this step
|
|
89
|
+
began"), and that refusal **is** the pause working as designed. Do exactly what the
|
|
90
|
+
pause already asks: present the diff and the self-review, **end the turn**, and the
|
|
91
|
+
human's next message is the tick that lands the checkpoint when you re-fire. **Never
|
|
92
|
+
route around a refusal with a raw `git commit`** — the work plane stays free (D10/D13),
|
|
93
|
+
but the *record* is latched on purpose.
|
|
94
|
+
|
|
86
95
|
## `--auto` — let the agent be the clock (opt-in)
|
|
87
96
|
|
|
88
97
|
`/pb-build --auto` is the explicit escape hatch when the human wants unattended
|
|
@@ -141,3 +150,8 @@ just the one more entry already in the halt list above.
|
|
|
141
150
|
`build` writes the diff, `after` is advisory — none can checkpoint, flip a step, or
|
|
142
151
|
chain. `blocked` → unblock and re-run; `drift` → `/pb-refine`. You are still the one
|
|
143
152
|
who verifies and (bar `--auto`) the human is still the clock.
|
|
153
|
+
- **A refused checkpoint is the pause, not an error (D64).** Under plumbbob's turn
|
|
154
|
+
hook a same-turn `checkpoint` is refused by design — present the diff, **end the
|
|
155
|
+
turn**, and the human's next message is the tick that lands it on re-fire. Never route
|
|
156
|
+
around it with a raw `git commit`; `--auto`, a typed range, or `auto: true` are the
|
|
157
|
+
only self-approvals.
|
package/skills/pb-plan/SKILL.md
CHANGED
|
@@ -45,6 +45,10 @@ an agent can follow with `/pb-build`. The argument only seeds how you get there.
|
|
|
45
45
|
1. **Scaffold.** If there is no active session, run `plumbbob start "<title>"`
|
|
46
46
|
to create `.plumbbob/` (baseline recorded, session opened). If a session already
|
|
47
47
|
exists, say so and edit the existing `intent.md` rather than starting over.
|
|
48
|
+
- **If `start` warns that the gate sees no code checks**, surface that to the
|
|
49
|
+
human now — while they're still deciding, not at the first refused
|
|
50
|
+
checkpoint — and offer to set the `check` key in `.plumbbob/settings.json`
|
|
51
|
+
(e.g. `"check": "npm test"`) before any step is built.
|
|
48
52
|
2. **Frame** (`.plumbbob/intent.md`), with the human: the **Problem** in plain words,
|
|
49
53
|
the **smallest thing** that solves it, what **done looks like**, and what you are
|
|
50
54
|
**explicitly NOT doing**. This is the human's convergence — propose wording, but
|
|
@@ -103,6 +107,19 @@ an agent can follow with `/pb-build`. The argument only seeds how you get there.
|
|
|
103
107
|
baseline → plan → steps. Pass a proportional `--body` (the single-quoted stdin
|
|
104
108
|
heredoc) when the rationale is worth carrying; skip it for a small plan. Do this
|
|
105
109
|
only on the human's approval — the plan is their convergence.
|
|
110
|
+
- **The plan commit is latched too (D64).** Once the turn ledger exists, `checkpoint
|
|
111
|
+
--plan` refuses to land in the same turn `start` stamped it: present the plan,
|
|
112
|
+
**end the turn**, and the human's approving message is the tick that lets it
|
|
113
|
+
commit on re-fire — the refusal *is* the plan pause. Never route around it with a
|
|
114
|
+
raw `git commit`. (One documented seam: the very first plan of a brand-new
|
|
115
|
+
session runs `start` *before* the turn hook has ever ticked, so that single
|
|
116
|
+
commit predates the ledger and stays guidance-governed — it lands without a
|
|
117
|
+
refusal.)
|
|
118
|
+
- **Say what the commit publishes.** The close-out line names the tracked folder:
|
|
119
|
+
`.plumbbob/builds/<slug>/` now rides this branch into the PR, so teammates will
|
|
120
|
+
see the plan and build record in review. In the same breath, offer the way out:
|
|
121
|
+
a repo that won't track a tool folder can restart with `plumbbob start --local`
|
|
122
|
+
(a fully untracked sidecar) before any steps are built.
|
|
106
123
|
7. **Offer to stress-test it.** Suggest `/pb-refine` to attack the frame for holes (or
|
|
107
124
|
to repair the plan as it drifts). Optional, the human's call.
|
|
108
125
|
|
|
@@ -63,6 +63,20 @@ this skill verifies it the same way: **it reads the diff, not the author**.
|
|
|
63
63
|
(done-when + seam + diffstat) on its own. Either way, do **not** bump the version or
|
|
64
64
|
touch the changelog — that is the human's `/version` call.
|
|
65
65
|
|
|
66
|
+
## The latch makes the pause real (D64)
|
|
67
|
+
|
|
68
|
+
When this session runs under plumbbob's turn hook, `plumbbob checkpoint` **refuses to
|
|
69
|
+
land a step in the same turn it was entered** — and that refusal **is** this pause, not
|
|
70
|
+
an error to route around. If the checkpoint prints *"checkpoint refused — no human turn
|
|
71
|
+
since this step began,"* you have reached the pause the hard way: present the diff and
|
|
72
|
+
the self-review, **end the turn**, and the human's next message is the tick that lets
|
|
73
|
+
the checkpoint land when you re-fire. **Never reach for a raw `git commit` to force the
|
|
74
|
+
land** — that forges the very record the latch exists to keep honest, and the
|
|
75
|
+
commit-ask hook asks the human about it anyway. The refusal is a healthy latch doing its
|
|
76
|
+
job on the *record* while the *work* plane stays free (D10/D13); a `/pb-build --auto` or
|
|
77
|
+
a typed step range in the human's own prompt is the only self-approval, and `auto: true`
|
|
78
|
+
in settings is the standing one.
|
|
79
|
+
|
|
66
80
|
## The hard contracts
|
|
67
81
|
|
|
68
82
|
- **Never skip the pause.** Check → self-review → validate, then wait. Approval is
|
|
@@ -76,3 +90,7 @@ this skill verifies it the same way: **it reads the diff, not the author**.
|
|
|
76
90
|
- **`after`-agents advise; they never gate**. Their output feeds the
|
|
77
91
|
self-review — checkride gates, the human approves. `blocked` → unblock and re-run;
|
|
78
92
|
`drift` → `/pb-refine` before checkpointing. No code path makes them blocking.
|
|
93
|
+
- **A refused checkpoint is the pause, never a workaround (D64).** Under the turn
|
|
94
|
+
hook a same-turn checkpoint is refused *by design* — present the diff, **end the
|
|
95
|
+
turn**, and let the human's next message re-tick it. Never route around it with a raw
|
|
96
|
+
`git commit`; the latch guards the record, not the work.
|