@vkenliu/adit-engine 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vkenliu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Working tree change detection.
3
+ *
4
+ * Detects modified, added, deleted, and untracked files
5
+ * relative to HEAD or a specified base SHA.
6
+ */
7
+ export interface FileChange {
8
+ path: string;
9
+ status: "M" | "A" | "D" | "R" | "??";
10
+ /** For renames, the original path */
11
+ oldPath?: string;
12
+ }
13
+ export interface NumstatEntry {
14
+ path: string;
15
+ additions: number;
16
+ deletions: number;
17
+ }
18
+ /** Get all changed files in the working tree (staged + unstaged + untracked) */
19
+ export declare function getChangedFiles(cwd: string): Promise<FileChange[]>;
20
+ /** Check if working tree has any uncommitted changes */
21
+ export declare function hasUncommittedChanges(cwd: string): Promise<boolean>;
22
+ /** Get line-level change stats (additions/deletions per file) */
23
+ export declare function getNumstat(cwd: string, baseSha?: string): Promise<NumstatEntry[]>;
24
+ /** Get a human-readable summary of changes */
25
+ export declare function getChangesSummary(cwd: string): Promise<string>;
26
+ /** Check if working tree differs from a specific SHA */
27
+ export declare function isDirtyFrom(cwd: string, baseSha: string): Promise<boolean>;
28
+ //# sourceMappingURL=working-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"working-tree.d.ts","sourceRoot":"","sources":["../../src/detector/working-tree.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;IACrC,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,gFAAgF;AAChF,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CA0CxE;AAED,wDAAwD;AACxD,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGzE;AAED,iEAAiE;AACjE,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,EAAE,CAAC,CAoBzB;AAED,8CAA8C;AAC9C,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBpE;AAED,wDAAwD;AACxD,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC,CAWlB"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Working tree change detection.
3
+ *
4
+ * Detects modified, added, deleted, and untracked files
5
+ * relative to HEAD or a specified base SHA.
6
+ */
7
+ import { runGit } from "../git/runner.js";
8
+ /** Get all changed files in the working tree (staged + unstaged + untracked) */
9
+ export async function getChangedFiles(cwd) {
10
+ const result = await runGit(["status", "--porcelain", "-z"], { cwd });
11
+ if (result.exitCode !== 0 || !result.stdout)
12
+ return [];
13
+ const changes = [];
14
+ const parts = result.stdout.split("\0");
15
+ let i = 0;
16
+ while (i < parts.length) {
17
+ const entry = parts[i];
18
+ if (!entry || entry.length < 3) {
19
+ i++;
20
+ continue;
21
+ }
22
+ const xy = entry.substring(0, 2);
23
+ const path = entry.substring(3);
24
+ // Rename entries have the old path as the next null-separated field
25
+ if (xy.includes("R")) {
26
+ const oldPath = parts[i + 1];
27
+ changes.push({ path, status: "R", oldPath });
28
+ i += 2;
29
+ continue;
30
+ }
31
+ let status;
32
+ if (xy === "??") {
33
+ status = "??";
34
+ }
35
+ else if (xy.includes("D")) {
36
+ status = "D";
37
+ }
38
+ else if (xy.includes("A")) {
39
+ status = "A";
40
+ }
41
+ else {
42
+ status = "M";
43
+ }
44
+ changes.push({ path, status });
45
+ i++;
46
+ }
47
+ return changes;
48
+ }
49
+ /** Check if working tree has any uncommitted changes */
50
+ export async function hasUncommittedChanges(cwd) {
51
+ const changes = await getChangedFiles(cwd);
52
+ return changes.length > 0;
53
+ }
54
+ /** Get line-level change stats (additions/deletions per file) */
55
+ export async function getNumstat(cwd, baseSha) {
56
+ const args = baseSha
57
+ ? ["diff", "--numstat", baseSha]
58
+ : ["diff", "--numstat"];
59
+ const result = await runGit(args, { cwd });
60
+ if (result.exitCode !== 0 || !result.stdout.trim())
61
+ return [];
62
+ return result.stdout
63
+ .trim()
64
+ .split("\n")
65
+ .map((line) => {
66
+ const [add, del, path] = line.split("\t");
67
+ return {
68
+ path: path ?? "",
69
+ additions: add === "-" ? 0 : parseInt(add, 10) || 0,
70
+ deletions: del === "-" ? 0 : parseInt(del, 10) || 0,
71
+ };
72
+ })
73
+ .filter((e) => e.path);
74
+ }
75
+ /** Get a human-readable summary of changes */
76
+ export async function getChangesSummary(cwd) {
77
+ const changes = await getChangedFiles(cwd);
78
+ if (changes.length === 0)
79
+ return "No changes";
80
+ const counts = { M: 0, A: 0, D: 0, R: 0, "??": 0 };
81
+ for (const c of changes) {
82
+ counts[c.status]++;
83
+ }
84
+ const parts = [];
85
+ if (counts.M)
86
+ parts.push(`${counts.M} modified`);
87
+ if (counts.A)
88
+ parts.push(`${counts.A} added`);
89
+ if (counts.D)
90
+ parts.push(`${counts.D} deleted`);
91
+ if (counts.R)
92
+ parts.push(`${counts.R} renamed`);
93
+ if (counts["??"])
94
+ parts.push(`${counts["??"]} untracked`);
95
+ return `${changes.length} files changed: ${parts.join(", ")}`;
96
+ }
97
+ /** Check if working tree differs from a specific SHA */
98
+ export async function isDirtyFrom(cwd, baseSha) {
99
+ const result = await runGit(["diff", "--stat", baseSha], { cwd });
100
+ if (result.exitCode !== 0)
101
+ return true;
102
+ if (result.stdout.trim())
103
+ return true;
104
+ // Also check for untracked files
105
+ const untracked = await runGit(["ls-files", "--others", "--exclude-standard"], { cwd });
106
+ return untracked.stdout.trim().length > 0;
107
+ }
108
+ //# sourceMappingURL=working-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"working-tree.js","sourceRoot":"","sources":["../../src/detector/working-tree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Environment snapshot capture.
3
+ *
4
+ * Captures the execution context beyond just code:
5
+ * git state, dependency versions, runtime versions,
6
+ * container info, system resources, and more.
7
+ */
8
+ import type Database from "better-sqlite3";
9
+ import { type AditConfig } from "@vkenliu/adit-core";
10
+ /** Pre-computed git state to avoid duplicate git calls within the same hook */
11
+ export interface PreComputedGitState {
12
+ branch?: string | null;
13
+ headSha?: string | null;
14
+ changedFiles?: {
15
+ path: string;
16
+ }[];
17
+ }
18
+ /** Capture a full environment snapshot */
19
+ export declare function captureEnvironment(db: Database.Database, config: AditConfig, sessionId: string, preComputedGit?: PreComputedGitState): Promise<string>;
20
+ //# sourceMappingURL=capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../src/environment/capture.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AA8D5B,+EAA+E;AAC/E,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACnC;AAED,0CAA0C;AAC1C,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,MAAM,EACjB,cAAc,CAAC,EAAE,mBAAmB,GACnC,OAAO,CAAC,MAAM,CAAC,CAuEjB"}
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Environment snapshot capture.
3
+ *
4
+ * Captures the execution context beyond just code:
5
+ * git state, dependency versions, runtime versions,
6
+ * container info, system resources, and more.
7
+ */
8
+ import { execFile } from "node:child_process";
9
+ import { promisify } from "node:util";
10
+ import { createHash } from "node:crypto";
11
+ import { readFileSync, existsSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { platform, release, cpus, totalmem, freemem, arch } from "node:os";
14
+ import { generateId, createClock, serialize, insertEnvSnapshot, } from "@vkenliu/adit-core";
15
+ import { getHeadSha, getCurrentBranch } from "../git/runner.js";
16
+ import { getChangedFiles } from "../detector/working-tree.js";
17
+ const execFileAsync = promisify(execFile);
18
+ const ENV_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
19
+ const versionCache = new Map();
20
+ /** Known lockfile names and their paths */
21
+ const LOCKFILES = [
22
+ "package-lock.json",
23
+ "pnpm-lock.yaml",
24
+ "yarn.lock",
25
+ "bun.lockb",
26
+ "Pipfile.lock",
27
+ "poetry.lock",
28
+ "Cargo.lock",
29
+ "go.sum",
30
+ "Gemfile.lock",
31
+ ];
32
+ /** Safe environment variable prefixes to capture */
33
+ const SAFE_ENV_PREFIXES = [
34
+ "NODE_",
35
+ "NPM_",
36
+ "PYTHON",
37
+ "SHELL",
38
+ "TERM",
39
+ "LANG",
40
+ "LC_",
41
+ "HOME",
42
+ "USER",
43
+ "PATH",
44
+ ];
45
+ /**
46
+ * Known secret environment variable names that match safe prefixes but must
47
+ * never be captured (authentication tokens, registry credentials, etc.).
48
+ */
49
+ const SECRET_ENV_NAMES = new Set([
50
+ "NPM_TOKEN",
51
+ "NPM_CONFIG_AUTHTOKEN",
52
+ "NPM_CONFIG__AUTH",
53
+ "NODE_AUTH_TOKEN",
54
+ "NODE_PRE_GYP_GITHUB_TOKEN",
55
+ ]);
56
+ /** Capture a full environment snapshot */
57
+ export async function captureEnvironment(db, config, sessionId, preComputedGit) {
58
+ const cwd = config.projectRoot;
59
+ const id = generateId();
60
+ // Use cached version data if available and fresh (within TTL).
61
+ // Version detections (node, python, runtimes, shell, package manager, container)
62
+ // rarely change within a session, so we cache them to avoid spawning
63
+ // multiple child processes on every hook event.
64
+ const now = Date.now();
65
+ let cached = versionCache.get(sessionId);
66
+ if (!cached || now - cached.cachedAt > ENV_CACHE_TTL_MS) {
67
+ // Cache miss or expired — run all version detections in parallel
68
+ const [nodeV, pythonV, container, runtimes, shell, pkgMgr] = await Promise.all([
69
+ getVersion("node", ["--version"]),
70
+ getVersion("python3", ["--version"]),
71
+ detectContainer(),
72
+ detectRuntimeVersions(),
73
+ detectShellInfo(),
74
+ detectPackageManager(cwd),
75
+ ]);
76
+ cached = {
77
+ nodeVersion: nodeV,
78
+ pythonVersion: pythonV,
79
+ containerInfo: container,
80
+ runtimeVersions: runtimes,
81
+ shellInfo: shell,
82
+ packageManagerInfo: pkgMgr,
83
+ cachedAt: now,
84
+ };
85
+ versionCache.set(sessionId, cached);
86
+ }
87
+ // Use pre-computed git state when available to avoid duplicate git calls
88
+ // within the same hook event. Only fetch what wasn't pre-computed.
89
+ const needBranch = preComputedGit?.branch === undefined;
90
+ const needHeadSha = preComputedGit?.headSha === undefined;
91
+ const needChanges = preComputedGit?.changedFiles === undefined;
92
+ const [branch, headSha, changes] = await Promise.all([
93
+ needBranch ? getCurrentBranch(cwd) : Promise.resolve(preComputedGit.branch),
94
+ needHeadSha ? getHeadSha(cwd) : Promise.resolve(preComputedGit.headSha),
95
+ needChanges ? getChangedFiles(cwd) : Promise.resolve(preComputedGit.changedFiles),
96
+ ]);
97
+ const modifiedFiles = changes.map((c) => c.path);
98
+ const lockfile = findLockfile(cwd);
99
+ const safeEnvVars = captureSafeEnvVars();
100
+ const systemResources = captureSystemResources();
101
+ insertEnvSnapshot(db, {
102
+ id,
103
+ sessionId,
104
+ gitBranch: branch ?? "unknown",
105
+ gitHeadSha: headSha ?? "unknown",
106
+ modifiedFiles: JSON.stringify(modifiedFiles),
107
+ depLockHash: lockfile ? hashFile(join(cwd, lockfile)) : null,
108
+ depLockPath: lockfile,
109
+ envVarsJson: JSON.stringify(safeEnvVars),
110
+ nodeVersion: cached.nodeVersion,
111
+ pythonVersion: cached.pythonVersion,
112
+ osInfo: `${platform()} ${release()}`,
113
+ containerInfo: cached.containerInfo ? JSON.stringify(cached.containerInfo) : null,
114
+ runtimeVersionsJson: cached.runtimeVersions ? JSON.stringify(cached.runtimeVersions) : null,
115
+ shellInfo: cached.shellInfo ? JSON.stringify(cached.shellInfo) : null,
116
+ systemResourcesJson: JSON.stringify(systemResources),
117
+ packageManagerJson: cached.packageManagerInfo ? JSON.stringify(cached.packageManagerInfo) : null,
118
+ vclockJson: serialize(createClock(config.clientId)),
119
+ });
120
+ return id;
121
+ }
122
+ /** Get version string from a command */
123
+ async function getVersion(cmd, args) {
124
+ try {
125
+ const { stdout } = await execFileAsync(cmd, args, { timeout: 5000 });
126
+ return stdout.trim();
127
+ }
128
+ catch {
129
+ return null;
130
+ }
131
+ }
132
+ /** Find the first matching lockfile in the project */
133
+ function findLockfile(cwd) {
134
+ for (const name of LOCKFILES) {
135
+ if (existsSync(join(cwd, name)))
136
+ return name;
137
+ }
138
+ return null;
139
+ }
140
+ /** Hash a file's contents */
141
+ function hashFile(path) {
142
+ try {
143
+ const content = readFileSync(path);
144
+ return createHash("sha256").update(content).digest("hex").substring(0, 16);
145
+ }
146
+ catch {
147
+ return null;
148
+ }
149
+ }
150
+ /** Capture safe environment variables (no secrets) */
151
+ function captureSafeEnvVars() {
152
+ const result = {};
153
+ for (const [key, value] of Object.entries(process.env)) {
154
+ if (value && !SECRET_ENV_NAMES.has(key) && SAFE_ENV_PREFIXES.some((p) => key.startsWith(p))) {
155
+ result[key] = value;
156
+ }
157
+ }
158
+ return result;
159
+ }
160
+ /** Detect Docker/container environment */
161
+ async function detectContainer() {
162
+ const inDocker = existsSync("/.dockerenv");
163
+ if (inDocker) {
164
+ return { inDocker: true };
165
+ }
166
+ // Check cgroup for container indicators
167
+ try {
168
+ const content = readFileSync("/proc/1/cgroup", "utf-8");
169
+ if (content.includes("docker") || content.includes("containerd") || content.includes("kubepods")) {
170
+ return { inDocker: true };
171
+ }
172
+ }
173
+ catch {
174
+ // Not in a container or no access to cgroup
175
+ }
176
+ return null;
177
+ }
178
+ /** Detect additional runtime versions */
179
+ async function detectRuntimeVersions() {
180
+ const checks = [
181
+ { name: "rust", cmd: "rustc", args: ["--version"] },
182
+ { name: "cargo", cmd: "cargo", args: ["--version"] },
183
+ { name: "go", cmd: "go", args: ["version"] },
184
+ { name: "java", cmd: "java", args: ["--version"] },
185
+ { name: "ruby", cmd: "ruby", args: ["--version"] },
186
+ ];
187
+ const results = {};
188
+ await Promise.allSettled(checks.map(async ({ name, cmd, args }) => {
189
+ const version = await getVersion(cmd, args);
190
+ if (version)
191
+ results[name] = version;
192
+ }));
193
+ return Object.keys(results).length > 0 ? results : null;
194
+ }
195
+ /** Detect shell information */
196
+ async function detectShellInfo() {
197
+ const shell = process.env.SHELL;
198
+ if (!shell)
199
+ return null;
200
+ const version = await getVersion(shell, ["--version"]);
201
+ return { shell, version: version ?? undefined };
202
+ }
203
+ /** Capture system resource information */
204
+ function captureSystemResources() {
205
+ const cpuList = cpus();
206
+ return {
207
+ arch: arch(),
208
+ cpuModel: cpuList[0]?.model ?? "unknown",
209
+ totalMem: totalmem(),
210
+ freeMem: freemem(),
211
+ };
212
+ }
213
+ /** Detect package manager and version */
214
+ async function detectPackageManager(cwd) {
215
+ const lockfileMap = {
216
+ "pnpm-lock.yaml": { name: "pnpm", cmd: "pnpm" },
217
+ "yarn.lock": { name: "yarn", cmd: "yarn" },
218
+ "bun.lockb": { name: "bun", cmd: "bun" },
219
+ "package-lock.json": { name: "npm", cmd: "npm" },
220
+ };
221
+ for (const [lockfile, info] of Object.entries(lockfileMap)) {
222
+ if (existsSync(join(cwd, lockfile))) {
223
+ const version = await getVersion(info.cmd, ["--version"]);
224
+ if (version) {
225
+ return { name: info.name, version };
226
+ }
227
+ }
228
+ }
229
+ return null;
230
+ }
231
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/environment/capture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Environment snapshot differ.
3
+ *
4
+ * Compares two environment snapshots and produces a structured diff
5
+ * with categorized changes and severity levels.
6
+ */
7
+ import type { EnvSnapshot, EnvDiff } from "@vkenliu/adit-core";
8
+ /**
9
+ * Compare two environment snapshots and produce a structured diff.
10
+ */
11
+ export declare function diffEnvironments(prev: EnvSnapshot, current: EnvSnapshot): EnvDiff;
12
+ //# sourceMappingURL=differ.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"differ.d.ts","sourceRoot":"","sources":["../../src/environment/differ.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAa,MAAM,oBAAoB,CAAC;AAE1E;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CA+BjF"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Environment snapshot differ.
3
+ *
4
+ * Compares two environment snapshots and produces a structured diff
5
+ * with categorized changes and severity levels.
6
+ */
7
+ /**
8
+ * Compare two environment snapshots and produce a structured diff.
9
+ */
10
+ export function diffEnvironments(prev, current) {
11
+ const changes = [];
12
+ // Git changes
13
+ compareField(changes, "gitBranch", "git", prev.gitBranch, current.gitBranch, "warning");
14
+ compareField(changes, "gitHeadSha", "git", prev.gitHeadSha, current.gitHeadSha, "info");
15
+ // Dependency changes
16
+ compareField(changes, "depLockHash", "dependency", prev.depLockHash, current.depLockHash, "warning");
17
+ compareField(changes, "depLockPath", "dependency", prev.depLockPath, current.depLockPath, "info");
18
+ // Runtime version changes
19
+ compareField(changes, "nodeVersion", "runtime", prev.nodeVersion, current.nodeVersion, "warning");
20
+ compareField(changes, "pythonVersion", "runtime", prev.pythonVersion, current.pythonVersion, "warning");
21
+ // Enriched runtime versions (JSON comparison)
22
+ compareJsonField(changes, "runtimeVersions", "runtime", prev.runtimeVersionsJson, current.runtimeVersionsJson);
23
+ // System changes
24
+ compareField(changes, "osInfo", "system", prev.osInfo, current.osInfo, "breaking");
25
+ compareJsonField(changes, "containerInfo", "system", prev.containerInfo, current.containerInfo);
26
+ compareJsonField(changes, "shellInfo", "system", prev.shellInfo, current.shellInfo);
27
+ compareJsonField(changes, "packageManager", "dependency", prev.packageManagerJson, current.packageManagerJson);
28
+ // Modified files changes
29
+ compareModifiedFiles(changes, prev.modifiedFiles, current.modifiedFiles);
30
+ // Compute overall severity
31
+ const severity = computeOverallSeverity(changes);
32
+ return { changes, severity };
33
+ }
34
+ function compareField(changes, field, category, oldValue, newValue, severity) {
35
+ if (oldValue !== newValue) {
36
+ changes.push({ field, category, oldValue, newValue, severity });
37
+ }
38
+ }
39
+ function compareJsonField(changes, field, category, oldJson, newJson) {
40
+ if (oldJson === newJson)
41
+ return;
42
+ // If one is null and the other isn't, that's a change
43
+ if (!oldJson || !newJson) {
44
+ changes.push({
45
+ field,
46
+ category,
47
+ oldValue: oldJson,
48
+ newValue: newJson,
49
+ severity: "info",
50
+ });
51
+ return;
52
+ }
53
+ // Parse and do field-by-field comparison
54
+ try {
55
+ const oldObj = JSON.parse(oldJson);
56
+ const newObj = JSON.parse(newJson);
57
+ const allKeys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
58
+ for (const key of allKeys) {
59
+ const oldVal = oldObj[key];
60
+ const newVal = newObj[key];
61
+ if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
62
+ changes.push({
63
+ field: `${field}.${key}`,
64
+ category,
65
+ oldValue: oldVal != null ? String(oldVal) : null,
66
+ newValue: newVal != null ? String(newVal) : null,
67
+ severity: "info",
68
+ });
69
+ }
70
+ }
71
+ }
72
+ catch {
73
+ // Fallback: treat as opaque string
74
+ changes.push({
75
+ field,
76
+ category,
77
+ oldValue: oldJson,
78
+ newValue: newJson,
79
+ severity: "info",
80
+ });
81
+ }
82
+ }
83
+ function compareModifiedFiles(changes, oldFiles, newFiles) {
84
+ try {
85
+ const oldList = oldFiles ? JSON.parse(oldFiles) : [];
86
+ const newList = newFiles ? JSON.parse(newFiles) : [];
87
+ const oldSet = new Set(oldList);
88
+ const newSet = new Set(newList);
89
+ const added = newList.filter((f) => !oldSet.has(f));
90
+ const removed = oldList.filter((f) => !newSet.has(f));
91
+ if (added.length > 0 || removed.length > 0) {
92
+ changes.push({
93
+ field: "modifiedFiles",
94
+ category: "git",
95
+ oldValue: `${oldList.length} files`,
96
+ newValue: `${newList.length} files (+${added.length} -${removed.length})`,
97
+ severity: "info",
98
+ });
99
+ }
100
+ }
101
+ catch {
102
+ // Ignore parse errors
103
+ }
104
+ }
105
+ function computeOverallSeverity(changes) {
106
+ if (changes.length === 0)
107
+ return "none";
108
+ if (changes.some((c) => c.severity === "breaking"))
109
+ return "breaking";
110
+ if (changes.some((c) => c.severity === "warning"))
111
+ return "warning";
112
+ return "info";
113
+ }
114
+ //# sourceMappingURL=differ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"differ.js","sourceRoot":"","sources":["../../src/environment/differ.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Git ref management for ADIT checkpoints.
3
+ *
4
+ * Checkpoints are stored under refs/adit/checkpoints/<id>
5
+ * This keeps them entirely out of the branch history.
6
+ */
7
+ /** Create or update a checkpoint ref */
8
+ export declare function storeCheckpointRef(cwd: string, stepId: string, sha: string): Promise<void>;
9
+ /** Resolve a checkpoint ref to its SHA */
10
+ export declare function resolveCheckpointRef(cwd: string, stepId: string): Promise<string | null>;
11
+ /** Delete a checkpoint ref */
12
+ export declare function deleteCheckpointRef(cwd: string, stepId: string): Promise<boolean>;
13
+ /** List all checkpoint refs with their SHAs */
14
+ export declare function listCheckpointRefs(cwd: string): Promise<Array<{
15
+ stepId: string;
16
+ sha: string;
17
+ }>>;
18
+ /** Get parent SHA of a commit */
19
+ export declare function getParentSha(cwd: string, sha: string): Promise<string | null>;
20
+ /** Get the ref prefix constant */
21
+ export declare function getRefPrefix(): string;
22
+ //# sourceMappingURL=refs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refs.d.ts","sourceRoot":"","sources":["../../src/git/refs.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,wCAAwC;AACxC,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,0CAA0C;AAC1C,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB;AAED,8BAA8B;AAC9B,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAMlB;AAED,+CAA+C;AAC/C,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAqBjD;AAED,iCAAiC;AACjC,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB;AAED,kCAAkC;AAClC,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Git ref management for ADIT checkpoints.
3
+ *
4
+ * Checkpoints are stored under refs/adit/checkpoints/<id>
5
+ * This keeps them entirely out of the branch history.
6
+ */
7
+ import { runGit, runGitOrThrow } from "./runner.js";
8
+ const REF_PREFIX = "refs/adit/checkpoints";
9
+ /** Create or update a checkpoint ref */
10
+ export async function storeCheckpointRef(cwd, stepId, sha) {
11
+ await runGitOrThrow(["update-ref", `${REF_PREFIX}/${stepId}`, sha], { cwd });
12
+ }
13
+ /** Resolve a checkpoint ref to its SHA */
14
+ export async function resolveCheckpointRef(cwd, stepId) {
15
+ const result = await runGit(["rev-parse", `${REF_PREFIX}/${stepId}`], { cwd });
16
+ return result.exitCode === 0 ? result.stdout.trim() : null;
17
+ }
18
+ /** Delete a checkpoint ref */
19
+ export async function deleteCheckpointRef(cwd, stepId) {
20
+ const result = await runGit(["update-ref", "-d", `${REF_PREFIX}/${stepId}`], { cwd });
21
+ return result.exitCode === 0;
22
+ }
23
+ /** List all checkpoint refs with their SHAs */
24
+ export async function listCheckpointRefs(cwd) {
25
+ const result = await runGit([
26
+ "for-each-ref",
27
+ `${REF_PREFIX}/`,
28
+ "--format=%(refname)%00%(objectname)",
29
+ ], { cwd });
30
+ if (result.exitCode !== 0 || !result.stdout.trim())
31
+ return [];
32
+ return result.stdout
33
+ .trim()
34
+ .split("\n")
35
+ .map((line) => {
36
+ const [refname, sha] = line.split("\0");
37
+ const stepId = refname.replace(`${REF_PREFIX}/`, "");
38
+ return { stepId, sha };
39
+ })
40
+ .filter((r) => r.stepId && r.sha);
41
+ }
42
+ /** Get parent SHA of a commit */
43
+ export async function getParentSha(cwd, sha) {
44
+ const result = await runGit(["rev-parse", `${sha}^1`], { cwd });
45
+ return result.exitCode === 0 ? result.stdout.trim() : null;
46
+ }
47
+ /** Get the ref prefix constant */
48
+ export function getRefPrefix() {
49
+ return REF_PREFIX;
50
+ }
51
+ //# sourceMappingURL=refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refs.js","sourceRoot":"","sources":["../../src/git/refs.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,UAAU,GAAG,uBAAuB,CAAC;AAE3C,wCAAwC;AACxC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,GAAW,EACX,MAAc,EACd,GAAW;IAEX,MAAM,aAAa,CACjB,CAAC,YAAY,EAAE,GAAG,UAAU,IAAI,MAAM,EAAE,EAAE,GAAG,CAAC,EAC9C,EAAE,GAAG,EAAE,CACR,CAAC;AACJ,CAAC;AAED,0CAA0C;AAC1C,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,GAAW,EACX,MAAc;IAEd,MAAM,MAAM,GAAG,MAAM,MAAM,CACzB,CAAC,WAAW,EAAE,GAAG,UAAU,IAAI,MAAM,EAAE,CAAC,EACxC,EAAE,GAAG,EAAE,CACR,CAAC;IACF,OAAO,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC;AAED,8BAA8B;AAC9B,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,GAAW,EACX,MAAc;IAEd,MAAM,MAAM,GAAG,MAAM,MAAM,CACzB,CAAC,YAAY,EAAE,IAAI,EAAE,GAAG,UAAU,IAAI,MAAM,EAAE,CAAC,EAC/C,EAAE,GAAG,EAAE,CACR,CAAC;IACF,OAAO,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,+CAA+C;AAC/C,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,GAAW;IAEX,MAAM,MAAM,GAAG,MAAM,MAAM,CACzB;QACE,cAAc;QACd,GAAG,UAAU,GAAG;QAChB,qCAAqC;KACtC,EACD,EAAE,GAAG,EAAE,CACR,CAAC;IAEF,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAE9D,OAAO,MAAM,CAAC,MAAM;SACjB,IAAI,EAAE;SACN,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,UAAU,GAAG,EAAE,EAAE,CAAC,CAAC;QACrD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IACzB,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;AACtC,CAAC;AAED,iCAAiC;AACjC,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,GAAW;IAEX,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,CAAC,WAAW,EAAE,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAChE,OAAO,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Git command runner.
3
+ *
4
+ * Thin wrapper around child_process that handles timeouts,
5
+ * error formatting, and environment variable injection
6
+ * (especially GIT_INDEX_FILE for temp-index operations).
7
+ */
8
+ export interface GitResult {
9
+ stdout: string;
10
+ stderr: string;
11
+ exitCode: number;
12
+ }
13
+ export interface GitRunOptions {
14
+ cwd: string;
15
+ env?: Record<string, string>;
16
+ timeout?: number;
17
+ }
18
+ /** Run a git command and return structured result */
19
+ export declare function runGit(args: string[], opts: GitRunOptions): Promise<GitResult>;
20
+ /** Run git and throw if it fails */
21
+ export declare function runGitOrThrow(args: string[], opts: GitRunOptions): Promise<string>;
22
+ /** Get the current HEAD SHA */
23
+ export declare function getHeadSha(cwd: string): Promise<string | null>;
24
+ /** Get the current branch name */
25
+ export declare function getCurrentBranch(cwd: string): Promise<string | null>;
26
+ /** Get the remote URL for origin */
27
+ export declare function getRemoteUrl(cwd: string): Promise<string | null>;
28
+ /** Check if a path is inside a git repository */
29
+ export declare function isGitRepo(cwd: string): Promise<boolean>;
30
+ /** Check if a local branch exists */
31
+ export declare function branchExists(cwd: string, branch: string): Promise<boolean>;
32
+ /** Check if a git object (commit, tree, blob) exists in the object store */
33
+ export declare function shaExists(cwd: string, sha: string): Promise<boolean>;
34
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/git/runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,qDAAqD;AACrD,wBAAsB,MAAM,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,SAAS,CAAC,CAqBpB;AAED,oCAAoC;AACpC,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,+BAA+B;AAC/B,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGpE;AAED,kCAAkC;AAClC,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAG1E;AAED,oCAAoC;AACpC,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGtE;AAED,iDAAiD;AACjD,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG7D;AAED,qCAAqC;AACrC,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGhF;AAED,4EAA4E;AAC5E,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG1E"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Git command runner.
3
+ *
4
+ * Thin wrapper around child_process that handles timeouts,
5
+ * error formatting, and environment variable injection
6
+ * (especially GIT_INDEX_FILE for temp-index operations).
7
+ */
8
+ import { execFile } from "node:child_process";
9
+ import { promisify } from "node:util";
10
+ const execFileAsync = promisify(execFile);
11
+ const DEFAULT_TIMEOUT = 30_000;
12
+ /** Run a git command and return structured result */
13
+ export async function runGit(args, opts) {
14
+ try {
15
+ const { stdout, stderr } = await execFileAsync("git", args, {
16
+ cwd: opts.cwd,
17
+ env: { ...process.env, ...opts.env },
18
+ timeout: opts.timeout ?? DEFAULT_TIMEOUT,
19
+ maxBuffer: 10 * 1024 * 1024, // 10MB
20
+ });
21
+ return { stdout, stderr, exitCode: 0 };
22
+ }
23
+ catch (err) {
24
+ const error = err;
25
+ return {
26
+ stdout: error.stdout ?? "",
27
+ stderr: error.stderr ?? String(err),
28
+ exitCode: typeof error.code === "number" ? error.code : 1,
29
+ };
30
+ }
31
+ }
32
+ /** Run git and throw if it fails */
33
+ export async function runGitOrThrow(args, opts) {
34
+ const result = await runGit(args, opts);
35
+ if (result.exitCode !== 0) {
36
+ throw new Error(`git ${args.join(" ")} failed (exit ${result.exitCode}): ${result.stderr}`);
37
+ }
38
+ return result.stdout.trim();
39
+ }
40
+ /** Get the current HEAD SHA */
41
+ export async function getHeadSha(cwd) {
42
+ const result = await runGit(["rev-parse", "HEAD"], { cwd });
43
+ return result.exitCode === 0 ? result.stdout.trim() : null;
44
+ }
45
+ /** Get the current branch name */
46
+ export async function getCurrentBranch(cwd) {
47
+ const result = await runGit(["rev-parse", "--abbrev-ref", "HEAD"], { cwd });
48
+ return result.exitCode === 0 ? result.stdout.trim() : null;
49
+ }
50
+ /** Get the remote URL for origin */
51
+ export async function getRemoteUrl(cwd) {
52
+ const result = await runGit(["remote", "get-url", "origin"], { cwd });
53
+ return result.exitCode === 0 ? result.stdout.trim() : null;
54
+ }
55
+ /** Check if a path is inside a git repository */
56
+ export async function isGitRepo(cwd) {
57
+ const result = await runGit(["rev-parse", "--git-dir"], { cwd });
58
+ return result.exitCode === 0;
59
+ }
60
+ /** Check if a local branch exists */
61
+ export async function branchExists(cwd, branch) {
62
+ const result = await runGit(["rev-parse", "--verify", `refs/heads/${branch}`], { cwd });
63
+ return result.exitCode === 0;
64
+ }
65
+ /** Check if a git object (commit, tree, blob) exists in the object store */
66
+ export async function shaExists(cwd, sha) {
67
+ const result = await runGit(["cat-file", "-t", sha], { cwd });
68
+ return result.exitCode === 0;
69
+ }
70
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/git/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @vkenliu/adit-engine — Git operations, snapshots, detection, timeline, and environment capture.
3
+ */
4
+ export { runGit, runGitOrThrow, getHeadSha, getCurrentBranch, getRemoteUrl, isGitRepo, branchExists, shaExists, type GitResult, type GitRunOptions, } from "./git/runner.js";
5
+ export { storeCheckpointRef, resolveCheckpointRef, deleteCheckpointRef, listCheckpointRefs, getParentSha, getRefPrefix, } from "./git/refs.js";
6
+ export { getChangedFiles, hasUncommittedChanges, getNumstat, getChangesSummary, isDirtyFrom, type FileChange, type NumstatEntry, } from "./detector/working-tree.js";
7
+ export { createSnapshot, getCheckpointDiff, type SnapshotResult, } from "./snapshot/creator.js";
8
+ export { createTimelineManager, type TimelineManager, type RecordEventParams, type ListOptions, } from "./timeline/manager.js";
9
+ export { captureEnvironment, type PreComputedGitState } from "./environment/capture.js";
10
+ export { diffEnvironments } from "./environment/differ.js";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EACL,MAAM,EACN,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,SAAS,EACT,KAAK,SAAS,EACd,KAAK,aAAa,GACnB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,YAAY,GACb,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,KAAK,UAAU,EACf,KAAK,YAAY,GAClB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,KAAK,cAAc,GACpB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,WAAW,GACjB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @vkenliu/adit-engine — Git operations, snapshots, detection, timeline, and environment capture.
3
+ */
4
+ // Git operations
5
+ export { runGit, runGitOrThrow, getHeadSha, getCurrentBranch, getRemoteUrl, isGitRepo, branchExists, shaExists, } from "./git/runner.js";
6
+ export { storeCheckpointRef, resolveCheckpointRef, deleteCheckpointRef, listCheckpointRefs, getParentSha, getRefPrefix, } from "./git/refs.js";
7
+ // Working tree detection
8
+ export { getChangedFiles, hasUncommittedChanges, getNumstat, getChangesSummary, isDirtyFrom, } from "./detector/working-tree.js";
9
+ // Snapshot creation
10
+ export { createSnapshot, getCheckpointDiff, } from "./snapshot/creator.js";
11
+ // Timeline management
12
+ export { createTimelineManager, } from "./timeline/manager.js";
13
+ // Environment capture & diffing
14
+ export { captureEnvironment } from "./environment/capture.js";
15
+ export { diffEnvironments } from "./environment/differ.js";
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,iBAAiB;AACjB,OAAO,EACL,MAAM,EACN,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,SAAS,GAGV,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,YAAY,GACb,MAAM,eAAe,CAAC;AAEvB,yBAAyB;AACzB,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,UAAU,EACV,iBAAiB,EACjB,WAAW,GAGZ,MAAM,4BAA4B,CAAC;AAEpC,oBAAoB;AACpB,OAAO,EACL,cAAc,EACd,iBAAiB,GAElB,MAAM,uBAAuB,CAAC;AAE/B,sBAAsB;AACtB,OAAO,EACL,qBAAqB,GAItB,MAAM,uBAAuB,CAAC;AAE/B,gCAAgC;AAChC,OAAO,EAAE,kBAAkB,EAA4B,MAAM,0BAA0B,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Snapshot creator using temporary Git index.
3
+ *
4
+ * Creates git commit snapshots WITHOUT touching the user's staging area.
5
+ * This is the key technique from Rewindo: we set GIT_INDEX_FILE to a
6
+ * temp file, stage all changes there, create a tree+commit, and store
7
+ * it as a ref. The user's real index is never modified.
8
+ */
9
+ import { type FileChange } from "../detector/working-tree.js";
10
+ export interface SnapshotResult {
11
+ /** The commit SHA of the snapshot */
12
+ sha: string;
13
+ /** The ref path where it's stored */
14
+ ref: string;
15
+ /** Files included in the snapshot */
16
+ files: Array<{
17
+ path: string;
18
+ status: string;
19
+ additions?: number;
20
+ deletions?: number;
21
+ }>;
22
+ }
23
+ /** Create a snapshot of the current working tree state */
24
+ export declare function createSnapshot(cwd: string, parentSha: string | null, message: string, refPath: string, preComputedChanges?: FileChange[]): Promise<SnapshotResult | null>;
25
+ /** Get the unified diff between a checkpoint and its parent */
26
+ export declare function getCheckpointDiff(cwd: string, sha: string, parentSha?: string, filePath?: string): Promise<string>;
27
+ //# sourceMappingURL=creator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"creator.d.ts","sourceRoot":"","sources":["../../src/snapshot/creator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAG/E,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,qCAAqC;IACrC,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACJ;AAED,0DAA0D;AAC1D,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,kBAAkB,CAAC,EAAE,UAAU,EAAE,GAChC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA8DhC;AAuBD,+DAA+D;AAC/D,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAgBjB"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Snapshot creator using temporary Git index.
3
+ *
4
+ * Creates git commit snapshots WITHOUT touching the user's staging area.
5
+ * This is the key technique from Rewindo: we set GIT_INDEX_FILE to a
6
+ * temp file, stage all changes there, create a tree+commit, and store
7
+ * it as a ref. The user's real index is never modified.
8
+ */
9
+ import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
11
+ import { unlinkSync, existsSync } from "node:fs";
12
+ import { randomBytes } from "node:crypto";
13
+ import { runGit, runGitOrThrow } from "../git/runner.js";
14
+ import { getChangedFiles } from "../detector/working-tree.js";
15
+ import { getNumstat } from "../detector/working-tree.js";
16
+ /** Create a snapshot of the current working tree state */
17
+ export async function createSnapshot(cwd, parentSha, message, refPath, preComputedChanges) {
18
+ // Reuse pre-computed changes when the caller already ran git status
19
+ const changes = preComputedChanges ?? await getChangedFiles(cwd);
20
+ if (changes.length === 0)
21
+ return null;
22
+ // Create temp index file
23
+ const tempIndex = join(tmpdir(), `adit-index-${randomBytes(8).toString("hex")}`);
24
+ const env = { GIT_INDEX_FILE: tempIndex };
25
+ try {
26
+ // Start from HEAD's tree in the temp index.
27
+ // On a brand-new repo with no commits (unborn HEAD), read-tree HEAD
28
+ // fails. In that case we start with an empty index, which is correct —
29
+ // the subsequent git-add will stage all files as new.
30
+ const readTreeResult = await runGit(["read-tree", "HEAD"], { cwd, env });
31
+ if (readTreeResult.exitCode !== 0) {
32
+ // Unborn HEAD — start from an empty tree
33
+ await runGitOrThrow(["read-tree", "--empty"], { cwd, env });
34
+ }
35
+ // Stage all changes into temp index
36
+ await stageChanges(cwd, changes, env);
37
+ // Write tree object
38
+ const treeSha = await runGitOrThrow(["write-tree"], { cwd, env });
39
+ // Create commit object
40
+ const commitArgs = ["commit-tree", treeSha, "-m", message];
41
+ if (parentSha) {
42
+ commitArgs.splice(2, 0, "-p", parentSha);
43
+ }
44
+ const commitSha = await runGitOrThrow(commitArgs, { cwd, env });
45
+ // Store as a ref
46
+ await runGitOrThrow(["update-ref", refPath, commitSha], { cwd });
47
+ // Get file stats
48
+ const numstat = await getNumstat(cwd, parentSha ?? undefined);
49
+ const files = changes.map((c) => {
50
+ const stat = numstat.find((n) => n.path === c.path);
51
+ return {
52
+ path: c.path,
53
+ status: c.status,
54
+ additions: stat?.additions,
55
+ deletions: stat?.deletions,
56
+ };
57
+ });
58
+ return { sha: commitSha, ref: refPath, files };
59
+ }
60
+ finally {
61
+ // Always clean up temp index
62
+ if (existsSync(tempIndex)) {
63
+ try {
64
+ unlinkSync(tempIndex);
65
+ }
66
+ catch {
67
+ // Best effort cleanup
68
+ }
69
+ }
70
+ }
71
+ }
72
+ /** Stage file changes into a (possibly temp) index */
73
+ async function stageChanges(cwd, changes, env) {
74
+ // Batch into at most 2 git commands instead of N sequential spawns.
75
+ // Use runGitOrThrow so that staging failures (e.g. files deleted between
76
+ // getChangedFiles and staging) surface immediately rather than producing
77
+ // corrupted checkpoint snapshots with missing files.
78
+ const toDelete = changes.filter((c) => c.status === "D").map((c) => c.path);
79
+ const toAdd = changes.filter((c) => c.status !== "D").map((c) => c.path);
80
+ if (toDelete.length > 0) {
81
+ await runGitOrThrow(["rm", "--cached", "--", ...toDelete], { cwd, env });
82
+ }
83
+ if (toAdd.length > 0) {
84
+ await runGitOrThrow(["add", "--", ...toAdd], { cwd, env });
85
+ }
86
+ }
87
+ /** Get the unified diff between a checkpoint and its parent */
88
+ export async function getCheckpointDiff(cwd, sha, parentSha, filePath) {
89
+ // When parentSha is provided, diff between the two commits.
90
+ // When parentSha is absent this is the first checkpoint — use diff-tree
91
+ // with --root to show the full diff against an empty tree. The old
92
+ // `diff ${sha}^ ${sha}` approach fails silently when the commit has no
93
+ // parent, producing an empty string and losing the diff data.
94
+ const args = parentSha
95
+ ? ["diff", parentSha, sha]
96
+ : ["diff-tree", "--root", "-p", sha];
97
+ if (filePath) {
98
+ args.push("--", filePath);
99
+ }
100
+ const result = await runGit(args, { cwd });
101
+ return result.stdout;
102
+ }
103
+ //# sourceMappingURL=creator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"creator.js","sourceRoot":"","sources":["../../src/snapshot/creator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Timeline manager — the high-level orchestrator.
3
+ *
4
+ * Coordinates between the database, git operations, and snapshot creation
5
+ * to provide the unified timeline experience.
6
+ */
7
+ import type Database from "better-sqlite3";
8
+ import { type AditEvent, type EventType, type Actor, type AditConfig } from "@vkenliu/adit-core";
9
+ import type { FileChange } from "../detector/working-tree.js";
10
+ export interface TimelineManager {
11
+ /** Record a new event in the timeline */
12
+ recordEvent(params: RecordEventParams): Promise<AditEvent>;
13
+ /** Create a git checkpoint for an event */
14
+ createCheckpoint(eventId: string, message: string, preComputedChanges?: FileChange[]): Promise<{
15
+ sha: string;
16
+ ref: string;
17
+ } | null>;
18
+ /** Revert working tree to a checkpoint */
19
+ revertTo(eventId: string): Promise<void>;
20
+ /** Undo the last checkpoint */
21
+ undo(): Promise<void>;
22
+ /** Search events by text */
23
+ search(query: string, limit?: number): Promise<AditEvent[]>;
24
+ /** List recent events */
25
+ list(opts?: ListOptions): Promise<AditEvent[]>;
26
+ /** Get a single event by ID */
27
+ get(eventId: string): Promise<AditEvent | null>;
28
+ /** Get diff text for an event */
29
+ getDiff(eventId: string, maxLines?: number, offsetLines?: number): Promise<string | null>;
30
+ }
31
+ export interface RecordEventParams {
32
+ sessionId: string;
33
+ parentEventId?: string | null;
34
+ eventType: EventType;
35
+ actor: Actor;
36
+ promptText?: string | null;
37
+ cotText?: string | null;
38
+ responseText?: string | null;
39
+ toolName?: string | null;
40
+ toolInputJson?: string | null;
41
+ toolOutputJson?: string | null;
42
+ errorJson?: string | null;
43
+ planTaskId?: string | null;
44
+ }
45
+ export interface ListOptions {
46
+ sessionId?: string;
47
+ eventType?: EventType;
48
+ actor?: Actor;
49
+ hasCheckpoint?: boolean;
50
+ limit?: number;
51
+ }
52
+ /** Create a timeline manager instance */
53
+ export declare function createTimelineManager(db: Database.Database, config: AditConfig): TimelineManager;
54
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/timeline/manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAYL,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,KAAK,EACV,KAAK,UAAU,EAEhB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAK9D,MAAM,WAAW,eAAe;IAC9B,yCAAyC;IACzC,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAE3D,2CAA2C;IAC3C,gBAAgB,CACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,kBAAkB,CAAC,EAAE,UAAU,EAAE,GAChC,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAEhD,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzC,+BAA+B;IAC/B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtB,4BAA4B;IAC5B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAE5D,yBAAyB;IACzB,IAAI,CAAC,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAE/C,+BAA+B;IAC/B,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAEhD,iCAAiC;IACjC,OAAO,CACL,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,SAAS,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,yCAAyC;AACzC,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,MAAM,EAAE,UAAU,GACjB,eAAe,CAyLjB"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Timeline manager — the high-level orchestrator.
3
+ *
4
+ * Coordinates between the database, git operations, and snapshot creation
5
+ * to provide the unified timeline experience.
6
+ */
7
+ import { generateId, createClock, serialize, insertEventAutoSeq, getEventById, queryEvents, updateEventCheckpoint, searchEvents, getLatestCheckpointEvent, insertDiff, getDiffText, withPerf, } from "@vkenliu/adit-core";
8
+ import { createSnapshot, getCheckpointDiff } from "../snapshot/creator.js";
9
+ import { getHeadSha, getCurrentBranch, shaExists } from "../git/runner.js";
10
+ import { getRefPrefix } from "../git/refs.js";
11
+ import { runGitOrThrow } from "../git/runner.js";
12
+ /** Create a timeline manager instance */
13
+ export function createTimelineManager(db, config) {
14
+ const cwd = config.projectRoot;
15
+ return {
16
+ async recordEvent(params) {
17
+ const id = generateId();
18
+ const now = new Date().toISOString();
19
+ const branch = await getCurrentBranch(cwd);
20
+ const headSha = await getHeadSha(cwd);
21
+ const vclock = serialize(createClock(config.clientId));
22
+ // Use insertEventAutoSeq to atomically allocate the sequence number
23
+ // inside the INSERT, preventing duplicate sequences from concurrent
24
+ // hook processes that could read the same MAX(sequence).
25
+ insertEventAutoSeq(db, {
26
+ id,
27
+ sessionId: params.sessionId,
28
+ parentEventId: params.parentEventId ?? null,
29
+ eventType: params.eventType,
30
+ actor: params.actor,
31
+ promptText: params.promptText ?? null,
32
+ cotText: params.cotText ?? null,
33
+ responseText: params.responseText ?? null,
34
+ toolName: params.toolName ?? null,
35
+ toolInputJson: params.toolInputJson ?? null,
36
+ toolOutputJson: params.toolOutputJson ?? null,
37
+ gitBranch: branch,
38
+ gitHeadSha: headSha,
39
+ startedAt: now,
40
+ status: "success",
41
+ endedAt: now,
42
+ errorJson: params.errorJson ?? null,
43
+ planTaskId: params.planTaskId ?? null,
44
+ clientId: config.clientId,
45
+ vclockJson: vclock,
46
+ });
47
+ const event = getEventById(db, id);
48
+ if (!event) {
49
+ throw new Error(`Failed to retrieve event after insert: ${id}`);
50
+ }
51
+ return event;
52
+ },
53
+ async createCheckpoint(eventId, message, preComputedChanges) {
54
+ return withPerf(config.dataDir, "snapshot", "createCheckpoint", async () => {
55
+ const event = getEventById(db, eventId);
56
+ if (!event)
57
+ throw new Error(`Event not found: ${eventId}`);
58
+ // Find the parent checkpoint SHA for proper chaining
59
+ const lastCheckpoint = getLatestCheckpointEvent(db, event.sessionId);
60
+ const parentSha = lastCheckpoint?.checkpointSha ?? (await getHeadSha(cwd));
61
+ const refPath = `${getRefPrefix()}/${eventId}`;
62
+ const result = await createSnapshot(cwd, parentSha, message, refPath, preComputedChanges);
63
+ if (!result)
64
+ return null;
65
+ // Store the diff
66
+ const diffText = await getCheckpointDiff(cwd, result.sha, parentSha ?? undefined);
67
+ if (diffText) {
68
+ insertDiff(db, {
69
+ id: generateId(),
70
+ eventId,
71
+ diffText,
72
+ });
73
+ }
74
+ // Update the event with checkpoint info
75
+ updateEventCheckpoint(db, eventId, result.sha, result.ref, JSON.stringify(result.files));
76
+ return { sha: result.sha, ref: result.ref };
77
+ });
78
+ },
79
+ async revertTo(eventId) {
80
+ const event = getEventById(db, eventId);
81
+ if (!event)
82
+ throw new Error(`Event not found: ${eventId}`);
83
+ if (!event.checkpointSha) {
84
+ throw new Error(`Event ${eventId} has no checkpoint`);
85
+ }
86
+ // Verify the checkpoint commit object is reachable (may have been
87
+ // garbage collected if the ref was deleted after a squash merge).
88
+ const reachable = await shaExists(cwd, event.checkpointSha);
89
+ if (!reachable) {
90
+ throw new Error(`Checkpoint ${event.checkpointSha.substring(0, 8)} is no longer reachable in the git object store. ` +
91
+ `The checkpoint ref may have been deleted and the object garbage collected.`);
92
+ }
93
+ // Use checkout to restore working tree content from the checkpoint
94
+ // WITHOUT moving the branch pointer. git reset --hard would move HEAD
95
+ // to the ADIT-internal checkpoint commit, corrupting branch history.
96
+ await runGitOrThrow(["checkout", event.checkpointSha, "--", "."], { cwd });
97
+ },
98
+ async undo() {
99
+ const latest = getLatestCheckpointEvent(db);
100
+ if (!latest?.checkpointSha) {
101
+ throw new Error("No checkpoints to undo");
102
+ }
103
+ // Find the target SHA to restore to. Try the git parent first;
104
+ // if that fails (e.g., after a squash merge where the parent commit
105
+ // is unreachable), fall back to the previous checkpoint in the DB
106
+ // by sequence order, or HEAD as a last resort.
107
+ let targetSha = null;
108
+ // Attempt 1: git parent of latest checkpoint commit
109
+ const parentResult = await import("../git/refs.js").then((m) => m.getParentSha(cwd, latest.checkpointSha));
110
+ if (parentResult && (await shaExists(cwd, parentResult))) {
111
+ targetSha = parentResult;
112
+ }
113
+ // Attempt 2: previous checkpoint in DB sequence
114
+ if (!targetSha) {
115
+ const allCheckpoints = queryEvents(db, { hasCheckpoint: true, limit: 2 });
116
+ // allCheckpoints is ordered by started_at DESC; [0] is latest, [1] is previous
117
+ const previous = allCheckpoints.find((e) => e.id !== latest.id);
118
+ if (previous?.checkpointSha && (await shaExists(cwd, previous.checkpointSha))) {
119
+ targetSha = previous.checkpointSha;
120
+ }
121
+ }
122
+ // Attempt 3: fall back to HEAD
123
+ if (!targetSha) {
124
+ targetSha = await getHeadSha(cwd);
125
+ }
126
+ if (!targetSha) {
127
+ throw new Error("Cannot find a valid target to undo to");
128
+ }
129
+ // Use checkout to restore working tree content from the target
130
+ // WITHOUT moving the branch pointer. git reset --hard would move HEAD
131
+ // to the ADIT-internal checkpoint commit, corrupting branch history.
132
+ await runGitOrThrow(["checkout", targetSha, "--", "."], { cwd });
133
+ },
134
+ async search(query, limit = 20) {
135
+ return searchEvents(db, query, limit);
136
+ },
137
+ async list(opts) {
138
+ return queryEvents(db, {
139
+ sessionId: opts?.sessionId,
140
+ eventType: opts?.eventType,
141
+ actor: opts?.actor,
142
+ hasCheckpoint: opts?.hasCheckpoint,
143
+ limit: opts?.limit ?? 50,
144
+ });
145
+ },
146
+ async get(eventId) {
147
+ return getEventById(db, eventId);
148
+ },
149
+ async getDiff(eventId, maxLines, offsetLines) {
150
+ return getDiffText(db, eventId, maxLines, offsetLines);
151
+ },
152
+ };
153
+ }
154
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/timeline/manager.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@vkenliu/adit-engine",
3
+ "version": "0.2.0",
4
+ "description": "Git operations, snapshots, detection, and timeline engine for ADIT",
5
+ "type": "module",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ }
16
+ },
17
+ "dependencies": {
18
+ "@vkenliu/adit-core": "0.2.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/better-sqlite3": "^7.6.13",
22
+ "typescript": "^5.7.2"
23
+ },
24
+ "scripts": {
25
+ "build": "tsc",
26
+ "typecheck": "tsc --noEmit"
27
+ }
28
+ }