@claudexor/workspace 4.8.2 → 5.0.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.
Files changed (136) hide show
  1. package/dist/anchor-store.d.ts +20 -9
  2. package/dist/anchor-store.d.ts.map +1 -1
  3. package/dist/anchor-store.js +89 -14
  4. package/dist/anchor-store.js.map +1 -1
  5. package/dist/capture-failure.d.ts.map +1 -1
  6. package/dist/capture-failure.js +7 -2
  7. package/dist/capture-failure.js.map +1 -1
  8. package/dist/capture-inventory.d.ts +10 -0
  9. package/dist/capture-inventory.d.ts.map +1 -0
  10. package/dist/capture-inventory.js +63 -0
  11. package/dist/capture-inventory.js.map +1 -0
  12. package/dist/capture-objects.d.ts +32 -0
  13. package/dist/capture-objects.d.ts.map +1 -0
  14. package/dist/capture-objects.js +129 -0
  15. package/dist/capture-objects.js.map +1 -0
  16. package/dist/cleanup-fault.d.ts +18 -0
  17. package/dist/cleanup-fault.d.ts.map +1 -0
  18. package/dist/cleanup-fault.js +45 -0
  19. package/dist/cleanup-fault.js.map +1 -0
  20. package/dist/continuation-evidence.d.ts +61 -15
  21. package/dist/continuation-evidence.d.ts.map +1 -1
  22. package/dist/continuation-evidence.js +151 -36
  23. package/dist/continuation-evidence.js.map +1 -1
  24. package/dist/continuation-facts.d.ts +93 -0
  25. package/dist/continuation-facts.d.ts.map +1 -0
  26. package/dist/continuation-facts.js +162 -0
  27. package/dist/continuation-facts.js.map +1 -0
  28. package/dist/directory-workspace.d.ts.map +1 -1
  29. package/dist/directory-workspace.js +1 -3
  30. package/dist/directory-workspace.js.map +1 -1
  31. package/dist/files-io.d.ts.map +1 -1
  32. package/dist/files-io.js +1 -7
  33. package/dist/files-io.js.map +1 -1
  34. package/dist/fs-presence.d.ts +12 -0
  35. package/dist/fs-presence.d.ts.map +1 -0
  36. package/dist/fs-presence.js +35 -0
  37. package/dist/fs-presence.js.map +1 -0
  38. package/dist/git-nested-repositories.d.ts.map +1 -1
  39. package/dist/git-nested-repositories.js +11 -2
  40. package/dist/git-nested-repositories.js.map +1 -1
  41. package/dist/git-observed.d.ts +75 -0
  42. package/dist/git-observed.d.ts.map +1 -0
  43. package/dist/git-observed.js +172 -0
  44. package/dist/git-observed.js.map +1 -0
  45. package/dist/git-result.d.ts +36 -0
  46. package/dist/git-result.d.ts.map +1 -0
  47. package/dist/git-result.js +96 -0
  48. package/dist/git-result.js.map +1 -0
  49. package/dist/git-run.d.ts +11 -0
  50. package/dist/git-run.d.ts.map +1 -1
  51. package/dist/git-run.js +24 -0
  52. package/dist/git-run.js.map +1 -1
  53. package/dist/git-snapshot.d.ts +40 -0
  54. package/dist/git-snapshot.d.ts.map +1 -0
  55. package/dist/git-snapshot.js +238 -0
  56. package/dist/git-snapshot.js.map +1 -0
  57. package/dist/git-transient-capture.d.ts +63 -16
  58. package/dist/git-transient-capture.d.ts.map +1 -1
  59. package/dist/git-transient-capture.js +170 -57
  60. package/dist/git-transient-capture.js.map +1 -1
  61. package/dist/git.d.ts +46 -21
  62. package/dist/git.d.ts.map +1 -1
  63. package/dist/git.js +164 -103
  64. package/dist/git.js.map +1 -1
  65. package/dist/index.d.ts +9 -1
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +9 -1
  68. package/dist/index.js.map +1 -1
  69. package/dist/lanes.d.ts +2 -1
  70. package/dist/lanes.d.ts.map +1 -1
  71. package/dist/lanes.js +7 -6
  72. package/dist/lanes.js.map +1 -1
  73. package/dist/manager.d.ts +21 -7
  74. package/dist/manager.d.ts.map +1 -1
  75. package/dist/manager.js +30 -17
  76. package/dist/manager.js.map +1 -1
  77. package/dist/object-compose.d.ts +58 -0
  78. package/dist/object-compose.d.ts.map +1 -0
  79. package/dist/object-compose.js +444 -0
  80. package/dist/object-compose.js.map +1 -0
  81. package/dist/object-materialize.d.ts +52 -0
  82. package/dist/object-materialize.d.ts.map +1 -0
  83. package/dist/object-materialize.js +467 -0
  84. package/dist/object-materialize.js.map +1 -0
  85. package/dist/object-occupied.d.ts +27 -0
  86. package/dist/object-occupied.d.ts.map +1 -0
  87. package/dist/object-occupied.js +131 -0
  88. package/dist/object-occupied.js.map +1 -0
  89. package/dist/object-revert.d.ts +45 -0
  90. package/dist/object-revert.d.ts.map +1 -0
  91. package/dist/object-revert.js +265 -0
  92. package/dist/object-revert.js.map +1 -0
  93. package/dist/object-saved-files.d.ts +50 -0
  94. package/dist/object-saved-files.d.ts.map +1 -0
  95. package/dist/object-saved-files.js +176 -0
  96. package/dist/object-saved-files.js.map +1 -0
  97. package/dist/object-sparse.d.ts +22 -0
  98. package/dist/object-sparse.d.ts.map +1 -0
  99. package/dist/object-sparse.js +72 -0
  100. package/dist/object-sparse.js.map +1 -0
  101. package/dist/object-store-leases.d.ts +41 -0
  102. package/dist/object-store-leases.d.ts.map +1 -0
  103. package/dist/object-store-leases.js +277 -0
  104. package/dist/object-store-leases.js.map +1 -0
  105. package/dist/object-store-roots.d.ts +25 -0
  106. package/dist/object-store-roots.d.ts.map +1 -0
  107. package/dist/object-store-roots.js +114 -0
  108. package/dist/object-store-roots.js.map +1 -0
  109. package/dist/object-store.d.ts +110 -0
  110. package/dist/object-store.d.ts.map +1 -0
  111. package/dist/object-store.js +404 -0
  112. package/dist/object-store.js.map +1 -0
  113. package/dist/object-transfer.d.ts +41 -0
  114. package/dist/object-transfer.d.ts.map +1 -0
  115. package/dist/object-transfer.js +267 -0
  116. package/dist/object-transfer.js.map +1 -0
  117. package/dist/plain-diff-capture.d.ts +1 -2
  118. package/dist/plain-diff-capture.d.ts.map +1 -1
  119. package/dist/plain-diff-capture.js +2 -7
  120. package/dist/plain-diff-capture.js.map +1 -1
  121. package/dist/plain-diff.d.ts +0 -4
  122. package/dist/plain-diff.d.ts.map +1 -1
  123. package/dist/plain-diff.js +2 -44
  124. package/dist/plain-diff.js.map +1 -1
  125. package/dist/raw-patch.d.ts.map +1 -1
  126. package/dist/raw-patch.js +5 -8
  127. package/dist/raw-patch.js.map +1 -1
  128. package/dist/run-expiry.d.ts +3 -0
  129. package/dist/run-expiry.d.ts.map +1 -0
  130. package/dist/run-expiry.js +15 -0
  131. package/dist/run-expiry.js.map +1 -0
  132. package/dist/thread-tree.d.ts +36 -2
  133. package/dist/thread-tree.d.ts.map +1 -1
  134. package/dist/thread-tree.js +87 -11
  135. package/dist/thread-tree.js.map +1 -1
  136. package/package.json +5 -5
