dsh-pstack 0.0.0-stage → 0.2.0-beta.1
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/LICENSE +22 -0
- package/README.md +133 -2
- package/assets/agents/comment-sicko.md +32 -0
- package/assets/agents/poteto-agent.md +8 -0
- package/assets/docs/guide/01-setup.md +64 -0
- package/assets/docs/guide/02-poteto-mode.md +139 -0
- package/assets/docs/guide/03-understand.md +79 -0
- package/assets/docs/guide/04-design.md +133 -0
- package/assets/docs/guide/05-build-and-clean.md +83 -0
- package/assets/docs/guide/06-verify-and-ship.md +152 -0
- package/assets/docs/guide/07-overnight.md +120 -0
- package/assets/docs/guide/08-principles.md +72 -0
- package/assets/docs/guide/09-make-it-yours.md +96 -0
- package/assets/docs/guide/10-recipes-and-pitfalls.md +180 -0
- package/assets/docs/guide/README.md +38 -0
- package/assets/docs/guide/images/design.jpg +0 -0
- package/assets/docs/guide/images/overnight.jpg +0 -0
- package/assets/docs/guide/images/recipes.jpg +0 -0
- package/assets/docs/guide/images/router.jpg +0 -0
- package/assets/docs/guide/images/understanding.jpg +0 -0
- package/assets/docs/guide/images/verification.jpg +0 -0
- package/assets/docs/harness/dsh.md +60 -0
- package/assets/docs/pstack-models.example.md +54 -0
- package/assets/skills/architect/SKILL.md +81 -0
- package/assets/skills/architect/references/design-red-flags.md +57 -0
- package/assets/skills/architect/references/rationale-template.md +35 -0
- package/assets/skills/architect/references/runner-prompt.md +20 -0
- package/assets/skills/arena/SKILL.md +50 -0
- package/assets/skills/automate-me/SKILL.md +106 -0
- package/assets/skills/benchmark-checklist/SKILL.md +39 -0
- package/assets/skills/blast-radius/SKILL.md +52 -0
- package/assets/skills/bro/SKILL.md +7 -0
- package/assets/skills/correct/SKILL.md +32 -0
- package/assets/skills/create-formal-verification-skill/SKILL.md +43 -0
- package/assets/skills/create-verification-skill/SKILL.md +46 -0
- package/assets/skills/create-verification-skill/references/feature-map-example/README.md +47 -0
- package/assets/skills/create-verification-skill/references/feature-map-example/create-note.md +39 -0
- package/assets/skills/create-verification-skill/references/feature-map-example/search.md +45 -0
- package/assets/skills/figure-it-out/SKILL.md +54 -0
- package/assets/skills/how/SKILL.md +38 -0
- package/assets/skills/how/references/explainer-prompt.md +55 -0
- package/assets/skills/how/references/explorer-prompt.md +35 -0
- package/assets/skills/in-your-own-words/SKILL.md +7 -0
- package/assets/skills/interrogate/SKILL.md +71 -0
- package/assets/skills/interrogate/references/code-quality-review.md +47 -0
- package/assets/skills/interrogate/references/lead-judgment.md +39 -0
- package/assets/skills/interrogate/references/reviewer-prompt.md +62 -0
- package/assets/skills/interrogate/references/rubric.md +77 -0
- package/assets/skills/ketchup/SKILL.md +45 -0
- package/assets/skills/maintain-formal-verification-skill/SKILL.md +60 -0
- package/assets/skills/maintain-verification-skill/SKILL.md +41 -0
- package/assets/skills/no-comments/SKILL.md +26 -0
- package/assets/skills/poteto-help/SKILL.md +162 -0
- package/assets/skills/poteto-help/references/prompting.md +51 -0
- package/assets/skills/poteto-help/references/recipes.md +51 -0
- package/assets/skills/poteto-mode/SKILL.md +151 -0
- package/assets/skills/poteto-mode/playbooks/authoring-a-skill.md +12 -0
- package/assets/skills/poteto-mode/playbooks/autonomous-run.md +15 -0
- package/assets/skills/poteto-mode/playbooks/autopilot-full.md +25 -0
- package/assets/skills/poteto-mode/playbooks/autopilot-stack.md +12 -0
- package/assets/skills/poteto-mode/playbooks/babysit.md +23 -0
- package/assets/skills/poteto-mode/playbooks/bug-fix.md +15 -0
- package/assets/skills/poteto-mode/playbooks/eval.md +25 -0
- package/assets/skills/poteto-mode/playbooks/feature.md +21 -0
- package/assets/skills/poteto-mode/playbooks/hillclimb.md +21 -0
- package/assets/skills/poteto-mode/playbooks/investigation.md +14 -0
- package/assets/skills/poteto-mode/playbooks/multi-phase-plan.md +155 -0
- package/assets/skills/poteto-mode/playbooks/opening-a-pr.md +34 -0
- package/assets/skills/poteto-mode/playbooks/orchestrate.md +112 -0
- package/assets/skills/poteto-mode/playbooks/pause-safely.md +10 -0
- package/assets/skills/poteto-mode/playbooks/perf-issue.md +25 -0
- package/assets/skills/poteto-mode/playbooks/prototype.md +14 -0
- package/assets/skills/poteto-mode/playbooks/refactoring.md +16 -0
- package/assets/skills/poteto-mode/playbooks/runtime-forensics.md +11 -0
- package/assets/skills/poteto-mode/playbooks/session-pickup.md +11 -0
- package/assets/skills/poteto-mode/playbooks/shipping.md +12 -0
- package/assets/skills/poteto-mode/playbooks/trace-forensics.md +14 -0
- package/assets/skills/poteto-mode/playbooks/visual-parity.md +11 -0
- package/assets/skills/poteto-mode/playbooks/worktree-cleanup.md +20 -0
- package/assets/skills/poteto-mode/references/bugbot-triage.md +142 -0
- package/assets/skills/poteto-mode/scripts/check-plan.mjs +220 -0
- package/assets/skills/poteto-mode/scripts/check-plan.test.mjs +343 -0
- package/assets/skills/poteto-mode/scripts/orch/orch.mjs +4282 -0
- package/assets/skills/poteto-mode/scripts/orch/orch.test.mjs +2042 -0
- package/assets/skills/poteto-mode/scripts/watch-pr/cli.test.mjs +5101 -0
- package/assets/skills/poteto-mode/scripts/watch-pr/github.test.mjs +1177 -0
- package/assets/skills/poteto-mode/scripts/watch-pr/policy.test.mjs +1441 -0
- package/assets/skills/poteto-mode/scripts/watch-pr/watch-pr.mjs +4601 -0
- package/assets/skills/poteto-mode/scripts/worktree-audit.mjs +675 -0
- package/assets/skills/principle-attack-the-premise/SKILL.md +21 -0
- package/assets/skills/principle-boundary-discipline/SKILL.md +34 -0
- package/assets/skills/principle-build-the-lever/SKILL.md +23 -0
- package/assets/skills/principle-encode-lessons-in-structure/SKILL.md +11 -0
- package/assets/skills/principle-exhaust-the-design-space/SKILL.md +9 -0
- package/assets/skills/principle-experience-first/SKILL.md +19 -0
- package/assets/skills/principle-explain-the-number/SKILL.md +17 -0
- package/assets/skills/principle-fix-root-causes/SKILL.md +21 -0
- package/assets/skills/principle-foundational-thinking/SKILL.md +21 -0
- package/assets/skills/principle-guard-the-context-window/SKILL.md +12 -0
- package/assets/skills/principle-laziness-protocol/SKILL.md +10 -0
- package/assets/skills/principle-make-operations-idempotent/SKILL.md +22 -0
- package/assets/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +14 -0
- package/assets/skills/principle-minimize-reader-load/SKILL.md +18 -0
- package/assets/skills/principle-model-the-domain/SKILL.md +24 -0
- package/assets/skills/principle-never-block-on-the-human/SKILL.md +15 -0
- package/assets/skills/principle-outcome-oriented-execution/SKILL.md +21 -0
- package/assets/skills/principle-prove-it-works/SKILL.md +24 -0
- package/assets/skills/principle-redesign-from-first-principles/SKILL.md +16 -0
- package/assets/skills/principle-separate-before-serializing-shared-state/SKILL.md +16 -0
- package/assets/skills/principle-sequence-verifiable-units/SKILL.md +17 -0
- package/assets/skills/principle-subtract-before-you-add/SKILL.md +18 -0
- package/assets/skills/principle-test-behavior-not-implementation/SKILL.md +20 -0
- package/assets/skills/principle-type-system-discipline/SKILL.md +13 -0
- package/assets/skills/recall/SKILL.md +37 -0
- package/assets/skills/reflect/SKILL.md +69 -0
- package/assets/skills/reflect/references/divergent-reviewer.md +38 -0
- package/assets/skills/reflect/references/judgment-reviewer.md +37 -0
- package/assets/skills/reflect/references/synthesizer.md +56 -0
- package/assets/skills/reflect/references/tooling-reviewer.md +48 -0
- package/assets/skills/setup-pstack/SKILL.md +67 -0
- package/assets/skills/sharpen/SKILL.md +16 -0
- package/assets/skills/show-me-your-work/SKILL.md +84 -0
- package/assets/skills/show-me-your-work/references/decision-log-template.tsv +1 -0
- package/assets/skills/show-me-your-work/scripts/log.mjs +108 -0
- package/assets/skills/swarm/SKILL.md +43 -0
- package/assets/skills/tdd/SKILL.md +42 -0
- package/assets/skills/teach/SKILL.md +23 -0
- package/assets/skills/technical-writing/SKILL.md +108 -0
- package/assets/skills/typescript-best-practices/SKILL.md +34 -0
- package/assets/skills/typescript-best-practices/references/patterns.md +328 -0
- package/assets/skills/unslop/SKILL.md +62 -0
- package/assets/skills/why/SKILL.md +100 -0
- package/assets/skills/why/references/epistemics.md +71 -0
- package/assets/skills/why/references/investigator-prompt.md +84 -0
- package/assets/skills/why/references/source-playbook.md +17 -0
- package/assets/skills/why/references/sources/code-archaeology.md +51 -0
- package/assets/skills/why/references/sources/databricks.md +70 -0
- package/assets/skills/why/references/sources/datadog.md +97 -0
- package/assets/skills/why/references/sources/incident-postmortem.md +5 -0
- package/assets/skills/why/references/sources/linear.md +48 -0
- package/assets/skills/why/references/sources/notion.md +55 -0
- package/assets/skills/why/references/sources/sentry.md +66 -0
- package/assets/skills/why/references/sources/slack.md +17 -0
- package/assets/skills/why/references/synthesizer-prompt.md +129 -0
- package/cordis.patch.yml +39 -0
- package/dist/frontmatter-BQX3ekwP.mjs +229 -0
- package/dist/frontmatter-BQX3ekwP.mjs.map +1 -0
- package/dist/history.d.mts +429 -0
- package/dist/history.mjs +1129 -0
- package/dist/history.mjs.map +1 -0
- package/dist/host-contract-o47JH_r8.d.mts +527 -0
- package/dist/index.d.mts +278 -0
- package/dist/index.mjs +272 -0
- package/dist/index.mjs.map +1 -0
- package/dist/paths-D_JlVmBk.mjs +42 -0
- package/dist/paths-D_JlVmBk.mjs.map +1 -0
- package/dist/script.d.mts +193 -0
- package/dist/script.mjs +782 -0
- package/dist/script.mjs.map +1 -0
- package/dist/subagent.d.mts +336 -0
- package/dist/subagent.mjs +1402 -0
- package/dist/subagent.mjs.map +1 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +106 -3
- package/static/icon.png +0 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { test } from "node:test";
|
|
7
|
+
import { inspect } from "node:util";
|
|
8
|
+
//#region .runtime-sources/skills/poteto-mode/scripts/expect.ts
|
|
9
|
+
/**
|
|
10
|
+
* A small `expect` for `node:test`.
|
|
11
|
+
*
|
|
12
|
+
* The five upstream test files were written against `bun:test`. Their assertions
|
|
13
|
+
* are ordinary value comparisons, so this module supplies exactly the matchers
|
|
14
|
+
* they use rather than a general test framework. It is deliberately not a drop-in
|
|
15
|
+
* replacement: a matcher that is not listed here fails to compile, which keeps the
|
|
16
|
+
* port auditable against the calls that actually exist.
|
|
17
|
+
*
|
|
18
|
+
* Covered: `toBe`, `toEqual`, `toContain`, `toMatch`, `toThrow`, `toBeNull`,
|
|
19
|
+
* `toBeInstanceOf`, `toHaveLength`, `toEndWith`, `toMatchObject`, `.not` on each,
|
|
20
|
+
* and `rejects` / `resolves` for promises.
|
|
21
|
+
*
|
|
22
|
+
* Two differences from `bun:test`, stated so a failure is not misread:
|
|
23
|
+
*
|
|
24
|
+
* - `toEqual` is structural for arrays and plain objects, `Object.is` otherwise.
|
|
25
|
+
* A class instance only equals itself; `toBeInstanceOf` is the matcher for type
|
|
26
|
+
* identity. The ported tests compare plain data, which is why this is enough.
|
|
27
|
+
* - A failure throws `AssertionError` with the two values rendered, not Bun's
|
|
28
|
+
* coloured diff.
|
|
29
|
+
*/
|
|
30
|
+
/** Raised by every failing assertion. */
|
|
31
|
+
var AssertionError = class extends Error {
|
|
32
|
+
constructor(message) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "AssertionError";
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
function display(value) {
|
|
38
|
+
if (typeof value === "function") return `[Function ${value.name || "anonymous"}]`;
|
|
39
|
+
return inspect(value, {
|
|
40
|
+
depth: 3,
|
|
41
|
+
breakLength: 120,
|
|
42
|
+
maxArrayLength: 20
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function messageOf(error) {
|
|
46
|
+
if (error instanceof Error) return `${error.name}: ${error.message}`;
|
|
47
|
+
return String(error);
|
|
48
|
+
}
|
|
49
|
+
/** Structural equality for the shapes the ported tests compare. */
|
|
50
|
+
function deepEqual(left, right) {
|
|
51
|
+
if (Object.is(left, right)) return true;
|
|
52
|
+
if (left instanceof Date || right instanceof Date) return left instanceof Date && right instanceof Date && left.getTime() === right.getTime();
|
|
53
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
54
|
+
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) return false;
|
|
55
|
+
return left.every((item, index) => deepEqual(item, right[index]));
|
|
56
|
+
}
|
|
57
|
+
if (left === null || right === null || typeof left !== "object" || typeof right !== "object") return false;
|
|
58
|
+
const leftProto = Object.getPrototypeOf(left);
|
|
59
|
+
if (leftProto !== Object.getPrototypeOf(right)) return false;
|
|
60
|
+
if (leftProto !== Object.prototype && leftProto !== null) return false;
|
|
61
|
+
const leftKeys = Object.keys(left);
|
|
62
|
+
const rightKeys = Object.keys(right);
|
|
63
|
+
if (leftKeys.length !== rightKeys.length) return false;
|
|
64
|
+
return leftKeys.every((key) => deepEqual(left[key], right[key]));
|
|
65
|
+
}
|
|
66
|
+
function contains(haystack, needle) {
|
|
67
|
+
if (typeof haystack === "string") return haystack.includes(String(needle));
|
|
68
|
+
if (Array.isArray(haystack)) return haystack.some((item) => deepEqual(item, needle));
|
|
69
|
+
if (haystack instanceof Set) return [...haystack].some((item) => deepEqual(item, needle));
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Whether `expected` is a partial description of `actual`.
|
|
74
|
+
*
|
|
75
|
+
* `bun:test`'s `toMatchObject` is recursive: every key in `expected` must exist and
|
|
76
|
+
* match, arrays match element by element with the same partial rule, and extra keys
|
|
77
|
+
* in `actual` are allowed. That is what lets an assertion name only the fields it
|
|
78
|
+
* cares about, which several ported cases rely on.
|
|
79
|
+
*/
|
|
80
|
+
function matchesObject(actual, expected) {
|
|
81
|
+
if (Array.isArray(expected)) {
|
|
82
|
+
if (!Array.isArray(actual) || actual.length !== expected.length) return false;
|
|
83
|
+
return expected.every((item, index) => matchesObject(actual[index], item));
|
|
84
|
+
}
|
|
85
|
+
if (expected instanceof Date) return deepEqual(actual, expected);
|
|
86
|
+
if (expected === null || typeof expected !== "object") return deepEqual(actual, expected);
|
|
87
|
+
if (actual === null || typeof actual !== "object") return false;
|
|
88
|
+
return Object.entries(expected).every(([key, value]) => matchesObject(actual[key], value));
|
|
89
|
+
}
|
|
90
|
+
function thrownBy(call) {
|
|
91
|
+
try {
|
|
92
|
+
call();
|
|
93
|
+
return { thrown: false };
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return {
|
|
96
|
+
thrown: true,
|
|
97
|
+
error
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function buildMatchers(actual, negate) {
|
|
102
|
+
/**
|
|
103
|
+
* Fail when `ok` is not what the matcher asked for.
|
|
104
|
+
*
|
|
105
|
+
* `negate` inverts the requirement, so `.not.toContain(x)` passes on a haystack
|
|
106
|
+
* that does not contain `x` and fails when it does.
|
|
107
|
+
*/
|
|
108
|
+
const check = (ok, matcher, expected) => {
|
|
109
|
+
if (ok === !negate) return;
|
|
110
|
+
const relation = negate ? `not to ${matcher}` : matcher;
|
|
111
|
+
throw new AssertionError(`expected ${display(actual)} ${relation} ${display(expected)}`);
|
|
112
|
+
};
|
|
113
|
+
const checkPlain = (ok, matcher) => {
|
|
114
|
+
if (ok === !negate) return;
|
|
115
|
+
throw new AssertionError(`expected ${display(actual)} ${negate ? `not to ${matcher}` : matcher}`);
|
|
116
|
+
};
|
|
117
|
+
const matchers = {
|
|
118
|
+
toBe(expected) {
|
|
119
|
+
check(Object.is(actual, expected), "toBe", expected);
|
|
120
|
+
},
|
|
121
|
+
toEqual(expected) {
|
|
122
|
+
check(deepEqual(actual, expected), "toEqual", expected);
|
|
123
|
+
},
|
|
124
|
+
toContain(expected) {
|
|
125
|
+
check(contains(actual, expected), "toContain", expected);
|
|
126
|
+
},
|
|
127
|
+
toMatch(expected) {
|
|
128
|
+
const text = String(actual);
|
|
129
|
+
check(typeof expected === "string" ? text.includes(expected) : expected.test(text), "toMatch", expected);
|
|
130
|
+
},
|
|
131
|
+
toThrow(expected) {
|
|
132
|
+
if (typeof actual !== "function") throw new AssertionError(`toThrow needs a function, received ${display(actual)}`);
|
|
133
|
+
const result = thrownBy(actual);
|
|
134
|
+
if (!result.thrown) {
|
|
135
|
+
checkPlain(false, "toThrow");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (expected === void 0) {
|
|
139
|
+
checkPlain(true, "toThrow");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const message = messageOf(result.error);
|
|
143
|
+
const ok = typeof expected === "string" ? message.includes(expected) : expected instanceof RegExp ? expected.test(message) : result.error instanceof expected;
|
|
144
|
+
check(ok, "toThrow", expected);
|
|
145
|
+
},
|
|
146
|
+
toBeNull() {
|
|
147
|
+
checkPlain(actual === null, "toBeNull");
|
|
148
|
+
},
|
|
149
|
+
toBeInstanceOf(expected) {
|
|
150
|
+
check(actual instanceof expected, "toBeInstanceOf", expected);
|
|
151
|
+
},
|
|
152
|
+
toHaveLength(expected) {
|
|
153
|
+
const length = actual?.length;
|
|
154
|
+
check(length === expected, "toHaveLength", expected);
|
|
155
|
+
},
|
|
156
|
+
toEndWith(expected) {
|
|
157
|
+
check(String(actual).endsWith(expected), "toEndWith", expected);
|
|
158
|
+
},
|
|
159
|
+
toMatchObject(expected) {
|
|
160
|
+
check(matchesObject(actual, expected), "toMatchObject", expected);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
Object.defineProperties(matchers, {
|
|
164
|
+
not: {
|
|
165
|
+
get: () => buildMatchers(actual, !negate),
|
|
166
|
+
enumerable: true
|
|
167
|
+
},
|
|
168
|
+
rejects: {
|
|
169
|
+
get: () => buildAsyncMatchers(actual, "rejects", negate),
|
|
170
|
+
enumerable: true
|
|
171
|
+
},
|
|
172
|
+
resolves: {
|
|
173
|
+
get: () => buildAsyncMatchers(actual, "resolves", negate),
|
|
174
|
+
enumerable: true
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
return matchers;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Compare against a settled promise.
|
|
181
|
+
*
|
|
182
|
+
* `rejects` reads the rejection reason and fails when the promise resolved;
|
|
183
|
+
* `resolves` is the mirror. Each matcher awaits the settlement, which is what
|
|
184
|
+
* makes `await expect(promise).rejects.toThrow(...)` wait before it compares.
|
|
185
|
+
*/
|
|
186
|
+
function buildAsyncMatchers(actual, mode, negate) {
|
|
187
|
+
if (typeof actual?.then !== "function") throw new AssertionError(`expected a promise, received ${display(actual)}`);
|
|
188
|
+
const wantRejection = mode === "rejects" !== negate;
|
|
189
|
+
const settle = async () => {
|
|
190
|
+
let resolved;
|
|
191
|
+
let rejected;
|
|
192
|
+
await Promise.resolve(actual).then((value) => {
|
|
193
|
+
resolved = { value };
|
|
194
|
+
}, (error) => {
|
|
195
|
+
rejected = { error };
|
|
196
|
+
});
|
|
197
|
+
if (wantRejection) {
|
|
198
|
+
if (resolved !== void 0) throw new AssertionError(`expected the promise to reject, it resolved to ${display(resolved.value)}`);
|
|
199
|
+
return rejected?.error;
|
|
200
|
+
}
|
|
201
|
+
if (rejected !== void 0) throw new AssertionError(`expected the promise to resolve, it rejected with ${messageOf(rejected.error)}`);
|
|
202
|
+
return resolved?.value;
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Run one matcher against the settled value.
|
|
206
|
+
*
|
|
207
|
+
* `toThrow` and `toBeInstanceOf` both read the rejection reason, so a thrown
|
|
208
|
+
* reason is turned back into a thunk for `toThrow` rather than special-cased
|
|
209
|
+
* twice. `negate` is already folded into the settlement mode, so the inner
|
|
210
|
+
* matchers run un-negated.
|
|
211
|
+
*/
|
|
212
|
+
const wrap = async (name, expected) => {
|
|
213
|
+
const subject = await settle();
|
|
214
|
+
if (name === "toThrow") {
|
|
215
|
+
buildMatchers(() => {
|
|
216
|
+
throw subject;
|
|
217
|
+
}, false).toThrow(expected);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (name === "toBeNull") {
|
|
221
|
+
buildMatchers(subject, false).toBeNull();
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const matchers = buildMatchers(subject, false);
|
|
225
|
+
const matcher = matchers[name];
|
|
226
|
+
if (typeof matcher !== "function") throw new AssertionError(`unknown matcher ${name}`);
|
|
227
|
+
matcher.call(matchers, expected);
|
|
228
|
+
};
|
|
229
|
+
const matchers = {
|
|
230
|
+
toBe: (expected) => wrap("toBe", expected),
|
|
231
|
+
toEqual: (expected) => wrap("toEqual", expected),
|
|
232
|
+
toContain: (expected) => wrap("toContain", expected),
|
|
233
|
+
toMatch: (expected) => wrap("toMatch", expected),
|
|
234
|
+
toThrow: (expected) => wrap("toThrow", expected),
|
|
235
|
+
toBeNull: () => wrap("toBeNull"),
|
|
236
|
+
toBeInstanceOf: (expected) => wrap("toBeInstanceOf", expected),
|
|
237
|
+
toHaveLength: (expected) => wrap("toHaveLength", expected),
|
|
238
|
+
toEndWith: (expected) => wrap("toEndWith", expected),
|
|
239
|
+
toMatchObject: (expected) => wrap("toMatchObject", expected)
|
|
240
|
+
};
|
|
241
|
+
Object.defineProperty(matchers, "not", {
|
|
242
|
+
get: () => buildAsyncMatchers(actual, mode, !negate),
|
|
243
|
+
enumerable: true
|
|
244
|
+
});
|
|
245
|
+
return matchers;
|
|
246
|
+
}
|
|
247
|
+
const expect = Object.assign((actual) => buildMatchers(actual, false), {
|
|
248
|
+
not: (actual) => buildMatchers(actual, true),
|
|
249
|
+
rejects: (actual) => buildAsyncMatchers(actual, "rejects", false),
|
|
250
|
+
resolves: (actual) => buildAsyncMatchers(actual, "resolves", false)
|
|
251
|
+
});
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region .runtime-sources/skills/poteto-mode/scripts/check-plan.test.ts
|
|
254
|
+
const checker = join(import.meta.dirname, "check-plan.mjs");
|
|
255
|
+
const rule = "Tests alone are not sufficient verification. A PR is verified only when its unit, live, and perf boxes are all checked.";
|
|
256
|
+
const validPlan = `# Demo
|
|
257
|
+
|
|
258
|
+
## How to read this
|
|
259
|
+
One box is one unit of work and names the evidence. Check a box only when its evidence exists. See playbooks/.
|
|
260
|
+
${rule}
|
|
261
|
+
|
|
262
|
+
## Program checklist
|
|
263
|
+
### Arm the program
|
|
264
|
+
### Spawn owners
|
|
265
|
+
### PR mechanics
|
|
266
|
+
### Verdict and merge
|
|
267
|
+
### Boot recipe
|
|
268
|
+
\`create_goal\` \`schedule_create\` status message
|
|
269
|
+
|
|
270
|
+
## PR 1
|
|
271
|
+
**Depends on.** None.
|
|
272
|
+
**Files.**
|
|
273
|
+
- [ ] Do the thing.
|
|
274
|
+
**Build.**
|
|
275
|
+
- [ ] Do the thing.
|
|
276
|
+
**You see.**
|
|
277
|
+
- [ ] Do the thing.
|
|
278
|
+
**Verify, unit.** ${rule}
|
|
279
|
+
- [ ] Check the thing.
|
|
280
|
+
**Verify, live.** ${rule} Ten lanes on \`example-model\` at the PR head
|
|
281
|
+
${Array.from({ length: 10 }, (_, i) => `- [ ] Lane ${i + 1}. Save \`lane-${i + 1}.png\`. Pass when evidence exists.`).join("\n")}
|
|
282
|
+
**Verify, perf.** ${rule}
|
|
283
|
+
- [ ] Metric. Recorded.
|
|
284
|
+
- [ ] Probe. Recorded.
|
|
285
|
+
- [ ] Baseline. Recorded.
|
|
286
|
+
- [ ] Rule. Recorded.
|
|
287
|
+
**Review gate.** None.
|
|
288
|
+
**Merge.**
|
|
289
|
+
- [ ] Merge when approved.
|
|
290
|
+
|
|
291
|
+
## Close the program
|
|
292
|
+
Close.
|
|
293
|
+
|
|
294
|
+
## Appendix Prototype evidence
|
|
295
|
+
Recorded.
|
|
296
|
+
`;
|
|
297
|
+
const runPlan = (example, expectedExit) => {
|
|
298
|
+
const directory = mkdtempSync(join(tmpdir(), "check-plan-test-"));
|
|
299
|
+
const file = join(directory, "plan.md");
|
|
300
|
+
writeFileSync(file, validPlan + example);
|
|
301
|
+
try {
|
|
302
|
+
const result = spawnSync("node", [checker, file], { encoding: "utf8" });
|
|
303
|
+
expect(result.status).toBe(expectedExit);
|
|
304
|
+
return result;
|
|
305
|
+
} finally {
|
|
306
|
+
rmSync(directory, {
|
|
307
|
+
recursive: true,
|
|
308
|
+
force: true
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
test("accepts ordinary, indented, and longer closing fences", () => {
|
|
313
|
+
for (const indent of [
|
|
314
|
+
"",
|
|
315
|
+
" ",
|
|
316
|
+
" ",
|
|
317
|
+
" "
|
|
318
|
+
]) runPlan(`${indent}\`\`\`markdown\n## Example only\n${indent}\`\`\`\n`, 0);
|
|
319
|
+
runPlan("```markdown\n## Example only\n````\n", 0);
|
|
320
|
+
});
|
|
321
|
+
test("accepts nested backticks and tilde info strings", () => {
|
|
322
|
+
runPlan("````markdown\n```markdown\n## Example only\n```\n````\n", 0);
|
|
323
|
+
runPlan("~~~markdown `info`\n## Example only\n ~~~\n", 0);
|
|
324
|
+
});
|
|
325
|
+
test("keeps mismatched, shorter, and suffixed closers inside the fence", () => {
|
|
326
|
+
runPlan("````markdown\n~~~\n## Example only\n````\n", 0);
|
|
327
|
+
runPlan("````markdown\n```\n## Example only\n````\n", 0);
|
|
328
|
+
runPlan("````markdown\n``` info\n## Example only\n````\n", 0);
|
|
329
|
+
});
|
|
330
|
+
test("rejects headings and prose outside a proper close", () => {
|
|
331
|
+
for (const suffix of [" ", " "]) {
|
|
332
|
+
const heading = runPlan(`\`\`\`markdown\n## Example only\n\`\`\`${suffix}\n## Example only\n`, 1);
|
|
333
|
+
expect(heading.stderr).toContain("\"## Example only\" after Close the program is not an appendix");
|
|
334
|
+
}
|
|
335
|
+
const prose = runPlan("```markdown\nThis prose has a mid-sentence colon: inside code\n```\nThis prose has a mid-sentence colon: bad.\n", 1);
|
|
336
|
+
expect(prose.stderr).toContain("mid-sentence colon");
|
|
337
|
+
});
|
|
338
|
+
test("rejects backtick info strings containing a backtick", () => {
|
|
339
|
+
const result = runPlan("```markdown `invalid`\n## Example only\n```\n", 1);
|
|
340
|
+
expect(result.stderr).toContain("\"## Example only\" after Close the program is not an appendix");
|
|
341
|
+
});
|
|
342
|
+
//#endregion
|
|
343
|
+
export {};
|