@osdlc/repo-cleaner 0.17.2 → 0.18.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/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";
@@ -62,55 +59,45 @@ export declare const StepSchema: z.ZodObject<{
62
59
  }, z.core.$strip>;
63
60
  export type Step = z.infer<typeof StepSchema>;
64
61
  export declare const ReceiptSchema: z.ZodObject<{
65
- version: z.ZodLiteral<1>;
62
+ version: z.ZodLiteral<2>;
66
63
  plan: z.ZodString;
67
64
  status: z.ZodEnum<{
68
65
  failed: "failed";
69
66
  "dry-run": "dry-run";
70
- applying: "applying";
67
+ running: "running";
71
68
  complete: "complete";
72
69
  }>;
73
- started_at: z.ZodString;
74
- finished_at: z.ZodNullable<z.ZodString>;
70
+ startedAt: z.ZodString;
71
+ finishedAt: z.ZodNullable<z.ZodString>;
75
72
  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
73
  repo: z.ZodString;
74
+ row: z.ZodCustom<`worktree:${string}` | `branch:${string}` | `remote:${string}`, `worktree:${string}` | `branch:${string}` | `remote:${string}`>;
83
75
  reason: z.ZodEnum<{
76
+ keep: "keep";
77
+ blocked: "blocked";
84
78
  gone: "gone";
85
- "not-deletable": "not-deletable";
86
- "needs-review": "needs-review";
87
- "facts-changed": "facts-changed";
88
- "not-authorized": "not-authorized";
79
+ held: "held";
80
+ "needs-pick": "needs-pick";
81
+ changed: "changed";
82
+ "not-allowed": "not-allowed";
89
83
  duplicate: "duplicate";
90
- kept: "kept";
84
+ spared: "spared";
91
85
  }>;
92
86
  detail: z.ZodString;
93
87
  }, z.core.$strip>>;
94
88
  steps: z.ZodArray<z.ZodObject<{
95
89
  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>>;
90
+ row: z.ZodCustom<`worktree:${string}` | `branch:${string}` | `remote:${string}`, `worktree:${string}` | `branch:${string}` | `remote:${string}`>;
104
91
  action: z.ZodEnum<{
105
92
  "verify-remote": "verify-remote";
106
93
  "unlock-worktree": "unlock-worktree";
107
94
  "remove-worktree": "remove-worktree";
108
- "delete-local": "delete-local";
95
+ "delete-branch": "delete-branch";
109
96
  "delete-remote": "delete-remote";
110
97
  "prune-worktrees": "prune-worktrees";
111
98
  }>;
112
99
  command: z.ZodString;
113
- expected_sha: z.ZodNullable<z.ZodString>;
100
+ expectedSha: z.ZodNullable<z.ZodString>;
114
101
  result: z.ZodEnum<{
115
102
  pending: "pending";
116
103
  "would-run": "would-run";
package/dist/receipt.js CHANGED
@@ -1,46 +1,64 @@
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(),
37
54
  });
38
55
  export const ReceiptSchema = z.object({
39
56
  version: z.literal(RECEIPT_VERSION),
57
+ /** Where the plan came from: a path, `(stdin)`, or `(swept)`. */
40
58
  plan: z.string(),
41
- status: z.enum(['dry-run', 'applying', 'complete', 'failed']),
42
- started_at: z.string(),
43
- finished_at: z.string().nullable(),
59
+ status: z.enum(['dry-run', 'running', 'complete', 'failed']),
60
+ startedAt: z.string(),
61
+ finishedAt: z.string().nullable(),
44
62
  dropped: z.array(DroppedSchema),
45
63
  steps: z.array(StepSchema),
46
64
  });
