@codyswann/lisa 4.10.15 → 4.11.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 (97) hide show
  1. package/dist/cli/doctor-worktree-hygiene.d.ts +5 -2
  2. package/dist/cli/doctor-worktree-hygiene.d.ts.map +1 -1
  3. package/dist/cli/doctor-worktree-hygiene.js +12 -6
  4. package/dist/cli/doctor-worktree-hygiene.js.map +1 -1
  5. package/dist/cli/index.d.ts.map +1 -1
  6. package/dist/cli/index.js +2 -0
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/cli/prune-commands.d.ts +52 -0
  9. package/dist/cli/prune-commands.d.ts.map +1 -0
  10. package/dist/cli/prune-commands.js +158 -0
  11. package/dist/cli/prune-commands.js.map +1 -0
  12. package/dist/cli/prune-report.d.ts +40 -0
  13. package/dist/cli/prune-report.d.ts.map +1 -0
  14. package/dist/cli/prune-report.js +81 -0
  15. package/dist/cli/prune-report.js.map +1 -0
  16. package/dist/cli/stash-prune-policy.d.ts +72 -0
  17. package/dist/cli/stash-prune-policy.d.ts.map +1 -0
  18. package/dist/cli/stash-prune-policy.js +90 -0
  19. package/dist/cli/stash-prune-policy.js.map +1 -0
  20. package/dist/cli/stash-prune.d.ts +104 -0
  21. package/dist/cli/stash-prune.d.ts.map +1 -0
  22. package/dist/cli/stash-prune.js +206 -0
  23. package/dist/cli/stash-prune.js.map +1 -0
  24. package/dist/cli/worktree-inventory.d.ts +82 -0
  25. package/dist/cli/worktree-inventory.d.ts.map +1 -0
  26. package/dist/cli/worktree-inventory.js +195 -0
  27. package/dist/cli/worktree-inventory.js.map +1 -0
  28. package/dist/cli/worktree-liveness.d.ts +52 -0
  29. package/dist/cli/worktree-liveness.d.ts.map +1 -0
  30. package/dist/cli/worktree-liveness.js +124 -0
  31. package/dist/cli/worktree-liveness.js.map +1 -0
  32. package/dist/cli/worktree-ownership.d.ts +65 -0
  33. package/dist/cli/worktree-ownership.d.ts.map +1 -0
  34. package/dist/cli/worktree-ownership.js +142 -0
  35. package/dist/cli/worktree-ownership.js.map +1 -0
  36. package/dist/cli/worktree-prune-policy.d.ts +76 -0
  37. package/dist/cli/worktree-prune-policy.d.ts.map +1 -0
  38. package/dist/cli/worktree-prune-policy.js +114 -0
  39. package/dist/cli/worktree-prune-policy.js.map +1 -0
  40. package/dist/cli/worktree-prune.d.ts +96 -0
  41. package/dist/cli/worktree-prune.d.ts.map +1 -0
  42. package/dist/cli/worktree-prune.js +153 -0
  43. package/dist/cli/worktree-prune.js.map +1 -0
  44. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  45. package/dist/core/upstream-evidence-manifest.js +16 -0
  46. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  47. package/package.json +1 -1
  48. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  50. package/plugins/lisa-agy/plugin.json +1 -1
  51. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  53. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  54. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-expo-agy/plugin.json +1 -1
  61. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  65. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  66. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  70. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  71. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  75. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  76. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  80. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  81. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  85. package/plugins/lisa-rails-agy/plugin.json +1 -1
  86. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  90. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  91. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  95. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  96. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The entitlement rule for dropping a stash entry.