@@ -0,0 +1,96 @@
1
+ import { createHash } from "node:crypto";
2
+ import { GIT_OBJECTS_V1, GitObjectResult, } from "@claudexor/schema";
3
+ import { WorkspaceError } from "@claudexor/core";
4
+ import { gitObserved } from "./git-observed.js";
5
+ /**
6
+ * The canonical descriptor of digest v1 (E). Keys are fixed and sorted, values
7
+ * are lowercase object ids, so equal results always serialize to equal bytes.
8
+ */
9
+ export function gitResultDescriptor(input) {
10
+ return JSON.stringify({
11
+ base_tree_oid: input.base_tree,
12
+ candidate_tree_oid: input.candidate_tree,
13
+ object_format: input.object_format,
14
+ schema: GIT_OBJECTS_V1,
15
+ });
16
+ }
17
+ export function gitResultDigest(input) {
18
+ return `sha256:${createHash("sha256").update(gitResultDescriptor(input), "utf8").digest("hex")}`;
19
+ }
20
+ export function makeGitObjectResult(input) {
21
+ return GitObjectResult.parse({
22
+ schema: GIT_OBJECTS_V1,
23
+ ...input,
24
+ digest: gitResultDigest(input),
25
+ });
26
+ }
27
+ /** A recorded result whose digest does not match its own descriptor is never trusted. */
28
+ export function verifiedGitObjectResult(value) {
29
+ const parsed = GitObjectResult.safeParse(value);
30
+ if (!parsed.success)
31
+ return null;
32
+ return parsed.data.digest === gitResultDigest(parsed.data) ? parsed.data : null;
33
+ }
34
+ export async function repositoryObjectFormat(repo, env) {
35
+ const result = await gitObserved(repo, ["rev-parse", "--show-object-format"], {
36
+ env,
37
+ label: "rev-parse --show-object-format",
38
+ allowFailure: true,
39
+ });
40
+ const format = result.stdout.trim();
41
+ // Git before 2.29 has no --show-object-format and only knows SHA-1.
42
+ if (result.code !== 0 || format === "" || format === "--show-object-format")
43
+ return "sha1";
44
+ if (format === "sha1" || format === "sha256")
45
+ return format;
46
+ throw new WorkspaceError(`unsupported Git object format: ${format}`);
47
+ }
48
+ /** The object format of the repository holding `dir`; null when `dir` is in none. */
49
+ export async function repositoryObjectFormatOrNull(dir) {
50
+ const inside = await gitObserved(dir, ["rev-parse", "--git-dir"], {
51
+ label: "rev-parse --git-dir",
52
+ allowFailure: true,
53
+ });
54
+ return inside.code === 0 ? repositoryObjectFormat(dir) : null;
55
+ }
56
+ /** The tree of a commit-ish (or a tree itself), resolved without reading blobs. */
57
+ export async function treeOf(repo, rev, env) {
58
+ const result = await gitObserved(repo, ["rev-parse", "--verify", "--quiet", `${rev}^{tree}`], {
59
+ env,
60
+ label: "rev-parse tree",
61
+ allowFailure: true,
62
+ });
63
+ const tree = result.stdout.trim();
64
+ if (result.code !== 0 || !/^[0-9a-f]{40,64}$/.test(tree))
65
+ throw new WorkspaceError(`not a tree-ish: ${rev}`);
66
+ return tree;
67
+ }
68
+ const SNAPSHOT_SUBJECT = "claudexor: dirty worktree snapshot (incl. untracked)";
69
+ const SNAPSHOT_AUTHOR = "Claudexor <noreply@claudexor.local>";
70
+ /**
71
+ * The user commit a captured base relies on: a Claudexor dirty snapshot is not
72
+ * referenced by any user ref, so its own closure must enter the project store
73
+ * and its single parent is the boundary; any other commit is its own boundary.
74
+ */
75
+ export async function storeBoundaryCommit(repo, commit, env) {
76
+ if (!commit)
77
+ return null;
78
+ const shown = await gitObserved(repo, ["cat-file", "commit", commit], {
79
+ env,
80
+ label: "cat-file commit",
81
+ allowFailure: true,
82
+ });
83
+ if (shown.code !== 0)
84
+ return null;
85
+ const [header = "", ...body] = shown.stdout.split("\n\n");
86
+ const parents = header
87
+ .split("\n")
88
+ .filter((line) => line.startsWith("parent "))
89
+ .map((line) => line.slice("parent ".length).trim());
90
+ const author = header.split("\n").find((line) => line.startsWith("author "));
91
+ const snapshot = parents.length === 1 &&
92
+ author?.startsWith(`author ${SNAPSHOT_AUTHOR} `) === true &&
93
+ body.join("\n\n").trim() === SNAPSHOT_SUBJECT;
94
+ return snapshot ? parents[0] : commit;
95
+ }
96
+ //# sourceMappingURL=git-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-result.js","sourceRoot":"","sources":["../src/git-result.ts"],"names":[],"mappings":"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"}
package/dist/git-run.d.ts CHANGED
@@ -1,3 +1,13 @@
1
+ /** A private operation decides from the bytes, whatever the user's configuration
2
+ * says: `core.ignoreStat` would mark every entry Git adds assume-valid, so a
3
+ * refresh and `diff-files` skip its content, and a filesystem monitor answers
4
+ * from its own last observation instead of looking. */
5
+ export declare const STAT_TRUST_OFF: string[];
6
+ /** A scratch index is always written whole: never as a split index (whose
7
+ * shared part lives in the user's Git directory) and never sparse. It trusts no
8
+ * stat shortcut, and an index copied from the user's carries over neither its
9
+ * monitor state nor its untracked cache (both are dropped as it is read). */
10
+ export declare const SCRATCH_INDEX_CONFIG: string[];
1
11
  /** BYTE-FAITHFUL git capture: raw buffers, never readline — CR
2
12
  * bytes in CRLF diff content survive, and no trailing newline is fabricated
3
13
  * (trim-based consumers like revParse are unaffected: git ends its own
@@ -7,6 +17,7 @@ export declare function git(repo: string, args: string[], input?: string): Promi
7
17
  stdout: string;
8
18
  stderr: string;
9
19
  }>;
20
+ export declare function revParse(repo: string, ref: string): Promise<string>;
10
21
  /** `git` with extra environment (e.g. a scratch GIT_INDEX_FILE for snapshots). */
