@claudexor/workspace 1.0.1 → 2.1.3
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/anchor-store.d.ts +9 -0
- package/dist/anchor-store.d.ts.map +1 -0
- package/dist/anchor-store.js +93 -0
- package/dist/anchor-store.js.map +1 -0
- package/dist/artifact-paths.d.ts +8 -36
- package/dist/artifact-paths.d.ts.map +1 -1
- package/dist/artifact-paths.js +11 -117
- package/dist/artifact-paths.js.map +1 -1
- package/dist/git.d.ts +32 -10
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +186 -165
- package/dist/git.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/manager.d.ts +14 -10
- package/dist/manager.d.ts.map +1 -1
- package/dist/manager.js +59 -31
- package/dist/manager.js.map +1 -1
- package/dist/raw-patch.d.ts +17 -0
- package/dist/raw-patch.d.ts.map +1 -0
- package/dist/raw-patch.js +158 -0
- package/dist/raw-patch.js.map +1 -0
- package/dist/thread-tree.d.ts +9 -2
- package/dist/thread-tree.d.ts.map +1 -1
- package/dist/thread-tree.js +49 -11
- package/dist/thread-tree.js.map +1 -1
- package/package.json +4 -4
- package/dist/self-ignore.d.ts +0 -10
- package/dist/self-ignore.d.ts.map +0 -1
- package/dist/self-ignore.js +0 -24
- package/dist/self-ignore.js.map +0 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Persist a binary/mode-aware turn patch outside Git before its dangling
|
|
2
|
+
* snapshot commits can be collected. The patch digest is the immutable ID. */
|
|
3
|
+
export declare function createRevertAnchor(repo: string, preTurnSha: string, postTurnSha: string): Promise<string>;
|
|
4
|
+
export declare function createRevertAnchorFromPatchOrNull(repo: string, patch: string): string | null;
|
|
5
|
+
export declare function readRevertAnchor(repo: string, id: string): string;
|
|
6
|
+
/** Revert is an optional recovery affordance; never advertise it until the
|
|
7
|
+
* immutable anchor has finalized successfully. */
|
|
8
|
+
export declare function createRevertAnchorOrNull(repo: string, preTurnSha: string | null, postTurnSha: string | null): Promise<string | null>;
|
|
9
|
+
//# sourceMappingURL=anchor-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor-store.d.ts","sourceRoot":"","sources":["../src/anchor-store.ts"],"names":[],"mappings":"AAuBA;8EAC8E;AAC9E,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CAGjB;AAOD,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM5F;AA6CD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAIjE;AAED;kDACkD;AAClD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { closeSync, constants, existsSync, fsyncSync, openSync, readFileSync, renameSync, unlinkSync, writeSync, } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { ensureDir, projectRuntimeDir, sha256 } from "@claudexor/util";
|
|
4
|
+
import { WorkspaceError } from "@claudexor/core";
|
|
5
|
+
import { diffTrees } from "./git.js";
|
|
6
|
+
const ANCHOR_ID = /^sha256:[0-9a-f]{64}$/;
|
|
7
|
+
function objectPath(repo, id) {
|
|
8
|
+
if (!ANCHOR_ID.test(id))
|
|
9
|
+
throw new WorkspaceError("invalid revert anchor id");
|
|
10
|
+
return join(projectRuntimeDir(repo), "anchors", "objects", `${id.slice(7)}.patch`);
|
|
11
|
+
}
|
|
12
|
+
/** Persist a binary/mode-aware turn patch outside Git before its dangling
|
|
13
|
+
* snapshot commits can be collected. The patch digest is the immutable ID. */
|
|
14
|
+
export async function createRevertAnchor(repo, preTurnSha, postTurnSha) {
|
|
15
|
+
const patch = await diffTrees(repo, preTurnSha, postTurnSha);
|
|
16
|
+
return persistRevertAnchor(repo, patch);
|
|
17
|
+
}
|
|
18
|
+
/** Persist the exact canonical patch written by a protected delivery. */
|
|
19
|
+
function createRevertAnchorFromPatch(repo, patch) {
|
|
20
|
+
return persistRevertAnchor(repo, patch);
|
|
21
|
+
}
|
|
22
|
+
export function createRevertAnchorFromPatchOrNull(repo, patch) {
|
|
23
|
+
try {
|
|
24
|
+
return createRevertAnchorFromPatch(repo, patch);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function persistRevertAnchor(repo, patch) {
|
|
31
|
+
const id = sha256(patch);
|
|
32
|
+
const target = objectPath(repo, id);
|
|
33
|
+
const targetDir = dirname(target);
|
|
34
|
+
ensureDir(targetDir);
|
|
35
|
+
if (existsSync(target)) {
|
|
36
|
+
if (sha256(readFileSync(target, "utf8")) !== id) {
|
|
37
|
+
throw new WorkspaceError(`revert anchor ${id} failed its content digest`);
|
|
38
|
+
}
|
|
39
|
+
fsyncDirectory(targetDir);
|
|
40
|
+
return id;
|
|
41
|
+
}
|
|
42
|
+
const temp = `${target}.tmp-${process.pid}-${crypto.randomUUID()}`;
|
|
43
|
+
const fd = openSync(temp, "wx", 0o600);
|
|
44
|
+
try {
|
|
45
|
+
writeSync(fd, patch, undefined, "utf8");
|
|
46
|
+
fsyncSync(fd);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
closeSync(fd);
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
renameSync(temp, target);
|
|
53
|
+
fsyncDirectory(targetDir);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
try {
|
|
57
|
+
unlinkSync(temp);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// The original rename error is authoritative.
|
|
61
|
+
}
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
return id;
|
|
65
|
+
}
|
|
66
|
+
function fsyncDirectory(path) {
|
|
67
|
+
const fd = openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
68
|
+
try {
|
|
69
|
+
fsyncSync(fd);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
closeSync(fd);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export function readRevertAnchor(repo, id) {
|
|
76
|
+
const patch = readFileSync(objectPath(repo, id), "utf8");
|
|
77
|
+
if (sha256(patch) !== id)
|
|
78
|
+
throw new WorkspaceError(`revert anchor ${id} is corrupt`);
|
|
79
|
+
return patch;
|
|
80
|
+
}
|
|
81
|
+
/** Revert is an optional recovery affordance; never advertise it until the
|
|
82
|
+
* immutable anchor has finalized successfully. */
|
|
83
|
+
export async function createRevertAnchorOrNull(repo, preTurnSha, postTurnSha) {
|
|
84
|
+
if (!preTurnSha || !postTurnSha)
|
|
85
|
+
return null;
|
|
86
|
+
try {
|
|
87
|
+
return await createRevertAnchor(repo, preTurnSha, postTurnSha);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=anchor-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor-store.js","sourceRoot":"","sources":["../src/anchor-store.ts"],"names":[],"mappings":"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"}
|
package/dist/artifact-paths.d.ts
CHANGED
|
@@ -1,40 +1,12 @@
|
|
|
1
|
-
export declare const CLAUDEXOR_ARTIFACT_DIRS: readonly [".claudexor", ".claudexor-review-evidence"];
|
|
2
|
-
/** True when the RELATIVE path is inside one of Claudexor's artifact dirs. */
|
|
3
|
-
export declare function isClaudexorArtifactPath(rel: string): boolean;
|
|
4
1
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
export declare function claudexorArtifactPredicate(repo: string): Promise<(rel: string) => boolean>;
|
|
14
|
-
/**
|
|
15
|
-
* User-state paths under the artifact dirs = INDEX ∪ HEAD. Index alone
|
|
16
|
-
* misses a deletion the user already STAGED (`git rm .claudexor/config.yaml`
|
|
17
|
-
* removes the index entry — the path must still count as user state so the
|
|
18
|
-
* staged deletion survives dirty checks and snapshots); HEAD alone misses a
|
|
19
|
-
* user-`git add`ed new config that is not yet committed.
|
|
20
|
-
*/
|
|
21
|
-
export declare function trackedArtifactDirPaths(repo: string): Promise<Set<string>>;
|
|
22
|
-
/**
|
|
23
|
-
* `git status --porcelain` minus Claudexor artifact paths: the honest
|
|
24
|
-
* "is the USER's tree dirty" check. Never uses `:(exclude)` pathspecs — a
|
|
25
|
-
* pathspec that names a gitignored path makes git hard-error in exactly the
|
|
26
|
-
* repos Claudexor itself initializes (it seeds `.claudexor/` into
|
|
27
|
-
* .gitignore).
|
|
2
|
+
* Honest user-tree status. Claudexor v2 has no repo-local runtime carve-out:
|
|
3
|
+
* `.claudexor/`, `.claudexor-review-evidence/`, and similarly named paths are
|
|
4
|
+
* ordinary user state and must participate in dirty/preimage checks.
|
|
5
|
+
*
|
|
6
|
+
* Entries are NUL-delimited raw porcelain records so unusual legal filenames
|
|
7
|
+
* cannot disappear through quote/trim parsing. Callers currently need only the
|
|
8
|
+
* empty/non-empty property; preserving each record keeps future diagnostics
|
|
9
|
+
* lossless.
|
|
28
10
|
*/
|
|
29
11
|
export declare function statusPorcelainMeaningful(repo: string): Promise<string[]>;
|
|
30
|
-
/**
|
|
31
|
-
* Stage EVERYTHING except Claudexor artifacts, loudly. Bare `git add -A`
|
|
32
|
-
* (honors .gitignore) followed by unstaging the artifact dirs from the
|
|
33
|
-
* index — the same recipe the snapshot scratch-index uses, because it is
|
|
34
|
-
* the only one that works whether or not `.claudexor` is gitignored.
|
|
35
|
-
* THROWS on failure: a silent staging error turns into "nothing to commit"
|
|
36
|
-
* AFTER the patch already mutated the tree (the audit's half-delivered
|
|
37
|
-
* state).
|
|
38
|
-
*/
|
|
39
|
-
export declare function stageAllExcludingArtifacts(repo: string): Promise<void>;
|
|
40
12
|
//# sourceMappingURL=artifact-paths.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-paths.d.ts","sourceRoot":"","sources":["../src/artifact-paths.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"artifact-paths.d.ts","sourceRoot":"","sources":["../src/artifact-paths.ts"],"names":[],"mappings":"AAUA;;;;;;;;;GASG;AACH,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAI/E"}
|
package/dist/artifact-paths.js
CHANGED
|
@@ -1,128 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
* The ONE owner of "Claudexor's own artifact dirs are not user tree state".
|
|
3
|
-
* Eight call sites used five different mechanisms — `:(exclude)`
|
|
4
|
-
* pathspecs (which HARD-ERROR when the project gitignores `.claudexor`),
|
|
5
|
-
* prefix filters (which over-match `.claudexorfoo`), skip-sets, and
|
|
6
|
-
* hardcoded name pairs. They converge here.
|
|
7
|
-
*/
|
|
8
|
-
import { existsSync } from "node:fs";
|
|
9
|
-
import { join } from "node:path";
|
|
10
|
-
import { runCaptureRaw } from "@claudexor/core";
|
|
11
|
-
import { WorkspaceError } from "@claudexor/core";
|
|
12
|
-
export const CLAUDEXOR_ARTIFACT_DIRS = [".claudexor", ".claudexor-review-evidence"];
|
|
13
|
-
/** True when the RELATIVE path is inside one of Claudexor's artifact dirs. */
|
|
14
|
-
export function isClaudexorArtifactPath(rel) {
|
|
15
|
-
const clean = rel.replace(/^"|"$/g, "");
|
|
16
|
-
return CLAUDEXOR_ARTIFACT_DIRS.some((dir) => clean === dir || clean.startsWith(`${dir}/`));
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* TRACKED files under the artifact dirs are USER STATE, not artifacts: a
|
|
20
|
-
* project may VERSION `.claudexor/config.yaml` / `review-panel.yaml` (this
|
|
21
|
-
* repo does), and a candidate legitimately editing a tracked config must not
|
|
22
|
-
* have that edit silently dropped from status/snapshots/patches (that would
|
|
23
|
-
* be silent truncation of user work). Runtime artifacts are, by
|
|
24
|
-
* construction, never tracked. Returns a predicate: true = artifact
|
|
25
|
-
* (filterable), false = user state.
|
|
26
|
-
*/
|
|
27
|
-
export async function claudexorArtifactPredicate(repo) {
|
|
28
|
-
const tracked = await trackedArtifactDirPaths(repo);
|
|
29
|
-
return (rel) => {
|
|
30
|
-
const clean = rel.replace(/^"|"$/g, "");
|
|
31
|
-
return isClaudexorArtifactPath(clean) && !tracked.has(clean);
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* User-state paths under the artifact dirs = INDEX ∪ HEAD. Index alone
|
|
36
|
-
* misses a deletion the user already STAGED (`git rm .claudexor/config.yaml`
|
|
37
|
-
* removes the index entry — the path must still count as user state so the
|
|
38
|
-
* staged deletion survives dirty checks and snapshots); HEAD alone misses a
|
|
39
|
-
* user-`git add`ed new config that is not yet committed.
|
|
40
|
-
*/
|
|
41
|
-
export async function trackedArtifactDirPaths(repo) {
|
|
42
|
-
const tracked = new Set();
|
|
43
|
-
// -z output is EXACT (NUL-delimited, no quoting): never trim — a legal
|
|
44
|
-
// filename may carry leading/trailing whitespace and a mangled key would
|
|
45
|
-
// silently reclassify it as an artifact.
|
|
46
|
-
const index = await git(repo, ["ls-files", "-z", "--", ...CLAUDEXOR_ARTIFACT_DIRS]);
|
|
47
|
-
if (index.code === 0) {
|
|
48
|
-
for (const p of index.stdout.split("\0")) {
|
|
49
|
-
if (p)
|
|
50
|
-
tracked.add(p);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// HEAD may not exist yet (fresh repo) — tolerated: nothing can be
|
|
54
|
-
// HEAD-tracked before the first commit, so the index alone IS complete.
|
|
55
|
-
const head = await git(repo, ["ls-tree", "-r", "--name-only", "-z", "HEAD", "--", ...CLAUDEXOR_ARTIFACT_DIRS]);
|
|
56
|
-
if (head.code === 0) {
|
|
57
|
-
for (const p of head.stdout.split("\0")) {
|
|
58
|
-
if (p)
|
|
59
|
-
tracked.add(p);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return tracked;
|
|
63
|
-
}
|
|
1
|
+
import { runCaptureRaw, WorkspaceError } from "@claudexor/core";
|
|
64
2
|
async function git(repo, args) {
|
|
65
3
|
const r = await runCaptureRaw("git", ["-C", repo, ...args], { timeoutMs: 60_000 });
|
|
66
4
|
return { code: r.code, stdout: r.stdout, stderr: r.stderr };
|
|
67
5
|
}
|
|
68
6
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
7
|
+
* Honest user-tree status. Claudexor v2 has no repo-local runtime carve-out:
|
|
8
|
+
* `.claudexor/`, `.claudexor-review-evidence/`, and similarly named paths are
|
|
9
|
+
* ordinary user state and must participate in dirty/preimage checks.
|
|
10
|
+
*
|
|
11
|
+
* Entries are NUL-delimited raw porcelain records so unusual legal filenames
|
|
12
|
+
* cannot disappear through quote/trim parsing. Callers currently need only the
|
|
13
|
+
* empty/non-empty property; preserving each record keeps future diagnostics
|
|
14
|
+
* lossless.
|
|
74
15
|
*/
|
|
75
16
|
export async function statusPorcelainMeaningful(repo) {
|
|
76
|
-
const r = await git(repo, ["status", "--porcelain"]);
|
|
17
|
+
const r = await git(repo, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
|
|
77
18
|
if (r.code !== 0)
|
|
78
19
|
throw new WorkspaceError(`git status failed: ${r.stderr.trim()}`);
|
|
79
|
-
|
|
80
|
-
return r.stdout
|
|
81
|
-
.split("\n")
|
|
82
|
-
.filter((line) => line.trim().length > 0)
|
|
83
|
-
.filter((line) => !isArtifact(line.slice(3).trim()));
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Stage EVERYTHING except Claudexor artifacts, loudly. Bare `git add -A`
|
|
87
|
-
* (honors .gitignore) followed by unstaging the artifact dirs from the
|
|
88
|
-
* index — the same recipe the snapshot scratch-index uses, because it is
|
|
89
|
-
* the only one that works whether or not `.claudexor` is gitignored.
|
|
90
|
-
* THROWS on failure: a silent staging error turns into "nothing to commit"
|
|
91
|
-
* AFTER the patch already mutated the tree (the audit's half-delivered
|
|
92
|
-
* state).
|
|
93
|
-
*/
|
|
94
|
-
export async function stageAllExcludingArtifacts(repo) {
|
|
95
|
-
// TRACKED files under the artifact dirs are user state (versioned config);
|
|
96
|
-
// capture their paths BEFORE `git add -A` (afterwards freshly-staged
|
|
97
|
-
// runtime artifacts would also read as index-tracked), then restore them
|
|
98
|
-
// into the index after the bulk unstage — dropping a tracked-config edit
|
|
99
|
-
// from the patch would be silent truncation of the candidate's work.
|
|
100
|
-
const tracked = [...(await trackedArtifactDirPaths(repo))];
|
|
101
|
-
const add = await git(repo, ["add", "-A"]);
|
|
102
|
-
if (add.code !== 0)
|
|
103
|
-
throw new WorkspaceError(`git add -A failed during staging: ${add.stderr.trim()}`);
|
|
104
|
-
const unstage = await git(repo, [
|
|
105
|
-
"rm",
|
|
106
|
-
"-r",
|
|
107
|
-
"--cached",
|
|
108
|
-
"--quiet",
|
|
109
|
-
"--ignore-unmatch",
|
|
110
|
-
...CLAUDEXOR_ARTIFACT_DIRS,
|
|
111
|
-
]);
|
|
112
|
-
if (unstage.code !== 0) {
|
|
113
|
-
throw new WorkspaceError(`unstaging Claudexor artifacts failed: ${unstage.stderr.trim()}`);
|
|
114
|
-
}
|
|
115
|
-
// Re-add only paths still PRESENT in the worktree: a tracked file the
|
|
116
|
-
// user DELETED needs no re-add — the bulk unstage already leaves the
|
|
117
|
-
// index without it, which IS the staged deletion (add on a path absent
|
|
118
|
-
// from both worktree and index hard-errors). -f covers the seeded-
|
|
119
|
-
// gitignore case where the whole dir is ignore-listed.
|
|
120
|
-
const present = tracked.filter((p) => existsSync(join(repo, p)));
|
|
121
|
-
if (present.length > 0) {
|
|
122
|
-
const readd = await git(repo, ["add", "-f", "--", ...present]);
|
|
123
|
-
if (readd.code !== 0) {
|
|
124
|
-
throw new WorkspaceError(`re-staging tracked artifact-dir user files failed: ${readd.stderr.trim()}`);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
20
|
+
return r.stdout.split("\0").filter((entry) => entry.length > 0);
|
|
127
21
|
}
|
|
128
22
|
//# sourceMappingURL=artifact-paths.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-paths.js","sourceRoot":"","sources":["../src/artifact-paths.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"artifact-paths.js","sourceRoot":"","sources":["../src/artifact-paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEhE,KAAK,UAAU,GAAG,CAChB,IAAY,EACZ,IAAc;IAEd,MAAM,CAAC,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IACnF,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AAC9D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,IAAY;IAC1D,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,uBAAuB,CAAC,CAAC,CAAC;IACvF,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC;QAAE,MAAM,IAAI,cAAc,CAAC,sBAAsB,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACpF,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAClE,CAAC"}
|
package/dist/git.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface EnsureGitRepositoryResult {
|
|
|
14
14
|
initialized: boolean;
|
|
15
15
|
/** True when a baseline commit was created (fresh repo or unborn HEAD). */
|
|
16
16
|
baselineCommitted: boolean;
|
|
17
|
-
/**
|
|
17
|
+
/** Always false in v2: Claudexor never changes a project's `.gitignore`. */
|
|
18
18
|
gitignoreSeeded: boolean;
|
|
19
19
|
/** HEAD sha after the call. */
|
|
20
20
|
headSha: string;
|
|
@@ -26,8 +26,8 @@ export interface EnsureGitRepositoryResult {
|
|
|
26
26
|
* quick start is `mkdir && git init && codex`); Claudexor goes one step
|
|
27
27
|
* further and creates the boundary itself, announced via the
|
|
28
28
|
* `project.git.initialized` run event. The baseline commit makes worktree
|
|
29
|
-
* diffs honest from the
|
|
30
|
-
*
|
|
29
|
+
* diffs honest from the first run. Runtime is external, so project ignore
|
|
30
|
+
* files are never created or edited as a side effect.
|
|
31
31
|
*
|
|
32
32
|
* The baseline commit is authored as "Claudexor" deterministically — it is a
|
|
33
33
|
* tool-created commit and must not depend on (or pollute) user git identity.
|
|
@@ -55,6 +55,13 @@ export declare function snapshotTree(repo: string): Promise<string>;
|
|
|
55
55
|
* `--binary`: binary changes carry an APPLYABLE payload instead of
|
|
56
56
|
* degrading to a "Binary files differ" stub that silently loses the work. */
|
|
57
57
|
export declare function diffTrees(repo: string, baseSha: string, endSha: string): Promise<string>;
|
|
58
|
+
/**
|
|
59
|
+
* Materialize a patch against an exact base in a scratch index and return the
|
|
60
|
+
* resulting tree id. The live index and worktree are never consulted or
|
|
61
|
+
* changed. Commit-class delivery uses this as its expected candidate tree so a
|
|
62
|
+
* concurrently staged user path can never be swept into a Claudexor commit.
|
|
63
|
+
*/
|
|
64
|
+
export declare function materializePatchTree(repo: string, baseSha: string, diff: string): Promise<string>;
|
|
58
65
|
export interface RevertResult {
|
|
59
66
|
reverted: boolean;
|
|
60
67
|
/** Turn-added files removed to restore the pre-turn state (`.claudexor` preserved). */
|
|
@@ -69,6 +76,10 @@ export interface RevertResult {
|
|
|
69
76
|
* turn added; `.claudexor` (run artifacts) is always preserved.
|
|
70
77
|
*/
|
|
71
78
|
export declare function revertWorkingTreeTo(repo: string, preTurnSha: string, expectedPostSha: string): Promise<RevertResult>;
|
|
79
|
+
/** Reverse an immutable turn patch. This is the GC-independent production
|
|
80
|
+
* path used by external revert anchors; the SHA-based wrapper remains useful
|
|
81
|
+
* while capturing and testing snapshots. */
|
|
82
|
+
export declare function revertWorkingTreePatch(repo: string, patch: string): Promise<RevertResult>;
|
|
72
83
|
export declare function worktreeAdd(repo: string, path: string, branch: string, baseSha: string): Promise<void>;
|
|
73
84
|
/** Recreate a worktree for an EXISTING branch (recovery: dir lost, branch survived). */
|
|
74
85
|
export declare function worktreeAddExisting(repo: string, path: string, branch: string): Promise<void>;
|
|
@@ -85,15 +96,26 @@ export interface ProtectedApplyResult {
|
|
|
85
96
|
detail?: string;
|
|
86
97
|
}
|
|
87
98
|
/**
|
|
88
|
-
* The
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* treeMutated:false` is a GUARANTEE the tree is byte-identical (INV-114);
|
|
94
|
-
* a failed restoration reports `treeMutated:true` honestly instead.
|
|
99
|
+
* The protected apply path deliberately does not use `--3way`: 3-way failures
|
|
100
|
+
* may leave conflict stages and historically triggered a destructive
|
|
101
|
+
* `checkout -- .` rollback. Plain `git apply` is all-or-nothing unless
|
|
102
|
+
* `--reject` is requested (we never request it). A stale/concurrent target is
|
|
103
|
+
* refused; no automatic rollback ever overwrites user state.
|
|
95
104
|
*/
|
|
96
105
|
export declare function applyPatchProtected(repo: string, diff: string): Promise<ProtectedApplyResult>;
|
|
106
|
+
/**
|
|
107
|
+
* Commit-class delivery updates the worktree and index as one Git operation.
|
|
108
|
+
* `git apply --index` refuses when either preimage diverged and does not sweep
|
|
109
|
+
* unrelated paths into the index.
|
|
110
|
+
*/
|
|
111
|
+
export declare function applyPatchAndIndexProtected(repo: string, diff: string): Promise<ProtectedApplyResult>;
|
|
112
|
+
/**
|
|
113
|
+
* Remove only an exact Claudexor-written patch postimage from worktree+index.
|
|
114
|
+
* This is the sole post-apply compensation path: no reset, checkout, or
|
|
115
|
+
* path-blind deletion is permitted. Concurrent overlapping edits make the
|
|
116
|
+
* reverse check fail and are preserved for an explicit recovery decision.
|
|
117
|
+
*/
|
|
118
|
+
export declare function reversePatchAndIndexProtected(repo: string, diff: string): Promise<ProtectedApplyResult>;
|
|
97
119
|
export declare function worktreePrune(repo: string): Promise<void>;
|
|
98
120
|
/** Delete a local branch (best-effort GC of per-attempt claudexor/* branches). */
|
|
99
121
|
export declare function branchDelete(repo: string, branch: string): Promise<void>;
|
package/dist/git.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
|