dsh-pstack 0.2.0-beta.1 → 0.2.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/LICENSE +2 -3
- package/README.md +148 -84
- package/README.zh.md +198 -0
- package/assets/pstack-config-entry.png +0 -0
- package/cordis.patch.yml +105 -27
- package/dist/client.mjs +2341 -0
- package/dist/client.mjs.map +1 -0
- package/dist/host-contract-CUPSpeNk.d.mts +22 -0
- package/dist/index.d.mts +49 -238
- package/dist/index.mjs +250 -123
- package/dist/models.d.mts +44 -0
- package/dist/models.mjs +201 -0
- package/dist/paths-BT77khwm.mjs +48 -0
- package/dist/pstack/LICENSE +21 -0
- package/dist/pstack/README.md +291 -0
- package/dist/pstack/agents/poteto-agent.md +9 -0
- package/{assets → dist/pstack}/docs/guide/01-setup.md +13 -11
- package/{assets → dist/pstack}/docs/guide/02-poteto-mode.md +4 -4
- package/{assets → dist/pstack}/docs/guide/04-design.md +4 -4
- package/{assets → dist/pstack}/docs/guide/05-build-and-clean.md +2 -2
- package/{assets → dist/pstack}/docs/guide/06-verify-and-ship.md +13 -3
- package/{assets → dist/pstack}/docs/guide/07-overnight.md +15 -13
- package/{assets → dist/pstack}/docs/guide/09-make-it-yours.md +6 -2
- package/{assets → dist/pstack}/docs/guide/10-recipes-and-pitfalls.md +7 -5
- package/{assets → dist/pstack}/docs/guide/README.md +1 -1
- package/dist/pstack/docs/harness/dsh.md +98 -0
- package/{assets → dist/pstack}/skills/architect/SKILL.md +12 -12
- package/dist/pstack/skills/arena/SKILL.md +50 -0
- package/{assets → dist/pstack}/skills/automate-me/SKILL.md +14 -14
- package/{assets → dist/pstack}/skills/benchmark-checklist/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/blast-radius/SKILL.md +4 -6
- package/{assets → dist/pstack}/skills/correct/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/create-formal-verification-skill/SKILL.md +4 -2
- package/{assets → dist/pstack}/skills/create-verification-skill/SKILL.md +5 -5
- package/dist/pstack/skills/figure-it-out/SKILL.md +54 -0
- package/dist/pstack/skills/how/SKILL.md +38 -0
- package/{assets → dist/pstack}/skills/in-your-own-words/SKILL.md +1 -1
- package/dist/pstack/skills/interrogate/SKILL.md +33 -0
- package/{assets → dist/pstack}/skills/ketchup/SKILL.md +3 -5
- package/dist/pstack/skills/ketchup-video/SKILL.md +29 -0
- package/{assets → dist/pstack}/skills/maintain-formal-verification-skill/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/maintain-verification-skill/SKILL.md +3 -3
- package/dist/pstack/skills/make-bot-ui/SKILL.md +117 -0
- package/dist/pstack/skills/no-comments/SKILL.md +26 -0
- package/{assets → dist/pstack}/skills/poteto-help/SKILL.md +34 -31
- package/{assets → dist/pstack}/skills/poteto-help/references/prompting.md +1 -1
- package/{assets → dist/pstack}/skills/poteto-help/references/recipes.md +3 -3
- package/{assets → dist/pstack}/skills/poteto-mode/SKILL.md +20 -19
- package/dist/pstack/skills/poteto-mode/playbooks/authoring-a-skill.md +12 -0
- package/dist/pstack/skills/poteto-mode/playbooks/autonomous-run.md +13 -0
- package/dist/pstack/skills/poteto-mode/playbooks/autopilot-full.md +25 -0
- package/dist/pstack/skills/poteto-mode/playbooks/autopilot-stack.md +8 -0
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/babysit.md +4 -4
- package/dist/pstack/skills/poteto-mode/playbooks/bug-fix.md +15 -0
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/eval.md +3 -3
- package/dist/pstack/skills/poteto-mode/playbooks/feature.md +21 -0
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/hillclimb.md +3 -3
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/investigation.md +2 -2
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/multi-phase-plan.md +23 -23
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/opening-a-pr.md +6 -6
- package/dist/pstack/skills/poteto-mode/playbooks/orchestrate.md +25 -0
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/pause-safely.md +2 -2
- package/dist/pstack/skills/poteto-mode/playbooks/perf-issue.md +25 -0
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/prototype.md +4 -4
- package/dist/pstack/skills/poteto-mode/playbooks/refactoring.md +16 -0
- package/dist/pstack/skills/poteto-mode/playbooks/runtime-forensics.md +11 -0
- package/dist/pstack/skills/poteto-mode/playbooks/session-pickup.md +11 -0
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/shipping.md +2 -2
- package/{assets → dist/pstack}/skills/poteto-mode/playbooks/trace-forensics.md +2 -2
- package/dist/pstack/skills/poteto-mode/playbooks/visual-parity.md +11 -0
- package/dist/pstack/skills/poteto-mode/playbooks/worktree-cleanup.md +12 -0
- package/{assets → dist/pstack}/skills/poteto-mode/scripts/check-plan.mjs +6 -8
- package/dist/pstack/skills/poteto-mode/scripts/check-plan.test.mjs +128 -0
- package/{assets/skills/poteto-mode/scripts/check-plan.test.mjs → dist/pstack/skills/poteto-mode/scripts/expect.mjs} +12 -101
- package/dist/pstack/skills/poteto-mode/scripts/support.mjs +93 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/cli.mjs +123 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/cli.test.mjs +217 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/fakes.test-helper.mjs +119 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/github.mjs +626 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/github.test.mjs +521 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/policy.mjs +657 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/policy.test.mjs +523 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/render.mjs +106 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/types.mjs +9 -0
- package/dist/pstack/skills/poteto-mode/scripts/watch-pr/watch-pr.mjs +5 -0
- package/{assets → dist/pstack}/skills/poteto-mode/scripts/worktree-audit.mjs +15 -22
- package/dist/pstack/skills/poteto-mode/scripts/worktree-audit.test.mjs +134 -0
- package/{assets → dist/pstack}/skills/principle-attack-the-premise/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-boundary-discipline/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-encode-lessons-in-structure/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-exhaust-the-design-space/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-experience-first/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-explain-the-number/SKILL.md +3 -2
- package/{assets → dist/pstack}/skills/principle-fix-root-causes/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-laziness-protocol/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-make-operations-idempotent/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-minimize-reader-load/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-model-the-domain/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-never-block-on-the-human/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-outcome-oriented-execution/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-prove-it-works/SKILL.md +2 -2
- package/{assets → dist/pstack}/skills/principle-redesign-from-first-principles/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-separate-before-serializing-shared-state/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-sequence-verifiable-units/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-subtract-before-you-add/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-test-behavior-not-implementation/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/principle-type-system-discipline/SKILL.md +1 -1
- package/dist/pstack/skills/recall/SKILL.md +37 -0
- package/dist/pstack/skills/reflect/SKILL.md +71 -0
- package/{assets → dist/pstack}/skills/reflect/references/divergent-reviewer.md +4 -4
- package/{assets → dist/pstack}/skills/reflect/references/judgment-reviewer.md +2 -2
- package/{assets → dist/pstack}/skills/reflect/references/synthesizer.md +5 -5
- package/{assets → dist/pstack}/skills/reflect/references/tooling-reviewer.md +2 -2
- package/dist/pstack/skills/setup-pstack/SKILL.md +80 -0
- package/{assets → dist/pstack}/skills/sharpen/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/show-me-your-work/SKILL.md +8 -8
- package/{assets → dist/pstack}/skills/show-me-your-work/scripts/log.mjs +1 -3
- package/dist/pstack/skills/show-me-your-work/scripts/log.test.mjs +56 -0
- package/dist/pstack/skills/statechart/SKILL.md +82 -0
- package/dist/pstack/skills/statechart/references/render.md +93 -0
- package/dist/pstack/skills/statechart/scripts/backends/cfg.mjs +31 -0
- package/dist/pstack/skills/statechart/scripts/backends/mermaid.mjs +186 -0
- package/dist/pstack/skills/statechart/scripts/backends/plain-text.mjs +62 -0
- package/dist/pstack/skills/statechart/scripts/backends/report.mjs +84 -0
- package/dist/pstack/skills/statechart/scripts/frontends/config.mjs +268 -0
- package/dist/pstack/skills/statechart/scripts/frontends/dot.mjs +67 -0
- package/dist/pstack/skills/statechart/scripts/frontends/marks.mjs +94 -0
- package/dist/pstack/skills/statechart/scripts/frontends/messages.mjs +48 -0
- package/dist/pstack/skills/statechart/scripts/frontends/spec.mjs +101 -0
- package/dist/pstack/skills/statechart/scripts/frontends/trace.mjs +39 -0
- package/dist/pstack/skills/statechart/scripts/frontends/value.mjs +180 -0
- package/dist/pstack/skills/statechart/scripts/frontends/verdict.mjs +40 -0
- package/dist/pstack/skills/statechart/scripts/frontends/view.mjs +99 -0
- package/dist/pstack/skills/statechart/scripts/ir.mjs +277 -0
- package/dist/pstack/skills/statechart/scripts/passes/annotate.mjs +92 -0
- package/dist/pstack/skills/statechart/scripts/passes/label.mjs +210 -0
- package/dist/pstack/skills/statechart/scripts/passes/lift.mjs +84 -0
- package/dist/pstack/skills/statechart/scripts/passes/loop-check.mjs +47 -0
- package/dist/pstack/skills/statechart/scripts/passes/paths.mjs +72 -0
- package/dist/pstack/skills/statechart/scripts/passes/reach.mjs +135 -0
- package/dist/pstack/skills/statechart/scripts/passes/region-check.mjs +82 -0
- package/dist/pstack/skills/statechart/scripts/passes/regions.mjs +268 -0
- package/dist/pstack/skills/statechart/scripts/pipeline.mjs +72 -0
- package/dist/pstack/skills/statechart/scripts/run.mjs +440 -0
- package/dist/pstack/skills/statechart/scripts/statechart.mjs +155 -0
- package/dist/pstack/skills/statechart/scripts/statechart.test.mjs +198 -0
- package/dist/pstack/skills/statechart/scripts/worker.mjs +31 -0
- package/dist/pstack/skills/swarm/SKILL.md +43 -0
- package/{assets → dist/pstack}/skills/tdd/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/teach/SKILL.md +2 -2
- package/{assets → dist/pstack}/skills/technical-writing/SKILL.md +1 -1
- package/{assets → dist/pstack}/skills/typescript-best-practices/SKILL.md +3 -6
- package/{assets → dist/pstack}/skills/why/SKILL.md +8 -8
- package/dist/remote.d.mts +27 -0
- package/dist/remote.mjs +234 -0
- package/dist/role-config-NNn5r1JX.mjs +415 -0
- package/dist/role-manifest-C3fUzjLI.d.mts +19 -0
- package/dist/role-wire-DZwTn2wF.d.mts +55 -0
- package/dist/roles.d.mts +18 -0
- package/dist/roles.mjs +323 -0
- package/dist/script.d.mts +9 -111
- package/dist/script.mjs +32 -329
- package/dist/skill.d.mts +38 -0
- package/dist/skill.mjs +183 -0
- package/dist/skills/authored/decision-audit/SKILL.md +103 -0
- package/dist/skills/authored/decision-audit/SKILL.zh.md +103 -0
- package/dist/skills/authored/decision-audit/references/recover-commit.md +17 -0
- package/dist/skills/authored/decision-audit/references/recover-commit.zh.md +17 -0
- package/dist/skills/authored/decision-write/SKILL.md +133 -0
- package/dist/skills/authored/decision-write/SKILL.zh.md +133 -0
- package/dist/skills/authored/decision-write/references/land.md +48 -0
- package/dist/skills/authored/decision-write/references/land.zh.md +48 -0
- package/dist/skills/authored/decision-write/references/scope.md +16 -0
- package/dist/skills/authored/decision-write/references/scope.zh.md +16 -0
- package/dist/skills/authored/docs-drift/SKILL.md +120 -0
- package/dist/skills/authored/docs-drift/SKILL.zh.md +120 -0
- package/dist/skills/authored/setup-project-docs/SKILL.md +63 -0
- package/dist/skills/authored/setup-project-docs/SKILL.zh.md +63 -0
- package/dist/skills/authored/setup-project-docs/references/decision-conventions.md +13 -0
- package/dist/skills/authored/setup-project-docs/references/decision-conventions.zh.md +13 -0
- package/dist/skills/vendored/retro/SKILL.md +44 -0
- package/dist/skills/vendored/retro/agents/openai.yaml +5 -0
- package/dist/skills/vendored/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/dist/skills/vendored/writing-for-agents/SKILL.md +81 -0
- package/dist/skills/vendored/writing-for-agents/agents/openai.yaml +3 -0
- package/dist/subagent.d.mts +54 -206
- package/dist/subagent.mjs +258 -963
- package/locale/en.json +3 -2
- package/locale/zh.json +3 -2
- package/package.json +93 -66
- package/assets/agents/poteto-agent.md +0 -8
- package/assets/docs/harness/dsh.md +0 -60
- package/assets/docs/pstack-models.example.md +0 -54
- package/assets/skills/arena/SKILL.md +0 -50
- package/assets/skills/figure-it-out/SKILL.md +0 -54
- package/assets/skills/how/SKILL.md +0 -38
- package/assets/skills/interrogate/SKILL.md +0 -71
- package/assets/skills/interrogate/references/code-quality-review.md +0 -47
- package/assets/skills/interrogate/references/lead-judgment.md +0 -39
- package/assets/skills/interrogate/references/reviewer-prompt.md +0 -62
- package/assets/skills/interrogate/references/rubric.md +0 -77
- package/assets/skills/no-comments/SKILL.md +0 -26
- package/assets/skills/poteto-mode/playbooks/authoring-a-skill.md +0 -12
- package/assets/skills/poteto-mode/playbooks/autonomous-run.md +0 -15
- package/assets/skills/poteto-mode/playbooks/autopilot-full.md +0 -25
- package/assets/skills/poteto-mode/playbooks/autopilot-stack.md +0 -12
- package/assets/skills/poteto-mode/playbooks/bug-fix.md +0 -15
- package/assets/skills/poteto-mode/playbooks/feature.md +0 -21
- package/assets/skills/poteto-mode/playbooks/orchestrate.md +0 -112
- package/assets/skills/poteto-mode/playbooks/perf-issue.md +0 -25
- package/assets/skills/poteto-mode/playbooks/refactoring.md +0 -16
- package/assets/skills/poteto-mode/playbooks/runtime-forensics.md +0 -11
- package/assets/skills/poteto-mode/playbooks/session-pickup.md +0 -11
- package/assets/skills/poteto-mode/playbooks/visual-parity.md +0 -11
- package/assets/skills/poteto-mode/playbooks/worktree-cleanup.md +0 -20
- package/assets/skills/poteto-mode/scripts/orch/orch.mjs +0 -4282
- package/assets/skills/poteto-mode/scripts/orch/orch.test.mjs +0 -2042
- package/assets/skills/poteto-mode/scripts/watch-pr/cli.test.mjs +0 -5101
- package/assets/skills/poteto-mode/scripts/watch-pr/github.test.mjs +0 -1177
- package/assets/skills/poteto-mode/scripts/watch-pr/policy.test.mjs +0 -1441
- package/assets/skills/poteto-mode/scripts/watch-pr/watch-pr.mjs +0 -4601
- package/assets/skills/recall/SKILL.md +0 -37
- package/assets/skills/reflect/SKILL.md +0 -69
- package/assets/skills/setup-pstack/SKILL.md +0 -67
- package/assets/skills/swarm/SKILL.md +0 -43
- package/dist/frontmatter-BQX3ekwP.mjs +0 -229
- package/dist/frontmatter-BQX3ekwP.mjs.map +0 -1
- package/dist/history.d.mts +0 -429
- package/dist/history.mjs +0 -1129
- package/dist/history.mjs.map +0 -1
- package/dist/host-contract-o47JH_r8.d.mts +0 -527
- package/dist/index.mjs.map +0 -1
- package/dist/paths-D_JlVmBk.mjs +0 -42
- package/dist/paths-D_JlVmBk.mjs.map +0 -1
- package/dist/script.mjs.map +0 -1
- package/dist/subagent.mjs.map +0 -1
- /package/{static/icon.png → assets/logo.png} +0 -0
- /package/{assets → dist/pstack}/agents/comment-sicko.md +0 -0
- /package/{assets → dist/pstack}/docs/guide/03-understand.md +0 -0
- /package/{assets → dist/pstack}/docs/guide/08-principles.md +0 -0
- /package/{assets → dist/pstack}/docs/guide/images/design.jpg +0 -0
- /package/{assets → dist/pstack}/docs/guide/images/overnight.jpg +0 -0
- /package/{assets → dist/pstack}/docs/guide/images/recipes.jpg +0 -0
- /package/{assets → dist/pstack}/docs/guide/images/router.jpg +0 -0
- /package/{assets → dist/pstack}/docs/guide/images/understanding.jpg +0 -0
- /package/{assets → dist/pstack}/docs/guide/images/verification.jpg +0 -0
- /package/{assets → dist/pstack}/skills/architect/references/design-red-flags.md +0 -0
- /package/{assets → dist/pstack}/skills/architect/references/rationale-template.md +0 -0
- /package/{assets → dist/pstack}/skills/architect/references/runner-prompt.md +0 -0
- /package/{assets → dist/pstack}/skills/bro/SKILL.md +0 -0
- /package/{assets → dist/pstack}/skills/create-verification-skill/references/feature-map-example/README.md +0 -0
- /package/{assets → dist/pstack}/skills/create-verification-skill/references/feature-map-example/create-note.md +0 -0
- /package/{assets → dist/pstack}/skills/create-verification-skill/references/feature-map-example/search.md +0 -0
- /package/{assets → dist/pstack}/skills/how/references/explainer-prompt.md +0 -0
- /package/{assets → dist/pstack}/skills/how/references/explorer-prompt.md +0 -0
- /package/{assets → dist/pstack}/skills/poteto-mode/references/bugbot-triage.md +0 -0
- /package/{assets → dist/pstack}/skills/principle-build-the-lever/SKILL.md +0 -0
- /package/{assets → dist/pstack}/skills/principle-foundational-thinking/SKILL.md +0 -0
- /package/{assets → dist/pstack}/skills/principle-guard-the-context-window/SKILL.md +0 -0
- /package/{assets → dist/pstack}/skills/show-me-your-work/references/decision-log-template.tsv +0 -0
- /package/{assets → dist/pstack}/skills/typescript-best-practices/references/patterns.md +0 -0
- /package/{assets → dist/pstack}/skills/unslop/SKILL.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/epistemics.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/investigator-prompt.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/source-playbook.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/code-archaeology.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/databricks.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/datadog.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/incident-postmortem.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/linear.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/notion.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/sentry.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/sources/slack.md +0 -0
- /package/{assets → dist/pstack}/skills/why/references/synthesizer-prompt.md +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import process from "node:process";
|
|
4
|
-
|
|
4
|
+
// runtime/skills/poteto-mode/scripts/check-plan.mjs
|
|
5
5
|
const RULE = "Tests alone are not sufficient verification. A PR is verified only when its unit, live, and perf boxes are all checked.";
|
|
6
6
|
const LANES = /Ten lanes on `[^`<>]+` at the PR head/;
|
|
7
7
|
const SUB_BLOCKS = [
|
|
@@ -22,11 +22,8 @@ const PROGRAM_H3 = [
|
|
|
22
22
|
"Verdict and merge",
|
|
23
23
|
"Boot recipe"
|
|
24
24
|
];
|
|
25
|
-
const PROGRAM_MARKERS = [
|
|
26
|
-
|
|
27
|
-
"schedule_create",
|
|
28
|
-
"status message"
|
|
29
|
-
];
|
|
25
|
+
const PROGRAM_MARKERS = ["git show origin/main:", "status message"];
|
|
26
|
+
const TICK_MARKERS = ["create_goal", "schedule_create"];
|
|
30
27
|
const HOW_TO_READ_MARKERS = [
|
|
31
28
|
"One box is one unit of work",
|
|
32
29
|
"names the evidence",
|
|
@@ -82,7 +79,7 @@ for (let i = start; i < raw.length; i++) {
|
|
|
82
79
|
code
|
|
83
80
|
});
|
|
84
81
|
if (code) continue;
|
|
85
|
-
const prose = text.replace(
|
|
82
|
+
const prose = text.replace(/(?<!`)(`+)(?!`)(.*?)(?<!`)\1(?!`)/g, "`").replace(/!\[[^\]]*\]\([^)]*\)/g, "").replace(/\]\([^)]*\)/g, "]");
|
|
86
83
|
if (/[\u2013\u2014]/.test(prose)) fail(n, "long dash");
|
|
87
84
|
if (/[\u2018\u2019\u201c\u201d]/.test(prose)) fail(n, "curly quote");
|
|
88
85
|
if (/: \S/.test(prose)) fail(n, "mid-sentence colon");
|
|
@@ -124,6 +121,8 @@ else {
|
|
|
124
121
|
else cursor = at + 1;
|
|
125
122
|
}
|
|
126
123
|
for (const marker of PROGRAM_MARKERS) if (!bodyText(program).includes(marker)) fail(program.n, `Program checklist lacks "${marker}"`);
|
|
124
|
+
const programText = bodyText(program);
|
|
125
|
+
if (!programText.includes("/loop 1h") && !TICK_MARKERS.every((marker) => programText.includes(marker))) fail(program.n, `Program checklist lacks "/loop 1h" or both "create_goal" and "schedule_create"`);
|
|
127
126
|
}
|
|
128
127
|
const close = find("Close the program");
|
|
129
128
|
if (!close) fail(1, "no \"## Close the program\" section");
|
|
@@ -216,5 +215,4 @@ for (const line of report) console.log(line);
|
|
|
216
215
|
console.log(`${prSections.length} PR sections, ${problems.length} problems`);
|
|
217
216
|
for (const p of problems) console.error(p);
|
|
218
217
|
process.exit(problems.length ? 1 : 0);
|
|
219
|
-
//#endregion
|
|
220
218
|
export {};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { expect } from "./expect.mjs";
|
|
2
|
+
import "./support.mjs";
|
|
3
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
import { test } from "node:test";
|
|
8
|
+
// runtime/skills/poteto-mode/scripts/check-plan.test.ts
|
|
9
|
+
const checker = join(import.meta.dirname, "check-plan.mjs");
|
|
10
|
+
const rule = "Tests alone are not sufficient verification. A PR is verified only when its unit, live, and perf boxes are all checked.";
|
|
11
|
+
const validPlan = `# Demo
|
|
12
|
+
|
|
13
|
+
## How to read this
|
|
14
|
+
One box is one unit of work and names the evidence. Check a box only when its evidence exists. See playbooks/.
|
|
15
|
+
${rule}
|
|
16
|
+
|
|
17
|
+
## Program checklist
|
|
18
|
+
### Arm the program
|
|
19
|
+
### Spawn owners
|
|
20
|
+
### PR mechanics
|
|
21
|
+
### Verdict and merge
|
|
22
|
+
### Boot recipe
|
|
23
|
+
\`git show origin/main:\` \`create_goal\` \`schedule_create\` status message
|
|
24
|
+
|
|
25
|
+
## PR 1
|
|
26
|
+
**Depends on.** None.
|
|
27
|
+
**Files.**
|
|
28
|
+
- [ ] Do the thing.
|
|
29
|
+
**Build.**
|
|
30
|
+
- [ ] Do the thing.
|
|
31
|
+
**You see.**
|
|
32
|
+
- [ ] Do the thing.
|
|
33
|
+
**Verify, unit.** ${rule}
|
|
34
|
+
- [ ] Check the thing.
|
|
35
|
+
**Verify, live.** ${rule} Ten lanes on \`example-model\` at the PR head
|
|
36
|
+
${Array.from({ length: 10 }, (_, i) => `- [ ] Lane ${i + 1}. Save \`lane-${i + 1}.png\`. Pass when evidence exists.`).join("\n")}
|
|
37
|
+
**Verify, perf.** ${rule}
|
|
38
|
+
- [ ] Metric. Recorded.
|
|
39
|
+
- [ ] Probe. Recorded.
|
|
40
|
+
- [ ] Baseline. Recorded.
|
|
41
|
+
- [ ] Rule. Recorded.
|
|
42
|
+
**Review gate.** None.
|
|
43
|
+
**Merge.**
|
|
44
|
+
- [ ] Merge when approved.
|
|
45
|
+
|
|
46
|
+
## Close the program
|
|
47
|
+
Close.
|
|
48
|
+
|
|
49
|
+
## Appendix Prototype evidence
|
|
50
|
+
Recorded.
|
|
51
|
+
`;
|
|
52
|
+
const runPlan = (example, expectedExit) => {
|
|
53
|
+
const directory = mkdtempSync(join(tmpdir(), "check-plan-test-"));
|
|
54
|
+
const file = join(directory, "plan.md");
|
|
55
|
+
writeFileSync(file, validPlan + example);
|
|
56
|
+
try {
|
|
57
|
+
const result = spawnSync("node", [checker, file], { encoding: "utf8" });
|
|
58
|
+
expect(result.status).toBe(expectedExit);
|
|
59
|
+
return result;
|
|
60
|
+
} finally {
|
|
61
|
+
rmSync(directory, {
|
|
62
|
+
recursive: true,
|
|
63
|
+
force: true
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
test("accepts ordinary, indented, and longer closing fences", () => {
|
|
68
|
+
for (const indent of [
|
|
69
|
+
"",
|
|
70
|
+
" ",
|
|
71
|
+
" ",
|
|
72
|
+
" "
|
|
73
|
+
]) runPlan(`${indent}\`\`\`markdown\n## Example only\n${indent}\`\`\`\n`, 0);
|
|
74
|
+
runPlan("```markdown\n## Example only\n````\n", 0);
|
|
75
|
+
});
|
|
76
|
+
test("accepts nested backticks and tilde info strings", () => {
|
|
77
|
+
runPlan("````markdown\n```markdown\n## Example only\n```\n````\n", 0);
|
|
78
|
+
runPlan("~~~markdown `info`\n## Example only\n ~~~\n", 0);
|
|
79
|
+
});
|
|
80
|
+
test("keeps mismatched, shorter, and suffixed closers inside the fence", () => {
|
|
81
|
+
runPlan("````markdown\n~~~\n## Example only\n````\n", 0);
|
|
82
|
+
runPlan("````markdown\n```\n## Example only\n````\n", 0);
|
|
83
|
+
runPlan("````markdown\n``` info\n## Example only\n````\n", 0);
|
|
84
|
+
});
|
|
85
|
+
test("rejects headings and prose outside a proper close", () => {
|
|
86
|
+
for (const suffix of [" ", " "]) {
|
|
87
|
+
const heading = runPlan(`\`\`\`markdown\n## Example only\n\`\`\`${suffix}\n## Example only\n`, 1);
|
|
88
|
+
expect(heading.stderr).toContain("\"## Example only\" after Close the program is not an appendix");
|
|
89
|
+
}
|
|
90
|
+
const prose = runPlan("```markdown\nThis prose has a mid-sentence colon: inside code\n```\nThis prose has a mid-sentence colon: bad.\n", 1);
|
|
91
|
+
expect(prose.stderr).toContain("mid-sentence colon");
|
|
92
|
+
});
|
|
93
|
+
test("rejects backtick info strings containing a backtick", () => {
|
|
94
|
+
const result = runPlan("```markdown `invalid`\n## Example only\n```\n", 1);
|
|
95
|
+
expect(result.stderr).toContain("\"## Example only\" after Close the program is not an appendix");
|
|
96
|
+
});
|
|
97
|
+
test("ignores prose punctuation inside matching multi-backtick code spans", () => {
|
|
98
|
+
for (const count of [
|
|
99
|
+
2,
|
|
100
|
+
3,
|
|
101
|
+
4
|
|
102
|
+
]) {
|
|
103
|
+
const delimiter = "`".repeat(count);
|
|
104
|
+
runPlan(`Use ${delimiter}echo \`x\`: “quoted” — example${delimiter}.\n`, 0);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
test("checks prose following a multi-backtick span and unmatched delimiters", () => {
|
|
108
|
+
const outside = runPlan("Use ``echo `x`: y``. Bad: prose.\n", 1);
|
|
109
|
+
expect(outside.stderr).toContain("mid-sentence colon");
|
|
110
|
+
const unmatched = runPlan("Use ``unclosed: bad` here.\n", 1);
|
|
111
|
+
expect(unmatched.stderr).toContain("mid-sentence colon");
|
|
112
|
+
});
|
|
113
|
+
test("accepts the upstream /loop tick or the DSH goal plus schedule tick", () => {
|
|
114
|
+
const upstream = validPlan.replace("`git show origin/main:` `create_goal` `schedule_create` status message", "`git show origin/main:` /loop 1h status message");
|
|
115
|
+
const directory = mkdtempSync(join(tmpdir(), "check-plan-test-"));
|
|
116
|
+
const file = join(directory, "plan.md");
|
|
117
|
+
writeFileSync(file, upstream);
|
|
118
|
+
try {
|
|
119
|
+
const result = spawnSync("node", [checker, file], { encoding: "utf8" });
|
|
120
|
+
expect(result.status).toBe(0);
|
|
121
|
+
} finally {
|
|
122
|
+
rmSync(directory, {
|
|
123
|
+
recursive: true,
|
|
124
|
+
force: true
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
export {};
|
|
@@ -1,11 +1,5 @@
|
|
|
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
1
|
import { inspect } from "node:util";
|
|
8
|
-
|
|
2
|
+
// runtime/skills/poteto-mode/scripts/expect.ts
|
|
9
3
|
/**
|
|
10
4
|
* A small `expect` for `node:test`.
|
|
11
5
|
*
|
|
@@ -16,8 +10,8 @@ import { inspect } from "node:util";
|
|
|
16
10
|
* port auditable against the calls that actually exist.
|
|
17
11
|
*
|
|
18
12
|
* Covered: `toBe`, `toEqual`, `toContain`, `toMatch`, `toThrow`, `toBeNull`,
|
|
19
|
-
* `toBeInstanceOf`, `toHaveLength`, `toEndWith`, `toMatchObject`,
|
|
20
|
-
* and `rejects` / `resolves` for promises.
|
|
13
|
+
* `toBeUndefined`, `toBeInstanceOf`, `toHaveLength`, `toEndWith`, `toMatchObject`,
|
|
14
|
+
* `.not` on each, and `rejects` / `resolves` for promises.
|
|
21
15
|
*
|
|
22
16
|
* Two differences from `bun:test`, stated so a failure is not misread:
|
|
23
17
|
*
|
|
@@ -146,6 +140,9 @@ function buildMatchers(actual, negate) {
|
|
|
146
140
|
toBeNull() {
|
|
147
141
|
checkPlain(actual === null, "toBeNull");
|
|
148
142
|
},
|
|
143
|
+
toBeUndefined() {
|
|
144
|
+
checkPlain(actual === void 0, "toBeUndefined");
|
|
145
|
+
},
|
|
149
146
|
toBeInstanceOf(expected) {
|
|
150
147
|
check(actual instanceof expected, "toBeInstanceOf", expected);
|
|
151
148
|
},
|
|
@@ -221,6 +218,10 @@ function buildAsyncMatchers(actual, mode, negate) {
|
|
|
221
218
|
buildMatchers(subject, false).toBeNull();
|
|
222
219
|
return;
|
|
223
220
|
}
|
|
221
|
+
if (name === "toBeUndefined") {
|
|
222
|
+
buildMatchers(subject, false).toBeUndefined();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
224
225
|
const matchers = buildMatchers(subject, false);
|
|
225
226
|
const matcher = matchers[name];
|
|
226
227
|
if (typeof matcher !== "function") throw new AssertionError(`unknown matcher ${name}`);
|
|
@@ -233,6 +234,7 @@ function buildAsyncMatchers(actual, mode, negate) {
|
|
|
233
234
|
toMatch: (expected) => wrap("toMatch", expected),
|
|
234
235
|
toThrow: (expected) => wrap("toThrow", expected),
|
|
235
236
|
toBeNull: () => wrap("toBeNull"),
|
|
237
|
+
toBeUndefined: () => wrap("toBeUndefined"),
|
|
236
238
|
toBeInstanceOf: (expected) => wrap("toBeInstanceOf", expected),
|
|
237
239
|
toHaveLength: (expected) => wrap("toHaveLength", expected),
|
|
238
240
|
toEndWith: (expected) => wrap("toEndWith", expected),
|
|
@@ -249,95 +251,4 @@ const expect = Object.assign((actual) => buildMatchers(actual, false), {
|
|
|
249
251
|
rejects: (actual) => buildAsyncMatchers(actual, "rejects", false),
|
|
250
252
|
resolves: (actual) => buildAsyncMatchers(actual, "resolves", false)
|
|
251
253
|
});
|
|
252
|
-
|
|
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 {};
|
|
254
|
+
export { AssertionError, deepEqual, expect };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { expect } from "./expect.mjs";
|
|
2
|
+
import { spawn as spawn$1, spawnSync as spawnSync$1 } from "node:child_process";
|
|
3
|
+
// runtime/skills/poteto-mode/scripts/support.ts
|
|
4
|
+
/**
|
|
5
|
+
* The one support module the ported upstream tests import.
|
|
6
|
+
*
|
|
7
|
+
* The five upstream test files were written for `bun:test` and use exactly two
|
|
8
|
+
* Bun facilities: its `expect`, and `Bun.spawnSync` / `Bun.spawn` for running git
|
|
9
|
+
* and short-lived processes. Rather than rewrite every call site, this module
|
|
10
|
+
* supplies those two shapes and the test patch only replaces the import line. The
|
|
11
|
+
* bodies below stay byte-for-byte the upstream text, so a future upstream change
|
|
12
|
+
* is still readable against this tree.
|
|
13
|
+
*
|
|
14
|
+
* `Bun.spawnSync` returns `{ exitCode, stdout, stderr, success }` with Buffer
|
|
15
|
+
* output and never throws on a nonzero exit; `node:child_process.spawnSync`
|
|
16
|
+
* returns `{ status, stdout, stderr }`. The adapter is the mapping between them,
|
|
17
|
+
* not a second implementation.
|
|
18
|
+
*
|
|
19
|
+
* `Bun.spawn` returns `{ pid, exited: Promise<number> }`. The adapter runs the
|
|
20
|
+
* child to completion and resolves `exited` with the exit status, which is what
|
|
21
|
+
* the one call site awaits.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Run a command to completion, Bun-shaped.
|
|
25
|
+
*
|
|
26
|
+
* A signal-terminated child reports `exitCode: 1` rather than `null`, so a test
|
|
27
|
+
* that formats the code never prints `undefined` for a killed process.
|
|
28
|
+
*/
|
|
29
|
+
function spawnSync(argv, options = {}) {
|
|
30
|
+
const [command, ...args] = argv;
|
|
31
|
+
if (command === void 0) throw new TypeError("spawnSync needs a command");
|
|
32
|
+
const result = spawnSync$1(command, args, {
|
|
33
|
+
cwd: options.cwd,
|
|
34
|
+
env: options.env,
|
|
35
|
+
stdio: [
|
|
36
|
+
options.stdin === "inherit" ? "inherit" : "pipe",
|
|
37
|
+
options.stdout === "inherit" ? "inherit" : "pipe",
|
|
38
|
+
options.stderr === "inherit" ? "inherit" : "pipe"
|
|
39
|
+
],
|
|
40
|
+
encoding: "buffer"
|
|
41
|
+
});
|
|
42
|
+
const exitCode = result.status ?? 1;
|
|
43
|
+
return {
|
|
44
|
+
exitCode,
|
|
45
|
+
stdout: Buffer.isBuffer(result.stdout) ? result.stdout : Buffer.alloc(0),
|
|
46
|
+
stderr: Buffer.isBuffer(result.stderr) ? result.stderr : Buffer.alloc(0),
|
|
47
|
+
success: exitCode === 0
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A child that exits immediately, as a real process.
|
|
52
|
+
*
|
|
53
|
+
* The current interpreter running an empty program is a process like any other: its
|
|
54
|
+
* `pid` is real and its exit status is 0.
|
|
55
|
+
*/
|
|
56
|
+
function immediateExit() {
|
|
57
|
+
return spawn$1(process.execPath, ["-e", ""], { stdio: "ignore" });
|
|
58
|
+
}
|
|
59
|
+
/** Whether `command` starts at all, checked without a shell. */
|
|
60
|
+
function resolves(command) {
|
|
61
|
+
return spawnSync$1(command, ["--version"], { stdio: "ignore" }).error === void 0;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Start a command and expose Bun's `{ pid, exited }` shape.
|
|
65
|
+
*
|
|
66
|
+
* The one upstream call is `spawn(["true"])`, used to obtain a pid that has already
|
|
67
|
+
* exited so the test can plant a stale lock. It reads `pid` *before* awaiting
|
|
68
|
+
* `exited`, so the pid must be real and already dead when this returns.
|
|
69
|
+
*
|
|
70
|
+
* That rules out "spawn it and fall back on failure": the failure is asynchronous, so
|
|
71
|
+
* the caller would read `-1`. The probe is synchronous instead, which lets this
|
|
72
|
+
* function choose the right child before returning. `true` is used when it exists and
|
|
73
|
+
* the current interpreter otherwise — a `PATH` without the usual Unix utilities is
|
|
74
|
+
* exactly the environment this release has to work in.
|
|
75
|
+
*/
|
|
76
|
+
function spawn(argv) {
|
|
77
|
+
const [command, ...args] = argv;
|
|
78
|
+
if (command === void 0) throw new TypeError("spawn needs a command");
|
|
79
|
+
const child = resolves(command) ? spawn$1(command, args, { stdio: "ignore" }) : immediateExit();
|
|
80
|
+
return {
|
|
81
|
+
pid: child.pid ?? -1,
|
|
82
|
+
exited: new Promise((resolve) => {
|
|
83
|
+
child.once("close", (code) => resolve(code ?? 1));
|
|
84
|
+
child.once("error", () => resolve(1));
|
|
85
|
+
})
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** The `Bun` global the upstream tests reference, limited to what they call. */
|
|
89
|
+
const Bun = {
|
|
90
|
+
spawnSync,
|
|
91
|
+
spawn
|
|
92
|
+
};
|
|
93
|
+
export { Bun, expect, spawn, spawnSync };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { nonEmpty, parsePrNumber } from "./types.mjs";
|
|
2
|
+
import { GhGitHubReader, WatcherQueryError, discoverStack, resolveContext } from "./github.mjs";
|
|
3
|
+
import { runQueued, runSimple, statusQueryVerdict, verdictFactory } from "./policy.mjs";
|
|
4
|
+
import { renderJson, renderPretty } from "./render.mjs";
|
|
5
|
+
import { setTimeout } from "node:timers/promises";
|
|
6
|
+
import { Command, CommanderError, InvalidArgumentError, Option } from "commander";
|
|
7
|
+
// runtime/skills/poteto-mode/scripts/watch-pr/cli.ts
|
|
8
|
+
function positiveNumber(value) {
|
|
9
|
+
const parsed = Number(value);
|
|
10
|
+
if (!Number.isFinite(parsed) || parsed <= 0) throw new InvalidArgumentError("must be greater than zero");
|
|
11
|
+
return parsed;
|
|
12
|
+
}
|
|
13
|
+
function nonNegativeNumber(value) {
|
|
14
|
+
const parsed = Number(value);
|
|
15
|
+
if (!Number.isFinite(parsed) || parsed < 0) throw new InvalidArgumentError("must be zero or greater");
|
|
16
|
+
return parsed;
|
|
17
|
+
}
|
|
18
|
+
function positiveInteger(value) {
|
|
19
|
+
const parsed = Number(value);
|
|
20
|
+
if (!Number.isInteger(parsed) || parsed <= 0) throw new InvalidArgumentError("must be a positive integer");
|
|
21
|
+
return parsed;
|
|
22
|
+
}
|
|
23
|
+
function prNumber(value) {
|
|
24
|
+
try {
|
|
25
|
+
return parsePrNumber(Number(value.replace(/^#/, "")));
|
|
26
|
+
} catch {
|
|
27
|
+
throw new InvalidArgumentError("must be a positive integer");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function stackPrList(value) {
|
|
31
|
+
const numbers = value.split(",").map((part) => prNumber(part.trim()));
|
|
32
|
+
if (new Set(numbers).size !== numbers.length) throw new InvalidArgumentError("contains a duplicate PR");
|
|
33
|
+
const parsed = nonEmpty(numbers);
|
|
34
|
+
if (parsed === null) throw new InvalidArgumentError("cannot be empty");
|
|
35
|
+
return parsed;
|
|
36
|
+
}
|
|
37
|
+
function parseArgs(argv, io) {
|
|
38
|
+
const program = new Command("watch-pr").description("Watch one pull request, a connected stack, or an immutable queued stack.\nJSON (NDJSON while polling) is the default; --pretty renders human text.").configureOutput({
|
|
39
|
+
writeOut: io.stdout,
|
|
40
|
+
writeErr: io.stderr
|
|
41
|
+
}).exitOverride().option("--owner <owner>", "GitHub repository owner").option("--repo <repo>", "GitHub repository name").option("--pr <number>", "pull request number", prNumber).addOption(new Option("--stack", "watch the connected open stack").default(false).conflicts("queuedStack")).option("--queued-stack", "watch the captured stack until all PRs merge", false).option("--stack-prs <n,...>", "frozen bottom-to-top queue (queued mode only)", stackPrList).option("--interval <seconds>", "poll interval", positiveNumber, 60).option("--sweep-interval <seconds>", "whole-stack sweep interval", positiveNumber, 300).option("--timeout <seconds>", "deadline; 0 disables it", nonNegativeNumber, 0).option("--max-query-errors <count>", "consecutive query-error budget", positiveInteger, 5).option("--status-only", "print one status table and exit 0", false).option("--allow-draft", "do not treat a draft as a merge gate", false).option("--pretty", "render human text instead of JSON", false);
|
|
42
|
+
program.parse(argv, { from: "user" });
|
|
43
|
+
const raw = program.opts();
|
|
44
|
+
if (raw.stackPrs !== void 0 && !raw.queuedStack) program.error("error: --stack-prs requires --queued-stack");
|
|
45
|
+
return {
|
|
46
|
+
owner: raw.owner ?? null,
|
|
47
|
+
repo: raw.repo ?? null,
|
|
48
|
+
pr: raw.pr ?? null,
|
|
49
|
+
mode: raw.queuedStack ? "queued-stack" : raw.stack ? "stack" : "single",
|
|
50
|
+
stackPrs: raw.stackPrs ?? [],
|
|
51
|
+
statusOnly: raw.statusOnly,
|
|
52
|
+
pretty: raw.pretty,
|
|
53
|
+
polling: {
|
|
54
|
+
interval: raw.interval,
|
|
55
|
+
sweepInterval: raw.sweepInterval,
|
|
56
|
+
timeout: raw.timeout,
|
|
57
|
+
maxQueryErrors: raw.maxQueryErrors,
|
|
58
|
+
allowDraft: raw.allowDraft
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function realRuntime() {
|
|
63
|
+
return {
|
|
64
|
+
reader: new GhGitHubReader(),
|
|
65
|
+
clock: {
|
|
66
|
+
now: () => performance.now() / 1e3,
|
|
67
|
+
observedAt: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
68
|
+
sleep: async (seconds) => {
|
|
69
|
+
await setTimeout(seconds * 1e3);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
stdout: (value) => process.stdout.write(value),
|
|
73
|
+
stderr: (value) => process.stderr.write(value)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async function main(argv, runtime = realRuntime()) {
|
|
77
|
+
let options;
|
|
78
|
+
try {
|
|
79
|
+
options = parseArgs(argv, runtime);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (!(error instanceof CommanderError)) throw error;
|
|
82
|
+
return error.exitCode === 0 ? 0 : 64;
|
|
83
|
+
}
|
|
84
|
+
const render = options.pretty ? renderPretty : renderJson;
|
|
85
|
+
const emit = (verdict) => runtime.stdout(render(verdict));
|
|
86
|
+
let contexts;
|
|
87
|
+
try {
|
|
88
|
+
const seed = await resolveContext({
|
|
89
|
+
reader: runtime.reader,
|
|
90
|
+
owner: options.owner,
|
|
91
|
+
repo: options.repo,
|
|
92
|
+
pr: options.pr ?? options.stackPrs[0] ?? null
|
|
93
|
+
});
|
|
94
|
+
contexts = nonEmpty(options.stackPrs.map((number) => ({
|
|
95
|
+
...seed,
|
|
96
|
+
number
|
|
97
|
+
}))) ?? (options.mode === "single" ? [seed] : await discoverStack(runtime.reader, seed));
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if (!(error instanceof WatcherQueryError)) throw error;
|
|
100
|
+
const verdict = statusQueryVerdict(verdictFactory(runtime.clock, options.mode), 1, error.failure);
|
|
101
|
+
runtime.stdout(render(verdict));
|
|
102
|
+
return verdict.exitCode;
|
|
103
|
+
}
|
|
104
|
+
const dependencies = {
|
|
105
|
+
reader: runtime.reader,
|
|
106
|
+
clock: runtime.clock,
|
|
107
|
+
emit
|
|
108
|
+
};
|
|
109
|
+
const verdict = options.mode === "queued-stack" && !options.statusOnly ? await runQueued({
|
|
110
|
+
dependencies,
|
|
111
|
+
contexts,
|
|
112
|
+
options: options.polling
|
|
113
|
+
}) : await runSimple({
|
|
114
|
+
dependencies,
|
|
115
|
+
contexts,
|
|
116
|
+
mode: options.mode,
|
|
117
|
+
statusOnly: options.statusOnly,
|
|
118
|
+
options: options.polling
|
|
119
|
+
});
|
|
120
|
+
runtime.stdout(render(verdict));
|
|
121
|
+
return verdict.exitCode;
|
|
122
|
+
}
|
|
123
|
+
export { main, parseArgs };
|