@osdlc/repo-cleaner 0.17.2 → 0.19.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +691 -305
  3. package/dist/branches.d.ts +37 -36
  4. package/dist/branches.js +199 -196
  5. package/dist/clean.d.ts +179 -0
  6. package/dist/clean.js +824 -0
  7. package/dist/cli.d.ts +12 -16
  8. package/dist/cli.js +367 -478
  9. package/dist/hooks.d.ts +8 -12
  10. package/dist/hooks.js +2 -2
  11. package/dist/index.d.ts +18 -25
  12. package/dist/index.js +16 -23
  13. package/dist/landing.d.ts +3 -3
  14. package/dist/landing.js +7 -7
  15. package/dist/plan.d.ts +76 -70
  16. package/dist/plan.js +82 -77
  17. package/dist/providers/github.d.ts +2 -1
  18. package/dist/providers/github.js +2 -1
  19. package/dist/providers/gitlab.d.ts +3 -2
  20. package/dist/providers/gitlab.js +14 -8
  21. package/dist/providers/index.d.ts +5 -4
  22. package/dist/providers/index.js +8 -5
  23. package/dist/providers/types.d.ts +8 -2
  24. package/dist/prs.d.ts +35 -7
  25. package/dist/prs.js +45 -12
  26. package/dist/receipt.d.ts +46 -57
  27. package/dist/receipt.js +52 -28
  28. package/dist/reference.d.ts +12 -0
  29. package/dist/reference.js +152 -0
  30. package/dist/remote-mirror.d.ts +9 -1
  31. package/dist/remote-mirror.js +19 -1
  32. package/dist/render.d.ts +66 -61
  33. package/dist/render.js +206 -255
  34. package/dist/resolve.d.ts +3 -3
  35. package/dist/resolve.js +4 -12
  36. package/dist/status-line.d.ts +1 -1
  37. package/dist/status-line.js +5 -5
  38. package/dist/step-board.d.ts +1 -1
  39. package/dist/step-board.js +2 -5
  40. package/dist/submodules.d.ts +120 -0
  41. package/dist/submodules.js +292 -0
  42. package/dist/sweep.d.ts +107 -64
  43. package/dist/sweep.js +333 -119
  44. package/dist/types.d.ts +294 -105
  45. package/dist/types.js +106 -54
  46. package/dist/util.d.ts +13 -0
  47. package/dist/util.js +36 -0
  48. package/dist/version.generated.d.ts +1 -1
  49. package/dist/version.generated.js +1 -1
  50. package/dist/worktrees.d.ts +21 -37
  51. package/dist/worktrees.js +198 -165
  52. package/package.json +5 -5
  53. package/dist/apply.d.ts +0 -131
  54. package/dist/apply.js +0 -541