11
22
  export declare function gitEnv(repo: string, args: string[], env: Record<string, string>, input?: string): Promise<{
12
23
  code: number | null;
@@ -1 +1 @@
1
- {"version":3,"file":"git-run.d.ts","sourceRoot":"","sources":["../src/git-run.ts"],"names":[],"mappings":"AAOA;;;sBAGsB;AACtB,wBAAsB,GAAG,CACvB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAQlE;AAED,kFAAkF;AAClF,wBAAsB,MAAM,CAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CASlE;AAED;;gEAEgE;AAChE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAQrE"}
1
+ {"version":3,"file":"git-run.d.ts","sourceRoot":"","sources":["../src/git-run.ts"],"names":[],"mappings":"AAOA;;;uDAGuD;AACvD,eAAO,MAAM,cAAc,UAAgE,CAAC;AAE5F;;;6EAG6E;AAC7E,eAAO,MAAM,oBAAoB,UAQhC,CAAC;AAEF;;;sBAGsB;AACtB,wBAAsB,GAAG,CACvB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAQlE;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIzE;AAED,kFAAkF;AAClF,wBAAsB,MAAM,CAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CASlE;AAED;;gEAEgE;AAChE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAQrE"}
package/dist/git-run.js CHANGED
@@ -1,4 +1,22 @@
1
1
  import { assertCaptureComplete, parseUnifiedDiff, runCaptureRaw, WorkspaceError, } from "@claudexor/core";
2
+ /** A private operation decides from the bytes, whatever the user's configuration
3
+ * says: `core.ignoreStat` would mark every entry Git adds assume-valid, so a
4
+ * refresh and `diff-files` skip its content, and a filesystem monitor answers
5
+ * from its own last observation instead of looking. */
6
+ export const STAT_TRUST_OFF = ["-c", "core.ignoreStat=false", "-c", "core.fsmonitor=false"];
7
+ /** A scratch index is always written whole: never as a split index (whose
8
+ * shared part lives in the user's Git directory) and never sparse. It trusts no
9
+ * stat shortcut, and an index copied from the user's carries over neither its
10
+ * monitor state nor its untracked cache (both are dropped as it is read). */
11
+ export const SCRATCH_INDEX_CONFIG = [
12
+ "-c",
13
+ "core.splitIndex=false",
14
+ "-c",
15
+ "index.sparse=false",
16
+ ...STAT_TRUST_OFF,
17
+ "-c",
18
+ "core.untrackedCache=false",
19
+ ];
2
20
  /** BYTE-FAITHFUL git capture: raw buffers, never readline — CR
3
21
  * bytes in CRLF diff content survive, and no trailing newline is fabricated
4
22
  * (trim-based consumers like revParse are unaffected: git ends its own
@@ -12,6 +30,12 @@ export async function git(repo, args, input) {
12
30
  assertCaptureComplete(r);
13
31
  return r;
14
32
  }
33
+ export async function revParse(repo, ref) {
34
+ const r = await git(repo, ["rev-parse", ref]);
35
+ if (r.code !== 0)
36
+ throw new WorkspaceError(`git rev-parse ${ref} failed: ${r.stderr.trim()}`);
37
+ return r.stdout.trim();
38
+ }
15
39
  /** `git` with extra environment (e.g. a scratch GIT_INDEX_FILE for snapshots). */
16
40
  export async function gitEnv(repo, args, env, input) {
17
41
  const r = await runCaptureRaw("git", ["-C", repo, ...args], {
@@ -1 +1 @@
1
- {"version":3,"file":"git-run.js","sourceRoot":"","sources":["../src/git-run.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,iBAAiB,CAAC;AAEzB;;;sBAGsB;AACtB,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,IAAY,EACZ,IAAc,EACd,KAAc;IAEd,MAAM,CAAC,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE;QAC1D,SAAS,EAAE,MAAM;QACjB,KAAK;QACL,cAAc,EAAE,MAAM;KACvB,CAAC,CAAC;IACH,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO,CAAC,CAAC;AACX,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,IAAY,EACZ,IAAc,EACd,GAA2B,EAC3B,KAAc;IAEd,MAAM,CAAC,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE;QAC1D,SAAS,EAAE,MAAM;QACjB,GAAG;QACH,KAAK;QACL,cAAc,EAAE,MAAM;KACvB,CAAC,CAAC;IACH,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;gEAEgE;AAChE,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,KAAa;IAC7D,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IACvE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,IAAI,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjF,MAAM,IAAI,cAAc,CACtB,GAAG,KAAK,+CAA+C,KAAK,sCAAsC,CACnG,CAAC;IACJ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"git-run.js","sourceRoot":"","sources":["../src/git-run.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,iBAAiB,CAAC;AAEzB;;;uDAGuD;AACvD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,uBAAuB,EAAE,IAAI,EAAE,sBAAsB,CAAC,CAAC;AAE5F;;;6EAG6E;AAC7E,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,IAAI;IACJ,uBAAuB;IACvB,IAAI;IACJ,oBAAoB;IACpB,GAAG,cAAc;IACjB,IAAI;IACJ,2BAA2B;CAC5B,CAAC;AAEF;;;sBAGsB;AACtB,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,IAAY,EACZ,IAAc,EACd,KAAc;IAEd,MAAM,CAAC,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE;QAC1D,SAAS,EAAE,MAAM;QACjB,KAAK;QACL,cAAc,EAAE,MAAM;KACvB,CAAC,CAAC;IACH,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY,EAAE,GAAW;IACtD,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC;IAC9C,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC;QAAE,MAAM,IAAI,cAAc,CAAC,iBAAiB,GAAG,YAAY,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC9F,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,IAAY,EACZ,IAAc,EACd,GAA2B,EAC3B,KAAc;IAEd,MAAM,CAAC,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE;QAC1D,SAAS,EAAE,MAAM;QACjB,GAAG;QACH,KAAK;QACL,cAAc,EAAE,MAAM;KACvB,CAAC,CAAC;IACH,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;gEAEgE;AAChE,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,KAAa;IAC7D,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IACvE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,IAAI,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjF,MAAM,IAAI,cAAc,CACtB,GAAG,KAAK,+CAA+C,KAAK,sCAAsC,CACnG,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,40 @@
1
+ type GitEnv = Record<string, string>;
2
+ /** Entries of an index the user marked so that Git skips their bytes: assume-unchanged
3
+ * (a lowercase `ls-files -v` tag) and skip-worktree (`S`). A name stdin cannot carry
4
+ * back to Git (not valid UTF-8) is only counted. */
5
+ export interface MarkedEntries {
6
+ assumed: string[];
7
+ skipped: string[];
8
+ unnamed: number;
9
+ /** Merged (stage 0) entries of the wanted paths, as `--index-info` lines. */
10
+ held: {
11
+ path: string;
12
+ info: string;
13
+ }[];
14
+ }
15
+ /** The marked entries of the index `env` names, read as a stream (only marked ones are
16
+ * kept), and the entries of `wanted` paths in the same pass. */
17
+ export declare function markedEntries(top: string, env: GitEnv, wanted?: ReadonlySet<string>): Promise<MarkedEntries>;
18
+ /**
19
+ * Capture the full dirty working tree (tracked AND untracked) as a dangling
20
+ * commit; null if clean. Plain `git stash create` captures only TRACKED
21
+ * changes, silently dropping the user's new-but-unstaged files from the run's
22
+ * base. We instead seed a scratch index from HEAD, `add -A` into it (which
23
+ * respects .gitignore, so run artifacts stay out), and commit-tree the result.
24
+ * `objects`: where the snapshot's objects go instead (a dry run's scratch).
25
+ * `exact`: paths the caller decides on, which a clean answer has read by content
26
+ * ({@link snapshotStatus}); a dirty snapshot reads every file anyway.
27
+ */
28
+ export declare function stashCreate(repo: string, objects?: GitEnv, exact?: readonly string[]): Promise<string | null>;
29
+ /**
30
+ * Capture the CURRENT working-tree state (tracked + untracked, minus `.claudexor`)
31
+ * as a commit sha — always returns one. Dirty trees become a dangling snapshot
32
+ * commit (via `stashCreate`); a clean tree resolves to HEAD. This is the per-turn
33
+ * diff base for in-place threads: snapshot at turn start, snapshot at turn end,
34
+ * `diffTrees(base, end)` yields exactly that turn's net change (pre-existing dirty
35
+ * state is folded into `base`, so the reviewer never sees prior turns' edits).
36
+ * `exact`: as for {@link stashCreate}.
37
+ */
38
+ export declare function snapshotTree(repo: string, exact?: readonly string[]): Promise<string>;
39
+ export {};
40
+ //# sourceMappingURL=git-snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-snapshot.d.ts","sourceRoot":"","sources":["../src/git-snapshot.ts"],"names":[],"mappings":"AAqBA,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAIrC;;oDAEoD;AACpD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACxC;AAED;gEACgE;AAChE,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,WAAW,CAAC,MAAM,CAAa,GACtC,OAAO,CAAC,aAAa,CAAC,CA6CxB;AA4FD;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAW,EACpB,KAAK,GAAE,SAAS,MAAM,EAAO,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmFxB;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,SAAS,MAAM,EAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAG/F"}
@@ -0,0 +1,238 @@
1
+ import { copyFileSync, existsSync, mkdtempSync, renameSync, rmSync, statSync, utimesSync, } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { WorkspaceError } from "@claudexor/core";
5
+ import { lstatPresent } from "./fs-presence.js";
6
+ import { nestedRepositoryExcludes, untrackedNestedRepositories, } from "./git-nested-repositories.js";
7
+ import { diskProgress, gitObserved, proofStallMs, treeBlobBytes } from "./git-observed.js";
8
+ import { git, gitEnv, revParse, SCRATCH_INDEX_CONFIG } from "./git-run.js";
9
+ import { outsideSparseCheckout } from "./object-sparse.js";
10
+ const STRICT_UTF8 = new TextDecoder("utf-8", { fatal: true });
11
+ /** The marked entries of the index `env` names, read as a stream (only marked ones are
12
+ * kept), and the entries of `wanted` paths in the same pass. */
13
+ export async function markedEntries(top, env, wanted = new Set()) {
14
+ const marked = { assumed: [], skipped: [], unnamed: 0, held: [] };
15
+ const staged = wanted.size > 0; // `-s`: "<tag> <mode> <oid> <stage>\t<path>"
16
+ const take = (record) => {
17
+ const tag = record[0] ?? 0;
18
+ const assumed = tag >= 0x61 && tag <= 0x7a; // a lowercase tag
19
+ const skipped = tag === 0x53 || tag === 0x73; // S or s
20
+ const tab = staged ? record.indexOf(0x09) : 1;
21
+ if ((!assumed && !skipped && !staged) || record.length < 3 || tab < 1)
22
+ return;
23
+ let path;
24
+ try {
25
+ path = STRICT_UTF8.decode(record.subarray(tab + 1));
26
+ }
27
+ catch {
28
+ if (assumed || skipped)
29
+ marked.unnamed += 1;
30
+ return;
31
+ }
32
+ if (assumed)
33
+ marked.assumed.push(path);
34
+ if (skipped)
35
+ marked.skipped.push(path);
36
+ if (staged && wanted.has(path)) {
37
+ const [mode, oid, stage] = record.subarray(2, tab).toString("latin1").split(" ");
38
+ if (stage === "0")
39
+ marked.held.push({ path, info: `${mode} ${oid}\t${path}` });
40
+ }
41
+ };
42
+ let carry = Buffer.alloc(0);
43
+ await gitObserved(top, [...SCRATCH_INDEX_CONFIG, "ls-files", "-v", ...(staged ? ["-s"] : []), "-z"], {
44
+ env,
45
+ label: "ls-files -v (snapshot)",
46
+ stdout: {
47
+ write(chunk) {
48
+ const data = carry.length > 0 ? Buffer.concat([carry, chunk]) : chunk;
49
+ let start = 0;
50
+ for (let end = data.indexOf(0); end !== -1; end = data.indexOf(0, start)) {
51
+ take(data.subarray(start, end));
52
+ start = end + 1;
53
+ }
54
+ carry = Buffer.from(data.subarray(start));
55
+ },
56
+ end: () => "",
57
+ },
58
+ });
59
+ return marked;
60
+ }
61
+ /**
62
+ * The snapshot's clean check: `git status`. No stat shortcut of the user's
63
+ * configuration applies (`STAT_TRUST_OFF`, no untracked cache), and an entry the
64
+ * user marked assume-unchanged or skip-worktree is compared like any other in a
65
+ * private copy of the index, so the check answers what the snapshot's `add -A`
66
+ * would record. Only an absent skip-worktree file the sparse-checkout patterns
67
+ * leave out stays as the index has it: a sparse checkout leaves such a file absent
68
+ * on purpose, and `add -A` skips it too. A marked file that is gone otherwise is a
69
+ * deletion (a clean check that trusted the mark held HEAD's blob where a dirty
70
+ * worktree's snapshot held the deletion). `git status` still trusts the index's stat
71
+ * cache for every other file, as the user's own does: an edit that kept a file's
72
+ * size and times (with `core.checkStat=minimal` and `core.trustctime=false`, a
73
+ * restored mtime is enough) reads as unchanged. So the `exact` paths, the ones the
74
+ * caller decides on, are re-added to the private copy without stat data, and the
75
+ * check reads exactly those files (bar the out-of-pattern absences above) by
76
+ * content. The copy keeps the user's index time, so an entry Git would compare as
77
+ * racily clean is still compared. The user's own index is never written. Null: the
78
+ * comparison could not be set up (or this Git cannot evaluate the sparse
79
+ * patterns), so the caller reads every file instead.
80
+ */
81
+ async function snapshotStatus(repo, objects, scratchDir, exact) {
82
+ const env = { ...objects, GIT_OPTIONAL_LOCKS: "0" };
83
+ const top = await gitEnv(repo, ["rev-parse", "--show-toplevel"], env);
84
+ if (top.code !== 0)
85
+ throw new WorkspaceError(`snapshot top level failed: ${top.stderr.trim()}`);
86
+ const root = top.stdout.trim();
87
+ const marked = await markedEntries(root, env, new Set(exact));
88
+ // A parent that became a file (ENOTDIR) means the marked file is not there.
89
+ const compared = [];
90
+ const absent = [];
91
+ for (const path of marked.skipped)
92
+ (lstatPresent(join(root, path)) === undefined ? absent : compared).push(path);
93
+ let kept = new Set();
94
+ if (absent.length > 0) {
95
+ const excluded = await outsideSparseCheckout(root, absent, env);
96
+ if (excluded === null)
97
+ return null;
98
+ kept = new Set(excluded);
99
+ compared.push(...absent.filter((path) => !kept.has(path)));
100
+ }
101
+ const reread = marked.held.filter(({ path }) => !kept.has(path));
102
+ if (marked.assumed.length > 0 || compared.length > 0 || marked.unnamed > 0 || reread.length > 0) {
103
+ if (marked.unnamed > 0)
104
+ return null;
105
+ const located = await gitEnv(root, ["rev-parse", "--path-format=absolute", "--git-path", "index"], env);
106
+ const userIndex = located.code === 0 ? located.stdout.trim() : "";
107
+ if (!userIndex || !existsSync(userIndex))
108
+ return null;
109
+ const copy = join(scratchDir, "status-index");
110
+ const { atimeMs, mtimeMs } = statSync(userIndex);
111
+ copyFileSync(userIndex, `${copy}.copy`);
112
+ // Git compares an entry's times to its index file's own: never later than the user's.
113
+ utimesSync(`${copy}.copy`, atimeMs / 1000, Math.floor(mtimeMs) / 1000);
114
+ renameSync(`${copy}.copy`, copy);
115
+ env["GIT_INDEX_FILE"] = copy;
116
+ for (const [flag, paths] of [
117
+ ["--no-assume-unchanged", marked.assumed],
118
+ ["--no-skip-worktree", compared],
119
+ ]) {
120
+ if (paths.length === 0)
121
+ continue;
122
+ const cleared = await gitEnv(root, [...SCRATCH_INDEX_CONFIG, "update-index", flag, "-z", "--stdin"], env, `${paths.join("\0")}\0`);
123
+ if (cleared.code !== 0)
124
+ return null;
125
+ }
126
+ if (reread.length > 0) {
127
+ const unstated = await gitEnv(root, [...SCRATCH_INDEX_CONFIG, "update-index", "-z", "--index-info"], env, reread.map(({ info }) => `${info}\0`).join(""));
128
+ if (unstated.code !== 0)
129
+ return null;
130
+ }
131
+ }
132
+ return gitEnv(repo, [...SCRATCH_INDEX_CONFIG, "status", "--porcelain=v1", "-z", "--untracked-files=all"], env);
133
+ }
134
+ /**
135
+ * Capture the full dirty working tree (tracked AND untracked) as a dangling
136
+ * commit; null if clean. Plain `git stash create` captures only TRACKED
137
+ * changes, silently dropping the user's new-but-unstaged files from the run's
138
+ * base. We instead seed a scratch index from HEAD, `add -A` into it (which
139
+ * respects .gitignore, so run artifacts stay out), and commit-tree the result.
140
+ * `objects`: where the snapshot's objects go instead (a dry run's scratch).
141
+ * `exact`: paths the caller decides on, which a clean answer has read by content
142
+ * ({@link snapshotStatus}); a dirty snapshot reads every file anyway.
143
+ */
144
+ export async function stashCreate(repo, objects = {}, exact = []) {
145
+ // Unique per call: concurrent envelope creates (a best_of_n wave) must never
146
+ // collide on the scratch index (same pid + same millisecond is real). It lives
147
+ // in the OS temp dir, NOT under `<repo>/.git` — in a linked worktree `.git` is
148
+ // a FILE (gitdir pointer), so a scratch path there fails (review #8).
149
+ const scratchDir = mkdtempSync(join(tmpdir(), "claudexor-snapshot-index-"));
150
+ const tmpIndex = join(scratchDir, "index");
151
+ const env = {
152
+ ...objects,
153
+ GIT_INDEX_FILE: tmpIndex,
154
+ GIT_AUTHOR_NAME: "Claudexor",
155
+ GIT_AUTHOR_EMAIL: "noreply@claudexor.local",
156
+ GIT_COMMITTER_NAME: "Claudexor",
157
+ GIT_COMMITTER_EMAIL: "noreply@claudexor.local",
158
+ };
159
+ try {
160
+ // NUL form is filename-faithful (newlines, quotes and whitespace are legal
161
+ // path bytes). Runtime is external, so every reported path is user state.
162
+ const status = await snapshotStatus(repo, objects, scratchDir, exact);
163
+ if (status && status.code !== 0)
164
+ throw new WorkspaceError(`snapshot status failed: ${status.stderr.trim()}`);
165
+ if (status && status.stdout.length === 0)
166
+ return null;
167
+ const head = await git(repo, ["rev-parse", "HEAD"]);
168
+ if (head.code !== 0)
169
+ throw new WorkspaceError(`snapshot rev-parse HEAD failed: ${head.stderr.trim()}`);
170
+ const headSha = head.stdout.trim();
171
+ const read = await gitEnv(repo, [...SCRATCH_INDEX_CONFIG, "read-tree", "HEAD"], env);
172
+ if (read.code !== 0)
173
+ throw new WorkspaceError(`snapshot read-tree failed: ${read.stderr.trim()}`);
174
+ // Bare add runs only against the scratch index. It respects the project's
175
+ // own ignore policy without changing the live index. `.claudexor/` is not a
176
+ // runtime exception in v2: tracked or untracked project config is user state.
177
+ // An undeclared nested repository stays out, as in the candidate capture:
178
+ // `add` refuses one without a commit and would freeze one with a commit as
179
+ // a commit link (`:/` keeps the whole-tree scope of the bare add).
180
+ const nested = await untrackedNestedRepositories(repo, env, [":/"], "snapshot");
181
+ const add = await gitObserved(repo, [
182
+ ...SCRATCH_INDEX_CONFIG,
183
+ "add",
184
+ "-A",
185
+ ...(nested.length ? ["--", ":/", ...nestedRepositoryExcludes(nested)] : []),
186
+ ], {
187
+ env,
188
+ label: "add -A (snapshot)",
189
+ progress: diskProgress(repo),
190
+ allowFailure: true,
191
+ // Re-hashing HEAD's files reads silently: bounded by their bytes, not a flat ten minutes.
192
+ stallMs: proofStallMs(await treeBlobBytes(repo, headSha, env)),
193
+ });
194
+ if (add.code !== 0)
195
+ throw new WorkspaceError(`snapshot add -A failed: ${add.stderr.trim()}`);
196
+ const writeTree = await gitEnv(repo, [...SCRATCH_INDEX_CONFIG, "write-tree"], env);
197
+ if (writeTree.code !== 0)
198
+ throw new WorkspaceError(`snapshot write-tree failed: ${writeTree.stderr.trim()}`);
199
+ const tree = writeTree.stdout.trim();
200
+ // Read without the clean check: a tree equal to HEAD's is no change either.
201
+ if (!status && tree === (await revParse(repo, "HEAD^{tree}")))
202
+ return null;
203
+ const commit = await gitEnv(repo, [
204
+ "commit-tree",
205
+ tree,
206
+ "-p",
207
+ headSha,
208
+ "-m",
209
+ "claudexor: dirty worktree snapshot (incl. untracked)",
210
+ ], env);
211
+ if (commit.code !== 0)
212
+ throw new WorkspaceError(`snapshot commit-tree failed: ${commit.stderr.trim()}`);
213
+ const sha = commit.stdout.trim();
214
+ return sha.length > 0 ? sha : null;
215
+ }
216
+ finally {
217
+ try {
218
+ rmSync(scratchDir, { recursive: true, force: true });
219
+ }
220
+ catch {
221
+ /* scratch index cleanup is best-effort */
222
+ }
223
+ }
224
+ }
225
+ /**
226
+ * Capture the CURRENT working-tree state (tracked + untracked, minus `.claudexor`)
227
+ * as a commit sha — always returns one. Dirty trees become a dangling snapshot
228
+ * commit (via `stashCreate`); a clean tree resolves to HEAD. This is the per-turn
229
+ * diff base for in-place threads: snapshot at turn start, snapshot at turn end,
230
+ * `diffTrees(base, end)` yields exactly that turn's net change (pre-existing dirty
231
+ * state is folded into `base`, so the reviewer never sees prior turns' edits).
232
+ * `exact`: as for {@link stashCreate}.
233
+ */
234
+ export async function snapshotTree(repo, exact = []) {
235
+ const snap = await stashCreate(repo, {}, exact);
236
+ return snap ?? (await revParse(repo, "HEAD"));
237
+ }
238
+ //# sourceMappingURL=git-snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-snapshot.js","sourceRoot":"","sources":["../src/git-snapshot.ts"],"names":[],"mappings":"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"}
@@ -1,30 +1,77 @@
1
- import { CaptureOutputExceeded } from "@claudexor/core";
1
+ import type { CaptureChangeInventory, GitObjectResult, PatchViewUnavailable } from "@claudexor/schema";
2
2
  /**
3
- * Capture the current worktree against an exact base without writing its blobs
4
- * into the user's Git object database. In-place harness output is untrusted
5
- * until the secret fence has inspected the resulting patch; `snapshotTree`
6
- * would otherwise leave a dangling raw-secret blob even when artifact
7
- * persistence later refused the patch.
3
+ * Capture the current worktree against an exact base as Git objects (#535
4
+ * phase 2). The candidate tree is written through a scratch index into a
5
+ * private scratch object directory (the user's object database and the
6
+ * project store are read-only alternates), then everything it needs beyond its
7
+ * boundary enters the project object store, where it is verified alone. The
8
+ * store is an alternate only under a pending lease: Git writes no object it
9
+ * finds there, so a prune must not delete one meanwhile. The object pair is
10
+ * the canonical result; capture runs no rename detection.
8
11
  *
9
- * Git still owns filename, mode, symlink, binary, ignored-file, and untracked
10
- * semantics. A scratch index plus an isolated temporary object directory holds
11
- * the candidate tree only for the duration of `git diff`; the real object
12
- * directory is read-only through Git's alternates mechanism.
12
+ * A patch is only a VIEW: a render is attempted when an estimate computed from its
13
+ * blob sizes fits today's 256 MiB view limit, and it is absent with a typed reason
14
+ * when that estimate does not fit or the render overflows or fails, while the
15
+ * result stays exact, verifiable, deliverable and revertable.
13
16
  */
14
17
  export interface TransientDiffCapture {
15
18
  captureIncomplete: boolean;
16
- captureFailure?: CaptureOutputExceeded;
19
+ captureFailure?: Error;
20
+ /** The rendered patch view, "" when no view was rendered. */
17
21
  patch: string;
18
- /** Changed binary files whose pre- or postimage holds secret-like bytes (or
19
- * could not be read for inspection). The exact patch still carries their
20
- * payload; a persisted COPY withholds it and discloses these paths (INV-062). */
21
- binarySecretPaths: string[];
22
22
  /** Untracked directories holding their own Git repository. A patch cannot
23
23
  * carry another repository (Git would record a commit link, or refuse a
24
24
  * repository without a commit), so they are left out of the capture and
25
25
  * named here instead of failing the whole capture. */
26
26
  excludedNestedRepositories?: string[];
27
+ /** The exact candidate as Git objects; absent only for a refused capture. */
28
+ result?: GitObjectResult;
29
+ /** Present when no patch view was rendered (the object result stays exact). */
30
+ patchViewUnavailable?: PatchViewUnavailable;
31
+ /** Bounded listing of the largest changed files of an unrendered result. */
32
+ inventory?: CaptureChangeInventory;
33
+ /** Every changed path of an unrendered result (no view to read them from),
34
+ * and those whose old side existed, from the no-renames raw diff. */
35
+ changedPaths?: string[];
36
+ existingPaths?: string[];
27
37
  }
28
- export declare function captureWorkingTreeTransient(repo: string, baseSha: string, pathExcludes?: string[]): Promise<TransientDiffCapture>;
38
+ export interface TransientCaptureOptions {
39
+ /** Project root owning the object store (the user's repository root). */
40
+ projectRoot?: string;
41
+ /** Render limit of the patch view; today's capture buffer by default. */
42
+ viewLimitBytes?: number;
43
+ /** Path whose existence keeps the result's objects in the store (the run directory). */
44
+ owner?: string;
45
+ }
46
+ export declare function captureWorkingTreeTransient(repo: string, baseSha: string, pathExcludes?: string[], options?: TransientCaptureOptions): Promise<TransientDiffCapture>;
29
47
  export declare function diffWorkingTreeTransient(repo: string, baseSha: string, pathExcludes?: string[]): Promise<string>;
48
+ /** The capture's object-backed facts, carried unchanged to its consumers. */
49
+ export declare function captureObjectFacts(captured: TransientDiffCapture): {
50
+ result?: {
51
+ schema: "git_objects/v1";
52
+ object_format: "sha1" | "sha256";
53
+ base_tree: string;
54
+ candidate_tree: string;
55
+ base_commit: string | null;
56
+ store_boundary_commit: string | null;
57
+ digest: string;
58
+ } | undefined;
59
+ patchViewUnavailable?: {
60
+ reason: "exceeds_view_limit" | "render_failed";
61
+ limitBytes: number;
62
+ estimatedBytes: number | null;
63
+ } | undefined;
64
+ inventory?: {
65
+ exact: false;
66
+ changedFiles: number;
67
+ largest: {
68
+ path: string;
69
+ change: "added" | "deleted" | "modified" | "type_changed";
70
+ sizeBytes: number | null;
71
+ }[];
72
+ } | undefined;
73
+ changedPaths?: string[] | undefined;
74
+ existingPaths?: string[] | undefined;
75
+ excludedNestedRepositories?: string[] | undefined;
76
+ };
30
77
  //# sourceMappingURL=git-transient-capture.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"git-transient-capture.d.ts","sourceRoot":"","sources":["../src/git-transient-capture.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,qBAAqB,EAAoC,MAAM,iBAAiB,CAAC;AAQ1F;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,oBAAoB;IACnC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,qBAAqB,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd;;qFAEiF;IACjF,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B;;;0DAGsD;IACtD,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;CACvC;AAED,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,YAAY,GAAE,MAAM,EAAO,GAC1B,OAAO,CAAC,oBAAoB,CAAC,CA2H/B;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,YAAY,GAAE,MAAM,EAAO,GAC1B,OAAO,CAAC,MAAM,CAAC,CAIjB"}
1
+ {"version":3,"file":"git-transient-capture.d.ts","sourceRoot":"","sources":["../src/git-transient-capture.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,sBAAsB,EACtB,eAAe,EACf,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AA8B3B;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,oBAAoB;IACnC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,KAAK,CAAC;IACvB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd;;;0DAGsD;IACtD,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,6EAA6E;IAC7E,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,+EAA+E;IAC/E,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,4EAA4E;IAC5E,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC;yEACqE;IACrE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAQD,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,YAAY,GAAE,MAAM,EAAO,EAC3B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,oBAAoB,CAAC,CA4M/B;AAyBD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,YAAY,GAAE,MAAM,EAAO,GAC1B,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAahE"}