@huseynsnmz/pi-board 0.15.3 → 0.16.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/CHANGELOG.md +13 -4
- package/CONTRIBUTING.md +7 -0
- package/README.md +53 -419
- package/SECURITY.md +19 -0
- package/docs/how-it-works.md +31 -0
- package/docs/reference.md +276 -0
- package/package.json +5 -11
- package/src/index.ts +1 -1
- package/src/runtime/service.mjs +12 -0
- package/src/ui/dashboard.ts +14 -8
- package/IMPLEMENTATION_PLAN.md +0 -920
- package/PRD.md +0 -484
- package/PROGRESS.md +0 -142
- package/VERIFY.md +0 -115
- package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
- package/docs/EXPLORATION.md +0 -187
- package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
- package/docs/RELEASE.md +0 -148
- package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
- package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
- package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
- package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
- package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
- package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
- package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
- package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
- package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
- package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
- package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
- package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
- package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
- package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
- package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
- package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
- package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
- package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
- package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
- package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
- package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
- package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
- package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
- package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
- package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
- package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
- package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
- package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
- package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
- package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
- package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
- package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
- package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
- package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
- package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
- package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
- package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
- package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
- package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
- package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
- package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
- package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
- package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
- package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
- package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
- package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
- package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
- package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
- package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
- package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
- package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
- package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
- package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
- package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
- package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
- package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
- package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
- package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
- package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
- package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
- package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
- package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
- package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
- package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
- package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
- package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
- package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
- package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
- package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
- package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
- package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
- package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
- package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
- package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
- package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
- package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
- package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
- package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
- package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
- package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
- package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
- package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
- package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
- package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
- package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
- package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
- package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
- package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
- package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
- package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
- package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
- package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
- package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
- package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
- package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
- package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
- package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
- package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
- package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
- package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
- package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
- package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
- package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
- package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
- package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
- package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
- package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
- package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
- package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
- package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
- package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
- package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
- package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
- package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
- package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
- package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
- package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
- package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
- package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
- package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
- package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
- package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
- package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
- package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
- package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
- package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
- package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
- package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
- package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
- package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
- package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
- package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
- package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
- package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
- package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
- package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
- package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
- package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
- package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
- package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
- package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
- package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
- package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
- package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
- package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
- package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
- package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
- package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
- package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
- package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
- package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
- package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
- package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
- package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
- package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
- package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
- package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
- package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
- package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
- package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
- package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
- package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
- package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
- package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
- package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
- package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
- package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
- package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
- package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
- package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
- package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
- package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
- package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
- package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
- package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
- package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
- package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
- package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
- package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
- package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
- package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
- package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
- package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
- package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
- package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
- package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
- package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
- package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
- package/scripts/budget-audit.mjs +0 -135
- package/scripts/release.mjs +0 -446
- package/scripts/release_helper.mjs +0 -309
- package/scripts/run-perf-gate.mjs +0 -40
- package/scripts/run-tests.mjs +0 -375
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Conventional-commit driven CHANGELOG helper (port of the jfox release
|
|
4
|
-
* helper, adapted to this repo's Node stack and `vX.Y.Z` tags).
|
|
5
|
-
*
|
|
6
|
-
* Modes:
|
|
7
|
-
* node scripts/release_helper.mjs [--dry-run] [patch|minor|major|X.Y.Z]
|
|
8
|
-
* Compute the next version (default patch) from package.json, parse
|
|
9
|
-
* conventional commits since the last `v*` tag, and print a JSON result
|
|
10
|
-
* with a `changelog_preview`. Without --dry-run, also insert the section
|
|
11
|
-
* at the top of CHANGELOG.md.
|
|
12
|
-
* node scripts/release_helper.mjs verify
|
|
13
|
-
* Check that every functional commit (feat/fix/refactor/docs/perf) in
|
|
14
|
-
* `last v* tag..HEAD` has its trailing (#N) present in the CHANGELOG top
|
|
15
|
-
* section. Exits 1 with a missing list when the changelog has drifted
|
|
16
|
-
* (e.g. a PR merged after the section was generated).
|
|
17
|
-
*
|
|
18
|
-
* The pure functions below are exported for unit tests; the CLI layer is the
|
|
19
|
-
* only place that touches git or the filesystem.
|
|
20
|
-
*/
|
|
21
|
-
import { execFileSync } from "node:child_process";
|
|
22
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
23
|
-
import { dirname, join } from "node:path";
|
|
24
|
-
import { fileURLToPath } from "node:url";
|
|
25
|
-
|
|
26
|
-
const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
27
|
-
const PACKAGE_JSON = join(REPO_ROOT, "package.json");
|
|
28
|
-
const CHANGELOG_MD = join(REPO_ROOT, "CHANGELOG.md");
|
|
29
|
-
const REPO_URL = "https://github.com/zhuxixi/pi-agent-board";
|
|
30
|
-
const COMPARE_BASE = `${REPO_URL}/compare`;
|
|
31
|
-
|
|
32
|
-
/** Conventional commit subject: type[(scope)][!]: message [(#N)] */
|
|
33
|
-
const CONVENTIONAL_RE =
|
|
34
|
-
/^(feat|fix|perf|refactor|docs|chore|test)(?:\(([^)]*)\))?!?:\s+(.+?)(?:\s+\(#(\d+)\))?$/;
|
|
35
|
-
/** Version-squash subjects produced by `npm version` PRs, e.g. `0.5.2 (#74)`. */
|
|
36
|
-
const VERSION_SQUASH_RE = /^\d+\.\d+\.\d+(?:[-.][\w.]+)?\s*\(#\d+\)$/;
|
|
37
|
-
/** Functional whitelist for verify (CHANGELOG-worthy types). */
|
|
38
|
-
const FUNCTIONAL_TYPES = new Set(["feat", "fix", "refactor", "docs", "perf"]);
|
|
39
|
-
/** A PR ref sitting at the very end of a subject, e.g. the `(#208)` in `... (#208)`. */
|
|
40
|
-
const TRAILING_PR_RE = /\s*\(#(\d+)\)\s*$/;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Strip trailing PR refs from a subject, keeping the rightmost as the PR.
|
|
44
|
-
*
|
|
45
|
-
* GitHub merge/squash subjects can carry the number twice (`... (#208) (#208)`),
|
|
46
|
-
* and the conventional regex above captures only the last one, leaving the
|
|
47
|
-
* earlier copy inside the message where the renderer appends a ref again
|
|
48
|
-
* (issue #218). Refs in the middle of a subject (`... (issue #189) (#196)`) are
|
|
49
|
-
* not tail refs and stay verbatim.
|
|
50
|
-
* @param {string} subject
|
|
51
|
-
* @returns {{message: string, pr: number|null}}
|
|
52
|
-
*/
|
|
53
|
-
export function stripTrailingPrRefs(subject) {
|
|
54
|
-
let text = String(subject ?? "").trim();
|
|
55
|
-
let pr = null;
|
|
56
|
-
for (let m = text.match(TRAILING_PR_RE); m; m = text.match(TRAILING_PR_RE)) {
|
|
57
|
-
// Rightmost ref wins: GitHub appends its own number after any that the
|
|
58
|
-
// author already wrote, so the first match is the authoritative one.
|
|
59
|
-
if (pr === null) pr = Number(m[1]);
|
|
60
|
-
text = text.slice(0, m.index).trimEnd();
|
|
61
|
-
}
|
|
62
|
-
return { message: text, pr };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Parse commit subject lines into changelog entries.
|
|
67
|
-
* @param {Iterable<string>} lines
|
|
68
|
-
* @returns {Array<{type: string, scope: string, message: string, pr: number|null}>}
|
|
69
|
-
*/
|
|
70
|
-
export function parseCommitLines(lines) {
|
|
71
|
-
const entries = [];
|
|
72
|
-
const seen = new Set();
|
|
73
|
-
for (const raw of lines) {
|
|
74
|
-
const s = String(raw ?? "").trim();
|
|
75
|
-
if (!s) continue;
|
|
76
|
-
if (VERSION_SQUASH_RE.test(s)) continue;
|
|
77
|
-
if (/bump\s+version/i.test(s)) continue;
|
|
78
|
-
if (s.startsWith("Merge ")) continue;
|
|
79
|
-
const { message: normalized, pr: tailPr } = stripTrailingPrRefs(s);
|
|
80
|
-
const m = normalized.match(CONVENTIONAL_RE);
|
|
81
|
-
const entry = m
|
|
82
|
-
? { type: m[1], scope: m[2] || "", message: m[3].trim(), pr: tailPr ?? (m[4] ? Number(m[4]) : null) }
|
|
83
|
-
: { type: "other", scope: "", message: normalized, pr: tailPr };
|
|
84
|
-
// PR-aware key: two commits with the same text merged under different
|
|
85
|
-
// PRs must both keep their number (issue #218).
|
|
86
|
-
const key = `${entry.type}|${entry.scope}|${entry.message}|${entry.pr ?? ""}`;
|
|
87
|
-
if (seen.has(key)) continue;
|
|
88
|
-
seen.add(key);
|
|
89
|
-
entries.push(entry);
|
|
90
|
-
}
|
|
91
|
-
return entries;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Render one changelog section for a version.
|
|
96
|
-
* @param {{version: string, date: string, entries: Array<{type: string, scope: string, message: string, pr: number|null}>, prevTag?: string|null}} opts
|
|
97
|
-
* @returns {string}
|
|
98
|
-
*/
|
|
99
|
-
export function generateChangelog({ version, date, entries, prevTag = null }) {
|
|
100
|
-
const groups = [
|
|
101
|
-
["Features", (e) => e.type === "feat"],
|
|
102
|
-
["Fixes", (e) => e.type === "fix"],
|
|
103
|
-
["Performance", (e) => e.type === "perf"],
|
|
104
|
-
["Changes", (e) => e.type !== "feat" && e.type !== "fix" && e.type !== "perf"],
|
|
105
|
-
];
|
|
106
|
-
const lines = [`## [${version}] - ${date}`, ""];
|
|
107
|
-
for (const [title, keep] of groups) {
|
|
108
|
-
const group = entries.filter(keep);
|
|
109
|
-
if (group.length === 0) continue;
|
|
110
|
-
lines.push(`### ${title}`, "");
|
|
111
|
-
for (const e of group) {
|
|
112
|
-
const scope = e.scope ? `**${e.scope}**: ` : "";
|
|
113
|
-
// Render defensively: an entry handed in with the ref already in its
|
|
114
|
-
// message (parse-time normalization missed, hand-authored fixtures)
|
|
115
|
-
// must still emit the number exactly once (issue #218).
|
|
116
|
-
const { message, pr: embeddedPr } = stripTrailingPrRefs(e.message);
|
|
117
|
-
const prNumber = e.pr ?? embeddedPr;
|
|
118
|
-
const pr = prNumber ? ` (#${prNumber})` : "";
|
|
119
|
-
lines.push(`- ${scope}${message}${pr}`);
|
|
120
|
-
}
|
|
121
|
-
lines.push("");
|
|
122
|
-
}
|
|
123
|
-
const prev = prevTag ? prevTag.replace(/^v/, "") : null;
|
|
124
|
-
if (prev) lines.push(`[${version}]: ${COMPARE_BASE}/v${prev}...v${version}`);
|
|
125
|
-
return lines.join("\n");
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* PR numbers referenced inside the first `## [...]` section of a CHANGELOG.
|
|
130
|
-
* @param {string} text
|
|
131
|
-
* @returns {Set<number>}
|
|
132
|
-
*/
|
|
133
|
-
export function changelogTopPrs(text) {
|
|
134
|
-
// Split before each `## [` heading and take the first real section — the
|
|
135
|
-
// JS port of jfox's regex uses split because Python's \Z (absolute end)
|
|
136
|
-
// has no JS equivalent and a `$` lookahead matches zero-width.
|
|
137
|
-
const sections = String(text ?? "").split(/(?=^##\s*\[)/m);
|
|
138
|
-
const first = sections.find((s) => /^##\s*\[/.test(s));
|
|
139
|
-
const section = first ?? String(text ?? "");
|
|
140
|
-
return new Set([...section.matchAll(/\(#(\d+)\)/g)].map((x) => Number(x[1])));
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Filter subject lines down to functional (CHANGELOG-worthy) commits:
|
|
145
|
-
* conventional whitelist types, skipping merges, version squashes, and
|
|
146
|
-
* `docs(changelog)` maintenance commits (their PRs fix the changelog itself
|
|
147
|
-
* and must not feed back into verify — infinite loop guard, jfox #333).
|
|
148
|
-
* @param {Iterable<string>} lines
|
|
149
|
-
* @returns {string[]}
|
|
150
|
-
*/
|
|
151
|
-
export function functionalLines(lines) {
|
|
152
|
-
const out = [];
|
|
153
|
-
for (const raw of lines) {
|
|
154
|
-
const s = String(raw ?? "").trim();
|
|
155
|
-
if (!s || VERSION_SQUASH_RE.test(s) || /bump\s+version/i.test(s)) continue;
|
|
156
|
-
if (s.startsWith("Merge ")) continue;
|
|
157
|
-
const m = s.match(/^(\w+)(?:\(([^)]*)\))?!?:/);
|
|
158
|
-
if (!m) continue;
|
|
159
|
-
if (!FUNCTIONAL_TYPES.has(m[1].toLowerCase())) continue;
|
|
160
|
-
if (m[1].toLowerCase() === "docs" && (m[2] || "").toLowerCase().includes("changelog")) continue;
|
|
161
|
-
out.push(s);
|
|
162
|
-
}
|
|
163
|
-
return out;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Pure verify: functional commit lines vs CHANGELOG text.
|
|
168
|
-
* @param {{lines: Iterable<string>, changelogText: string}} input
|
|
169
|
-
* @returns {{ok: boolean, missing: number[], extra: number[], functionalCommits: number}}
|
|
170
|
-
*/
|
|
171
|
-
export function verifyFrom({ lines, changelogText }) {
|
|
172
|
-
const funcPrs = new Set();
|
|
173
|
-
for (const s of functionalLines(lines)) {
|
|
174
|
-
const m = s.match(/\(#(\d+)\)\s*$/);
|
|
175
|
-
if (m) funcPrs.add(Number(m[1]));
|
|
176
|
-
}
|
|
177
|
-
const clPrs = changelogTopPrs(changelogText);
|
|
178
|
-
const missing = [...funcPrs].filter((n) => !clPrs.has(n)).sort((a, b) => a - b);
|
|
179
|
-
const extra = [...clPrs].filter((n) => !funcPrs.has(n)).sort((a, b) => a - b);
|
|
180
|
-
return { ok: missing.length === 0, missing, extra, functionalCommits: funcPrs.size };
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Whether the CHANGELOG already has a section for this exact version
|
|
185
|
-
* (apply idempotency guard — a re-apply must not duplicate the heading).
|
|
186
|
-
* @param {string} existing
|
|
187
|
-
* @param {string} version
|
|
188
|
-
* @returns {boolean}
|
|
189
|
-
*/
|
|
190
|
-
export function hasSection(existing, version) {
|
|
191
|
-
const escaped = version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
192
|
-
return new RegExp(`^##\\s*\\[${escaped}]`, "m").test(String(existing ?? ""));
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Insert a rendered section above the first existing `## ` heading (or append
|
|
197
|
-
* after the header when none exists yet).
|
|
198
|
-
* @param {string} existing
|
|
199
|
-
* @param {string} section
|
|
200
|
-
* @returns {string}
|
|
201
|
-
*/
|
|
202
|
-
export function insertSection(existing, section) {
|
|
203
|
-
const content = String(existing ?? "");
|
|
204
|
-
const insertAt = content.indexOf("\n## ");
|
|
205
|
-
if (insertAt === -1) return `${content.replace(/\s*$/, "")}\n\n${section}\n`;
|
|
206
|
-
return `${content.slice(0, insertAt + 1)}${section}\n\n${content.slice(insertAt + 1)}`;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/** @returns {string|null} latest `v*` tag, null when the repo has none, throws on git errors (fail-closed) */
|
|
210
|
-
function lastTag(root = REPO_ROOT) {
|
|
211
|
-
const tag = gitOut(["tag", "--list", "v*"], root);
|
|
212
|
-
const tags = tag.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
213
|
-
if (tags.length === 0) return null;
|
|
214
|
-
return gitOut(["describe", "--tags", "--abbrev=0", "--match", "v*"], root).trim() || null;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/** @param {string[]} args @param {string} root @returns {string} */
|
|
218
|
-
function gitOut(args, root) {
|
|
219
|
-
return execFileSync("git", args, { cwd: root, encoding: "utf8" });
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/** @returns {string[]} commit subjects in `lastTag..HEAD` (or all when no tag); throws on git errors */
|
|
223
|
-
function commitSubjects(tag, root = REPO_ROOT) {
|
|
224
|
-
const range = tag ? `${tag}..HEAD` : "HEAD";
|
|
225
|
-
return execFileSync("git", ["log", range, "--format=%s"], { cwd: root, encoding: "utf8" })
|
|
226
|
-
.split("\n")
|
|
227
|
-
.map((s) => s.trim())
|
|
228
|
-
.filter(Boolean);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/** @returns {string} current version from package.json */
|
|
232
|
-
function currentVersion() {
|
|
233
|
-
const pkg = JSON.parse(readFileSync(PACKAGE_JSON, "utf8"));
|
|
234
|
-
if (!/^\d+\.\d+\.\d+$/.test(pkg.version ?? "")) throw new Error(`package.json has no semver version: ${pkg.version}`);
|
|
235
|
-
return pkg.version;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/** @param {string} arg @param {string} current */
|
|
239
|
-
export function nextVersion(arg, current) {
|
|
240
|
-
const cur = current.split(".").map(Number);
|
|
241
|
-
if (arg === "major") return `${cur[0] + 1}.0.0`;
|
|
242
|
-
if (arg === "minor") return `${cur[0]}.${cur[1] + 1}.0`;
|
|
243
|
-
if (arg === "patch") return `${cur[0]}.${cur[1]}.${cur[2] + 1}`;
|
|
244
|
-
if (/^\d+\.\d+\.\d+$/.test(arg)) {
|
|
245
|
-
// Component-wise compare — lexicographic join(".") misorders "0.10.0" vs "0.5.2".
|
|
246
|
-
const parts = arg.split(".").map(Number);
|
|
247
|
-
for (let i = 0; i < 3; i++) {
|
|
248
|
-
if (parts[i] !== cur[i]) {
|
|
249
|
-
if (parts[i] < cur[i]) throw new Error(`version ${arg} is not greater than current ${current}`);
|
|
250
|
-
break;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
if (arg === current) throw new Error(`version ${arg} is not greater than current ${current}`);
|
|
254
|
-
return arg;
|
|
255
|
-
}
|
|
256
|
-
throw new Error(`invalid version or bump type: ${arg} (expected patch/minor/major or X.Y.Z)`);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function main(argv) {
|
|
260
|
-
if (argv[0] === "verify") {
|
|
261
|
-
let result;
|
|
262
|
-
try {
|
|
263
|
-
const tag = lastTag();
|
|
264
|
-
const lines = tag ? commitSubjects(tag) : [];
|
|
265
|
-
const changelogText = existsSync(CHANGELOG_MD) ? readFileSync(CHANGELOG_MD, "utf8") : "";
|
|
266
|
-
result = verifyFrom({ lines, changelogText });
|
|
267
|
-
result.lastTag = tag;
|
|
268
|
-
} catch (err) {
|
|
269
|
-
// Fail-closed: a git/filesystem failure must block the release, not pass it.
|
|
270
|
-
result = { ok: false, error: String(err?.message ?? err), missing: [], extra: [], functionalCommits: 0, lastTag: null };
|
|
271
|
-
}
|
|
272
|
-
console.log(JSON.stringify(result));
|
|
273
|
-
process.exitCode = result.ok ? 0 : 1;
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
const dryRun = argv.includes("--dry-run");
|
|
277
|
-
const versionArg = argv.find((a) => !a.startsWith("--")) ?? "patch";
|
|
278
|
-
try {
|
|
279
|
-
const current = currentVersion();
|
|
280
|
-
const version = nextVersion(versionArg, current);
|
|
281
|
-
const tag = lastTag();
|
|
282
|
-
const entries = parseCommitLines(tag ? commitSubjects(tag) : commitSubjects(null));
|
|
283
|
-
const date = new Date().toISOString().slice(0, 10);
|
|
284
|
-
const changelogPreview = generateChangelog({ version, date, entries, prevTag: tag });
|
|
285
|
-
const result = {
|
|
286
|
-
current_version: current,
|
|
287
|
-
new_version: version,
|
|
288
|
-
last_tag: tag,
|
|
289
|
-
changelog_summary: `${entries.filter((e) => e.type === "feat").length} features, ${entries.filter((e) => e.type === "fix").length} fixes, ${entries.filter((e) => !["feat", "fix"].includes(e.type)).length} changes`,
|
|
290
|
-
changelog_preview: changelogPreview,
|
|
291
|
-
};
|
|
292
|
-
if (!dryRun) {
|
|
293
|
-
const existing = existsSync(CHANGELOG_MD) ? readFileSync(CHANGELOG_MD, "utf8") : "# Changelog\n";
|
|
294
|
-
if (hasSection(existing, version)) {
|
|
295
|
-
throw new Error(`CHANGELOG.md already has a [${version}] section; remove the stale section before re-applying`);
|
|
296
|
-
}
|
|
297
|
-
writeFileSync(CHANGELOG_MD, insertSection(existing, changelogPreview));
|
|
298
|
-
result.files_modified = ["CHANGELOG.md"];
|
|
299
|
-
}
|
|
300
|
-
console.log(JSON.stringify(result, null, 1));
|
|
301
|
-
} catch (err) {
|
|
302
|
-
console.log(JSON.stringify({ error: String(err?.message ?? err) }));
|
|
303
|
-
process.exitCode = 1;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
308
|
-
main(process.argv.slice(2));
|
|
309
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Authoritative entry for the A11 perf gate (issue #121).
|
|
4
|
-
*
|
|
5
|
-
* Loudly refuses under coverage instrumentation: c8 sets NODE_V8_COVERAGE
|
|
6
|
-
* for its whole process tree, and instrumented latency measurements are
|
|
7
|
-
* invalid (2.5–6× inflation). Silently skipping here would manufacture a
|
|
8
|
-
* fake-green gate, so refusal is a nonzero exit with an explicit message.
|
|
9
|
-
*
|
|
10
|
-
* This script is the side-effect layer only — no decision logic, no
|
|
11
|
-
* measurement. The pure gate decision lives in test-support/perf-gate.mjs
|
|
12
|
-
* and serves the test file; do not merge the layers (spec §3.2/§4).
|
|
13
|
-
*/
|
|
14
|
-
import { spawn } from "node:child_process";
|
|
15
|
-
import { fileURLToPath } from "node:url";
|
|
16
|
-
|
|
17
|
-
if (process.env.NODE_V8_COVERAGE !== undefined) {
|
|
18
|
-
console.error(
|
|
19
|
-
"run-perf-gate: refusing to measure under coverage instrumentation " +
|
|
20
|
-
"(NODE_V8_COVERAGE is set). Perf assertions are only valid without c8; " +
|
|
21
|
-
"run `npm run test:perf` directly, outside any coverage wrapper.",
|
|
22
|
-
);
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const perfTest = fileURLToPath(new URL("../test/terminal-model-perf.test.mjs", import.meta.url));
|
|
27
|
-
// No --test-concurrency=1: the flag requires Node 21+ but package.json
|
|
28
|
-
// declares engines >=20, and it is a no-op while the gate runs a single
|
|
29
|
-
// file (spec D3, revised 2026-09-20). Re-add it when a second perf file
|
|
30
|
-
// lands — parallel measurement across files must stay impossible.
|
|
31
|
-
const child = spawn(
|
|
32
|
-
process.execPath,
|
|
33
|
-
["--test", perfTest],
|
|
34
|
-
{ stdio: "inherit", env: { ...process.env, AGENT_BOARD_PERF_GATE: "1" } },
|
|
35
|
-
);
|
|
36
|
-
child.on("error", (err) => {
|
|
37
|
-
console.error(`run-perf-gate: failed to spawn the perf suite: ${err.message}`);
|
|
38
|
-
process.exit(1);
|
|
39
|
-
});
|
|
40
|
-
child.on("exit", (code) => process.exit(code ?? 1));
|