@codyswann/lisa 4.10.15 → 4.11.1
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/cli/doctor-worktree-hygiene.d.ts +5 -2
- package/dist/cli/doctor-worktree-hygiene.d.ts.map +1 -1
- package/dist/cli/doctor-worktree-hygiene.js +12 -6
- package/dist/cli/doctor-worktree-hygiene.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/prune-commands.d.ts +52 -0
- package/dist/cli/prune-commands.d.ts.map +1 -0
- package/dist/cli/prune-commands.js +158 -0
- package/dist/cli/prune-commands.js.map +1 -0
- package/dist/cli/prune-report.d.ts +52 -0
- package/dist/cli/prune-report.d.ts.map +1 -0
- package/dist/cli/prune-report.js +143 -0
- package/dist/cli/prune-report.js.map +1 -0
- package/dist/cli/stash-prune-policy.d.ts +72 -0
- package/dist/cli/stash-prune-policy.d.ts.map +1 -0
- package/dist/cli/stash-prune-policy.js +90 -0
- package/dist/cli/stash-prune-policy.js.map +1 -0
- package/dist/cli/stash-prune.d.ts +104 -0
- package/dist/cli/stash-prune.d.ts.map +1 -0
- package/dist/cli/stash-prune.js +206 -0
- package/dist/cli/stash-prune.js.map +1 -0
- package/dist/cli/worktree-inventory.d.ts +82 -0
- package/dist/cli/worktree-inventory.d.ts.map +1 -0
- package/dist/cli/worktree-inventory.js +195 -0
- package/dist/cli/worktree-inventory.js.map +1 -0
- package/dist/cli/worktree-liveness.d.ts +52 -0
- package/dist/cli/worktree-liveness.d.ts.map +1 -0
- package/dist/cli/worktree-liveness.js +124 -0
- package/dist/cli/worktree-liveness.js.map +1 -0
- package/dist/cli/worktree-ownership.d.ts +65 -0
- package/dist/cli/worktree-ownership.d.ts.map +1 -0
- package/dist/cli/worktree-ownership.js +142 -0
- package/dist/cli/worktree-ownership.js.map +1 -0
- package/dist/cli/worktree-prune-policy.d.ts +76 -0
- package/dist/cli/worktree-prune-policy.d.ts.map +1 -0
- package/dist/cli/worktree-prune-policy.js +114 -0
- package/dist/cli/worktree-prune-policy.js.map +1 -0
- package/dist/cli/worktree-prune.d.ts +96 -0
- package/dist/cli/worktree-prune.d.ts.map +1 -0
- package/dist/cli/worktree-prune.js +153 -0
- package/dist/cli/worktree-prune.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +18 -0
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stash-prune-policy.d.ts","sourceRoot":"","sources":["../../src/cli/stash-prune-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,oCAAoC;AACpC,MAAM,MAAM,YAAY,GACpB,wBAAwB,GACxB,YAAY,GACZ,YAAY,CAAC;AAEjB,0CAA0C;AAC1C,MAAM,MAAM,eAAe,GAAG,qBAAqB,GAAG,gBAAgB,CAAC;AAEvE,wEAAwE;AACxE,MAAM,WAAW,UAAU;IACzB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,kFAAkF;IAClF,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AAED,gCAAgC;AAChC,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAED,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,oCAAoC;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,0CAA0C;IAC1C,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;IACtC,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;CAC5C;AAED,yEAAyE;AACzE,eAAO,MAAM,8BAA8B,QAAe,CAAC;AAW3D,sDAAsD;AACtD,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAC/C,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAO5B,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,gBAAgB,GACvB,YAAY,CA6Bd"}
|
|
@@ -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"}
|