3
+ *
4
+ * Stashes are harder to own than worktrees: `refs/stash` is a single reflog
5
+ * shared by every worktree of a repository, so an entry an agent finds may have
6
+ * been pushed a minute ago by a sibling working three directories away. There
7
+ * is no path, no branch, and no author to scope by. The only defensible
8
+ * entitlement is therefore not "mine" but **provably redundant** — an entry
9
+ * whose content is already preserved somewhere else, or machine-generated
10
+ * debris whose producing run is provably long dead.
11
+ *
12
+ * Everything else is kept and reported. A stash that cannot be proven redundant
13
+ * is precisely the case the standing block on `git stash drop` exists for.
14
+ * @module cli/stash-prune-policy
15
+ */
16
+ /** Default debris age: a lint-staged run that old is not coming back. */
17
+ export const DEFAULT_MIN_DEBRIS_AGE_SECONDS = 24 * 60 * 60;
18
+ /**
19
+ * The exact message lint-staged gives its automatic backup stash.
20
+ *
21
+ * Matched exactly, and with the `On <branch>: ` prefix git adds when the entry
22
+ * is stored through `git stash push`, because a substring match would sweep a
23
+ * human stash whose message merely mentions lint-staged.
24
+ */
25
+ const LINT_STAGED_BACKUP = /^(?:On [^:]+: )?lint-staged automatic backup$/;
26
+ /** Operator-readable explanation for each blocker. */
27
+ export const STASH_BLOCKER_EXPLANATIONS = Object.freeze({
28
+ "not-provably-redundant": "its content is not provably preserved anywhere else, so dropping it could destroy the only copy",
29
+ "too-recent": "it is machine-generated backup debris, but recent enough that the run which created it may still be alive",
30
+ unreadable: "git could not report on it, so nothing about it was proven",
31
+ });
32
+ /**
33
+ * Report whether a reflog subject is lint-staged's automatic backup.
34
+ * @param subject - Reflog subject of the stash entry
35
+ * @returns True when the entry is machine-generated backup debris
36
+ */
37
+ export function isLintStagedBackup(subject) {
38
+ return LINT_STAGED_BACKUP.test(subject.trim());
39
+ }
40
+ /**
41
+ * Decide whether one stash entry may be dropped, and say why not when it may not.
42
+ * @param facts - Gathered facts about the entry
43
+ * @param policy - Tunable thresholds
44
+ * @returns Verdict with the reason it may be dropped, or every reason it may not
45
+ */
46
+ export function classifyStash(facts, policy) {
47
+ if (!facts.readable) {
48
+ return {
49
+ index: facts.index,
50
+ sha: facts.sha,
51
+ eligible: false,
52
+ blockers: ["unreadable"],
53
+ };
54
+ }
55
+ const redundancy = findRedundancy(facts, policy);
56
+ if (redundancy !== undefined) {
57
+ return {
58
+ index: facts.index,
59
+ sha: facts.sha,
60
+ eligible: true,
61
+ redundancy,
62
+ blockers: [],
63
+ };
64
+ }
65
+ return {
66
+ index: facts.index,
67
+ sha: facts.sha,
68
+ eligible: false,
69
+ blockers: [
70
+ isLintStagedBackup(facts.subject)
71
+ ? "too-recent"
72
+ : "not-provably-redundant",
73
+ ],
74
+ };
75
+ }
76
+ /**
77
+ * Identify which redundancy proof, if any, one entry satisfies.
78
+ * @param facts - Gathered facts about the entry
79
+ * @param policy - Tunable thresholds
80
+ * @returns The satisfied proof, or undefined when none is
81
+ */
82
+ function findRedundancy(facts, policy) {
83
+ if (facts.recordsNoChange && facts.baseOnRemote)
84
+ return "empty-and-published";
85
+ return isLintStagedBackup(facts.subject) &&
86
+ facts.ageSeconds >= policy.minDebrisAgeSeconds
87
+ ? "machine-debris"
88
+ : undefined;
89
+ }
90
+ //# sourceMappingURL=stash-prune-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stash-prune-policy.js","sourceRoot":"","sources":["../../src/cli/stash-prune-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAiDH,yEAAyE;AACzE,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAE3D;;;;;;GAMG;AACH,MAAM,kBAAkB,GAAG,+CAA+C,CAAC;AAE3E,sDAAsD;AACtD,MAAM,CAAC,MAAM,0BAA0B,GAEnC,MAAM,CAAC,MAAM,CAAC;IAChB,wBAAwB,EACtB,iGAAiG;IACnG,YAAY,EACV,2GAA2G;IAC7G,UAAU,EAAE,4DAA4D;CACzE,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,OAAO,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACjD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAiB,EACjB,MAAwB;IAExB,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpB,OAAO;YACL,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,CAAC,YAAY,CAAC;SACzB,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACjD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO;YACL,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,QAAQ,EAAE,IAAI;YACd,UAAU;YACV,QAAQ,EAAE,EAAE;SACb,CAAC;IACJ,CAAC;IACD,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,QAAQ,EAAE,KAAK;QACf,QAAQ,EAAE;YACR,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC;gBAC/B,CAAC,CAAC,YAAY;gBACd,CAAC,CAAC,wBAAwB;SAC7B;KACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,cAAc,CACrB,KAAiB,EACjB,MAAwB;IAExB,IAAI,KAAK,CAAC,eAAe,IAAI,KAAK,CAAC,YAAY;QAAE,OAAO,qBAAqB,CAAC;IAC9E,OAAO,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC;QACtC,KAAK,CAAC,UAAU,IAAI,MAAM,CAAC,mBAAmB;QAC9C,CAAC,CAAC,gBAAgB;QAClB,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * `lisa stash prune` — the vetted verb for clearing redundant stash entries.
3
+ *
4
+ * The guard blocks `git stash drop` and `git stash clear` because they destroy
5
+ * stashed work. That block stays. This command answers it rather than relaxing
6
+ * it: every entry it drops is first re-anchored under
7
+ * `refs/lisa/pruned-stashes/`, so the stash commit — trees, index state, and
8
+ * the untracked-files parent alike — stays in the object database and is
9
+ * recoverable with a single `git stash apply <ref>`. Nothing is destroyed; the
10
+ * reflog entry is what goes away.
11
+ *
12
+ * Dropping is index-shifting on a ref that every worktree of the repository
13
+ * shares, so entries are addressed by commit id and re-located immediately
14
+ * before each drop. An entry whose id no longer sits where it was is skipped,
15
+ * never dropped by position — that is the difference between a cleaner and a
16
+ * race that eats a sibling's stash.
17
+ * @module cli/stash-prune
18
+ */
19
+ import { type StashFacts, type StashVerdict } from "./stash-prune-policy.js";
20
+ /** Namespace the preserved stash commits are anchored under. */
21
+ export declare const PRESERVED_STASH_REF_PREFIX = "refs/lisa/pruned-stashes";
22
+ /** Command-line options for the stash prune verb. */
23
+ export interface StashPruneOptions {
24
+ /** Perform the drops instead of reporting them. */
25
+ readonly apply?: boolean;
26
+ /** Age in hours a machine backup must reach before it counts as debris. */
27
+ readonly olderThanHours?: string;
28
+ /** Emit machine-readable output. */
29
+ readonly json?: boolean;
30
+ }
31
+ /** One entry as the listing reports it. */
32
+ export interface StashRecord {
33
+ /** Commit id of the entry. */
34
+ readonly sha: string;
35
+ /** Reflog subject. */
36
+ readonly subject: string;
37
+ /** Commit timestamp in epoch seconds. */
38
+ readonly timestamp: number;
39
+ }
40
+ /** What one run decided, before anything is dropped. */
41
+ export interface StashPrunePlan {
42
+ /** Repository the plan was computed for. */
43
+ readonly repoPath: string;
44
+ /** Verdict for every stash entry. */
45
+ readonly verdicts: readonly StashVerdict[];
46
+ /** Facts behind each verdict, in the same order. */
47
+ readonly facts: readonly StashFacts[];
48
+ }
49
+ /** Outcome of dropping one stash entry. */
50
+ export interface StashDropOutcome {
51
+ /** Commit id of the entry. */
52
+ readonly sha: string;
53
+ /** Whether the entry was dropped. */
54
+ readonly dropped: boolean;
55
+ /** Ref the stash commit was anchored under before the drop. */
56
+ readonly preservedRef?: string;
57
+ /** Why the drop did not happen. */
58
+ readonly error?: string;
59
+ }
60
+ /**
61
+ * Resolve the debris age threshold from the command line.
62
+ * @param olderThanHours - Raw `--older-than-hours` value
63
+ * @returns Threshold in seconds
64
+ */
65
+ export declare function resolveMinDebrisAgeSeconds(olderThanHours: string | undefined): number;
66
+ /**
67
+ * Parse the formatted stash listing into its raw fields.
68
+ * @param stdout - Output of the formatted `git stash list`
69
+ * @returns One record per entry, in list order
70
+ */
71
+ export declare function parseStashList(stdout: string): readonly StashRecord[];
72
+ /**
73
+ * The ref one stash commit is preserved under.
74
+ * @param sha - Commit id of the stash entry
75
+ * @returns Fully-qualified ref name
76
+ */
77
+ export declare function preservedStashRef(sha: string): string;
78
+ /**
79
+ * Compute what a run would do, without dropping anything.
80
+ * @param repoPath - Any path inside the repository
81
+ * @param options - Command-line options
82
+ * @param now - Clock in epoch milliseconds
83
+ * @returns The plan
84
+ */
85
+ export declare function planStashPrune(repoPath: string, options: StashPruneOptions, now?: () => number): Promise<StashPrunePlan>;
86
+ /**
87
+ * Locate a stash entry by commit id, as `git stash list` currently numbers it.
88
+ *
89
+ * Re-derived immediately before every drop. Positions shift under a concurrent
90
+ * push or drop from any worktree of the repository, and a stale position is how
91
+ * a cleaner destroys the entry after the one it meant to.
92
+ * @param repoPath - Any path inside the repository
93
+ * @param sha - Commit id of the entry
94
+ * @returns Current `stash@{n}` reference, or undefined when the entry is gone
95
+ */
96
+ export declare function locateStash(repoPath: string, sha: string): Promise<string | undefined>;
97
+ /**
98
+ * Preserve and drop every eligible stash entry in a plan.
99
+ * @param plan - Plan produced by {@link planStashPrune}
100
+ * @param index - Index of the eligible entry to drop next
101
+ * @returns One outcome per eligible entry
102
+ */
103
+ export declare function applyStashPrune(plan: StashPrunePlan, index?: number): Promise<readonly StashDropOutcome[]>;
104
+ //# sourceMappingURL=stash-prune.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stash-prune.d.ts","sourceRoot":"","sources":["../../src/cli/stash-prune.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,YAAY,EAClB,MAAM,yBAAyB,CAAC;AASjC,gEAAgE;AAChE,eAAO,MAAM,0BAA0B,6BAA6B,CAAC;AAErE,qDAAqD;AACrD,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,2EAA2E;IAC3E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,oCAAoC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B,8BAA8B;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC7B,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;CACvC;AAED,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB;IAC/B,8BAA8B;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,mCAAmC;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,MAAM,CAOR;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,WAAW,EAAE,CAWrE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAkFD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,iBAAiB,EAC1B,GAAG,GAAE,MAAM,MAAyB,GACnC,OAAO,CAAC,cAAc,CAAC,CAkBzB;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAM7B;AA+CD;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,cAAc,EACpB,KAAK,SAAI,GACR,OAAO,CAAC,SAAS,gBAAgB,EAAE,CAAC,CAQtC"}
@@ -0,0 +1,206 @@
1
+ /**
2
+ * `lisa stash prune` — the vetted verb for clearing redundant stash entries.
3
+ *
4
+ * The guard blocks `git stash drop` and `git stash clear` because they destroy
5
+ * stashed work. That block stays. This command answers it rather than relaxing
6
+ * it: every entry it drops is first re-anchored under
7
+ * `refs/lisa/pruned-stashes/`, so the stash commit — trees, index state, and
8
+ * the untracked-files parent alike — stays in the object database and is
9
+ * recoverable with a single `git stash apply <ref>`. Nothing is destroyed; the
10
+ * reflog entry is what goes away.
11
+ *
12
+ * Dropping is index-shifting on a ref that every worktree of the repository
13
+ * shares, so entries are addressed by commit id and re-located immediately
14
+ * before each drop. An entry whose id no longer sits where it was is skipped,
15
+ * never dropped by position — that is the difference between a cleaner and a
16
+ * race that eats a sibling's stash.
17
+ * @module cli/stash-prune
18
+ */
19
+ import { classifyStash, DEFAULT_MIN_DEBRIS_AGE_SECONDS, } from "./stash-prune-policy.js";
20
+ import { git } from "./worktree-inventory.js";
21
+ /** Unit-separator field delimiter, matching the `%x1f` in the list format. */
22
+ const FIELD = "\u001F";
23
+ /** Format that makes `git stash list` machine-readable. */
24
+ const STASH_LIST_FORMAT = "--format=%H%x1f%gs%x1f%ct";
25
+ /** Namespace the preserved stash commits are anchored under. */
26
+ export const PRESERVED_STASH_REF_PREFIX = "refs/lisa/pruned-stashes";
27
+ /**
28
+ * Resolve the debris age threshold from the command line.
29
+ * @param olderThanHours - Raw `--older-than-hours` value
30
+ * @returns Threshold in seconds
31
+ */
32
+ export function resolveMinDebrisAgeSeconds(olderThanHours) {
33
+ if (olderThanHours === undefined)
34
+ return DEFAULT_MIN_DEBRIS_AGE_SECONDS;
35
+ const parsed = Number.parseFloat(olderThanHours);
36
+ if (!Number.isFinite(parsed) || parsed < 0) {
37
+ throw new Error("--older-than-hours must be a non-negative number");
38
+ }
39
+ return Math.floor(parsed * 3600);
40
+ }
41
+ /**
42
+ * Parse the formatted stash listing into its raw fields.
43
+ * @param stdout - Output of the formatted `git stash list`
44
+ * @returns One record per entry, in list order
45
+ */
46
+ export function parseStashList(stdout) {
47
+ return stdout
48
+ .split("\n")
49
+ .filter(line => line.includes(FIELD))
50
+ .map(line => line.split(FIELD))
51
+ .map(fields => ({
52
+ sha: (fields[0] ?? "").trim(),
53
+ subject: fields[1] ?? "",
54
+ timestamp: Number.parseInt(fields[2] ?? "", 10) || 0,
55
+ }))
56
+ .filter(record => record.sha !== "");
57
+ }
58
+ /**
59
+ * The ref one stash commit is preserved under.
60
+ * @param sha - Commit id of the stash entry
61
+ * @returns Fully-qualified ref name
62
+ */
63
+ export function preservedStashRef(sha) {
64
+ return `${PRESERVED_STASH_REF_PREFIX}/${sha}`;
65
+ }
66
+ /**
67
+ * Report whether two revisions have an identical tree.
68
+ * @param repoPath - Any path inside the repository
69
+ * @param left - First revision
70
+ * @param right - Second revision
71
+ * @returns True when the revisions differ in nothing
72
+ */
73
+ async function isSameTree(repoPath, left, right) {
74
+ const [leftTree, rightTree] = await Promise.all([
75
+ git(["rev-parse", `${left}^{tree}`], repoPath).then(out => out.trim()),
76
+ git(["rev-parse", `${right}^{tree}`], repoPath).then(out => out.trim()),
77
+ ]);
78
+ return leftTree !== "" && leftTree === rightTree;
79
+ }
80
+ /**
81
+ * Establish whether one entry holds anything, and whether its base is published.
82
+ *
83
+ * A stash created with `--include-untracked` carries a third parent holding
84
+ * those files. Its presence alone means the entry holds content no comparison
85
+ * against the first two parents would reveal, so it is treated as non-empty
86
+ * without further inspection.
87
+ * @param repoPath - Any path inside the repository
88
+ * @param sha - Commit id of the stash entry
89
+ * @returns Content and publication facts
90
+ */
91
+ async function probeStashContent(repoPath, sha) {
92
+ const hasUntrackedParent = await git(["rev-parse", "--verify", "--quiet", `${sha}^3`], repoPath).then(() => true, () => false);
93
+ const [sameWorktree, sameIndex, unpublished] = await Promise.all([
94
+ isSameTree(repoPath, `${sha}^`, sha),
95
+ isSameTree(repoPath, `${sha}^`, `${sha}^2`),
96
+ git(["rev-list", "--count", `${sha}^`, "--not", "--remotes"], repoPath),
97
+ ]);
98
+ return {
99
+ recordsNoChange: !hasUntrackedParent && sameWorktree && sameIndex,
100
+ baseOnRemote: (Number.parseInt(unpublished.trim(), 10) || 0) === 0,
101
+ };
102
+ }
103
+ /**
104
+ * Gather every fact the entitlement rule may consider for one entry.
105
+ * @param repoPath - Any path inside the repository
106
+ * @param record - Raw listing fields
107
+ * @param index - Position in the listing
108
+ * @param nowSeconds - Clock in epoch seconds
109
+ * @returns Facts for the entitlement rule
110
+ */
111
+ async function gatherStashFacts(repoPath, record, index, nowSeconds) {
112
+ const shell = {
113
+ index,
114
+ sha: record.sha,
115
+ subject: record.subject,
116
+ ageSeconds: Math.max(0, nowSeconds - record.timestamp),
117
+ };
118
+ const probed = await probeStashContent(repoPath, record.sha).catch(() => undefined);
119
+ return probed === undefined
120
+ ? { ...shell, readable: false, recordsNoChange: false, baseOnRemote: false }
121
+ : { ...shell, readable: true, ...probed };
122
+ }
123
+ /**
124
+ * Compute what a run would do, without dropping anything.
125
+ * @param repoPath - Any path inside the repository
126
+ * @param options - Command-line options
127
+ * @param now - Clock in epoch milliseconds
128
+ * @returns The plan
129
+ */
130
+ export async function planStashPrune(repoPath, options, now = () => Date.now()) {
131
+ const minDebrisAgeSeconds = resolveMinDebrisAgeSeconds(options.olderThanHours);
132
+ const listed = parseStashList(await git(["stash", "list", STASH_LIST_FORMAT], repoPath).catch(() => ""));
133
+ const nowSeconds = Math.floor(now() / 1000);
134
+ const facts = await Promise.all(listed.map((record, index) => gatherStashFacts(repoPath, record, index, nowSeconds)));
135
+ return {
136
+ repoPath,
137
+ facts,
138
+ verdicts: facts.map(fact => classifyStash(fact, { minDebrisAgeSeconds })),
139
+ };
140
+ }
141
+ /**
142
+ * Locate a stash entry by commit id, as `git stash list` currently numbers it.
143
+ *
144
+ * Re-derived immediately before every drop. Positions shift under a concurrent
145
+ * push or drop from any worktree of the repository, and a stale position is how
146
+ * a cleaner destroys the entry after the one it meant to.
147
+ * @param repoPath - Any path inside the repository
148
+ * @param sha - Commit id of the entry
149
+ * @returns Current `stash@{n}` reference, or undefined when the entry is gone
150
+ */
151
+ export async function locateStash(repoPath, sha) {
152
+ const listed = parseStashList(await git(["stash", "list", STASH_LIST_FORMAT], repoPath));
153
+ const position = listed.findIndex(record => record.sha === sha);
154
+ return position === -1 ? undefined : `stash@{${position}}`;
155
+ }
156
+ /**
157
+ * Preserve one stash commit under a ref, verify it, then drop the entry.
158
+ * @param repoPath - Any path inside the repository
159
+ * @param sha - Commit id of the entry
160
+ * @returns Outcome for the entry
161
+ */
162
+ async function dropOne(repoPath, sha) {
163
+ const ref = preservedStashRef(sha);
164
+ const preserved = await git(["update-ref", ref, sha], repoPath).then(() => git(["rev-parse", "--verify", ref], repoPath), () => "");
165
+ if (preserved.trim() !== sha) {
166
+ return {
167
+ sha,
168
+ dropped: false,
169
+ error: "could not preserve the stash commit, so it was not dropped",
170
+ };
171
+ }
172
+ const located = await locateStash(repoPath, sha);
173
+ if (located === undefined) {
174
+ return {
175
+ sha,
176
+ dropped: false,
177
+ preservedRef: ref,
178
+ error: "entry moved or was dropped elsewhere before this drop",
179
+ };
180
+ }
181
+ return git(["stash", "drop", located], repoPath).then(() => ({ sha, dropped: true, preservedRef: ref }), (error) => ({
182
+ sha,
183
+ dropped: false,
184
+ preservedRef: ref,
185
+ error: error instanceof Error
186
+ ? (error.message.split("\n")[0] ?? "unknown")
187
+ : "unknown",
188
+ }));
189
+ }
190
+ /**
191
+ * Preserve and drop every eligible stash entry in a plan.
192
+ * @param plan - Plan produced by {@link planStashPrune}
193
+ * @param index - Index of the eligible entry to drop next
194
+ * @returns One outcome per eligible entry
195
+ */
196
+ export async function applyStashPrune(plan, index = 0) {
197
+ const eligible = plan.verdicts.filter(verdict => verdict.eligible);
198
+ if (index >= eligible.length)
199
+ return [];
200
+ const target = eligible[index];
201
+ return [
202
+ await dropOne(plan.repoPath, target.sha),
203
+ ...(await applyStashPrune(plan, index + 1)),
204
+ ];
205
+ }
206
+ //# sourceMappingURL=stash-prune.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stash-prune.js","sourceRoot":"","sources":["../../src/cli/stash-prune.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import type { WorktreeFacts } from "./worktree-prune-policy.js";
2
+ /** One entry as git itself reports it. */
3
+ export interface WorktreeEntry {
4
+ /** Absolute worktree path. */
5
+ readonly path: string;
6
+ /** Checked-out branch, or undefined when detached. */
7
+ readonly branch?: string;
8
+ /** Whether git reports the entry locked. */
9
+ readonly locked: boolean;
10
+ /** Whether git reports the entry's directory as gone. */
11
+ readonly prunable: boolean;
12
+ /** Whether this is the primary checkout (always the first entry). */
13
+ readonly isPrimary: boolean;
14
+ }
15
+ /**
16
+ * Parse `git worktree list --porcelain` output.
17
+ *
18
+ * The first record is always the primary checkout, which is how the primary is
19
+ * identified — not by comparing it against a path the caller supplied, which
20
+ * would be wrong the moment the caller ran from a worktree.
21
+ * @param stdout - Porcelain output
22
+ * @returns Parsed entries in git's order
23
+ */
24
+ export declare function parseWorktreeList(stdout: string): readonly WorktreeEntry[];
25
+ /**
26
+ * Run one bounded git command.
27
+ * @param args - Git arguments
28
+ * @param cwd - Directory to run git in
29
+ * @returns Trimmed stdout
30
+ */
31
+ export declare function git(args: readonly string[], cwd: string): Promise<string>;
32
+ /**
33
+ * List every worktree registered for a checkout.
34
+ * @param repoPath - Any path inside the repository
35
+ * @returns Registered entries
36
+ */
37
+ export declare function listWorktrees(repoPath: string): Promise<readonly WorktreeEntry[]>;
38
+ /** The half of the facts that only git and the filesystem can answer. */
39
+ export interface WorktreeGitState {
40
+ /** Ownership verdict from the receipt check. */
41
+ readonly ownership: WorktreeFacts["ownership"];
42
+ /** Seconds since the worktree last showed activity. */
43
+ readonly idleSeconds: number;
44
+ /** Modified, staged, added, deleted, or renamed TRACKED files. */
45
+ readonly trackedChanges: number;
46
+ /** Untracked, non-ignored files. */
47
+ readonly untrackedFiles: number;
48
+ /** Commits present locally and on no remote. */
49
+ readonly unpushedCommits: number;
50
+ }
51
+ /** Inputs shared by every fact-gathering call in one run. */
52
+ export interface GatherContext {
53
+ /** Live working directories, or undefined when the probe could not run. */
54
+ readonly liveWorkingDirectories: readonly string[] | undefined;
55
+ /** Owner id of the process running the cleaner. */
56
+ readonly callerOwnerId: string | undefined;
57
+ /** Absolute path the cleaner is running from. */
58
+ readonly currentPath: string;
59
+ /** Clock in epoch milliseconds. */
60
+ readonly nowMs: number;
61
+ }
62
+ /**
63
+ * Gather every fact the entitlement rule is allowed to consider.
64
+ *
65
+ * A worktree git cannot report on yields `readable: false` with zeroed counts.
66
+ * The zeros are never read as "holds nothing" because `readable: false` is
67
+ * itself a blocker — the same reason the doctor check returns undefined for an
68
+ * unreadable worktree instead of a zeroed exposure.
69
+ * @param entry - Registered worktree entry
70
+ * @param context - Shared run inputs
71
+ * @returns Facts for the entitlement rule
72
+ */
73
+ export declare function gatherWorktreeFacts(entry: WorktreeEntry, context: GatherContext): Promise<WorktreeFacts>;
74
+ /**
75
+ * Gather facts for many worktrees without spawning git for all of them at once.
76
+ * @param entries - Registered worktree entries
77
+ * @param context - Shared run inputs
78
+ * @param start - Batch start index
79
+ * @returns Facts in the original entry order
80
+ */
81
+ export declare function gatherAllWorktreeFacts(entries: readonly WorktreeEntry[], context: GatherContext, start?: number): Promise<readonly WorktreeFacts[]>;
82
+ //# sourceMappingURL=worktree-inventory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree-inventory.d.ts","sourceRoot":"","sources":["../../src/cli/worktree-inventory.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAOhE,0CAA0C;AAC1C,MAAM,WAAW,aAAa;IAC5B,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,aAAa,EAAE,CAM1E;AA+BD;;;;;GAKG;AACH,wBAAsB,GAAG,CACvB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAInC;AAED,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC/C,uDAAuD;IACvD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,oCAAoC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,gDAAgD;IAChD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED,6DAA6D;AAC7D,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,sBAAsB,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC/D,mDAAmD;IACnD,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,iDAAiD;IACjD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,mCAAmC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,aAAa,CAAC,CAgCxB;AAuFD;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,OAAO,EAAE,aAAa,EACtB,KAAK,SAAI,GACR,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAanC"}