@@ -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
+ };
@@ -0,0 +1,148 @@
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
+ const TARGETS = `Targets
7
+ A target is what to sweep:
8
+ a checkout that repository; a linked worktree sweeps the repository it belongs to
9
+ a directory every checkout found under it, three levels deep at most
10
+ a remote URL its remote branches only, through a clone kept in the cache
11
+ directory (https://, ssh://, file://, or user@host:path)
12
+ No target sweeps the checkout you are in. A target never fans out further than
13
+ it says.`;
14
+ const SWEEP = `Sweep options (the report, plan and clean)
15
+ --remote Also judge remote branches. Turns on the network: fetches
16
+ each remote first and asks GitHub or GitLab about pull
17
+ requests. Slower.
18
+ --offline With --remote: do not fetch; judge against the
19
+ remote-tracking refs already on disk.
20
+ --only <kinds> Only these kinds of row: worktree, branch, remote,
21
+ comma-separated. Default: worktree,branch, plus remote with
22
+ --remote. Naming remote implies --remote.
23
+ --branch <name|HEAD> Only the rows of one branch: its worktree, its local branch,
24
+ and its remote branch with --remote. HEAD is the branch
25
+ checked out at the target. Never the default branch.
26
+ --against <remote> The remote whose default branch counts as "merged into".
27
+ Default: origin, then the first remote.
28
+ --provider <name> With --remote: where pull requests come from. auto (by the
29
+ remote's host), github, gitlab, or none to judge on git
30
+ evidence alone.
31
+ --no-progress No progress line on stderr.`;
32
+ const REPORT = `The report: repo-cleaner [target...]
33
+ Deletes nothing. Lists each row with its verdict, why, the risks it carries,
34
+ and the command that would delete it.
35
+ --all Also show keep rows. By default only delete, review and
36
+ blocked rows are listed. Only with --format text.
37
+ --format text The report. The default.
38
+ --format summary Counts per repository: total, merged, delete, prune,
39
+ review, blocked, keep.
40
+ --format commands Only the commands of delete rows that carry no risk.
41
+ --format json Everything, evidence included, for scripts.`;
42
+ const PLAN = `repo-cleaner plan [target...]
43
+ Sweeps and saves every row, each with a fingerprint of the evidence behind its
44
+ verdict, so it can be reviewed now and run later with clean --plan.
45
+ -o, --out <file> Where the plan goes; - (the default) is stdout.`;
46
+ const CLEAN = `repo-cleaner clean [target...]
47
+ Sweeps (or reads --plan), checks every row again, shows the steps, asks, then
48
+ deletes, compare-and-swap, verifying each step and stopping at the first failure.
49
+ Without --pick it runs every row judged delete.
50
+ --pick <row> Work on exactly these rows. Narrows the run to them, and opts
51
+ in review rows and kept rows with an action (an open pull
52
+ request), which never run otherwise. A picked row still
53
+ needs --allow for its risks. A blocked row is skipped.
54
+ Repeatable.
55
+ --spare <row> Leave this row alone, and make sure it is what you saw. A
56
+ spared row is not deleted, even if it is judged delete. A
57
+ spared remote branch is still checked: if it is gone or its
58
+ tip moved since the plan, the run stops before deleting
59
+ anything. Sparing a worktree also spares its branch.
60
+ Repeatable.
61
+ --allow <risk> Accept this risk for every row that carries it.
62
+ --allow <risk>=<row> Accept it for that one row only.
63
+ --plan <file|-> Run a saved plan instead of sweeping now (- reads stdin).
64
+ Rows whose evidence changed since the plan are dropped.
65
+ -y, --yes Do not ask. Without it, and with no terminal to ask on,
66
+ clean refuses.
67
+ --dry-run Check and show the steps, then stop.
68
+ --receipt <file> Write a record of every step, rewritten after each one.
69
+ Default: next to a --plan file; none otherwise.
70
+ --format text|json How the steps and the outcome are shown.`;
71
+ const CACHE = `repo-cleaner cache [list | remove <url>... | remove --all]
72
+ Lists or removes the clones kept for remote URL targets. A removed clone is
73
+ cloned again by the next sweep of its URL; a plan that names it cannot run.`;
74
+ const WORDS = `Rows
75
+ One thing that could be deleted, named <kind>:<name>:
76
+ worktree:<path> a linked worktree: removed with git worktree remove, or,
77
+ when its directory is already gone, its record pruned
78
+ branch:<name> a local branch, deleted with git update-ref -d <sha>
79
+ remote:<remote>/<name> a branch on a remote, deleted by a push with a lease
80
+ A bare name or path works when only one row has it.
81
+
82
+ Verdicts
83
+ delete Its work is on the default branch, or it is otherwise safe to lose.
84
+ clean deletes it, once its risks are allowed.
85
+ review The signals are mixed, or it holds work. Deleted only when picked and
86
+ its risks are allowed.
87
+ blocked Something rules removal out: a lock, a running process, a submodule
88
+ that cannot be read, hidden index flags, a nested repository, a branch
89
+ checked out twice. Never deleted; fix the cause and sweep again.
90
+ keep Structural (the default branch, the current checkout, the primary
91
+ worktree): never deleted. Or in use (an open pull request): deleted
92
+ only when picked and confirm-open-pr is allowed.
93
+ prune Shown for a delete row whose worktree directory is already gone: only
94
+ the stale record goes.
95
+
96
+ Risks (approve each by name with --allow)
97
+ abandon-unmerged Commits that are not on the default branch, or a worktree's
98
+ (or its submodules') commits and stashes that no remote has.
99
+ discard-changes Uncommitted or untracked files in the worktree or any of its
100
+ submodules.
101
+ confirm-open-pr The branch has an open pull request; deleting the remote branch
102
+ closes it.
103
+ A worktree's submodules are part of it: one --allow on the worktree row covers
104
+ the same risk in every submodule.`;
105
+ const CONFLICTS = `Refused before anything is deleted (exit 2)
106
+ A combination is refused when it contradicts itself, or when one of its options
107
+ would have no effect. Most are refused before anything is swept; a row named
108
+ two ways (a bare name and its id), and an --allow for a row, are checked
109
+ against the plan, after the sweep.
110
+ the same row in --pick and --spare
111
+ --allow <risk>=<row> for a spared row, or for a row outside --pick
112
+ a picked or spared row of a kind --only leaves out
113
+ a picked or spared row that is not one of --branch's rows
114
+ --branch with more than one target, or with a remote URL, or the default branch
115
+ --plan with targets or sweep options: the plan already fixed them
116
+ --plan - without --yes or --dry-run: stdin carries the plan, so nothing can ask
117
+ --yes with --dry-run
118
+ --receipt with --dry-run
119
+ --offline or --provider without --remote
120
+ --remote with an --only that leaves remote out
121
+ a remote URL target with --only worktree or --only branch, with --offline, or
122
+ mixed with checkout targets
123
+ --all with any --format but text
124
+
125
+ Refused after the sweep, before deleting (exit 1)
126
+ a picked, spared or allowed row the plan does not have
127
+ a row name that matches rows in more than one repository
128
+ a spared remote branch that is gone or moved since the plan
129
+
130
+ Allowed, because nothing contradicts
131
+ the same row picked twice: it runs once
132
+ --allow <risk> with --allow <risk>=<row>: the broader one covers it
133
+ --allow for a risk no row that runs carries: the preview lists it as unused
134
+ picking a blocked or keep row: it is skipped and shown with the reason
135
+ --only remote without --remote: asking for remote branches implies the network`;
136
+ const EXIT = `Exit codes
137
+ 0 swept; or clean ran, was declined, or had nothing to do
138
+ 1 failed against the state it found: an unreadable plan, a row not in it
139
+ 2 cannot run as typed: an unknown flag, a malformed row, a refused combination
140
+ 10 no target named a repository
141
+ 11 clean stopped at a failed step; the receipt says which`;
142
+ /** The extended help for each command: its own options, then the shared vocabulary. */
143
+ export const REFERENCE = {
144
+ report: [TARGETS, SWEEP, REPORT, WORDS, CONFLICTS, EXIT].join('\n\n'),
145
+ plan: [TARGETS, SWEEP, PLAN, WORDS, EXIT].join('\n\n'),
146
+ clean: [TARGETS, SWEEP, CLEAN, WORDS, CONFLICTS, EXIT].join('\n\n'),
147
+ cache: CACHE,
148
+ };
@@ -77,3 +77,11 @@ export declare function listMirrors(cacheDir: string, opts?: {
77
77
  * given, credentials included.
78
78
  */
79
79
  export declare function cachedMirrorOf(url: string, cacheDir: string): string | null;
80
+ /**
81
+ * True when `target` names a remote rather than a directory: a URL with a
82
+ * scheme (`https://`, `ssh://`, `file://`), or the scp form `user@host:path`
83
+ * that names no directory on disk. A Windows drive path is never one.
84
+ */
85
+ export declare function isRemoteUrl(target: string, exists: (path: string) => boolean): boolean;
86
+ /** Where remote URL targets' clones are kept: `repo-cleaner/remotes` under the user's cache directory. */
87
+ export declare function defaultCacheDir(env?: Record<string, string | undefined>): string;
@@ -15,7 +15,9 @@
15
15
  import { createHash } from 'node:crypto';
16
16
  import { existsSync, lstatSync, mkdirSync, readdirSync, renameSync, rmSync } from 'node:fs';
17
17
  import { dirname, join } from 'node:path';
18
+ import { systemDirs } from '@osdlc/cli-tool';
18
19
  import { Git, parseRemoteRepo } from '@osdlc/typed-git';
20
+ import { VENDOR_DIRS } from "./devlink.js";
19
21
  /** A remote URL that cannot be mirrored, or a mirror that cannot be made or reused. */
20
22
  export class RemoteMirrorError extends Error {
21
23
  constructor(message) {
@@ -163,3 +165,19 @@ export function cachedMirrorOf(url, cacheDir) {
163
165
  const dir = join(cacheDir, mirrorName(url));
164
166
  return existsSync(dir) ? dir : null;
165
167
  }
168
+ /**
169
+ * True when `target` names a remote rather than a directory: a URL with a
170
+ * scheme (`https://`, `ssh://`, `file://`), or the scp form `user@host:path`
171
+ * that names no directory on disk. A Windows drive path is never one.
172
+ */
173
+ export function isRemoteUrl(target, exists) {
174
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(target))
175
+ return true;
176
+ if (/^[a-z]:[\\/]/i.test(target))
177
+ return false;
178
+ return /^(?:[^@/:\\]+@)?[^/:\\]+:(?!\/\/).+$/.test(target) && !exists(target);
179
+ }
180
+ /** Where remote URL targets' clones are kept: `repo-cleaner/remotes` under the user's cache directory. */
181
+ export function defaultCacheDir(env = process.env) {
182
+ return systemDirs(VENDOR_DIRS, { env }).standalone('repo-cleaner').cache().app('remotes').path;
183
+ }
package/dist/render.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  /**
2
- * Rendering a sweep for a terminal, and for a program.
2
+ * Rendering a sweep and a run, for a terminal and for a program.
3
3
  *
4
4
  * The text report is columnar because it is read by eye across repositories:
5
- * the eye finds a column, and a line-per-item paragraph gives it nothing to
6
- * find. JSON carries everything the text report elides, including the facts
7
- * behind every verdict.
5
+ * the eye finds a column, and a line-per-row paragraph gives it nothing to
6
+ * find. JSON carries everything the text report leaves out, the evidence
7
+ * behind every verdict included.
8
8
  */
9
- import type { Receipt, Step } from './receipt.ts';
10
- import { type RepoSweep, type SweepItem, type SweepResult } from './types.ts';
9
+ import type { Preview } from './clean.ts';
10
+ import type { Dropped, Receipt, Step } from './receipt.ts';
11
+ import { type Kind, type RepoSweep, type Sweep } from './types.ts';
11
12
  /**
12
13
  * How a report colors its words. {@link PLAIN} leaves them as they are; the
13
14
  * CLI passes {@link TERMINAL_STYLE} when the stream is a terminal that takes
@@ -31,57 +32,38 @@ export interface Style {
31
32
  export declare const PLAIN: Style;
32
33
  /** The basic terminal colors: red to delete, blue to prune, yellow to review, green to keep. */
33
34
  export declare const TERMINAL_STYLE: Style;
34
- /**
35
- * A row's authorizations for a report, e.g. `requires abandon-unmerged (drops 3
36
- * commits), confirm-open-pr`. Empty when the row needs none.
37
- */
38
- export declare function requiresNote(item: Pick<SweepItem, 'requires' | 'facts'>): string;
39
- /** One repository's rows, indented under its header. */
35
+ /** One repository's rows, indented under its header. `all` shows `keep` rows too. */
40
36
  export declare function renderRepo(sweep: RepoSweep, opts?: {
41
- keep?: boolean;
37
+ all?: boolean;
42
38
  style?: Style;
43
39
  }): string;
44
40
  /**
45
- * What the search cost, or null when nothing was searched — every entry
41
+ * What the search cost, or null when nothing was searched — every target
46
42
  * named a checkout outright.
47
43
  *
48
- * Worth a line because the numbers are how a person catches an entry
44
+ * Worth a line because the numbers are how a person catches a target
49
45
  * pointing at the wrong directory: thousands of directories read for three
50
- * repositories says the entry is a home directory, not a workspace.
46
+ * repositories says the target is a home directory, not a workspace.
51
47
  */
52
- export declare function renderScan(result: SweepResult): string | null;
48
+ export declare function renderScan(sweep: Pick<Sweep, 'scan'>): string | null;
53
49
  /** The one-line verdict a person reads first, and often only. */
54
- export declare function renderSummary(result: SweepResult, style?: Style): string;
55
- /**
56
- * One repository as counts: a row per category with its total, how many are
57
- * merged, and how many the sweep would delete, prune, review or keep.
58
- */
59
- export declare function renderCounts(sweep: RepoSweep): string;
60
- /** The whole run as counts: a table per repository, then the one-line summary. */
61
- export declare function renderCountsText(result: SweepResult, style?: Style): string;
62
- /** The whole run's counts as JSON: no rows, no facts. */
63
- export declare function renderCountsJson(result: SweepResult): string;
64
- /** The whole run as text: duplicate checkouts, a section per repository, then the summary. */
65
- export declare function renderText(result: SweepResult, opts?: {
66
- keep?: boolean;
50
+ export declare function renderTotals(sweep: Sweep, style?: Style): string;
51
+ /** The sweep as counts (`--format summary`): a table per repository, then the totals line. */
52
+ export declare function renderSummary(sweep: Sweep, style?: Style): string;
53
+ /** The sweep as text: duplicate checkouts, a section per repository, then the totals. */
54
+ export declare function renderText(sweep: Sweep, opts?: {
55
+ all?: boolean;
67
56
  style?: Style;
68
57
  }): string;
69
- /**
70
- * The whole run as JSON, facts included. `extra` adds top-level keys, such as
71
- * the path of a plan the run wrote.
72
- */
73
- export declare function renderJson(result: SweepResult, extra?: Record<string, unknown>): string;
74
- /** Every command a `safe-to-delete` row that needs no authorization proposes, in report order. */
75
- export declare function proposedCommands(result: SweepResult): string[];
76
- /** What an apply run did, or would do, counted by kind. */
77
- export interface ApplySummary {
78
- /** Rows deleted (or, in a dry run, that would be), by category. Pruned worktrees are not counted here. */
79
- deleted: {
80
- worktree: number;
81
- local_branch: number;
82
- remote_branch: number;
83
- };
84
- /** Worktrees whose stale registration was pruned; their directories were gone or left alone. */
58
+ /** The sweep as JSON: every row with its evidence, the counts, and what was swept. */
59
+ export declare function renderJson(sweep: Sweep, extra?: Record<string, unknown>): string;
60
+ /** Every command of a `delete` row that carries no risk, in report order (`--format commands`). */
61
+ export declare function renderCommands(sweep: Sweep): string[];
62
+ /** What a run did, or would do, counted by kind. */
63
+ export interface RunSummary {
64
+ /** Rows deleted (or, in a preview, that would be), by kind. Pruned worktrees are counted apart. */
65
+ deleted: Record<Kind, number>;
66
+ /** Worktrees whose stale record was pruned; their directories were gone or left alone. */
85
67
  pruned: number;
86
68
  /** Dropped rows by reason. */
87
69
  dropped: Record<string, number>;
@@ -90,29 +72,47 @@ export interface ApplySummary {
90
72
  /** Row steps never reached because a step failed. */
91
73
  notRun: number;
92
74
  }
93
- /** Count a receipt's outcome. A dry run's `would-run` steps count as done. */
94
- export declare function summarizeReceipt(receipt: Receipt): ApplySummary;
95
- /** Rows a receipt deletes or prunes, which is what a person is asked to approve. */
96
- export declare function rowsActedOn(summary: ApplySummary): number;
97
- /** The closing lines of an apply run: what it deleted, skipped and could not finish. */
98
- export declare function renderApplySummary(receipt: Receipt): string[];
75
+ /** Count what `steps` did and `dropped` left out. A preview's `would-run` steps count as done. */
76
+ export declare function summarizeRun(r: {
77
+ steps: readonly Step[];
78
+ dropped: readonly Dropped[];
79
+ }): RunSummary;
80
+ /** Rows a run deletes or prunes, which is what a person is asked to approve. */
81
+ export declare function rowsActedOn(summary: RunSummary): number;
82
+ /** The closing lines of a run or preview: what it deleted, skipped and could not finish. */
83
+ export declare function renderRunSummary(r: {
84
+ steps: readonly Step[];
85
+ dropped: readonly Dropped[];
86
+ }, opts?: {
87
+ preview?: boolean;
88
+ }): string[];
99
89
  /** The word a preview shows for a step: what it does to the row. */
100
90
  export declare const STEP_VERB: Record<Step['action'], string>;
101
91
  /**
102
- * What `apply` is about to do, for the person asked to approve it: per
103
- * repository, each row and the command that deletes it, the rows it will
104
- * skip and why, then the counts. Terminal controls are stripped from what it
105
- * shows, so a path or name cannot hide part of what is approved.
92
+ * What a run is about to do, for the person asked to approve it: per
93
+ * repository, each row and the command that deletes it, the rows it will skip
94
+ * and why, the allows no row needed, then the counts. Terminal controls are
95
+ * stripped from what it shows, so a path or name cannot hide part of what is
96
+ * approved.
106
97
  */
107
- export declare function renderPreview(receipt: Receipt, style?: Style): string;
108
- /** Rows dropped by a live run's own re-check, after the preview: each with why. */
109
- export declare function renderPreviewDrops(dropped: Receipt['dropped'], style?: Style): string;
98
+ export declare function renderPreview(p: Pick<Preview, 'steps' | 'dropped'> & Partial<Pick<Preview, 'unusedAllows'>>, style?: Style): string;
99
+ /** Rows dropped by a run's own re-check after the preview, each with why. */
100
+ export declare function renderLateDrops(dropped: readonly Dropped[], style?: Style): string;
110
101
  /**
111
102
  * A failed step for a run shown live: `✗`, the row, and the error in full,
112
- * which the step's row on the board is too narrow to hold.
103
+ * which the step's line on the board is too narrow to hold.
113
104
  */
114
105
  export declare function renderStepFailure(step: Step, style?: Style): string;
115
- /** An apply run as text: dropped rows, each step, a summary, then where the receipt is. */
106
+ /** A run as text: dropped rows, each step, a summary, then where the receipt is. */
116
107
  export declare function renderReceipt(receipt: Receipt, receiptPath: string | null, style?: Style, opts?: {
117
108
  steps?: boolean;
118
109
  }): string;
110
+ /** The renderers a library user needs: the CLI's own formats. */
111
+ export declare const render: {
112
+ text: typeof renderText;
113
+ json: typeof renderJson;
114
+ summary: typeof renderSummary;
115
+ commands: typeof renderCommands;
116
+ preview: typeof renderPreview;
117
+ receipt: typeof renderReceipt;
118
+ };