@@ -25,8 +25,9 @@ interface MergeRequest {
25
25
  /** One merge request as a {@link PRInfo}, or null for a fork's or an unknown state's. */
26
26
  export declare function mergeRequestToPr(mr: MergeRequest): PRInfo | null;
27
27
  /**
28
- * A provider over `glab`. It needs `target.project`: without one there is
29
- * nothing to ask, and the listing answers null.
28
+ * A provider over `glab`. It needs `target.project`. A listing it cannot
29
+ * make or read throws with the reason: no project in the remote URL, `glab`'s
30
+ * own error, or a page that cannot be read.
30
31
  */
31
32
  export declare function gitlabProvider(glab: CommandRunner): PrProvider;
32
33
  export {};
@@ -6,7 +6,7 @@
6
6
  * an explicit host and project, so a checkout with several remotes is read
7
7
  * against the one the sweep measures.
8
8
  */
9
- import { parseJsonResult, runFailed, } from '@osdlc/command-seam';
9
+ import { parseJsonResult, runFailed, stderrOrError, } from '@osdlc/command-seam';
10
10
  import { PR_LIMIT } from "./github.js";
11
11
  const PER_PAGE = 100;
12
12
  /** GitLab's states, as the GitHub names classification reads. A locked MR is an open one mid-update. */
@@ -59,8 +59,9 @@ function succeeded(result) {
59
59
  return !runFailed(result);
60
60
  }
61
61
  /**
62
- * A provider over `glab`. It needs `target.project`: without one there is
63
- * nothing to ask, and the listing answers null.
62
+ * A provider over `glab`. It needs `target.project`. A listing it cannot
63
+ * make or read throws with the reason: no project in the remote URL, `glab`'s
64
+ * own error, or a page that cannot be read.
64
65
  */
65
66
  export function gitlabProvider(glab) {
66
67
  const run = (root, argv) => glab.run(argv, { cwd: root });
@@ -75,7 +76,7 @@ export function gitlabProvider(glab) {
75
76
  },
76
77
  listPrs(root, target, branch) {
77
78
  if (target.project === null)
78
- return null;
79
+ throw new Error('the remote URL names no GitLab project');
79
80
  const project = encodeURIComponent(target.project);
80
81
  const prs = [];
81
82
  for (let page = 1; (page - 1) * PER_PAGE < PR_LIMIT; page++) {
@@ -95,11 +96,16 @@ export function gitlabProvider(glab) {
95
96
  ]);
96
97
  // A failed, cut-short or unreadable page fails the listing: a missing
97
98
  // open MR would read as no MR.
98
- if (!succeeded(result))
99
- return null;
99
+ if (!succeeded(result)) {
100
+ // Not stdout: a page cut short is half a JSON listing, not a reason.
101
+ throw new Error(result.outputIncomplete === true
102
+ ? `page ${page} was cut short`
103
+ : stderrOrError(result) || `glab exited ${String(result.exitCode)}`);
104
+ }
100
105
  const listed = parseJsonResult(result);
101
- if (!listed.ok || !Array.isArray(listed.value))
102
- return null;
106
+ if (!listed.ok || !Array.isArray(listed.value)) {
107
+ throw new Error(`page ${page} is not a merge request list`);
108
+ }
103
109
  const rows = listed.value;
104
110
  for (const row of rows) {
105
111
  if (!isMergeRequest(row))
@@ -20,10 +20,11 @@ export declare function providers(clients: {
20
20
  */
21
21
  export declare function forgeTarget(url: string | null): ForgeTarget;
22
22
  /**
23
- * The provider `choice` names for `target`. `auto` reads the host: one with
24
- * `gitlab` in its name is GitLab, any other is GitHub, which is what the sweep
25
- * did before providers existed (`gh` answers nothing for a host it does not
26
- * know). `none` is no provider.
23
+ * The provider `choice` names for `target`. `auto` reads the host: none, for
24
+ * a remote that names no host (a local path or `file://`), which cannot have
25
+ * pull requests; one with `gitlab` in its name is GitLab; any other is
26
+ * GitHub, which is what the sweep did before providers existed (`gh` answers
27
+ * nothing for a host it does not know). `none` is no provider.
27
28
  */
28
29
  export declare function chooseProvider(choice: ProviderChoice, target: ForgeTarget): ProviderName | null;
29
30
  /** The chosen provider for a repository, probed; null for none. */
@@ -41,17 +41,20 @@ export function forgeTarget(url) {
41
41
  return { host: match[1].toLowerCase(), project: parts.join('/') };
42
42
  }
43
43
  /**
44
- * The provider `choice` names for `target`. `auto` reads the host: one with
45
- * `gitlab` in its name is GitLab, any other is GitHub, which is what the sweep
46
- * did before providers existed (`gh` answers nothing for a host it does not
47
- * know). `none` is no provider.
44
+ * The provider `choice` names for `target`. `auto` reads the host: none, for
45
+ * a remote that names no host (a local path or `file://`), which cannot have
46
+ * pull requests; one with `gitlab` in its name is GitLab; any other is
47
+ * GitHub, which is what the sweep did before providers existed (`gh` answers
48
+ * nothing for a host it does not know). `none` is no provider.
48
49
  */
49
50
  export function chooseProvider(choice, target) {
50
51
  if (choice === 'none')
51
52
  return null;
52
53
  if (choice !== 'auto')
53
54
  return choice;
54
- return target.host !== null && target.host.includes('gitlab') ? 'gitlab' : 'github';
55
+ if (target.host === null)
56
+ return null;
57
+ return target.host.includes('gitlab') ? 'gitlab' : 'github';
55
58
  }
56
59
  /** The chosen provider for a repository, probed; null for none. */
57
60
  export function probeProvider(choice, root, target, all) {
@@ -34,6 +34,11 @@ export interface ProviderStatus extends ForgeTarget, ProviderWords {
34
34
  available: boolean;
35
35
  /** The CLI has credentials for `host`. */
36
36
  authenticated: boolean;
37
+ /**
38
+ * Set by the sweep when the provider was asked and its listing failed: why,
39
+ * in the provider's words. Absent when it answered or was not asked.
40
+ */
41
+ listError?: string | undefined;
37
42
  }
38
43
  /** One source of pull request evidence. */
39
44
  export interface PrProvider {
@@ -46,8 +51,9 @@ export interface PrProvider {
46
51
  };
47
52
  /**
48
53
  * The repository's pull requests, fork PRs already dropped, in any order;
49
- * null when the listing failed. A failure is not "no PRs": the caller
50
- * classifies on git evidence alone.
54
+ * null, or a throw whose message says why, when the listing failed. A
55
+ * failure is not "no PRs": the caller classifies on git evidence alone and
56
+ * notes the failure on the repository.
51
57
  *
52
58
  * @param branch Narrow the listing to this exact head branch. The caller
53
59
  * checks the head again, so a provider that cannot narrow may list all.
package/dist/prs.d.ts CHANGED
@@ -7,24 +7,52 @@
7
7
  * `providers/`.
8
8
  */
9
9
  import type { PrProvider } from './providers/index.ts';
10
+ import type { ProviderStatus } from './providers/types.ts';
10
11
  import type { PRInfo, RepoInfo } from './types.ts';
12
+ /** Why the provider did not answer, and the words for it. */
13
+ export interface PrsUnchecked {
14
+ kind: 'not-installed' | 'not-signed-in' | 'list-failed';
15
+ /** For a sentence: "`gh` is not installed", "`gh pr list` failed". */
16
+ why: string;
17
+ }
11
18
  /**
12
- * Every same-repository pull request, keyed by head branch. Fork PRs are
13
- * dropped by the provider: a fork branch that shares a name proves nothing
14
- * about ours.
19
+ * Why open pull requests cannot be ruled out for this repository, or null
20
+ * when the provider answered. A missing or signed-out CLI was never asked, and
21
+ * a failed listing answered nothing; either way a remote branch with no known
22
+ * PR may still have an open one. Every sentence about a lookup that did not
23
+ * answer is built from this one answer.
24
+ */
25
+ export declare function prsUnchecked(status: ProviderStatus): PrsUnchecked | null;
26
+ /** A provider's answer for one repository: its pull requests, or why there are none to read. */
27
+ export interface PrLookup {
28
+ /** Pull requests keyed by head branch; empty when the lookup failed or was not made. */
29
+ prs: Map<string, PRInfo>;
30
+ /**
31
+ * Why the listing failed, in the provider's own words where it gave any;
32
+ * null when it answered, or when it was not asked at all.
33
+ */
34
+ error: string | null;
35
+ }
36
+ /**
37
+ * Every same-repository pull request, keyed by head branch, and why there are
38
+ * none when the listing failed. Fork PRs are dropped by the provider: a fork
39
+ * branch that shares a name proves nothing about ours.
15
40
  *
16
41
  * A branch can carry several PRs over its life. The one that decides a
17
42
  * verdict is the most live: an open PR means keep whatever else happened, and
18
43
  * a merged one means the work landed. Ties go to the higher number, so two
19
44
  * runs over unchanged state agree.
20
45
  *
21
- * An empty map is the honest answer when the provider's CLI is missing,
22
- * unauthenticated or failing, or when there is no provider. Classification
23
- * degrades to git evidence and the report says so.
46
+ * The map is empty when the provider's CLI is missing or unauthenticated, when
47
+ * there is no provider, and when the listing fails. A provider that answers
48
+ * null or throws has failed (a repository the signed-in account cannot see, a
49
+ * forge that is down, output that cannot be read), and `error` says why.
50
+ * Classification then degrades to git evidence, and the sweep notes it rather
51
+ * than losing every row to one listing.
24
52
  *
25
53
  * @param provider The provider to ask. It is asked only when it is the one
26
54
  * `repo.provider` names and that probe found it signed in.
27
55
  * @param branch Ask only for PRs whose head is exactly this branch; rows
28
56
  * for any other head are dropped here too.
29
57
  */
30
- export declare function fetchPrs(repo: RepoInfo, provider: PrProvider, branch?: string): Map<string, PRInfo>;
58
+ export declare function lookupPrs(repo: RepoInfo, provider: PrProvider, branch?: string): PrLookup;
package/dist/prs.js CHANGED
@@ -6,37 +6,70 @@
6
6
  * one listing covers. Which forge answers is the provider's business; see
7
7
  * `providers/`.
8
8
  */
9
+ import { errorText } from "./util.js";
10
+ /**
11
+ * Why open pull requests cannot be ruled out for this repository, or null
12
+ * when the provider answered. A missing or signed-out CLI was never asked, and
13
+ * a failed listing answered nothing; either way a remote branch with no known
14
+ * PR may still have an open one. Every sentence about a lookup that did not
15
+ * answer is built from this one answer.
16
+ */
17
+ export function prsUnchecked(status) {
18
+ if (!status.available)
19
+ return { kind: 'not-installed', why: `\`${status.tool}\` is not installed` };
20
+ if (!status.authenticated) {
21
+ return { kind: 'not-signed-in', why: `\`${status.tool}\` is not signed in` };
22
+ }
23
+ if (status.listError !== undefined) {
24
+ return { kind: 'list-failed', why: `\`${status.listing}\` failed` };
25
+ }
26
+ return null;
27
+ }
9
28
  /** Open outranks merged outranks closed, and a newer PR outranks an older. */
10
29
  function priority(state) {
11
30
  return { OPEN: 3, MERGED: 2, CLOSED: 1 }[state] ?? 0;
12
31
  }
13
32
  /**
14
- * Every same-repository pull request, keyed by head branch. Fork PRs are
15
- * dropped by the provider: a fork branch that shares a name proves nothing
16
- * about ours.
33
+ * Every same-repository pull request, keyed by head branch, and why there are
34
+ * none when the listing failed. Fork PRs are dropped by the provider: a fork
35
+ * branch that shares a name proves nothing about ours.
17
36
  *
18
37
  * A branch can carry several PRs over its life. The one that decides a
19
38
  * verdict is the most live: an open PR means keep whatever else happened, and
20
39
  * a merged one means the work landed. Ties go to the higher number, so two
21
40
  * runs over unchanged state agree.
22
41
  *
23
- * An empty map is the honest answer when the provider's CLI is missing,
24
- * unauthenticated or failing, or when there is no provider. Classification
25
- * degrades to git evidence and the report says so.
42
+ * The map is empty when the provider's CLI is missing or unauthenticated, when
43
+ * there is no provider, and when the listing fails. A provider that answers
44
+ * null or throws has failed (a repository the signed-in account cannot see, a
45
+ * forge that is down, output that cannot be read), and `error` says why.
46
+ * Classification then degrades to git evidence, and the sweep notes it rather
47
+ * than losing every row to one listing.
26
48
  *
27
49
  * @param provider The provider to ask. It is asked only when it is the one
28
50
  * `repo.provider` names and that probe found it signed in.
29
51
  * @param branch Ask only for PRs whose head is exactly this branch; rows
30
52
  * for any other head are dropped here too.
31
53
  */
32
- export function fetchPrs(repo, provider, branch) {
54
+ export function lookupPrs(repo, provider, branch) {
33
55
  const byHead = new Map();
34
56
  const status = repo.provider;
35
- if (status === null || status.name !== provider.name || !status.authenticated)
36
- return byHead;
37
- const rows = provider.listPrs(repo.root, status, branch);
57
+ if (status === null || status.name !== provider.name || !status.authenticated) {
58
+ return { prs: byHead, error: null };
59
+ }
60
+ let rows;
61
+ try {
62
+ rows = provider.listPrs(repo.root, status, branch);
63
+ }
64
+ catch (err) {
65
+ // A bug in the provider is not a listing that failed: let it fail loudly,
66
+ // as an error on the repository, rather than gate every remote branch.
67
+ if (err instanceof TypeError || err instanceof ReferenceError)
68
+ throw err;
69
+ return { prs: byHead, error: errorText(err) };
70
+ }
38
71
  if (rows === null)
39
- return byHead;
72
+ return { prs: byHead, error: 'the provider gave no reason' };
40
73
  for (const info of rows) {
41
74
  const head = info.headRef;
42
75
  if (!head)
@@ -54,5 +87,5 @@ export function fetchPrs(repo, provider, branch) {
54
87
  byHead.set(head, info);
55
88
  }
56
89
  }
57
- return byHead;
90
+ return { prs: byHead, error: null };
58
91
  }
package/dist/receipt.d.ts CHANGED
@@ -1,56 +1,53 @@
1
- /** What `applyPlan` did, step by step, rewritten after every step. */
1
+ /** What a run did, step by step, rewritten after every step. */
2
2
  import { z } from 'zod';
3
3
  /** The receipt format's version, written into every receipt. */
4
- export declare const RECEIPT_VERSION = 1;
5
- export declare const RowRefSchema: z.ZodObject<{
6
- category: z.ZodEnum<{
7
- local_branch: "local_branch";
8
- worktree: "worktree";
9
- remote_branch: "remote_branch";
10
- }>;
11
- id: z.ZodString;
12
- }, z.core.$strip>;
13
- export type RowRef = z.infer<typeof RowRefSchema>;
4
+ export declare const RECEIPT_VERSION = 2;
5
+ /**
6
+ * Why a row did not run:
7
+ *
8
+ * `gone` no longer in the sweep
9
+ * `blocked` the sweep now finds a blocker
10
+ * `keep` the sweep keeps it for structure; it has no action
11
+ * `held` a branch whose worktree this run does not remove
12
+ * `needs-pick` not judged `delete`; it runs only when picked
13
+ * `changed` its evidence moved since the plan
14
+ * `not-allowed` it carries a risk that was not allowed
15
+ * `duplicate` another checkout of the same remote deletes the same branch
16
+ * `spared` the same remote branch is spared through another checkout
17
+ */
18
+ export declare const DROP_REASONS: readonly ["gone", "blocked", "keep", "held", "needs-pick", "changed", "not-allowed", "duplicate", "spared"];
19
+ export type DropReason = (typeof DROP_REASONS)[number];
14
20
  export declare const DroppedSchema: z.ZodObject<{
15
- category: z.ZodEnum<{
16
- local_branch: "local_branch";
17
- worktree: "worktree";
18
- remote_branch: "remote_branch";
19
- }>;
20
- id: z.ZodString;
21
21
  repo: z.ZodString;
22
+ row: z.ZodCustom<`worktree:${string}` | `branch:${string}` | `remote:${string}`, `worktree:${string}` | `branch:${string}` | `remote:${string}`>;
22
23
  reason: z.ZodEnum<{
24
+ keep: "keep";
25
+ blocked: "blocked";
23
26
  gone: "gone";
24
- "not-deletable": "not-deletable";
25
- "needs-review": "needs-review";
26
- "facts-changed": "facts-changed";
27
- "not-authorized": "not-authorized";
27
+ held: "held";
28
+ "needs-pick": "needs-pick";
29
+ changed: "changed";
30
+ "not-allowed": "not-allowed";
28
31
  duplicate: "duplicate";
29
- kept: "kept";
32
+ spared: "spared";
30
33
  }>;
31
34
  detail: z.ZodString;
32
35
  }, z.core.$strip>;
33
36
  export type Dropped = z.infer<typeof DroppedSchema>;
37
+ export declare const STEP_ACTIONS: readonly ["verify-remote", "unlock-worktree", "remove-worktree", "delete-branch", "delete-remote", "prune-worktrees"];
34
38
  export declare const StepSchema: z.ZodObject<{
35
39
  repo: z.ZodString;
36
- row: z.ZodNullable<z.ZodObject<{
37
- category: z.ZodEnum<{
38
- local_branch: "local_branch";
39
- worktree: "worktree";
40
- remote_branch: "remote_branch";
41
- }>;
42
- id: z.ZodString;
43
- }, z.core.$strip>>;
40
+ row: z.ZodCustom<`worktree:${string}` | `branch:${string}` | `remote:${string}`, `worktree:${string}` | `branch:${string}` | `remote:${string}`>;
44
41
  action: z.ZodEnum<{
45
42
  "verify-remote": "verify-remote";
46
43
  "unlock-worktree": "unlock-worktree";
47
44
  "remove-worktree": "remove-worktree";
48
- "delete-local": "delete-local";
45
+ "delete-branch": "delete-branch";
49
46
  "delete-remote": "delete-remote";
50
47
  "prune-worktrees": "prune-worktrees";
51
48
  }>;
52
49
  command: z.ZodString;
53
- expected_sha: z.ZodNullable<z.ZodString>;
50
+ expectedSha: z.ZodNullable<z.ZodString>;
54
51
  result: z.ZodEnum<{
55
52
  pending: "pending";
56
53
  "would-run": "would-run";
@@ -59,58 +56,49 @@ export declare const StepSchema: z.ZodObject<{
59
56
  }>;
60
57
  error: z.ZodNullable<z.ZodString>;
61
58
  verification: z.ZodNullable<z.ZodString>;
59
+ gone: z.ZodOptional<z.ZodLiteral<true>>;
62
60
  }, z.core.$strip>;
63
61
  export type Step = z.infer<typeof StepSchema>;
64
62
  export declare const ReceiptSchema: z.ZodObject<{
65
- version: z.ZodLiteral<1>;
63
+ version: z.ZodLiteral<2>;
66
64
  plan: z.ZodString;
67
65
  status: z.ZodEnum<{
68
66
  failed: "failed";
69
67
  "dry-run": "dry-run";
70
- applying: "applying";
68
+ running: "running";
71
69
  complete: "complete";
72
70
  }>;
73
- started_at: z.ZodString;
74
- finished_at: z.ZodNullable<z.ZodString>;
71
+ startedAt: z.ZodString;
72
+ finishedAt: z.ZodNullable<z.ZodString>;
75
73
  dropped: z.ZodArray<z.ZodObject<{
76
- category: z.ZodEnum<{
77
- local_branch: "local_branch";
78
- worktree: "worktree";
79
- remote_branch: "remote_branch";
80
- }>;
81
- id: z.ZodString;
82
74
  repo: z.ZodString;
75
+ row: z.ZodCustom<`worktree:${string}` | `branch:${string}` | `remote:${string}`, `worktree:${string}` | `branch:${string}` | `remote:${string}`>;
83
76
  reason: z.ZodEnum<{
77
+ keep: "keep";
78
+ blocked: "blocked";
84
79
  gone: "gone";
85
- "not-deletable": "not-deletable";
86
- "needs-review": "needs-review";
87
- "facts-changed": "facts-changed";
88
- "not-authorized": "not-authorized";
80
+ held: "held";
81
+ "needs-pick": "needs-pick";
82
+ changed: "changed";
83
+ "not-allowed": "not-allowed";
89
84
  duplicate: "duplicate";
90
- kept: "kept";
85
+ spared: "spared";
91
86
  }>;
92
87
  detail: z.ZodString;
93
88
  }, z.core.$strip>>;
94
89
  steps: z.ZodArray<z.ZodObject<{
95
90
  repo: z.ZodString;
96
- row: z.ZodNullable<z.ZodObject<{
97
- category: z.ZodEnum<{
98
- local_branch: "local_branch";
99
- worktree: "worktree";
100
- remote_branch: "remote_branch";
101
- }>;
102
- id: z.ZodString;
103
- }, z.core.$strip>>;
91
+ row: z.ZodCustom<`worktree:${string}` | `branch:${string}` | `remote:${string}`, `worktree:${string}` | `branch:${string}` | `remote:${string}`>;
104
92
  action: z.ZodEnum<{
105
93
  "verify-remote": "verify-remote";
106
94
  "unlock-worktree": "unlock-worktree";
107
95
  "remove-worktree": "remove-worktree";
108
- "delete-local": "delete-local";
96
+ "delete-branch": "delete-branch";
109
97
  "delete-remote": "delete-remote";
110
98
  "prune-worktrees": "prune-worktrees";
111
99
  }>;
112
100
  command: z.ZodString;
113
- expected_sha: z.ZodNullable<z.ZodString>;
101
+ expectedSha: z.ZodNullable<z.ZodString>;
114
102
  result: z.ZodEnum<{
115
103
  pending: "pending";
116
104
  "would-run": "would-run";
@@ -119,6 +107,7 @@ export declare const ReceiptSchema: z.ZodObject<{
119
107
  }>;
120
108
  error: z.ZodNullable<z.ZodString>;
121
109
  verification: z.ZodNullable<z.ZodString>;
110
+ gone: z.ZodOptional<z.ZodLiteral<true>>;
122
111
  }, z.core.$strip>>;
123
112
  }, z.core.$strip>;
124
113
  export type Receipt = z.infer<typeof ReceiptSchema>;
package/dist/receipt.js CHANGED
@@ -1,46 +1,70 @@
1
- /** What `applyPlan` did, step by step, rewritten after every step. */
1
+ /** What a run did, step by step, rewritten after every step. */
2
2
  import { z } from 'zod';
3
- import { PLAN_CATEGORIES } from "./plan.js";
3
+ import { RowIdSchema } from "./plan.js";
4
4
  /** The receipt format's version, written into every receipt. */
5
- export const RECEIPT_VERSION = 1;
6
- export const RowRefSchema = z.object({ category: z.enum(PLAN_CATEGORIES), id: z.string() });
7
- export const DroppedSchema = RowRefSchema.extend({
5
+ export const RECEIPT_VERSION = 2;
6
+ /**
7
+ * Why a row did not run:
8
+ *
9
+ * `gone` no longer in the sweep
10
+ * `blocked` the sweep now finds a blocker
11
+ * `keep` the sweep keeps it for structure; it has no action
12
+ * `held` a branch whose worktree this run does not remove
13
+ * `needs-pick` not judged `delete`; it runs only when picked
14
+ * `changed` its evidence moved since the plan
15
+ * `not-allowed` it carries a risk that was not allowed
16
+ * `duplicate` another checkout of the same remote deletes the same branch
17
+ * `spared` the same remote branch is spared through another checkout
18
+ */
19
+ export const DROP_REASONS = [
20
+ 'gone',
21
+ 'blocked',
22
+ 'keep',
23
+ 'held',
24
+ 'needs-pick',
25
+ 'changed',
26
+ 'not-allowed',
27
+ 'duplicate',
28
+ 'spared',
29
+ ];
30
+ export const DroppedSchema = z.object({
8
31
  repo: z.string(),
9
- reason: z.enum([
10
- 'gone',
11
- 'not-deletable',
12
- 'needs-review',
13
- 'facts-changed',
14
- 'not-authorized',
15
- 'duplicate',
16
- 'kept',
17
- ]),
32
+ row: RowIdSchema,
33
+ reason: z.enum(DROP_REASONS),
18
34
  detail: z.string(),
19
35
  });
36
+ export const STEP_ACTIONS = [
37
+ 'verify-remote',
38
+ 'unlock-worktree',
39
+ 'remove-worktree',
40
+ 'delete-branch',
41
+ 'delete-remote',
42
+ 'prune-worktrees',
43
+ ];
20
44
  export const StepSchema = z.object({
21
45
  repo: z.string(),
22
- /** Null for the closing prune, which belongs to no row. */
23
- row: RowRefSchema.nullable(),
24
- action: z.enum([
25
- 'verify-remote',
26
- 'unlock-worktree',
27
- 'remove-worktree',
28
- 'delete-local',
29
- 'delete-remote',
30
- 'prune-worktrees',
31
- ]),
46
+ /** The row the step acts on. */
47
+ row: RowIdSchema,
48
+ action: z.enum(STEP_ACTIONS),
32
49
  command: z.string(),
33
- expected_sha: z.string().nullable(),
50
+ expectedSha: z.string().nullable(),
34
51
  result: z.enum(['pending', 'would-run', 'ok', 'failed']),
35
52
  error: z.string().nullable(),
36
53
  verification: z.string().nullable(),
54
+ /**
55
+ * The remote no longer listed the branch when the step ran: someone deleted
56
+ * it after the run's re-check. A `delete-remote` step so marked deleted
57
+ * nothing, and the run summary counts it apart. Absent otherwise.
58
+ */
59
+ gone: z.literal(true).optional(),
37
60
  });
38
61
  export const ReceiptSchema = z.object({
39
62
  version: z.literal(RECEIPT_VERSION),
63
+ /** Where the plan came from: a path, `(stdin)`, or `(swept)`. */
40
64
  plan: z.string(),
41
- status: z.enum(['dry-run', 'applying', 'complete', 'failed']),
42
- started_at: z.string(),
43
- finished_at: z.string().nullable(),
65
+ status: z.enum(['dry-run', 'running', 'complete', 'failed']),
66
+ startedAt: z.string(),
67
+ finishedAt: z.string().nullable(),
44
68
  dropped: z.array(DroppedSchema),
45
69
  steps: z.array(StepSchema),
46
70
  });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The full reference `--help --extended` prints: what every option means,
3
+ * the words the report uses, and which combinations are refused. The README
4
+ * says the same in its own layout; keep the two in step.
5
+ */
6
+ /** The extended help for each command: its own options, then the shared vocabulary. */
7
+ export declare const REFERENCE: {
8
+ readonly report: string;
9
+ readonly plan: string;
10
+ readonly clean: string;
11
+ readonly cache: "repo-cleaner cache [list | remove <url>... | remove --all]\n Lists or removes the clones kept for remote URL targets. A removed clone is\n cloned again by the next sweep of its URL; a plan that names it cannot run.";
12
+ };