@webpieces/pr-gate 0.4.508 → 0.4.510
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/package.json +2 -2
- package/src/scripts/commands/cleanup-command.d.ts +6 -2
- package/src/scripts/commands/cleanup-command.js +9 -2
- package/src/scripts/commands/cleanup-command.js.map +1 -1
- package/src/scripts/pr-gate-app.d.ts +1 -1
- package/src/scripts/pr-gate-app.js +1 -1
- package/src/scripts/pr-gate-app.js.map +1 -1
- package/src/scripts/wp-cleanup.js +1 -1
- package/src/scripts/wp-cleanup.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/pr-gate",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.510",
|
|
4
4
|
"description": "Gated PR system: 3-point squash-merge, merge validation gate, and red/yellow/green PR dashboard. Standalone scripts, no Nx dependency required.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"directory": "packages/tooling/pr-gate"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@webpieces/rules-config": "0.4.
|
|
29
|
+
"@webpieces/rules-config": "0.4.510",
|
|
30
30
|
"@inversifyjs/binding-decorators": "1.1.5",
|
|
31
31
|
"inversify": "7.10.4",
|
|
32
32
|
"reflect-metadata": "0.2.2"
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { BranchArchiver, BranchReaper, RepoRootFinder } from '@webpieces/rules-config';
|
|
2
2
|
import { WorktreeCleanupSection } from './worktree-cleanup';
|
|
3
3
|
/**
|
|
4
|
-
* wp-cleanup: remove the dead WORKTREES, delete the local branches whose PR is already
|
|
5
|
-
*
|
|
4
|
+
* wp-cleanup: remove the dead WORKTREES, delete the local branches whose PR is already MERGED, then ASK
|
|
5
|
+
* about the ones that are merely probably-dead.
|
|
6
|
+
*
|
|
7
|
+
* A merged PR is the ONLY proof that reaps anything unattended. "Holds no commits" used to be a second
|
|
8
|
+
* proof and no longer is: it is equally the signature of a worktree an agent is working in right now,
|
|
9
|
+
* so it moved into the group this command ASKS about.
|
|
6
10
|
*
|
|
7
11
|
* WHY WORKTREES ARE PART OF THIS: they were the half that never got reaped. The verdicts existed —
|
|
8
12
|
* merged-branches.ts has been writing a full `DeletableWorktree[]` into the cache all along — and their
|
|
@@ -17,10 +17,17 @@ const CLASSIFICATION_HEADINGS = {
|
|
|
17
17
|
+ ' The work is not unique to this branch; only the commit objects are.',
|
|
18
18
|
[rules_config_1.CLASSIFICATION_NEVER_PROPOSED]: 'NEVER PROPOSED — no PR was ever opened, and these commits may be the ONLY copy in existence.\n'
|
|
19
19
|
+ ' Read the unique-commit counts before answering. This is the group to say no to if unsure.',
|
|
20
|
+
[rules_config_1.CLASSIFICATION_NO_COMMITS]: 'NO COMMITS YET — identical to origin/main, so deleting the REF loses nothing. But this is also\n'
|
|
21
|
+
+ ' exactly what a worktree looks like while somebody is still working in it and has not committed,\n'
|
|
22
|
+
+ ' so it is asked about rather than reaped. Say no to any of these you (or an agent) are using.',
|
|
20
23
|
};
|
|
21
24
|
/**
|
|
22
|
-
* wp-cleanup: remove the dead WORKTREES, delete the local branches whose PR is already
|
|
23
|
-
*
|
|
25
|
+
* wp-cleanup: remove the dead WORKTREES, delete the local branches whose PR is already MERGED, then ASK
|
|
26
|
+
* about the ones that are merely probably-dead.
|
|
27
|
+
*
|
|
28
|
+
* A merged PR is the ONLY proof that reaps anything unattended. "Holds no commits" used to be a second
|
|
29
|
+
* proof and no longer is: it is equally the signature of a worktree an agent is working in right now,
|
|
30
|
+
* so it moved into the group this command ASKS about.
|
|
24
31
|
*
|
|
25
32
|
* WHY WORKTREES ARE PART OF THIS: they were the half that never got reaped. The verdicts existed —
|
|
26
33
|
* merged-branches.ts has been writing a full `DeletableWorktree[]` into the cache all along — and their
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup-command.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/commands/cleanup-command.ts"],"names":[],"mappings":"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","sourcesContent":["import * as readline from 'readline';\nimport {\n BranchArchiver,\n BranchReaper,\n DeletableBranch,\n DeletableWorktree,\n ReapResult,\n ReapedBranch,\n RepoRootFinder,\n loadAndValidate,\n CLASSIFICATION_SUPERSEDED,\n CLASSIFICATION_CONTENT_IN_MAIN,\n CLASSIFICATION_NEVER_PROPOSED,\n PROMPTABLE_CLASSIFICATIONS,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nimport { WorktreeCleanupSection } from './worktree-cleanup';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n// One line of human-facing explanation per promptable classification, ordered most-safe first. These\n// replace the single string `no merged PR found — a human must decide`, which covered all three of\n// these situations identically and so told a human nothing they could act on.\nconst CLASSIFICATION_HEADINGS: Readonly<Record<string, string>> = {\n [CLASSIFICATION_SUPERSEDED]:\n 'SUPERSEDED — the PR was closed WITHOUT merging and later PRs have merged since. Near-certainly\\n'\n + ' the abandoned first attempt at work that landed under a different number. Safest group to delete.',\n [CLASSIFICATION_CONTENT_IN_MAIN]:\n 'CONTENT ALREADY IN MAIN — every commit has a patch-equivalent in origin/main (git cherry).\\n'\n + ' The work is not unique to this branch; only the commit objects are.',\n [CLASSIFICATION_NEVER_PROPOSED]:\n 'NEVER PROPOSED — no PR was ever opened, and these commits may be the ONLY copy in existence.\\n'\n + ' Read the unique-commit counts before answering. This is the group to say no to if unsure.',\n};\n\n/**\n * wp-cleanup: remove the dead WORKTREES, delete the local branches whose PR is already merged (or that\n * hold no commits), then ASK about the ones that are merely probably-dead.\n *\n * WHY WORKTREES ARE PART OF THIS: they were the half that never got reaped. The verdicts existed —\n * merged-branches.ts has been writing a full `DeletableWorktree[]` into the cache all along — and their\n * only consumer used them to BLOCK the next `git worktree add`, never to remove anything. Meanwhile a\n * live worktree pins its branch, so the branch was spared too. Two things the tooling could PROVE were\n * dead, accumulating forever, until the guard refused to create the next branch and the only remedy it\n * could offer was loosening its own cap. See WorktreeCleanupSection and WorktreeReaper.\n *\n * WHY a named command instead of the `git branch -D a b c` the guards used to print: an AI agent\n * reads a raw `-D` as destructive, so it asks permission and stops — which is exactly why branches\n * piled up despite the tooling knowing precisely which ones were dead. `pnpm wp-cleanup` is one\n * boring, allowlistable verb whose safety is a property of the command itself rather than of the\n * agent's judgement about a git flag.\n *\n * WHY IT NOW PROMPTS: sparing silently was the other half of the same problem. Every spared branch\n * reported the identical `no merged PR found — a human must decide`, so the human could not decide,\n * so nothing got deleted, so the pile grew until branch-creation-guard refused to make the next branch\n * and an agent went looking for a config knob to loosen. Shown a real classification with unique-commit\n * counts, the human in that session answered in five words: \"these should all be delete branches\".\n * The prompt is cheap because archiving happens FIRST — a yes costs a tag, not the history.\n *\n * All the danger still lives in the verdicts, not here — see BranchReaper for why every AUTOMATICALLY\n * deleted branch is provably dead and recoverable, and note that nothing in the prompted group is ever\n * deleted without an explicit typed answer.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class CleanupCommand {\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly branchReaper: BranchReaper,\n private readonly archiver: BranchArchiver,\n private readonly worktreeSection: WorktreeCleanupSection,\n ) {}\n\n /**\n * WORKTREES FIRST, then branches. The order is the fix, not a detail: a worktree HOLDS its branch,\n * so that branch is spared `in-use` (\"remove that worktree before deleting the branch\") and nothing\n * used to remove the worktree — so both piled up until branch-creation-guard refused to make the\n * next one. Reaping the worktree takes its branch with it, and the branch pass then recomputes its\n * verdicts from scratch against the post-removal truth.\n */\n async run(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n const retention = loadAndValidate(repoRoot).prGate.landPr.branchRetention;\n await this.cleanUpWorktrees(repoRoot, retention);\n await this.cleanUpBranches(repoRoot, retention);\n }\n\n private async cleanUpBranches(repoRoot: string, retention: string): Promise<void> {\n // No cache argument: wp-cleanup recomputes the verdicts itself. The file on disk is allowed to\n // go stale, and stale evidence is fine for BLOCKING but never for DELETING.\n const result = this.branchReaper.reap(repoRoot, 'wp-cleanup', null, retention);\n process.stdout.write(this.report(result));\n\n const promptable = this.promptable(result.spared);\n if (promptable.length === 0) return;\n process.stdout.write(this.classifiedBlock(promptable));\n const approved = await this.askWhichToDelete(promptable, 'branch');\n if (approved.length === 0) {\n process.stdout.write('\\nNothing deleted — the branches above were kept.\\n');\n return;\n }\n const second = this.branchReaper.reapApproved(repoRoot, 'wp-cleanup', approved, retention);\n process.stdout.write(this.report(second));\n }\n\n /**\n * Reap the provably-dead worktrees, then ASK about the probably-dead ones — the same two-tier\n * posture the branch half has, because it is the same verdict on the same branch.\n *\n * WorktreeReaper enforces the safety rails regardless of what is passed or answered: never the\n * primary clone, never the tree this command is running in, and never `--force` (git's refusal to\n * remove a worktree holding untracked or modified files is a feature, and forcing it is how a\n * cleanup command becomes a data-loss command).\n */\n private async cleanUpWorktrees(repoRoot: string, retention: string): Promise<void> {\n const verdicts = this.worktreeSection.verdicts(repoRoot);\n const dead = this.worktreeSection.provablyDead(verdicts);\n if (dead.length > 0) {\n process.stdout.write(\n this.worktreeSection.report(\n this.worktreeSection.reap(repoRoot, 'wp-cleanup', dead, retention)));\n }\n process.stdout.write(this.worktreeSection.sparedBlock(verdicts, dead));\n\n const promptable = this.worktreeSection.promptable(verdicts);\n if (promptable.length === 0) return;\n process.stdout.write(this.worktreeSection.promptBlock(promptable));\n const approved = await this.askWhichToDelete(promptable, 'worktree');\n if (approved.length === 0) {\n process.stdout.write('\\nNothing removed — the worktrees above were kept.\\n');\n return;\n }\n process.stdout.write(\n this.worktreeSection.report(\n this.worktreeSection.reap(repoRoot, 'wp-cleanup', approved, retention)));\n }\n\n private report(result: ReapResult): string {\n if (result.reaped.length === 0 && result.failed.length === 0) {\n return '\\n✅ Nothing to clean up — no local branch is provably dead.\\n';\n }\n\n let out = '\\n' + SEP + `🧹 Cleaned up ${String(result.reaped.length)} dead local branch(es)\\n` + SEP + '\\n';\n for (const entry of result.reaped) out += this.reapedLine(entry);\n\n if (result.failed.length > 0) {\n out += `\\n⚠️ ${String(result.failed.length)} branch(es) could not be deleted:\\n`;\n for (const entry of result.failed) out += ` ✗ ${entry.branch} — ${entry.error}\\n`;\n }\n\n // Printed even on success: a deletion the human cannot undo is a deletion they have to trust\n // blindly, and the whole argument for auto-cleanup is that they never have to.\n out += '\\nEvery deletion is logged with its pre-delete SHA in .webpieces/hooks/branch-mutations.log —\\n'\n + 'recover any of them with the `recover=` command on its line.\\n';\n return out;\n }\n\n private reapedLine(entry: ReapedBranch): string {\n const sha = entry.sha !== '' ? ` (was ${entry.sha.slice(0, 8)})` : '';\n // The archive tag is printed inline because it is the ONE thing that makes this delete casually\n // reversible — a name a human can type, rather than a sha they have to go dig out of a log.\n const archived = entry.archiveTag !== ''\n ? `\\n archived → ${entry.archiveTag} (restore: ${this.archiver.restoreCommand(entry.branch, entry.archiveTag)})`\n : '';\n return ` ✓ ${entry.branch}${sha} — ${entry.reason}${archived}\\n`;\n }\n\n /**\n * The spared branches a human can meaningfully rule on, grouped and ordered most-safe first.\n *\n * Branches spared as IN_USE — checked out in a worktree — are still excluded here, but the reason\n * is no longer \"git would simply refuse\". That premise died the moment worktrees became reapable.\n * The real reason is the ORDER in run(): the worktree pass has already run, so an IN_USE branch is\n * one of exactly two things. Either its worktree was dead and the reap took the branch with it (so\n * it is not in this list at all), or its worktree is one we are deliberately keeping — locked, held\n * open by uncommitted work, or the one we are standing in — and offering to delete the branch out\n * from under a live checkout is not a question worth asking. The pair is offered TOGETHER by the\n * worktree prompt, which shows both the path and the branch it holds, or it is not offered at all.\n */\n private promptable(spared: DeletableBranch[]): DeletableBranch[] {\n const out: DeletableBranch[] = [];\n for (const classification of PROMPTABLE_CLASSIFICATIONS) {\n for (const entry of spared) {\n if (entry.classification === classification) out.push(entry);\n }\n }\n return out;\n }\n\n // The classification table: what each group means, and per branch its unique-commit count — the one\n // number that says whether a yes costs nothing or costs the only copy of somebody's work.\n private classifiedBlock(promptable: DeletableBranch[]): string {\n let out = '\\n' + SEP + `🤔 ${String(promptable.length)} branch(es) are probably dead — your call\\n` + SEP;\n let current = '';\n for (let i = 0; i < promptable.length; i += 1) {\n const entry = promptable[i];\n if (entry.classification !== current) {\n current = entry.classification;\n out += `\\n${CLASSIFICATION_HEADINGS[current] ?? current}\\n\\n`;\n }\n const commits = entry.commits >= 0 ? `${String(entry.commits)} unique commit(s)` : 'unique commits unknown';\n out += ` [${String(i + 1)}] ${entry.branch}\\n ${commits} — ${entry.reason}\\n`;\n }\n return out;\n }\n\n /**\n * Ask which of the classified branches to delete. Answers: `all`, `none` (default), or a\n * comma/space-separated list of the numbers shown.\n *\n * NON-INTERACTIVE (no TTY — CI, a hook, a piped agent shell) answers NONE and says so. A prompt\n * nobody can see must never be read as consent, and this is the one place in the tooling where a\n * deletion is not backed by a proof.\n */\n private async askWhichToDelete<T extends DeletableBranch | DeletableWorktree>(\n promptable: T[], kind: string,\n ): Promise<T[]> {\n if (process.stdin.isTTY !== true) {\n process.stdout.write(\n `\\nNot a terminal — no ${kind} was deleted and nothing was assumed.\\n`\n + 'Run `pnpm wp-cleanup` in an interactive shell to answer, or delete individually.\\n',\n );\n return [];\n }\n const answer = (await this.question(\n `\\nDelete which ${kind}(s)? [all / none / e.g. \"1,3\"] (default none): `)).trim().toLowerCase();\n if (answer === '' || answer === 'none' || answer === 'n') return [];\n if (answer === 'all' || answer === 'a') return promptable;\n return this.pickByNumber(promptable, answer);\n }\n\n // Parse `1,3` / `1 3` into branches, ignoring anything out of range. An unparseable answer selects\n // nothing, which is the fail-safe direction for a question about deleting.\n private pickByNumber<T extends DeletableBranch | DeletableWorktree>(promptable: T[], answer: string): T[] {\n const out: T[] = [];\n for (const token of answer.split(/[\\s,]+/)) {\n const index = Number(token);\n if (!Number.isInteger(index) || index < 1 || index > promptable.length) continue;\n out.push(promptable[index - 1]);\n }\n return out;\n }\n\n // Seam: overridden in the spec so the prompt parsing is testable with no terminal.\n protected question(prompt: string): Promise<string> {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n return new Promise<string>((resolve: (value: string) => void): void => {\n rl.question(prompt, (answer: string): void => {\n rl.close();\n resolve(answer);\n });\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cleanup-command.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/commands/cleanup-command.ts"],"names":[],"mappings":"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","sourcesContent":["import * as readline from 'readline';\nimport {\n BranchArchiver,\n BranchReaper,\n DeletableBranch,\n DeletableWorktree,\n ReapResult,\n ReapedBranch,\n RepoRootFinder,\n loadAndValidate,\n CLASSIFICATION_SUPERSEDED,\n CLASSIFICATION_CONTENT_IN_MAIN,\n CLASSIFICATION_NEVER_PROPOSED,\n CLASSIFICATION_NO_COMMITS,\n PROMPTABLE_CLASSIFICATIONS,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nimport { WorktreeCleanupSection } from './worktree-cleanup';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n// One line of human-facing explanation per promptable classification, ordered most-safe first. These\n// replace the single string `no merged PR found — a human must decide`, which covered all three of\n// these situations identically and so told a human nothing they could act on.\nconst CLASSIFICATION_HEADINGS: Readonly<Record<string, string>> = {\n [CLASSIFICATION_SUPERSEDED]:\n 'SUPERSEDED — the PR was closed WITHOUT merging and later PRs have merged since. Near-certainly\\n'\n + ' the abandoned first attempt at work that landed under a different number. Safest group to delete.',\n [CLASSIFICATION_CONTENT_IN_MAIN]:\n 'CONTENT ALREADY IN MAIN — every commit has a patch-equivalent in origin/main (git cherry).\\n'\n + ' The work is not unique to this branch; only the commit objects are.',\n [CLASSIFICATION_NEVER_PROPOSED]:\n 'NEVER PROPOSED — no PR was ever opened, and these commits may be the ONLY copy in existence.\\n'\n + ' Read the unique-commit counts before answering. This is the group to say no to if unsure.',\n [CLASSIFICATION_NO_COMMITS]:\n 'NO COMMITS YET — identical to origin/main, so deleting the REF loses nothing. But this is also\\n'\n + ' exactly what a worktree looks like while somebody is still working in it and has not committed,\\n'\n + ' so it is asked about rather than reaped. Say no to any of these you (or an agent) are using.',\n};\n\n/**\n * wp-cleanup: remove the dead WORKTREES, delete the local branches whose PR is already MERGED, then ASK\n * about the ones that are merely probably-dead.\n *\n * A merged PR is the ONLY proof that reaps anything unattended. \"Holds no commits\" used to be a second\n * proof and no longer is: it is equally the signature of a worktree an agent is working in right now,\n * so it moved into the group this command ASKS about.\n *\n * WHY WORKTREES ARE PART OF THIS: they were the half that never got reaped. The verdicts existed —\n * merged-branches.ts has been writing a full `DeletableWorktree[]` into the cache all along — and their\n * only consumer used them to BLOCK the next `git worktree add`, never to remove anything. Meanwhile a\n * live worktree pins its branch, so the branch was spared too. Two things the tooling could PROVE were\n * dead, accumulating forever, until the guard refused to create the next branch and the only remedy it\n * could offer was loosening its own cap. See WorktreeCleanupSection and WorktreeReaper.\n *\n * WHY a named command instead of the `git branch -D a b c` the guards used to print: an AI agent\n * reads a raw `-D` as destructive, so it asks permission and stops — which is exactly why branches\n * piled up despite the tooling knowing precisely which ones were dead. `pnpm wp-cleanup` is one\n * boring, allowlistable verb whose safety is a property of the command itself rather than of the\n * agent's judgement about a git flag.\n *\n * WHY IT NOW PROMPTS: sparing silently was the other half of the same problem. Every spared branch\n * reported the identical `no merged PR found — a human must decide`, so the human could not decide,\n * so nothing got deleted, so the pile grew until branch-creation-guard refused to make the next branch\n * and an agent went looking for a config knob to loosen. Shown a real classification with unique-commit\n * counts, the human in that session answered in five words: \"these should all be delete branches\".\n * The prompt is cheap because archiving happens FIRST — a yes costs a tag, not the history.\n *\n * All the danger still lives in the verdicts, not here — see BranchReaper for why every AUTOMATICALLY\n * deleted branch is provably dead and recoverable, and note that nothing in the prompted group is ever\n * deleted without an explicit typed answer.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class CleanupCommand {\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly branchReaper: BranchReaper,\n private readonly archiver: BranchArchiver,\n private readonly worktreeSection: WorktreeCleanupSection,\n ) {}\n\n /**\n * WORKTREES FIRST, then branches. The order is the fix, not a detail: a worktree HOLDS its branch,\n * so that branch is spared `in-use` (\"remove that worktree before deleting the branch\") and nothing\n * used to remove the worktree — so both piled up until branch-creation-guard refused to make the\n * next one. Reaping the worktree takes its branch with it, and the branch pass then recomputes its\n * verdicts from scratch against the post-removal truth.\n */\n async run(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n const retention = loadAndValidate(repoRoot).prGate.landPr.branchRetention;\n await this.cleanUpWorktrees(repoRoot, retention);\n await this.cleanUpBranches(repoRoot, retention);\n }\n\n private async cleanUpBranches(repoRoot: string, retention: string): Promise<void> {\n // No cache argument: wp-cleanup recomputes the verdicts itself. The file on disk is allowed to\n // go stale, and stale evidence is fine for BLOCKING but never for DELETING.\n const result = this.branchReaper.reap(repoRoot, 'wp-cleanup', null, retention);\n process.stdout.write(this.report(result));\n\n const promptable = this.promptable(result.spared);\n if (promptable.length === 0) return;\n process.stdout.write(this.classifiedBlock(promptable));\n const approved = await this.askWhichToDelete(promptable, 'branch');\n if (approved.length === 0) {\n process.stdout.write('\\nNothing deleted — the branches above were kept.\\n');\n return;\n }\n const second = this.branchReaper.reapApproved(repoRoot, 'wp-cleanup', approved, retention);\n process.stdout.write(this.report(second));\n }\n\n /**\n * Reap the provably-dead worktrees, then ASK about the probably-dead ones — the same two-tier\n * posture the branch half has, because it is the same verdict on the same branch.\n *\n * WorktreeReaper enforces the safety rails regardless of what is passed or answered: never the\n * primary clone, never the tree this command is running in, and never `--force` (git's refusal to\n * remove a worktree holding untracked or modified files is a feature, and forcing it is how a\n * cleanup command becomes a data-loss command).\n */\n private async cleanUpWorktrees(repoRoot: string, retention: string): Promise<void> {\n const verdicts = this.worktreeSection.verdicts(repoRoot);\n const dead = this.worktreeSection.provablyDead(verdicts);\n if (dead.length > 0) {\n process.stdout.write(\n this.worktreeSection.report(\n this.worktreeSection.reap(repoRoot, 'wp-cleanup', dead, retention)));\n }\n process.stdout.write(this.worktreeSection.sparedBlock(verdicts, dead));\n\n const promptable = this.worktreeSection.promptable(verdicts);\n if (promptable.length === 0) return;\n process.stdout.write(this.worktreeSection.promptBlock(promptable));\n const approved = await this.askWhichToDelete(promptable, 'worktree');\n if (approved.length === 0) {\n process.stdout.write('\\nNothing removed — the worktrees above were kept.\\n');\n return;\n }\n process.stdout.write(\n this.worktreeSection.report(\n this.worktreeSection.reap(repoRoot, 'wp-cleanup', approved, retention)));\n }\n\n private report(result: ReapResult): string {\n if (result.reaped.length === 0 && result.failed.length === 0) {\n return '\\n✅ Nothing to clean up — no local branch is provably dead.\\n';\n }\n\n let out = '\\n' + SEP + `🧹 Cleaned up ${String(result.reaped.length)} dead local branch(es)\\n` + SEP + '\\n';\n for (const entry of result.reaped) out += this.reapedLine(entry);\n\n if (result.failed.length > 0) {\n out += `\\n⚠️ ${String(result.failed.length)} branch(es) could not be deleted:\\n`;\n for (const entry of result.failed) out += ` ✗ ${entry.branch} — ${entry.error}\\n`;\n }\n\n // Printed even on success: a deletion the human cannot undo is a deletion they have to trust\n // blindly, and the whole argument for auto-cleanup is that they never have to.\n out += '\\nEvery deletion is logged with its pre-delete SHA in .webpieces/hooks/branch-mutations.log —\\n'\n + 'recover any of them with the `recover=` command on its line.\\n';\n return out;\n }\n\n private reapedLine(entry: ReapedBranch): string {\n const sha = entry.sha !== '' ? ` (was ${entry.sha.slice(0, 8)})` : '';\n // The archive tag is printed inline because it is the ONE thing that makes this delete casually\n // reversible — a name a human can type, rather than a sha they have to go dig out of a log.\n const archived = entry.archiveTag !== ''\n ? `\\n archived → ${entry.archiveTag} (restore: ${this.archiver.restoreCommand(entry.branch, entry.archiveTag)})`\n : '';\n return ` ✓ ${entry.branch}${sha} — ${entry.reason}${archived}\\n`;\n }\n\n /**\n * The spared branches a human can meaningfully rule on, grouped and ordered most-safe first.\n *\n * Branches spared as IN_USE — checked out in a worktree — are still excluded here, but the reason\n * is no longer \"git would simply refuse\". That premise died the moment worktrees became reapable.\n * The real reason is the ORDER in run(): the worktree pass has already run, so an IN_USE branch is\n * one of exactly two things. Either its worktree was dead and the reap took the branch with it (so\n * it is not in this list at all), or its worktree is one we are deliberately keeping — locked, held\n * open by uncommitted work, or the one we are standing in — and offering to delete the branch out\n * from under a live checkout is not a question worth asking. The pair is offered TOGETHER by the\n * worktree prompt, which shows both the path and the branch it holds, or it is not offered at all.\n */\n private promptable(spared: DeletableBranch[]): DeletableBranch[] {\n const out: DeletableBranch[] = [];\n for (const classification of PROMPTABLE_CLASSIFICATIONS) {\n for (const entry of spared) {\n if (entry.classification === classification) out.push(entry);\n }\n }\n return out;\n }\n\n // The classification table: what each group means, and per branch its unique-commit count — the one\n // number that says whether a yes costs nothing or costs the only copy of somebody's work.\n private classifiedBlock(promptable: DeletableBranch[]): string {\n let out = '\\n' + SEP + `🤔 ${String(promptable.length)} branch(es) are probably dead — your call\\n` + SEP;\n let current = '';\n for (let i = 0; i < promptable.length; i += 1) {\n const entry = promptable[i];\n if (entry.classification !== current) {\n current = entry.classification;\n out += `\\n${CLASSIFICATION_HEADINGS[current] ?? current}\\n\\n`;\n }\n const commits = entry.commits >= 0 ? `${String(entry.commits)} unique commit(s)` : 'unique commits unknown';\n out += ` [${String(i + 1)}] ${entry.branch}\\n ${commits} — ${entry.reason}\\n`;\n }\n return out;\n }\n\n /**\n * Ask which of the classified branches to delete. Answers: `all`, `none` (default), or a\n * comma/space-separated list of the numbers shown.\n *\n * NON-INTERACTIVE (no TTY — CI, a hook, a piped agent shell) answers NONE and says so. A prompt\n * nobody can see must never be read as consent, and this is the one place in the tooling where a\n * deletion is not backed by a proof.\n */\n private async askWhichToDelete<T extends DeletableBranch | DeletableWorktree>(\n promptable: T[], kind: string,\n ): Promise<T[]> {\n if (process.stdin.isTTY !== true) {\n process.stdout.write(\n `\\nNot a terminal — no ${kind} was deleted and nothing was assumed.\\n`\n + 'Run `pnpm wp-cleanup` in an interactive shell to answer, or delete individually.\\n',\n );\n return [];\n }\n const answer = (await this.question(\n `\\nDelete which ${kind}(s)? [all / none / e.g. \"1,3\"] (default none): `)).trim().toLowerCase();\n if (answer === '' || answer === 'none' || answer === 'n') return [];\n if (answer === 'all' || answer === 'a') return promptable;\n return this.pickByNumber(promptable, answer);\n }\n\n // Parse `1,3` / `1 3` into branches, ignoring anything out of range. An unparseable answer selects\n // nothing, which is the fail-safe direction for a question about deleting.\n private pickByNumber<T extends DeletableBranch | DeletableWorktree>(promptable: T[], answer: string): T[] {\n const out: T[] = [];\n for (const token of answer.split(/[\\s,]+/)) {\n const index = Number(token);\n if (!Number.isInteger(index) || index < 1 || index > promptable.length) continue;\n out.push(promptable[index - 1]);\n }\n return out;\n }\n\n // Seam: overridden in the spec so the prompt parsing is testable with no terminal.\n protected question(prompt: string): Promise<string> {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n return new Promise<string>((resolve: (value: string) => void): void => {\n rl.question(prompt, (answer: string): void => {\n rl.close();\n resolve(answer);\n });\n });\n }\n}\n"]}
|
|
@@ -30,7 +30,7 @@ export declare class PrGateApp {
|
|
|
30
30
|
startUpsertPr(): Promise<void>;
|
|
31
31
|
/** `wp-finish-upsert-pr`: finalize merge, authoritative build gate, dashboard, create/update PR. */
|
|
32
32
|
finishUpsertPr(): Promise<void>;
|
|
33
|
-
/** `wp-cleanup`:
|
|
33
|
+
/** `wp-cleanup`: reap what is provably merged, and ASK about everything that merely looks dead. */
|
|
34
34
|
cleanup(): Promise<void>;
|
|
35
35
|
/** `wp-land-pr`: squash-merge this branch's PR into main with the compact commit body. */
|
|
36
36
|
landPr(): Promise<void>;
|
|
@@ -53,7 +53,7 @@ let PrGateApp = class PrGateApp {
|
|
|
53
53
|
finishUpsertPr() {
|
|
54
54
|
return this.finishUpsertPrCommand.run();
|
|
55
55
|
}
|
|
56
|
-
/** `wp-cleanup`:
|
|
56
|
+
/** `wp-cleanup`: reap what is provably merged, and ASK about everything that merely looks dead. */
|
|
57
57
|
cleanup() {
|
|
58
58
|
return this.cleanupCommand.run();
|
|
59
59
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr-gate-app.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/pr-gate-app.ts"],"names":[],"mappings":";;;;AAAA,0DAAyD;AACzD,yCAA2D;AAC3D,0EAAqE;AACrE,4EAAuE;AACvE,gFAA0E;AAC1E,kFAA4E;AAC5E,gEAA4D;AAC5D,gEAA2D;AAC3D,kEAA6D;AAC7D,kFAA4E;AAE5E;;;;;GAKG;AAGI,IAAM,SAAS,GAAf,MAAM,SAAS;IAEG;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IARrB,YACqB,kBAAsC,EACtC,mBAAwC,EACxC,oBAA0C,EAC1C,qBAA4C,EAC5C,cAA8B,EAC9B,aAA4B,EAC5B,cAA8B,EAC9B,qBAA4C;QAP5C,uBAAkB,GAAlB,kBAAkB,CAAoB;QACtC,wBAAmB,GAAnB,mBAAmB,CAAqB;QACxC,yBAAoB,GAApB,oBAAoB,CAAsB;QAC1C,0BAAqB,GAArB,qBAAqB,CAAuB;QAC5C,mBAAc,GAAd,cAAc,CAAgB;QAC9B,kBAAa,GAAb,aAAa,CAAe;QAC5B,mBAAc,GAAd,cAAc,CAAgB;QAC9B,0BAAqB,GAArB,qBAAqB,CAAuB;IAC9D,CAAC;IAEJ,kEAAkE;IAClE,WAAW;QACP,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC;IACzC,CAAC;IAED,gFAAgF;IAChF,YAAY;QACR,OAAO,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,CAAC;IAC1C,CAAC;IAED,4GAA4G;IAC5G,aAAa;QACT,OAAO,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,CAAC;IAC3C,CAAC;IAED,oGAAoG;IACpG,cAAc;QACV,OAAO,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAC;IAC5C,CAAC;IAED,
|
|
1
|
+
{"version":3,"file":"pr-gate-app.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/pr-gate-app.ts"],"names":[],"mappings":";;;;AAAA,0DAAyD;AACzD,yCAA2D;AAC3D,0EAAqE;AACrE,4EAAuE;AACvE,gFAA0E;AAC1E,kFAA4E;AAC5E,gEAA4D;AAC5D,gEAA2D;AAC3D,kEAA6D;AAC7D,kFAA4E;AAE5E;;;;;GAKG;AAGI,IAAM,SAAS,GAAf,MAAM,SAAS;IAEG;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IARrB,YACqB,kBAAsC,EACtC,mBAAwC,EACxC,oBAA0C,EAC1C,qBAA4C,EAC5C,cAA8B,EAC9B,aAA4B,EAC5B,cAA8B,EAC9B,qBAA4C;QAP5C,uBAAkB,GAAlB,kBAAkB,CAAoB;QACtC,wBAAmB,GAAnB,mBAAmB,CAAqB;QACxC,yBAAoB,GAApB,oBAAoB,CAAsB;QAC1C,0BAAqB,GAArB,qBAAqB,CAAuB;QAC5C,mBAAc,GAAd,cAAc,CAAgB;QAC9B,kBAAa,GAAb,aAAa,CAAe;QAC5B,mBAAc,GAAd,cAAc,CAAgB;QAC9B,0BAAqB,GAArB,qBAAqB,CAAuB;IAC9D,CAAC;IAEJ,kEAAkE;IAClE,WAAW;QACP,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC;IACzC,CAAC;IAED,gFAAgF;IAChF,YAAY;QACR,OAAO,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,CAAC;IAC1C,CAAC;IAED,4GAA4G;IAC5G,aAAa;QACT,OAAO,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,CAAC;IAC3C,CAAC;IAED,oGAAoG;IACpG,cAAc;QACV,OAAO,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAC;IAC5C,CAAC;IAED,mGAAmG;IACnG,OAAO;QACH,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC;IACrC,CAAC;IAED,0FAA0F;IAC1F,MAAM;QACF,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC;IACpC,CAAC;IAED;;;;OAIG;IACH,cAAc;QACV,OAAO,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAC;IAC5C,CAAC;IAED,+GAA+G;IAC/G,OAAO;QACH,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC;IACrC,CAAC;CACJ,CAAA;AAvDY,8BAAS;oBAAT,SAAS;IAFrB,IAAA,6BAAc,GAAE;IAChB,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;6CAGI,yCAAkB;QACjB,2CAAmB;QAClB,8CAAoB;QACnB,gDAAqB;QAC5B,gCAAc;QACf,+BAAa;QACZ,iCAAc;QACP,gDAAqB;GATxD,SAAS,CAuDrB","sourcesContent":["import { DocumentDesign } from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { StartUpdateCommand } from './commands/start-update-command';\nimport { FinishUpdateCommand } from './commands/finish-update-command';\nimport { StartUpsertPrCommand } from './commands/start-upsert-pr-command';\nimport { FinishUpsertPrCommand } from './commands/finish-upsert-pr-command';\nimport { CleanupCommand } from './commands/cleanup-command';\nimport { LandPrCommand } from './commands/land-pr-command';\nimport { CheckPrCommand } from './commands/check-pr-command';\nimport { ReviewUpsertPrCommand } from './commands/review-upsert-pr-command';\n\n/**\n * The pr-gate application root. `container.get(PrGateApp)` resolves the entire workflow DAG (the command\n * classes → the injected git/merge/dashboard services). `@DocumentDesign` marks it the\n * top-of-DAG the DI-design analyzer roots on, so `role:app` pr-gate draws its design. Each `bin/*`\n * entry resolves THIS and calls the matching command method.\n */\n@DocumentDesign()\n@injectable(bindingScopeValues.Singleton)\nexport class PrGateApp {\n constructor(\n private readonly startUpdateCommand: StartUpdateCommand,\n private readonly finishUpdateCommand: FinishUpdateCommand,\n private readonly startUpsertPrCommand: StartUpsertPrCommand,\n private readonly finishUpsertPrCommand: FinishUpsertPrCommand,\n private readonly cleanupCommand: CleanupCommand,\n private readonly landPrCommand: LandPrCommand,\n private readonly checkPrCommand: CheckPrCommand,\n private readonly reviewUpsertPrCommand: ReviewUpsertPrCommand,\n ) {}\n\n /** `wp-start-update`: 3-point squash-update from main (no PR). */\n startUpdate(): Promise<void> {\n return this.startUpdateCommand.run();\n }\n\n /** `wp-finish-update`: validate + finalize a resolved 3-point merge (no PR). */\n finishUpdate(): Promise<void> {\n return this.finishUpdateCommand.run();\n }\n\n /** `wp-start-upsert-pr`: update from main (3-point merge), hand off review.json. No build gate, no push. */\n startUpsertPr(): Promise<void> {\n return this.startUpsertPrCommand.run();\n }\n\n /** `wp-finish-upsert-pr`: finalize merge, authoritative build gate, dashboard, create/update PR. */\n finishUpsertPr(): Promise<void> {\n return this.finishUpsertPrCommand.run();\n }\n\n /** `wp-cleanup`: reap what is provably merged, and ASK about everything that merely looks dead. */\n cleanup(): Promise<void> {\n return this.cleanupCommand.run();\n }\n\n /** `wp-land-pr`: squash-merge this branch's PR into main with the compact commit body. */\n landPr(): Promise<void> {\n return this.landPrCommand.run();\n }\n\n /**\n * `wp-review-upsert-pr`: STAGE ② — validate the 3-point merge, run the build gate, extract this\n * branch's diff, and brief the reviewer subagents. Unlike the report-only command it replaces, this CAN\n * fail — before any reviewer is spawned, so a broken branch costs no reviewer tokens.\n */\n reviewUpsertPr(): Promise<void> {\n return this.reviewUpsertPrCommand.run();\n }\n\n /** `wp-check-pr`: READ-ONLY CI check — verify the PR body carries a valid HMAC gate token for its head sha. */\n checkPr(): Promise<void> {\n return this.checkPrCommand.run();\n }\n}\n"]}
|
|
@@ -10,7 +10,7 @@ const pr_gate_app_1 = require("./pr-gate-app");
|
|
|
10
10
|
// autobind self-binds every @injectable(Singleton) tooling class (replaces the buildProviderModule registry scan)
|
|
11
11
|
const container = new inversify_1.Container({ autobind: true });
|
|
12
12
|
// Reject `--help`/bogus flags BEFORE the app touches git — an ignored flag must never start the flow.
|
|
13
|
-
container.get(rules_config_1.CliArgs).assertNoArgs(new rules_config_1.CliUsage('wp-cleanup', '
|
|
13
|
+
container.get(rules_config_1.CliArgs).assertNoArgs(new rules_config_1.CliUsage('wp-cleanup', 'Remove worktrees and branches whose PR is already merged; ask about the rest.'));
|
|
14
14
|
await container.get(pr_gate_app_1.PrGateApp).cleanup();
|
|
15
15
|
});
|
|
16
16
|
//# sourceMappingURL=wp-cleanup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wp-cleanup.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/wp-cleanup.ts"],"names":[],"mappings":";;;AACA,4BAA0B;AAC1B,yCAAsC;AACtC,0DAAqE;AACrE,+CAA0C;AAE1C,mGAAmG;AACnG,IAAA,sBAAO,EAAC,KAAK,IAAmB,EAAE;IAC9B,kHAAkH;IAClH,MAAM,SAAS,GAAG,IAAI,qBAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,sGAAsG;IACtG,SAAS,CAAC,GAAG,CAAC,sBAAO,CAAC,CAAC,YAAY,CAAC,IAAI,uBAAQ,CAC5C,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"wp-cleanup.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/wp-cleanup.ts"],"names":[],"mappings":";;;AACA,4BAA0B;AAC1B,yCAAsC;AACtC,0DAAqE;AACrE,+CAA0C;AAE1C,mGAAmG;AACnG,IAAA,sBAAO,EAAC,KAAK,IAAmB,EAAE;IAC9B,kHAAkH;IAClH,MAAM,SAAS,GAAG,IAAI,qBAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,sGAAsG;IACtG,SAAS,CAAC,GAAG,CAAC,sBAAO,CAAC,CAAC,YAAY,CAAC,IAAI,uBAAQ,CAC5C,YAAY,EAAE,+EAA+E,CAAC,CAAC,CAAC;IACpG,MAAM,SAAS,CAAC,GAAG,CAAC,uBAAS,CAAC,CAAC,OAAO,EAAE,CAAC;AAC7C,CAAC,CAAC,CAAC","sourcesContent":["#!/usr/bin/env node\nimport 'reflect-metadata';\nimport { Container } from 'inversify';\nimport { runMain, CliArgs, CliUsage } from '@webpieces/rules-config';\nimport { PrGateApp } from './pr-gate-app';\n\n// Composition root: build the container and resolve the app so inversify constructs the whole DAG.\nrunMain(async (): Promise<void> => {\n // autobind self-binds every @injectable(Singleton) tooling class (replaces the buildProviderModule registry scan)\n const container = new Container({ autobind: true });\n // Reject `--help`/bogus flags BEFORE the app touches git — an ignored flag must never start the flow.\n container.get(CliArgs).assertNoArgs(new CliUsage(\n 'wp-cleanup', 'Remove worktrees and branches whose PR is already merged; ask about the rest.'));\n await container.get(PrGateApp).cleanup();\n});\n"]}
|