@aigne/afs-git 1.11.0-beta.11 → 1.11.0-beta.13
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/index.cjs +110 -66
- package/dist/index.d.cts +3 -9
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +3 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +110 -67
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
- package/dist/_virtual/rolldown_runtime.mjs +0 -7
package/dist/index.cjs
CHANGED
|
@@ -9,6 +9,7 @@ let node_util = require("node:util");
|
|
|
9
9
|
let _aigne_afs = require("@aigne/afs");
|
|
10
10
|
let _aigne_afs_provider = require("@aigne/afs/provider");
|
|
11
11
|
let _aigne_afs_utils_zod = require("@aigne/afs/utils/zod");
|
|
12
|
+
let _aigne_afs_provider_utils = require("@aigne/afs-provider-utils");
|
|
12
13
|
let simple_git = require("simple-git");
|
|
13
14
|
let zod = require("zod");
|
|
14
15
|
|
|
@@ -43,13 +44,94 @@ var AFSGit = class AFSGit extends _aigne_afs_provider.AFSBaseProvider {
|
|
|
43
44
|
name: "git",
|
|
44
45
|
description: "Git repository browser with branch-based access.\n- Browse branches, read files at any ref, search across repository\n- Exec actions (readwrite): `diff`, `create-branch`, `commit`, `merge`\n- Virtual `.log/` tree exposes commit history per branch\n- Path structure: `/{branch}/{file-path}` (branch `/` encoded as `~`)",
|
|
45
46
|
uriTemplate: "git://{localPath+}",
|
|
46
|
-
category: "
|
|
47
|
+
category: "vcs",
|
|
47
48
|
schema: zod.z.object({
|
|
48
49
|
localPath: zod.z.string(),
|
|
49
50
|
branch: zod.z.string().optional(),
|
|
50
51
|
remoteUrl: zod.z.string().optional()
|
|
51
52
|
}),
|
|
52
|
-
tags: ["git", "version-control"]
|
|
53
|
+
tags: ["git", "version-control"],
|
|
54
|
+
capabilityTags: [
|
|
55
|
+
"read-write",
|
|
56
|
+
"search",
|
|
57
|
+
"auth:none",
|
|
58
|
+
"local"
|
|
59
|
+
],
|
|
60
|
+
security: {
|
|
61
|
+
riskLevel: "local",
|
|
62
|
+
resourceAccess: ["local-filesystem"],
|
|
63
|
+
requires: ["git"],
|
|
64
|
+
dataSensitivity: ["code"],
|
|
65
|
+
notes: ["Accesses local git repositories; readwrite mode creates worktrees and can commit"]
|
|
66
|
+
},
|
|
67
|
+
capabilities: {
|
|
68
|
+
filesystem: {
|
|
69
|
+
read: true,
|
|
70
|
+
write: true
|
|
71
|
+
},
|
|
72
|
+
process: {
|
|
73
|
+
spawn: true,
|
|
74
|
+
allowedCommands: ["git"]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
static treeSchema() {
|
|
80
|
+
return {
|
|
81
|
+
operations: [
|
|
82
|
+
"list",
|
|
83
|
+
"read",
|
|
84
|
+
"write",
|
|
85
|
+
"delete",
|
|
86
|
+
"search",
|
|
87
|
+
"stat",
|
|
88
|
+
"explain"
|
|
89
|
+
],
|
|
90
|
+
tree: {
|
|
91
|
+
"/": {
|
|
92
|
+
kind: "git:root",
|
|
93
|
+
operations: ["list", "read"]
|
|
94
|
+
},
|
|
95
|
+
"/{branch}": {
|
|
96
|
+
kind: "git:branch",
|
|
97
|
+
operations: [
|
|
98
|
+
"list",
|
|
99
|
+
"read",
|
|
100
|
+
"search"
|
|
101
|
+
],
|
|
102
|
+
actions: [
|
|
103
|
+
"diff",
|
|
104
|
+
"create-branch",
|
|
105
|
+
"commit",
|
|
106
|
+
"merge"
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
"/{branch}/{path+}": {
|
|
110
|
+
kind: "git:file",
|
|
111
|
+
operations: [
|
|
112
|
+
"list",
|
|
113
|
+
"read",
|
|
114
|
+
"write",
|
|
115
|
+
"delete",
|
|
116
|
+
"search"
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
"/{branch}/.log": {
|
|
120
|
+
kind: "git:log",
|
|
121
|
+
operations: ["list"]
|
|
122
|
+
},
|
|
123
|
+
"/{branch}/.log/{index}": {
|
|
124
|
+
kind: "git:commit",
|
|
125
|
+
operations: ["read"]
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
auth: { type: "none" },
|
|
129
|
+
bestFor: [
|
|
130
|
+
"code browsing",
|
|
131
|
+
"version control",
|
|
132
|
+
"branch comparison"
|
|
133
|
+
],
|
|
134
|
+
notFor: ["binary storage", "large files"]
|
|
53
135
|
};
|
|
54
136
|
}
|
|
55
137
|
static async load({ basePath, config } = {}) {
|
|
@@ -88,15 +170,6 @@ var AFSGit = class AFSGit extends _aigne_afs_provider.AFSBaseProvider {
|
|
|
88
170
|
const repoHash = (0, node_crypto.createHash)("md5").update(options.remoteUrl).digest("hex").substring(0, 8);
|
|
89
171
|
repoPath = (0, node_path.join)((0, node_os.tmpdir)(), `afs-git-remote-${repoHash}`);
|
|
90
172
|
} else throw new Error("Either repoPath or remoteUrl must be provided");
|
|
91
|
-
if (options.repoPath && !options.remoteUrl) {
|
|
92
|
-
const { existsSync, mkdirSync } = require("node:fs");
|
|
93
|
-
const { execSync } = require("node:child_process");
|
|
94
|
-
if (!existsSync(repoPath)) mkdirSync(repoPath, { recursive: true });
|
|
95
|
-
if (!existsSync((0, node_path.join)(repoPath, ".git"))) execSync("git init -b main", {
|
|
96
|
-
cwd: repoPath,
|
|
97
|
-
stdio: "ignore"
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
173
|
this.repoPath = repoPath;
|
|
101
174
|
this.name = options.name || repoName;
|
|
102
175
|
this.description = options.description;
|
|
@@ -118,6 +191,15 @@ var AFSGit = class AFSGit extends _aigne_afs_provider.AFSBaseProvider {
|
|
|
118
191
|
*/
|
|
119
192
|
async initialize() {
|
|
120
193
|
const options = this.options;
|
|
194
|
+
if (options.repoPath && !options.remoteUrl) {
|
|
195
|
+
const { existsSync, mkdirSync } = await import("node:fs");
|
|
196
|
+
const { execSync } = await import("node:child_process");
|
|
197
|
+
if (!existsSync(this.repoPath)) mkdirSync(this.repoPath, { recursive: true });
|
|
198
|
+
if (!existsSync((0, node_path.join)(this.repoPath, ".git"))) execSync("git init -b main", {
|
|
199
|
+
cwd: this.repoPath,
|
|
200
|
+
stdio: "ignore"
|
|
201
|
+
});
|
|
202
|
+
}
|
|
121
203
|
if (options.remoteUrl) {
|
|
122
204
|
const targetPath = options.repoPath ? (0, node_path.isAbsolute)(options.repoPath) ? options.repoPath : (0, node_path.join)(options.cwd || process.cwd(), options.repoPath) : this.repoPath;
|
|
123
205
|
if (!options.repoPath) this.isAutoCloned = true;
|
|
@@ -303,14 +385,15 @@ var AFSGit = class AFSGit extends _aigne_afs_provider.AFSBaseProvider {
|
|
|
303
385
|
const worktreePath = this.worktrees.get(branch);
|
|
304
386
|
if (worktreePath) try {
|
|
305
387
|
const fullPath = (0, node_path.join)(worktreePath, filePath);
|
|
388
|
+
await this.assertWithinWorktree(fullPath, worktreePath);
|
|
306
389
|
const stats = await (0, node_fs_promises.stat)(fullPath);
|
|
307
390
|
if (stats.isDirectory()) {
|
|
308
391
|
const files = await (0, node_fs_promises.readdir)(fullPath);
|
|
309
392
|
const afsPath$2 = this.buildBranchPath(branch, filePath);
|
|
310
393
|
return this.buildEntry(afsPath$2, { meta: { childrenCount: files.length } });
|
|
311
394
|
}
|
|
312
|
-
const mimeType$1 =
|
|
313
|
-
const isBinary$1 =
|
|
395
|
+
const mimeType$1 = (0, _aigne_afs_provider_utils.getMimeType)(filePath);
|
|
396
|
+
const isBinary$1 = (0, _aigne_afs_provider_utils.isBinaryFile)(filePath);
|
|
314
397
|
let content$1;
|
|
315
398
|
const meta$1 = {
|
|
316
399
|
size: stats.size,
|
|
@@ -327,7 +410,9 @@ var AFSGit = class AFSGit extends _aigne_afs_provider.AFSBaseProvider {
|
|
|
327
410
|
createdAt: stats.birthtime,
|
|
328
411
|
updatedAt: stats.mtime
|
|
329
412
|
});
|
|
330
|
-
} catch {
|
|
413
|
+
} catch (err) {
|
|
414
|
+
if (err instanceof Error && "code" in err && err.code === "AFS_PERMISSION_DENIED") throw err;
|
|
415
|
+
}
|
|
331
416
|
const objectType = await this.git.raw([
|
|
332
417
|
"cat-file",
|
|
333
418
|
"-t",
|
|
@@ -344,8 +429,8 @@ var AFSGit = class AFSGit extends _aigne_afs_provider.AFSBaseProvider {
|
|
|
344
429
|
"-s",
|
|
345
430
|
`${branch}:${filePath}`
|
|
346
431
|
]).then((s) => Number.parseInt(s.trim(), 10));
|
|
347
|
-
const mimeType =
|
|
348
|
-
const isBinary =
|
|
432
|
+
const mimeType = (0, _aigne_afs_provider_utils.getMimeType)(filePath);
|
|
433
|
+
const isBinary = (0, _aigne_afs_provider_utils.isBinaryFile)(filePath);
|
|
349
434
|
let content;
|
|
350
435
|
const meta = {
|
|
351
436
|
size,
|
|
@@ -389,9 +474,10 @@ var AFSGit = class AFSGit extends _aigne_afs_provider.AFSBaseProvider {
|
|
|
389
474
|
await this.ready();
|
|
390
475
|
const branch = this.decodeBranchName(ctx.params.branch);
|
|
391
476
|
const filePath = ctx.params.path;
|
|
392
|
-
const
|
|
477
|
+
const mode = ctx.options?.mode ?? "replace";
|
|
393
478
|
const worktreePath = await this.ensureWorktree(branch);
|
|
394
479
|
const fullPath = (0, node_path.join)(worktreePath, filePath);
|
|
480
|
+
await this.assertWithinWorktree(fullPath, worktreePath);
|
|
395
481
|
await (0, node_fs_promises.mkdir)((0, node_path.dirname)(fullPath), { recursive: true });
|
|
396
482
|
if (payload.content !== void 0) {
|
|
397
483
|
let contentToWrite;
|
|
@@ -399,7 +485,7 @@ var AFSGit = class AFSGit extends _aigne_afs_provider.AFSBaseProvider {
|
|
|
399
485
|
else contentToWrite = JSON.stringify(payload.content, null, 2);
|
|
400
486
|
await (0, node_fs_promises.writeFile)(fullPath, contentToWrite, {
|
|
401
487
|
encoding: "utf8",
|
|
402
|
-
flag: append ? "a" : "w"
|
|
488
|
+
flag: mode === "append" ? "a" : "w"
|
|
403
489
|
});
|
|
404
490
|
}
|
|
405
491
|
if (this.options.autoCommit) {
|
|
@@ -454,6 +540,7 @@ var AFSGit = class AFSGit extends _aigne_afs_provider.AFSBaseProvider {
|
|
|
454
540
|
const recursive = ctx.options?.recursive ?? false;
|
|
455
541
|
const worktreePath = await this.ensureWorktree(branch);
|
|
456
542
|
const fullPath = (0, node_path.join)(worktreePath, filePath);
|
|
543
|
+
await this.assertWithinWorktree(fullPath, worktreePath);
|
|
457
544
|
let stats;
|
|
458
545
|
try {
|
|
459
546
|
stats = await (0, node_fs_promises.stat)(fullPath);
|
|
@@ -491,6 +578,8 @@ var AFSGit = class AFSGit extends _aigne_afs_provider.AFSBaseProvider {
|
|
|
491
578
|
const worktreePath = await this.ensureWorktree(oldBranch);
|
|
492
579
|
const oldFullPath = (0, node_path.join)(worktreePath, oldFilePath);
|
|
493
580
|
const newFullPath = (0, node_path.join)(worktreePath, newFilePath);
|
|
581
|
+
await this.assertWithinWorktree(oldFullPath, worktreePath);
|
|
582
|
+
await this.assertWithinWorktree(newFullPath, worktreePath);
|
|
494
583
|
try {
|
|
495
584
|
await (0, node_fs_promises.stat)(oldFullPath);
|
|
496
585
|
} catch (error) {
|
|
@@ -1197,6 +1286,9 @@ var AFSGit = class AFSGit extends _aigne_afs_provider.AFSBaseProvider {
|
|
|
1197
1286
|
/**
|
|
1198
1287
|
* Decode branch name (replace ~ with /)
|
|
1199
1288
|
*/
|
|
1289
|
+
async assertWithinWorktree(fullPath, worktreeRoot) {
|
|
1290
|
+
return (0, _aigne_afs_provider_utils.assertPathWithinRoot)(fullPath, worktreeRoot);
|
|
1291
|
+
}
|
|
1200
1292
|
decodeBranchName(encoded) {
|
|
1201
1293
|
return encoded.replace(/~/g, "/");
|
|
1202
1294
|
}
|
|
@@ -1397,54 +1489,6 @@ var AFSGit = class AFSGit extends _aigne_afs_provider.AFSBaseProvider {
|
|
|
1397
1489
|
}
|
|
1398
1490
|
}
|
|
1399
1491
|
/**
|
|
1400
|
-
* Detect MIME type based on file extension
|
|
1401
|
-
*/
|
|
1402
|
-
getMimeType(filePath) {
|
|
1403
|
-
return {
|
|
1404
|
-
png: "image/png",
|
|
1405
|
-
jpg: "image/jpeg",
|
|
1406
|
-
jpeg: "image/jpeg",
|
|
1407
|
-
gif: "image/gif",
|
|
1408
|
-
bmp: "image/bmp",
|
|
1409
|
-
webp: "image/webp",
|
|
1410
|
-
svg: "image/svg+xml",
|
|
1411
|
-
ico: "image/x-icon",
|
|
1412
|
-
pdf: "application/pdf",
|
|
1413
|
-
txt: "text/plain",
|
|
1414
|
-
md: "text/markdown",
|
|
1415
|
-
js: "text/javascript",
|
|
1416
|
-
ts: "text/typescript",
|
|
1417
|
-
json: "application/json",
|
|
1418
|
-
html: "text/html",
|
|
1419
|
-
css: "text/css",
|
|
1420
|
-
xml: "text/xml"
|
|
1421
|
-
}[filePath.split(".").pop()?.toLowerCase() || ""] || "application/octet-stream";
|
|
1422
|
-
}
|
|
1423
|
-
/**
|
|
1424
|
-
* Check if file is likely binary based on extension
|
|
1425
|
-
*/
|
|
1426
|
-
isBinaryFile(filePath) {
|
|
1427
|
-
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
1428
|
-
return [
|
|
1429
|
-
"png",
|
|
1430
|
-
"jpg",
|
|
1431
|
-
"jpeg",
|
|
1432
|
-
"gif",
|
|
1433
|
-
"bmp",
|
|
1434
|
-
"webp",
|
|
1435
|
-
"ico",
|
|
1436
|
-
"pdf",
|
|
1437
|
-
"zip",
|
|
1438
|
-
"tar",
|
|
1439
|
-
"gz",
|
|
1440
|
-
"exe",
|
|
1441
|
-
"dll",
|
|
1442
|
-
"so",
|
|
1443
|
-
"dylib",
|
|
1444
|
-
"wasm"
|
|
1445
|
-
].includes(ext || "");
|
|
1446
|
-
}
|
|
1447
|
-
/**
|
|
1448
1492
|
* Fetch latest changes from remote
|
|
1449
1493
|
*/
|
|
1450
1494
|
async fetch() {
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AFSAccessMode, AFSEntry, AFSExecResult, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSSearchOptions, AFSStatResult, AFSWriteEntryPayload, ProviderManifest } from "@aigne/afs";
|
|
1
|
+
import { AFSAccessMode, AFSEntry, AFSExecResult, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSSearchOptions, AFSStatResult, AFSWriteEntryPayload, ProviderManifest, ProviderTreeSchema } from "@aigne/afs";
|
|
2
2
|
import { AFSBaseProvider, RouteContext } from "@aigne/afs/provider";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
@@ -120,6 +120,7 @@ declare class AFSGit extends AFSBaseProvider {
|
|
|
120
120
|
} | undefined;
|
|
121
121
|
}, unknown>>;
|
|
122
122
|
static manifest(): ProviderManifest;
|
|
123
|
+
static treeSchema(): ProviderTreeSchema;
|
|
123
124
|
static load({
|
|
124
125
|
basePath,
|
|
125
126
|
config
|
|
@@ -368,6 +369,7 @@ declare class AFSGit extends AFSBaseProvider {
|
|
|
368
369
|
/**
|
|
369
370
|
* Decode branch name (replace ~ with /)
|
|
370
371
|
*/
|
|
372
|
+
private assertWithinWorktree;
|
|
371
373
|
private decodeBranchName;
|
|
372
374
|
/**
|
|
373
375
|
* Encode branch name (replace / with ~)
|
|
@@ -416,14 +418,6 @@ declare class AFSGit extends AFSBaseProvider {
|
|
|
416
418
|
* Note: list() returns only children, never the path itself (per new semantics)
|
|
417
419
|
*/
|
|
418
420
|
private listWithGitLsTree;
|
|
419
|
-
/**
|
|
420
|
-
* Detect MIME type based on file extension
|
|
421
|
-
*/
|
|
422
|
-
private getMimeType;
|
|
423
|
-
/**
|
|
424
|
-
* Check if file is likely binary based on extension
|
|
425
|
-
*/
|
|
426
|
-
private isBinaryFile;
|
|
427
421
|
/**
|
|
428
422
|
* Fetch latest changes from remote
|
|
429
423
|
*/
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;UAkDiB,aAAA;EACf,IAAA;EADe;;;;;EAOf,QAAA;EAQA;;;;;;;EAAA,SAAA;EACA,WAAA;EAgCA;;;;;;;EAxBA,QAAA;EAqFW;;;;;;EA9EX,UAAA,GAAa,aAAA;EA+GQ;;;;EA1GrB,UAAA;EAiIgE;;;EA7HhE,YAAA;IACE,IAAA;IACA,KAAA;EAAA;EAmYK;;;;EA7XP,KAAA;EA6YG;;;;;EAvYH,WAAA;EA2aG;;;EAvaH,YAAA;IAoe4B;;;IAhe1B,IAAA;MACE,QAAA;MACA,QAAA;IAAA;EAAA;AAAA;AAAA,cA6CO,MAAA,SAAe,eAAA;EA6EjB,OAAA,EAAS,aAAA;IACd,GAAA;IACA,SAAA;IACA,MAAA;IACA,GAAA;EAAA;EAAA,OAhFG,MAAA,CAAA,GAAM,CAAA,CAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAIN,QAAA,CAAA,GAAY,gBAAA;EAAA,OA4BZ,UAAA,CAAA,GAAc,kBAAA;EAAA,OAuBR,IAAA,CAAA;IAAO,QAAA;IAAU;EAAA,IAAU,mBAAA,GAAwB,OAAA,CAAA,MAAA;EAAA,SAOvD,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA,EAAY,aAAA;EAAA,QAEb,WAAA;EAAA,QACA,GAAA;EAAA,QACA,QAAA;EAAA,QACA,SAAA;EAAA,QACA,QAAA;EAAA,QACA,YAAA;EAAA,QACA,UAAA;EAAA,QACA,QAAA;cAGC,OAAA,EAAS,aAAA;IACd,GAAA;IACA,SAAA;IACA,MAAA;IACA,GAAA;EAAA;EAuiDO;;;EA3+CL,KAAA,CAAA,GAAS,OAAA;EAq0Dc;;;;EAAA,QA7zDf,UAAA;EArJY;;;EAAA,eAqOL,eAAA;EAtJjB;;;;EAkME,eAAA,CAAgB,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,aAAA;IAAkB,QAAA;EAAA;;;;EA8C9D,qBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;EAAA,KACnB,OAAA,CAAQ,aAAA;IAAkB,QAAA;EAAA;;;;EAavB,iBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;IAAgB,IAAA;EAAA,KACnC,OAAA,CAAQ,aAAA;IAAkB,QAAA;EAAA;;;;EAqBvB,mBAAA,CAAoB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,QAAA;;;;EAajD,qBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;EAAA,KACnB,OAAA,CAAQ,QAAA;;;;EAmBL,mBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;IAAgB,IAAA;EAAA,KACnC,OAAA,CAAQ,QAAA;EApYJ;;;EA4aD,eAAA,CAAgB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,QAAA;EAzXtC;;;EAsYP,qBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;EAAA,KACnB,OAAA,CAAQ,QAAA;EAxYqD;;;EAyZ1D,iBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;IAAgB,IAAA;EAAA,KACnC,OAAA,CAAQ,QAAA;EA/YH;;;EA6gBF,gBAAA,CAAA,GAAoB,OAAA;EAzgBlB;;;EAihBF,sBAAA,CAAA,GAA0B,OAAA;EA5gBd;;;EAohBZ,YAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;IAAgB,IAAA;EAAA,IACpC,OAAA,EAAS,oBAAA,GACR,OAAA;IAAU,IAAA,EAAM,QAAA;EAAA;EA/XE;;;EAgdf,iBAAA,CAAA,GAAqB,OAAA;EApae;;;EA4apC,uBAAA,CAAwB,GAAA,EAAK,YAAA;IAAe,MAAA;EAAA,KAAoB,OAAA;EA7XpE;;;EAwYI,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;IAAgB,IAAA;EAAA,KACnC,OAAA;IAAU,OAAA;EAAA;EA1XV;;;EAubG,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;IAAgB,IAAA;EAAA,IACpC,OAAA,WACC,OAAA;IAAU,OAAA;EAAA;EAvZN;;;EAgfD,uBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;EAAA,IACpB,KAAA,UACA,OAAA,GAAU,gBAAA,GACT,OAAA;IAAU,IAAA,EAAM,QAAA;IAAY,OAAA;EAAA;EA/d7B;;;EAueI,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;IAAgB,IAAA;EAAA,IACpC,KAAA,UACA,OAAA,GAAU,gBAAA,GACT,OAAA;IAAU,IAAA,EAAM,QAAA;IAAY,OAAA;EAAA;EApbT;;;EAAA,QA2bR,cAAA;EAzaR;;;EA6fA,eAAA,CAAgB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,aAAA;EA5fjD;;;EA0gBI,qBAAA,CAAsB,GAAA,EAAK,YAAA;IAAe,MAAA;EAAA,KAAoB,OAAA,CAAQ,aAAA;EAnY5C;;;EAiZ1B,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,MAAA;IAAgB,IAAA;EAAA,KAAkB,OAAA,CAAQ,aAAA;EAvY9E;;;EAuZI,kBAAA,CAAmB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,gBAAA;EArUhD;;;EA6WA,oBAAA,CAAqB,GAAA,EAAK,YAAA;IAAe,MAAA;EAAA,KAAoB,OAAA,CAAQ,gBAAA;EArWL;;;EA8XhE,kBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;IAAgB,IAAA;EAAA,KACnC,OAAA,CAAQ,gBAAA;EA4DL,uBAAA,CAAwB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,QAAA;EAlXrD;;;EAsdA,iBAAA,CAAkB,GAAA,EAAK,YAAA;IAAe,MAAA;EAAA,KAAoB,OAAA,CAAQ,aAAA;EAndrE;;;EA8iBG,UAAA,CACJ,IAAA,EAAM,YAAA;IAAe,MAAA;EAAA,IACrB,IAAA,EAAM,MAAA,oBACL,OAAA,CAAQ,aAAA;EAtdT;;;EAihBI,kBAAA,CACJ,IAAA,EAAM,YAAA;IAAe,MAAA;EAAA,IACrB,IAAA,EAAM,MAAA,oBACL,OAAA,CAAQ,aAAA;EAlhBoB;;;EAikBzB,YAAA,CACJ,IAAA,EAAM,YAAA;IAAe,MAAA;EAAA,IACrB,IAAA,EAAM,MAAA,oBACL,OAAA,CAAQ,aAAA;EA1jBT;;;EAonBI,WAAA,CACJ,IAAA,EAAM,YAAA;IAAe,MAAA;EAAA,IACrB,IAAA,EAAM,MAAA,oBACL,OAAA,CAAQ,aAAA;EArnBoB;;;EAsrBzB,cAAA,CAAe,GAAA,EAAK,YAAA;IAAe,MAAA;EAAA,KAAoB,OAAA,CAAQ,aAAA;EA3lBlB;;;EA2nB7C,mBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;IAAgB,KAAA;EAAA,KACnC,OAAA,CAAQ,QAAA;EAjmBL;;;EA4oBA,uBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,MAAA;IAAgB,KAAA;EAAA,KACnC,OAAA,CAAQ,QAAA;EA9nBL;;;EAAA,QAkqBQ,oBAAA;EAAA,QAIN,gBAAA;EA9nBF;;;EAAA,QAqoBE,gBAAA;EAroB2D;;;;EAAA,QA6oB3D,SAAA;EAnnB8B;;;;EAAA,QAsoB9B,eAAA;EAzkB4B;;;EAAA,QAolBtB,WAAA;EAhfR;;;EAAA,QA+fQ,kBAAA;EA/fkD;;;EAAA,QAygBlD,gBAAA;EA7aS;;;EAAA,QA2bT,aAAA;EAzbX;;;EAAA,QA0cW,gBAAA;EA9YS;;;EAAA,QA4ZT,aAAA;EA1ZX;;;EAAA,QA4bW,cAAA;EA5YS;;;;EAAA,QA6aT,iBAAA;EA3aH;;;EAuhBL,KAAA,CAAA,GAAS,OAAA;EA5db;;;EAoeI,IAAA,CAAA,GAAQ,OAAA;EAleH;;;EA0eL,IAAA,CAAK,MAAA,YAAkB,OAAA;EAzaR;;;EAqbf,OAAA,CAAA,GAAW,OAAA;AAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AFSAccessMode, AFSEntry, AFSExecResult, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSSearchOptions, AFSStatResult, AFSWriteEntryPayload, ProviderManifest } from "@aigne/afs";
|
|
1
|
+
import { AFSAccessMode, AFSEntry, AFSExecResult, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSSearchOptions, AFSStatResult, AFSWriteEntryPayload, ProviderManifest, ProviderTreeSchema } from "@aigne/afs";
|
|
2
2
|
import { AFSBaseProvider, RouteContext } from "@aigne/afs/provider";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
@@ -120,6 +120,7 @@ declare class AFSGit extends AFSBaseProvider {
|
|
|
120
120
|
} | undefined;
|
|
121
121
|
}, unknown>>;
|
|
122
122
|
static manifest(): ProviderManifest;
|
|
123
|
+
static treeSchema(): ProviderTreeSchema;
|
|
123
124
|
static load({
|
|
124
125
|
basePath,
|
|
125
126
|
config
|
|
@@ -368,6 +369,7 @@ declare class AFSGit extends AFSBaseProvider {
|
|
|
368
369
|
/**
|
|
369
370
|
* Decode branch name (replace ~ with /)
|
|
370
371
|
*/
|
|
372
|
+
private assertWithinWorktree;
|
|
371
373
|
private decodeBranchName;
|
|
372
374
|
/**
|
|
373
375
|
* Encode branch name (replace / with ~)
|
|
@@ -416,14 +418,6 @@ declare class AFSGit extends AFSBaseProvider {
|
|
|
416
418
|
* Note: list() returns only children, never the path itself (per new semantics)
|
|
417
419
|
*/
|
|
418
420
|
private listWithGitLsTree;
|
|
419
|
-
/**
|
|
420
|
-
* Detect MIME type based on file extension
|
|
421
|
-
*/
|
|
422
|
-
private getMimeType;
|
|
423
|
-
/**
|
|
424
|
-
* Check if file is likely binary based on extension
|
|
425
|
-
*/
|
|
426
|
-
private isBinaryFile;
|
|
427
421
|
/**
|
|
428
422
|
* Fetch latest changes from remote
|
|
429
423
|
*/
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { __require } from "./_virtual/rolldown_runtime.mjs";
|
|
2
1
|
import { __decorate } from "./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs";
|
|
3
2
|
import { execFile } from "node:child_process";
|
|
4
3
|
import { createHash } from "node:crypto";
|
|
@@ -9,6 +8,7 @@ import { promisify } from "node:util";
|
|
|
9
8
|
import { AFSNotFoundError } from "@aigne/afs";
|
|
10
9
|
import { AFSBaseProvider, Actions, Delete, Explain, List, Meta, Read, Rename, Search, Stat, Write } from "@aigne/afs/provider";
|
|
11
10
|
import { camelize, optionalize, zodParse } from "@aigne/afs/utils/zod";
|
|
11
|
+
import { assertPathWithinRoot, getMimeType, isBinaryFile } from "@aigne/afs-provider-utils";
|
|
12
12
|
import { simpleGit } from "simple-git";
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
|
|
@@ -43,13 +43,94 @@ var AFSGit = class AFSGit extends AFSBaseProvider {
|
|
|
43
43
|
name: "git",
|
|
44
44
|
description: "Git repository browser with branch-based access.\n- Browse branches, read files at any ref, search across repository\n- Exec actions (readwrite): `diff`, `create-branch`, `commit`, `merge`\n- Virtual `.log/` tree exposes commit history per branch\n- Path structure: `/{branch}/{file-path}` (branch `/` encoded as `~`)",
|
|
45
45
|
uriTemplate: "git://{localPath+}",
|
|
46
|
-
category: "
|
|
46
|
+
category: "vcs",
|
|
47
47
|
schema: z.object({
|
|
48
48
|
localPath: z.string(),
|
|
49
49
|
branch: z.string().optional(),
|
|
50
50
|
remoteUrl: z.string().optional()
|
|
51
51
|
}),
|
|
52
|
-
tags: ["git", "version-control"]
|
|
52
|
+
tags: ["git", "version-control"],
|
|
53
|
+
capabilityTags: [
|
|
54
|
+
"read-write",
|
|
55
|
+
"search",
|
|
56
|
+
"auth:none",
|
|
57
|
+
"local"
|
|
58
|
+
],
|
|
59
|
+
security: {
|
|
60
|
+
riskLevel: "local",
|
|
61
|
+
resourceAccess: ["local-filesystem"],
|
|
62
|
+
requires: ["git"],
|
|
63
|
+
dataSensitivity: ["code"],
|
|
64
|
+
notes: ["Accesses local git repositories; readwrite mode creates worktrees and can commit"]
|
|
65
|
+
},
|
|
66
|
+
capabilities: {
|
|
67
|
+
filesystem: {
|
|
68
|
+
read: true,
|
|
69
|
+
write: true
|
|
70
|
+
},
|
|
71
|
+
process: {
|
|
72
|
+
spawn: true,
|
|
73
|
+
allowedCommands: ["git"]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
static treeSchema() {
|
|
79
|
+
return {
|
|
80
|
+
operations: [
|
|
81
|
+
"list",
|
|
82
|
+
"read",
|
|
83
|
+
"write",
|
|
84
|
+
"delete",
|
|
85
|
+
"search",
|
|
86
|
+
"stat",
|
|
87
|
+
"explain"
|
|
88
|
+
],
|
|
89
|
+
tree: {
|
|
90
|
+
"/": {
|
|
91
|
+
kind: "git:root",
|
|
92
|
+
operations: ["list", "read"]
|
|
93
|
+
},
|
|
94
|
+
"/{branch}": {
|
|
95
|
+
kind: "git:branch",
|
|
96
|
+
operations: [
|
|
97
|
+
"list",
|
|
98
|
+
"read",
|
|
99
|
+
"search"
|
|
100
|
+
],
|
|
101
|
+
actions: [
|
|
102
|
+
"diff",
|
|
103
|
+
"create-branch",
|
|
104
|
+
"commit",
|
|
105
|
+
"merge"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
"/{branch}/{path+}": {
|
|
109
|
+
kind: "git:file",
|
|
110
|
+
operations: [
|
|
111
|
+
"list",
|
|
112
|
+
"read",
|
|
113
|
+
"write",
|
|
114
|
+
"delete",
|
|
115
|
+
"search"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
"/{branch}/.log": {
|
|
119
|
+
kind: "git:log",
|
|
120
|
+
operations: ["list"]
|
|
121
|
+
},
|
|
122
|
+
"/{branch}/.log/{index}": {
|
|
123
|
+
kind: "git:commit",
|
|
124
|
+
operations: ["read"]
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
auth: { type: "none" },
|
|
128
|
+
bestFor: [
|
|
129
|
+
"code browsing",
|
|
130
|
+
"version control",
|
|
131
|
+
"branch comparison"
|
|
132
|
+
],
|
|
133
|
+
notFor: ["binary storage", "large files"]
|
|
53
134
|
};
|
|
54
135
|
}
|
|
55
136
|
static async load({ basePath, config } = {}) {
|
|
@@ -88,15 +169,6 @@ var AFSGit = class AFSGit extends AFSBaseProvider {
|
|
|
88
169
|
const repoHash = createHash("md5").update(options.remoteUrl).digest("hex").substring(0, 8);
|
|
89
170
|
repoPath = join(tmpdir(), `afs-git-remote-${repoHash}`);
|
|
90
171
|
} else throw new Error("Either repoPath or remoteUrl must be provided");
|
|
91
|
-
if (options.repoPath && !options.remoteUrl) {
|
|
92
|
-
const { existsSync, mkdirSync } = __require("node:fs");
|
|
93
|
-
const { execSync } = __require("node:child_process");
|
|
94
|
-
if (!existsSync(repoPath)) mkdirSync(repoPath, { recursive: true });
|
|
95
|
-
if (!existsSync(join(repoPath, ".git"))) execSync("git init -b main", {
|
|
96
|
-
cwd: repoPath,
|
|
97
|
-
stdio: "ignore"
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
172
|
this.repoPath = repoPath;
|
|
101
173
|
this.name = options.name || repoName;
|
|
102
174
|
this.description = options.description;
|
|
@@ -118,6 +190,15 @@ var AFSGit = class AFSGit extends AFSBaseProvider {
|
|
|
118
190
|
*/
|
|
119
191
|
async initialize() {
|
|
120
192
|
const options = this.options;
|
|
193
|
+
if (options.repoPath && !options.remoteUrl) {
|
|
194
|
+
const { existsSync, mkdirSync } = await import("node:fs");
|
|
195
|
+
const { execSync } = await import("node:child_process");
|
|
196
|
+
if (!existsSync(this.repoPath)) mkdirSync(this.repoPath, { recursive: true });
|
|
197
|
+
if (!existsSync(join(this.repoPath, ".git"))) execSync("git init -b main", {
|
|
198
|
+
cwd: this.repoPath,
|
|
199
|
+
stdio: "ignore"
|
|
200
|
+
});
|
|
201
|
+
}
|
|
121
202
|
if (options.remoteUrl) {
|
|
122
203
|
const targetPath = options.repoPath ? isAbsolute(options.repoPath) ? options.repoPath : join(options.cwd || process.cwd(), options.repoPath) : this.repoPath;
|
|
123
204
|
if (!options.repoPath) this.isAutoCloned = true;
|
|
@@ -303,14 +384,15 @@ var AFSGit = class AFSGit extends AFSBaseProvider {
|
|
|
303
384
|
const worktreePath = this.worktrees.get(branch);
|
|
304
385
|
if (worktreePath) try {
|
|
305
386
|
const fullPath = join(worktreePath, filePath);
|
|
387
|
+
await this.assertWithinWorktree(fullPath, worktreePath);
|
|
306
388
|
const stats = await stat(fullPath);
|
|
307
389
|
if (stats.isDirectory()) {
|
|
308
390
|
const files = await readdir(fullPath);
|
|
309
391
|
const afsPath$2 = this.buildBranchPath(branch, filePath);
|
|
310
392
|
return this.buildEntry(afsPath$2, { meta: { childrenCount: files.length } });
|
|
311
393
|
}
|
|
312
|
-
const mimeType$1 =
|
|
313
|
-
const isBinary$1 =
|
|
394
|
+
const mimeType$1 = getMimeType(filePath);
|
|
395
|
+
const isBinary$1 = isBinaryFile(filePath);
|
|
314
396
|
let content$1;
|
|
315
397
|
const meta$1 = {
|
|
316
398
|
size: stats.size,
|
|
@@ -327,7 +409,9 @@ var AFSGit = class AFSGit extends AFSBaseProvider {
|
|
|
327
409
|
createdAt: stats.birthtime,
|
|
328
410
|
updatedAt: stats.mtime
|
|
329
411
|
});
|
|
330
|
-
} catch {
|
|
412
|
+
} catch (err) {
|
|
413
|
+
if (err instanceof Error && "code" in err && err.code === "AFS_PERMISSION_DENIED") throw err;
|
|
414
|
+
}
|
|
331
415
|
const objectType = await this.git.raw([
|
|
332
416
|
"cat-file",
|
|
333
417
|
"-t",
|
|
@@ -344,8 +428,8 @@ var AFSGit = class AFSGit extends AFSBaseProvider {
|
|
|
344
428
|
"-s",
|
|
345
429
|
`${branch}:${filePath}`
|
|
346
430
|
]).then((s) => Number.parseInt(s.trim(), 10));
|
|
347
|
-
const mimeType =
|
|
348
|
-
const isBinary =
|
|
431
|
+
const mimeType = getMimeType(filePath);
|
|
432
|
+
const isBinary = isBinaryFile(filePath);
|
|
349
433
|
let content;
|
|
350
434
|
const meta = {
|
|
351
435
|
size,
|
|
@@ -389,9 +473,10 @@ var AFSGit = class AFSGit extends AFSBaseProvider {
|
|
|
389
473
|
await this.ready();
|
|
390
474
|
const branch = this.decodeBranchName(ctx.params.branch);
|
|
391
475
|
const filePath = ctx.params.path;
|
|
392
|
-
const
|
|
476
|
+
const mode = ctx.options?.mode ?? "replace";
|
|
393
477
|
const worktreePath = await this.ensureWorktree(branch);
|
|
394
478
|
const fullPath = join(worktreePath, filePath);
|
|
479
|
+
await this.assertWithinWorktree(fullPath, worktreePath);
|
|
395
480
|
await mkdir(dirname(fullPath), { recursive: true });
|
|
396
481
|
if (payload.content !== void 0) {
|
|
397
482
|
let contentToWrite;
|
|
@@ -399,7 +484,7 @@ var AFSGit = class AFSGit extends AFSBaseProvider {
|
|
|
399
484
|
else contentToWrite = JSON.stringify(payload.content, null, 2);
|
|
400
485
|
await writeFile(fullPath, contentToWrite, {
|
|
401
486
|
encoding: "utf8",
|
|
402
|
-
flag: append ? "a" : "w"
|
|
487
|
+
flag: mode === "append" ? "a" : "w"
|
|
403
488
|
});
|
|
404
489
|
}
|
|
405
490
|
if (this.options.autoCommit) {
|
|
@@ -454,6 +539,7 @@ var AFSGit = class AFSGit extends AFSBaseProvider {
|
|
|
454
539
|
const recursive = ctx.options?.recursive ?? false;
|
|
455
540
|
const worktreePath = await this.ensureWorktree(branch);
|
|
456
541
|
const fullPath = join(worktreePath, filePath);
|
|
542
|
+
await this.assertWithinWorktree(fullPath, worktreePath);
|
|
457
543
|
let stats;
|
|
458
544
|
try {
|
|
459
545
|
stats = await stat(fullPath);
|
|
@@ -491,6 +577,8 @@ var AFSGit = class AFSGit extends AFSBaseProvider {
|
|
|
491
577
|
const worktreePath = await this.ensureWorktree(oldBranch);
|
|
492
578
|
const oldFullPath = join(worktreePath, oldFilePath);
|
|
493
579
|
const newFullPath = join(worktreePath, newFilePath);
|
|
580
|
+
await this.assertWithinWorktree(oldFullPath, worktreePath);
|
|
581
|
+
await this.assertWithinWorktree(newFullPath, worktreePath);
|
|
494
582
|
try {
|
|
495
583
|
await stat(oldFullPath);
|
|
496
584
|
} catch (error) {
|
|
@@ -1197,6 +1285,9 @@ var AFSGit = class AFSGit extends AFSBaseProvider {
|
|
|
1197
1285
|
/**
|
|
1198
1286
|
* Decode branch name (replace ~ with /)
|
|
1199
1287
|
*/
|
|
1288
|
+
async assertWithinWorktree(fullPath, worktreeRoot) {
|
|
1289
|
+
return assertPathWithinRoot(fullPath, worktreeRoot);
|
|
1290
|
+
}
|
|
1200
1291
|
decodeBranchName(encoded) {
|
|
1201
1292
|
return encoded.replace(/~/g, "/");
|
|
1202
1293
|
}
|
|
@@ -1397,54 +1488,6 @@ var AFSGit = class AFSGit extends AFSBaseProvider {
|
|
|
1397
1488
|
}
|
|
1398
1489
|
}
|
|
1399
1490
|
/**
|
|
1400
|
-
* Detect MIME type based on file extension
|
|
1401
|
-
*/
|
|
1402
|
-
getMimeType(filePath) {
|
|
1403
|
-
return {
|
|
1404
|
-
png: "image/png",
|
|
1405
|
-
jpg: "image/jpeg",
|
|
1406
|
-
jpeg: "image/jpeg",
|
|
1407
|
-
gif: "image/gif",
|
|
1408
|
-
bmp: "image/bmp",
|
|
1409
|
-
webp: "image/webp",
|
|
1410
|
-
svg: "image/svg+xml",
|
|
1411
|
-
ico: "image/x-icon",
|
|
1412
|
-
pdf: "application/pdf",
|
|
1413
|
-
txt: "text/plain",
|
|
1414
|
-
md: "text/markdown",
|
|
1415
|
-
js: "text/javascript",
|
|
1416
|
-
ts: "text/typescript",
|
|
1417
|
-
json: "application/json",
|
|
1418
|
-
html: "text/html",
|
|
1419
|
-
css: "text/css",
|
|
1420
|
-
xml: "text/xml"
|
|
1421
|
-
}[filePath.split(".").pop()?.toLowerCase() || ""] || "application/octet-stream";
|
|
1422
|
-
}
|
|
1423
|
-
/**
|
|
1424
|
-
* Check if file is likely binary based on extension
|
|
1425
|
-
*/
|
|
1426
|
-
isBinaryFile(filePath) {
|
|
1427
|
-
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
1428
|
-
return [
|
|
1429
|
-
"png",
|
|
1430
|
-
"jpg",
|
|
1431
|
-
"jpeg",
|
|
1432
|
-
"gif",
|
|
1433
|
-
"bmp",
|
|
1434
|
-
"webp",
|
|
1435
|
-
"ico",
|
|
1436
|
-
"pdf",
|
|
1437
|
-
"zip",
|
|
1438
|
-
"tar",
|
|
1439
|
-
"gz",
|
|
1440
|
-
"exe",
|
|
1441
|
-
"dll",
|
|
1442
|
-
"so",
|
|
1443
|
-
"dylib",
|
|
1444
|
-
"wasm"
|
|
1445
|
-
].includes(ext || "");
|
|
1446
|
-
}
|
|
1447
|
-
/**
|
|
1448
1491
|
* Fetch latest changes from remote
|
|
1449
1492
|
*/
|
|
1450
1493
|
async fetch() {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["afsPath","mimeType","isBinary","content","meta"],"sources":["../src/index.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { mkdir, readdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { basename, dirname, isAbsolute, join } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport {\n type AFSAccessMode,\n type AFSDeleteOptions,\n type AFSEntry,\n type AFSEntryMetadata,\n type AFSExecResult,\n type AFSExplainOptions,\n type AFSExplainResult,\n type AFSListResult,\n type AFSModuleClass,\n type AFSModuleLoadParams,\n AFSNotFoundError,\n type AFSRenameOptions,\n type AFSSearchOptions,\n type AFSStatResult,\n type AFSWriteEntryPayload,\n type CapabilitiesManifest,\n type ProviderManifest,\n} from \"@aigne/afs\";\nimport {\n Actions,\n AFSBaseProvider,\n Delete,\n Explain,\n List,\n Meta,\n Read,\n Rename,\n type RouteContext,\n Search,\n Stat,\n Write,\n} from \"@aigne/afs/provider\";\nimport { camelize, optionalize, zodParse } from \"@aigne/afs/utils/zod\";\nimport { type SimpleGit, simpleGit } from \"simple-git\";\nimport { z } from \"zod\";\n\nconst LIST_MAX_LIMIT = 1000;\n\nconst execFileAsync = promisify(execFile);\n\nexport interface AFSGitOptions {\n name?: string;\n /**\n * Local path to git repository.\n * If remoteUrl is provided and repoPath doesn't exist, will clone to this path.\n * If remoteUrl is provided and repoPath is not specified, clones to temp directory.\n */\n repoPath?: string;\n /**\n * Remote repository URL (https or git protocol).\n * If provided, will clone the repository if repoPath doesn't exist.\n * Examples:\n * - https://github.com/user/repo.git\n * - git@github.com:user/repo.git\n */\n remoteUrl?: string;\n description?: string;\n /**\n * List of branches to expose/access.\n * Also used for clone optimization when cloning from remoteUrl:\n * - Single branch (e.g., ['main']): Uses --single-branch for faster clone\n * - Multiple branches: Clones all branches, filters access to specified ones\n * - Not specified: All branches are accessible\n */\n branches?: string[];\n /**\n * Access mode for this module.\n * - \"readonly\": Only read operations are allowed, uses git commands (no worktree)\n * - \"readwrite\": All operations are allowed, creates worktrees as needed\n * @default \"readonly\"\n */\n accessMode?: AFSAccessMode;\n /**\n * Automatically commit changes after write operations\n * @default false\n */\n autoCommit?: boolean;\n /**\n * Author information for commits when autoCommit is enabled\n */\n commitAuthor?: {\n name: string;\n email: string;\n };\n /**\n * Clone depth for shallow clone (only used when cloning from remoteUrl)\n * @default 1\n */\n depth?: number;\n /**\n * Automatically clean up cloned repository on cleanup()\n * Only applies when repository was auto-cloned to temp directory\n * @default true\n */\n autoCleanup?: boolean;\n /**\n * Git clone options (only used when cloning from remoteUrl)\n */\n cloneOptions?: {\n /**\n * Authentication credentials for private repositories\n */\n auth?: {\n username?: string;\n password?: string;\n };\n };\n}\n\nconst afsGitOptionsSchema = camelize(\n z\n .object({\n name: optionalize(z.string()),\n repoPath: optionalize(z.string().describe(\"The path to the git repository\")),\n remoteUrl: optionalize(z.string().describe(\"Remote repository URL (https or git protocol)\")),\n description: optionalize(z.string().describe(\"A description of the repository\")),\n branches: optionalize(z.array(z.string()).describe(\"List of branches to expose\")),\n accessMode: optionalize(\n z.enum([\"readonly\", \"readwrite\"]).describe(\"Access mode for this module\"),\n ),\n autoCommit: optionalize(\n z.boolean().describe(\"Automatically commit changes after write operations\"),\n ),\n commitAuthor: optionalize(\n z.object({\n name: z.string(),\n email: z.string(),\n }),\n ),\n depth: optionalize(z.number().describe(\"Clone depth for shallow clone\")),\n autoCleanup: optionalize(\n z.boolean().describe(\"Automatically clean up cloned repository on cleanup()\"),\n ),\n cloneOptions: optionalize(\n z.object({\n auth: optionalize(\n z.object({\n username: optionalize(z.string()),\n password: optionalize(z.string()),\n }),\n ),\n }),\n ),\n })\n .refine((data) => data.repoPath || data.remoteUrl, {\n message: \"Either repoPath or remoteUrl must be provided\",\n }),\n);\n\nexport class AFSGit extends AFSBaseProvider {\n static schema() {\n return afsGitOptionsSchema;\n }\n\n static manifest(): ProviderManifest {\n return {\n name: \"git\",\n description:\n \"Git repository browser with branch-based access.\\n- Browse branches, read files at any ref, search across repository\\n- Exec actions (readwrite): `diff`, `create-branch`, `commit`, `merge`\\n- Virtual `.log/` tree exposes commit history per branch\\n- Path structure: `/{branch}/{file-path}` (branch `/` encoded as `~`)\",\n uriTemplate: \"git://{localPath+}\",\n category: \"version-control\",\n schema: z.object({\n localPath: z.string(),\n branch: z.string().optional(),\n remoteUrl: z.string().optional(),\n }),\n tags: [\"git\", \"version-control\"],\n };\n }\n\n static async load({ basePath, config }: AFSModuleLoadParams = {}) {\n const valid = await AFSGit.schema().parseAsync(config);\n const instance = new AFSGit({ ...valid, cwd: basePath });\n await instance.ready();\n return instance;\n }\n\n readonly name: string;\n readonly description?: string;\n readonly accessMode: AFSAccessMode;\n\n private initPromise: Promise<void>;\n private git: SimpleGit;\n private tempBase: string;\n private worktrees: Map<string, string> = new Map();\n private repoHash: string;\n private isAutoCloned = false;\n private clonedPath?: string;\n private repoPath: string;\n\n constructor(\n public options: AFSGitOptions & {\n cwd?: string;\n localPath?: string;\n branch?: string;\n uri?: string;\n },\n ) {\n super();\n\n // Normalize registry-passed template vars\n if ((options as any).localPath && !options.repoPath) {\n options.repoPath = (options as any).localPath;\n }\n if ((options as any).branch && !options.branches) {\n options.branches = [(options as any).branch];\n }\n\n zodParse(afsGitOptionsSchema, options);\n\n // Synchronously determine repoPath to initialize name\n let repoPath: string;\n let repoName: string;\n\n if (options.repoPath) {\n // Use provided repoPath\n repoPath = isAbsolute(options.repoPath)\n ? options.repoPath\n : join(options.cwd || process.cwd(), options.repoPath);\n repoName = basename(repoPath);\n } else if (options.remoteUrl) {\n // Extract repo name from URL for temporary name\n const urlParts = options.remoteUrl.split(\"/\");\n const lastPart = urlParts[urlParts.length - 1];\n repoName = lastPart?.replace(/\\.git$/, \"\") || \"git\";\n\n // Will be updated during async init, use temp path for now\n const repoHash = createHash(\"md5\").update(options.remoteUrl).digest(\"hex\").substring(0, 8);\n repoPath = join(tmpdir(), `afs-git-remote-${repoHash}`);\n } else {\n // This should never happen due to schema validation\n throw new Error(\"Either repoPath or remoteUrl must be provided\");\n }\n\n // Auto-create local repository if it doesn't exist (skip for remoteUrl — will be cloned)\n if (options.repoPath && !options.remoteUrl) {\n const { existsSync, mkdirSync } = require(\"node:fs\") as typeof import(\"node:fs\");\n const { execSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n if (!existsSync(repoPath)) {\n mkdirSync(repoPath, { recursive: true });\n }\n if (!existsSync(join(repoPath, \".git\"))) {\n execSync(\"git init -b main\", { cwd: repoPath, stdio: \"ignore\" });\n }\n }\n\n // Initialize basic properties immediately\n this.repoPath = repoPath;\n this.name = options.name || repoName;\n this.description = options.description;\n this.accessMode = options.accessMode ?? \"readonly\";\n\n // Calculate hash for temp directories\n this.repoHash = createHash(\"md5\").update(repoPath).digest(\"hex\").substring(0, 8);\n this.tempBase = join(tmpdir(), `afs-git-${this.repoHash}`);\n\n // Note: git and other properties will be initialized in initialize() after cloning\n // We need to delay simpleGit() initialization until the directory exists\n this.git = null as any; // Will be set in initialize()\n\n // Start async initialization (cloning if needed)\n this.initPromise = this.initialize();\n }\n\n /**\n * Wait for async initialization to complete\n */\n async ready(): Promise<void> {\n await this.initPromise;\n }\n\n /**\n * Async initialization logic (runs in constructor)\n * Handles cloning remote repositories if needed\n */\n private async initialize(): Promise<void> {\n const options = this.options;\n\n // If remoteUrl is provided, handle cloning\n if (options.remoteUrl) {\n const targetPath = options.repoPath\n ? isAbsolute(options.repoPath)\n ? options.repoPath\n : join(options.cwd || process.cwd(), options.repoPath)\n : this.repoPath; // Use temp path set in constructor\n\n // Mark as auto-cloned if we're using temp directory\n if (!options.repoPath) {\n this.isAutoCloned = true;\n }\n\n // Check if targetPath exists and is a valid git repository\n const exists = await stat(targetPath)\n .then(() => true)\n .catch(() => false);\n\n let needsClone = !exists;\n\n // If directory exists but is not a valid git repo, clean it up and re-clone\n if (exists) {\n const tempGit = simpleGit(targetPath);\n const isValidRepo = await tempGit.checkIsRepo().catch(() => false);\n if (!isValidRepo) {\n // Remove invalid directory and re-clone\n await rm(targetPath, { recursive: true, force: true });\n needsClone = true;\n }\n }\n\n if (needsClone) {\n // Determine if single-branch optimization should be used\n const singleBranch = options.branches?.length === 1 ? options.branches[0] : undefined;\n\n await AFSGit.cloneRepository(options.remoteUrl, targetPath, {\n depth: options.depth ?? 1,\n branch: singleBranch,\n auth: options.cloneOptions?.auth,\n });\n }\n\n // Update properties if targetPath differs from constructor initialization\n if (targetPath !== this.repoPath) {\n this.repoPath = targetPath;\n this.repoHash = createHash(\"md5\").update(targetPath).digest(\"hex\").substring(0, 8);\n this.tempBase = join(tmpdir(), `afs-git-${this.repoHash}`);\n }\n\n this.clonedPath = this.isAutoCloned ? targetPath : undefined;\n }\n\n // Now that the directory exists (either it was there or we cloned it), initialize git\n this.git = simpleGit(this.repoPath);\n\n // Validate that the directory is actually a git repository\n const isRepo = await this.git.checkIsRepo();\n if (!isRepo) {\n throw new Error(`Not a git repository: ${this.repoPath}`);\n }\n }\n\n /**\n * Clone a remote repository to local path\n */\n private static async cloneRepository(\n remoteUrl: string,\n targetPath: string,\n options: {\n depth?: number;\n branch?: string;\n auth?: { username?: string; password?: string };\n } = {},\n ): Promise<void> {\n const git = simpleGit();\n\n // Build clone options\n const cloneArgs: string[] = [];\n\n if (options.depth) {\n cloneArgs.push(\"--depth\", options.depth.toString());\n }\n\n if (options.branch) {\n cloneArgs.push(\"--branch\", options.branch, \"--single-branch\");\n }\n\n // Handle authentication in URL if provided\n let cloneUrl = remoteUrl;\n if (options.auth?.username && options.auth?.password) {\n // Insert credentials into HTTPS URL\n if (remoteUrl.startsWith(\"https://\")) {\n const url = new URL(remoteUrl);\n url.username = encodeURIComponent(options.auth.username);\n url.password = encodeURIComponent(options.auth.password);\n cloneUrl = url.toString();\n }\n }\n\n await git.clone(cloneUrl, targetPath, cloneArgs);\n }\n\n // ========== Route Handlers ==========\n\n /**\n * List root (branches)\n * Note: list() returns only children (branches), never the path itself (per new semantics)\n */\n @List(\"/\", { handleDepth: true })\n async listRootHandler(ctx: RouteContext): Promise<AFSListResult & { noExpand?: string[] }> {\n await this.ready();\n\n const options = ctx.options as { limit?: number; maxDepth?: number };\n const maxDepth = options?.maxDepth ?? 1;\n const limit = Math.min(options?.limit || LIST_MAX_LIMIT, LIST_MAX_LIMIT);\n\n // maxDepth: 0 means return no children\n if (maxDepth === 0) {\n return { data: [] };\n }\n\n const branches = await this.getBranches();\n const entries: AFSEntry[] = [];\n\n for (const name of branches) {\n if (entries.length >= limit) break;\n\n const encodedPath = this.buildBranchPath(name);\n\n // Get children count for this branch\n const branchChildrenCount = await this.getChildrenCount(name, \"\");\n\n entries.push(\n this.buildEntry(encodedPath, {\n meta: { kind: \"git:branch\", childrenCount: branchChildrenCount },\n }),\n );\n\n // If maxDepth > 1, also list contents of each branch\n if (maxDepth > 1) {\n const branchResult = await this.listWithGitLsTree(name, \"\", {\n maxDepth: maxDepth - 1,\n limit: limit - entries.length,\n });\n entries.push(...branchResult.data);\n }\n }\n\n return { data: entries };\n }\n\n /**\n * List branch root (matches /main, /develop, etc.)\n */\n @List(\"/:branch\", { handleDepth: true })\n async listBranchRootHandler(\n ctx: RouteContext<{ branch: string }>,\n ): Promise<AFSListResult & { noExpand?: string[] }> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n\n return this.listWithGitLsTree(branch, \"\", ctx.options as { maxDepth?: number; limit?: number });\n }\n\n /**\n * List files in branch with subpath (matches /main/src, /main/src/foo, etc.)\n */\n @List(\"/:branch/:path+\", { handleDepth: true })\n async listBranchHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n ): Promise<AFSListResult & { noExpand?: string[] }> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const filePath = ctx.params.path;\n\n return this.listWithGitLsTree(\n branch,\n filePath,\n ctx.options as { maxDepth?: number; limit?: number },\n );\n }\n\n // ========== Meta Handlers ==========\n // These handlers provide metadata access for all paths via .meta suffix\n\n /**\n * Read root metadata (introspection only, read-only)\n */\n @Meta(\"/\")\n async readRootMetaHandler(_ctx: RouteContext): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branches = await this.getBranches();\n return this.buildEntry(\"/.meta\", {\n meta: { childrenCount: branches.length, type: \"root\" },\n });\n }\n\n /**\n * Read branch root metadata (introspection only, read-only)\n */\n @Meta(\"/:branch\")\n async readBranchMetaHandler(\n ctx: RouteContext<{ branch: string }>,\n ): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const childrenCount = await this.getChildrenCount(branch, \"\");\n const branchPath = `/${this.encodeBranchName(branch)}`;\n const metaPath = `${branchPath}/.meta`;\n const lastCommit = await this.getLastCommit(branch);\n\n return this.buildEntry(metaPath, {\n meta: { childrenCount, type: \"branch\", lastCommit },\n });\n }\n\n /**\n * Read file or directory metadata in branch (introspection only, read-only)\n */\n @Meta(\"/:branch/:path+\")\n async readPathMetaHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n ): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const filePath = ctx.params.path;\n\n // Check if the path exists\n const objectType = await this.git\n .raw([\"cat-file\", \"-t\", `${branch}:${filePath}`])\n .then((t) => t.trim())\n .catch(() => null);\n\n if (objectType === null) {\n throw new AFSNotFoundError(this.buildBranchPath(branch, filePath));\n }\n\n const isDir = objectType === \"tree\";\n const metaPath = `/${this.encodeBranchName(branch)}/${filePath}/.meta`;\n\n let childrenCount: number | undefined;\n if (isDir) {\n childrenCount = await this.getChildrenCount(branch, filePath);\n }\n\n return this.buildEntry(metaPath, {\n meta: {\n childrenCount,\n type: isDir ? \"directory\" : \"file\",\n gitObjectType: objectType,\n },\n });\n }\n\n // ========== Regular Read Handlers ==========\n\n /**\n * Read root\n */\n @Read(\"/\")\n async readRootHandler(_ctx: RouteContext): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branches = await this.getBranches();\n return this.buildEntry(\"/\", {\n meta: { childrenCount: branches.length },\n });\n }\n\n /**\n * Read branch root\n */\n @Read(\"/:branch\")\n async readBranchRootHandler(\n ctx: RouteContext<{ branch: string }>,\n ): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const branchPath = this.buildBranchPath(branch);\n const childrenCount = await this.getChildrenCount(branch, \"\");\n const lastCommit = await this.getLastCommit(branch);\n return this.buildEntry(branchPath, {\n meta: { childrenCount, lastCommit },\n });\n }\n\n /**\n * Read file or directory in branch\n */\n @Read(\"/:branch/:path+\")\n async readBranchHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n ): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const filePath = ctx.params.path;\n\n // Check if there's an active worktree for this branch - read from there first\n // This handles files that were written but may be in a different state than git index\n const worktreePath = this.worktrees.get(branch);\n if (worktreePath) {\n try {\n const fullPath = join(worktreePath, filePath);\n const stats = await stat(fullPath);\n\n if (stats.isDirectory()) {\n // It's a directory - count children\n const files = await readdir(fullPath);\n const afsPath = this.buildBranchPath(branch, filePath);\n return this.buildEntry(afsPath, {\n meta: { childrenCount: files.length },\n });\n }\n\n // It's a file - read content\n const mimeType = this.getMimeType(filePath);\n const isBinary = this.isBinaryFile(filePath);\n\n let content: string;\n const meta: AFSEntryMetadata = {\n size: stats.size,\n mimeType,\n };\n\n if (isBinary) {\n const buffer = await readFile(fullPath);\n content = buffer.toString(\"base64\");\n meta.contentType = \"base64\";\n } else {\n content = await readFile(fullPath, \"utf8\");\n }\n\n const afsPath = this.buildBranchPath(branch, filePath);\n return this.buildEntry(afsPath, {\n content,\n meta,\n createdAt: stats.birthtime,\n updatedAt: stats.mtime,\n });\n } catch {\n // File doesn't exist in worktree, fall through to git\n }\n }\n\n // Read from git repository\n // Check if path is a blob (file) or tree (directory)\n const objectType = await this.git\n .raw([\"cat-file\", \"-t\", `${branch}:${filePath}`])\n .then((t) => t.trim())\n .catch(() => null);\n\n if (objectType === null) {\n // Path doesn't exist in git\n throw new AFSNotFoundError(this.buildBranchPath(branch, filePath));\n }\n\n if (objectType === \"tree\") {\n // It's a directory\n const afsPath = this.buildBranchPath(branch, filePath);\n const childrenCount = await this.getChildrenCount(branch, filePath);\n return this.buildEntry(afsPath, {\n meta: { childrenCount },\n });\n }\n\n // It's a file, get content\n const size = await this.git\n .raw([\"cat-file\", \"-s\", `${branch}:${filePath}`])\n .then((s) => Number.parseInt(s.trim(), 10));\n\n // Determine mimeType based on file extension\n const mimeType = this.getMimeType(filePath);\n const isBinary = this.isBinaryFile(filePath);\n\n let content: string;\n const meta: AFSEntryMetadata = {\n size,\n mimeType,\n };\n\n if (isBinary) {\n // For binary files, use execFileAsync to get raw buffer\n const { stdout } = await execFileAsync(\"git\", [\"cat-file\", \"-p\", `${branch}:${filePath}`], {\n cwd: this.options.repoPath,\n encoding: \"buffer\",\n maxBuffer: 10 * 1024 * 1024, // 10MB max\n });\n // Store only base64 string without data URL prefix\n content = (stdout as Buffer).toString(\"base64\");\n // Mark content as base64 in metadata\n meta.contentType = \"base64\";\n } else {\n // For text files, use git.show\n content = await this.git.show([`${branch}:${filePath}`]);\n }\n\n const afsPath = this.buildBranchPath(branch, filePath);\n return this.buildEntry(afsPath, {\n content,\n meta,\n });\n }\n\n /**\n * Write to root is not allowed\n */\n @Write(\"/\")\n async writeRootHandler(): Promise<never> {\n throw new Error(\"Cannot write to root\");\n }\n\n /**\n * Write to branch root is not allowed\n */\n @Write(\"/:branch\")\n async writeBranchRootHandler(): Promise<never> {\n throw new Error(\"Cannot write to branch root\");\n }\n\n /**\n * Write file in branch\n */\n @Write(\"/:branch/:path+\")\n async writeHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n payload: AFSWriteEntryPayload,\n ): Promise<{ data: AFSEntry }> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n const filePath = ctx.params.path;\n const options = ctx.options as { append?: boolean };\n const append = options?.append ?? false;\n\n // Create worktree for write operations\n const worktreePath = await this.ensureWorktree(branch);\n const fullPath = join(worktreePath, filePath);\n\n // Ensure parent directory exists\n const parentDir = dirname(fullPath);\n await mkdir(parentDir, { recursive: true });\n\n // Write content\n if (payload.content !== undefined) {\n let contentToWrite: string;\n if (typeof payload.content === \"string\") {\n contentToWrite = payload.content;\n } else {\n contentToWrite = JSON.stringify(payload.content, null, 2);\n }\n await writeFile(fullPath, contentToWrite, { encoding: \"utf8\", flag: append ? \"a\" : \"w\" });\n }\n\n // Auto commit if enabled\n if (this.options.autoCommit) {\n const gitInstance = simpleGit(worktreePath);\n await gitInstance.add(filePath);\n\n if (this.options.commitAuthor) {\n await gitInstance.addConfig(\n \"user.name\",\n this.options.commitAuthor.name,\n undefined,\n \"local\",\n );\n await gitInstance.addConfig(\n \"user.email\",\n this.options.commitAuthor.email,\n undefined,\n \"local\",\n );\n }\n\n await gitInstance.commit(`Update ${filePath}`);\n }\n\n // Get file stats\n const stats = await stat(fullPath);\n\n const afsPath = this.buildBranchPath(branch, filePath);\n const writtenEntry: AFSEntry = {\n id: afsPath,\n path: afsPath,\n content: payload.content,\n summary: payload.summary,\n createdAt: stats.birthtime,\n updatedAt: stats.mtime,\n meta: {\n ...payload.meta,\n size: stats.size,\n } as AFSEntryMetadata,\n userId: payload.userId,\n sessionId: payload.sessionId,\n linkTo: payload.linkTo,\n };\n\n return { data: writtenEntry };\n }\n\n /**\n * Delete root is not allowed\n */\n @Delete(\"/\")\n async deleteRootHandler(): Promise<never> {\n throw new Error(\"Cannot delete root\");\n }\n\n /**\n * Delete branch root is not allowed\n */\n @Delete(\"/:branch\")\n async deleteBranchRootHandler(ctx: RouteContext<{ branch: string }>): Promise<never> {\n await this.ready();\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n throw new Error(\"Cannot delete branch root\");\n }\n\n /**\n * Delete file in branch\n */\n @Delete(\"/:branch/:path+\")\n async deleteHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n ): Promise<{ message: string }> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n const filePath = ctx.params.path;\n const options = ctx.options as AFSDeleteOptions | undefined;\n const recursive = options?.recursive ?? false;\n\n // Create worktree for delete operations\n const worktreePath = await this.ensureWorktree(branch);\n const fullPath = join(worktreePath, filePath);\n\n let stats: Awaited<ReturnType<typeof stat>>;\n try {\n stats = await stat(fullPath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n throw new AFSNotFoundError(this.buildBranchPath(branch, filePath));\n }\n throw error;\n }\n\n if (stats.isDirectory() && !recursive) {\n throw new Error(\n `Cannot delete directory '/${ctx.params.branch}/${filePath}' without recursive option. Set recursive: true to delete directories.`,\n );\n }\n\n await rm(fullPath, { recursive, force: true });\n\n // Auto commit if enabled\n if (this.options.autoCommit) {\n const gitInstance = simpleGit(worktreePath);\n await gitInstance.add(filePath);\n\n if (this.options.commitAuthor) {\n await gitInstance.addConfig(\n \"user.name\",\n this.options.commitAuthor.name,\n undefined,\n \"local\",\n );\n await gitInstance.addConfig(\n \"user.email\",\n this.options.commitAuthor.email,\n undefined,\n \"local\",\n );\n }\n\n await gitInstance.commit(`Delete ${filePath}`);\n }\n\n return { message: `Successfully deleted: /${ctx.params.branch}/${filePath}` };\n }\n\n /**\n * Rename file in branch\n */\n @Rename(\"/:branch/:path+\")\n async renameHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n newPath: string,\n ): Promise<{ message: string }> {\n await this.ready();\n\n const oldBranch = this.decodeBranchName(ctx.params.branch);\n const oldFilePath = ctx.params.path;\n\n // Parse new path\n const { branch: newBranch, filePath: newFilePath } = this.parsePath(newPath);\n const options = ctx.options as AFSRenameOptions | undefined;\n const overwrite = options?.overwrite ?? false;\n\n if (!newBranch || !newFilePath) {\n throw new Error(\"Cannot rename to root or branch root\");\n }\n\n if (oldBranch !== newBranch) {\n throw new Error(\"Cannot rename across branches\");\n }\n\n // Create worktree for rename operations\n const worktreePath = await this.ensureWorktree(oldBranch);\n const oldFullPath = join(worktreePath, oldFilePath);\n const newFullPath = join(worktreePath, newFilePath);\n\n // Check if source exists\n try {\n await stat(oldFullPath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n throw new AFSNotFoundError(this.buildBranchPath(oldBranch, oldFilePath));\n }\n throw error;\n }\n\n // Check if destination exists\n try {\n await stat(newFullPath);\n if (!overwrite) {\n throw new Error(\n `Destination '${newPath}' already exists. Set overwrite: true to replace it.`,\n );\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw error;\n }\n }\n\n // Ensure parent directory exists\n const newParentDir = dirname(newFullPath);\n await mkdir(newParentDir, { recursive: true });\n\n // Perform rename\n await rename(oldFullPath, newFullPath);\n\n // Auto commit if enabled\n if (this.options.autoCommit) {\n const gitInstance = simpleGit(worktreePath);\n await gitInstance.add([oldFilePath, newFilePath]);\n\n if (this.options.commitAuthor) {\n await gitInstance.addConfig(\n \"user.name\",\n this.options.commitAuthor.name,\n undefined,\n \"local\",\n );\n await gitInstance.addConfig(\n \"user.email\",\n this.options.commitAuthor.email,\n undefined,\n \"local\",\n );\n }\n\n await gitInstance.commit(`Rename ${oldFilePath} to ${newFilePath}`);\n }\n\n return {\n message: `Successfully renamed '/${ctx.params.branch}/${oldFilePath}' to '${newPath}'`,\n };\n }\n\n /**\n * Search files in branch root\n */\n @Search(\"/:branch\")\n async searchBranchRootHandler(\n ctx: RouteContext<{ branch: string }>,\n query: string,\n options?: AFSSearchOptions,\n ): Promise<{ data: AFSEntry[]; message?: string }> {\n return this.searchInBranch(ctx.params.branch, \"\", query, options);\n }\n\n /**\n * Search files in branch path\n */\n @Search(\"/:branch/:path+\")\n async searchHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n query: string,\n options?: AFSSearchOptions,\n ): Promise<{ data: AFSEntry[]; message?: string }> {\n return this.searchInBranch(ctx.params.branch, ctx.params.path, query, options);\n }\n\n /**\n * Internal search implementation\n */\n private async searchInBranch(\n encodedBranch: string,\n filePath: string,\n query: string,\n options?: AFSSearchOptions,\n ): Promise<{ data: AFSEntry[]; message?: string }> {\n await this.ready();\n\n const branch = this.decodeBranchName(encodedBranch);\n const limit = Math.min(options?.limit || LIST_MAX_LIMIT, LIST_MAX_LIMIT);\n\n try {\n // Use git grep for searching (no worktree needed)\n const args = [\"grep\", \"-n\", \"-I\"]; // -n for line numbers, -I to skip binary files\n\n if (options?.caseSensitive === false) {\n args.push(\"-i\");\n }\n\n args.push(query, branch);\n\n // Add path filter if specified\n if (filePath) {\n args.push(\"--\", filePath);\n }\n\n const output = await this.git.raw(args);\n const lines = output.split(\"\\n\").filter((line) => line.trim());\n\n const entries: AFSEntry[] = [];\n const processedFiles = new Set<string>();\n\n for (const line of lines) {\n // Format when searching in branch: branch:path:linenum:content\n // Try the format with branch prefix first\n let matchPath: string;\n let lineNum: string;\n let content: string;\n\n const matchWithBranch = line.match(/^[^:]+:([^:]+):(\\d+):(.+)$/);\n if (matchWithBranch) {\n matchPath = matchWithBranch[1]!;\n lineNum = matchWithBranch[2]!;\n content = matchWithBranch[3]!;\n } else {\n // Try format without branch: path:linenum:content\n const matchNoBranch = line.match(/^([^:]+):(\\d+):(.+)$/);\n if (!matchNoBranch) continue;\n matchPath = matchNoBranch[1]!;\n lineNum = matchNoBranch[2]!;\n content = matchNoBranch[3]!;\n }\n\n const afsPath = this.buildBranchPath(branch, matchPath);\n\n if (processedFiles.has(afsPath)) continue;\n processedFiles.add(afsPath);\n\n const entry = this.buildEntry(afsPath);\n entry.summary = `Line ${lineNum}: ${content}`;\n entries.push(entry);\n\n if (entries.length >= limit) {\n break;\n }\n }\n\n return {\n data: entries,\n message: entries.length >= limit ? `Results truncated to limit ${limit}` : undefined,\n };\n } catch (error) {\n // git grep returns exit code 1 if no matches found\n if ((error as Error).message.includes(\"did not match any file(s)\")) {\n return { data: [] };\n }\n return { data: [], message: (error as Error).message };\n }\n }\n\n /**\n * Stat root\n */\n @Stat(\"/\")\n async statRootHandler(_ctx: RouteContext): Promise<AFSStatResult> {\n const entry = await this.readRootHandler(_ctx);\n if (!entry) {\n return { data: undefined };\n }\n // Return entry without content\n const { content: _content, ...rest } = entry;\n return { data: rest };\n }\n\n /**\n * Stat branch root\n */\n @Stat(\"/:branch\")\n async statBranchRootHandler(ctx: RouteContext<{ branch: string }>): Promise<AFSStatResult> {\n const entry = await this.readBranchRootHandler(ctx);\n if (!entry) {\n return { data: undefined };\n }\n // Return entry without content\n const { content: _content, ...rest } = entry;\n return { data: rest };\n }\n\n /**\n * Stat file or directory in branch\n */\n @Stat(\"/:branch/:path+\")\n async statHandler(ctx: RouteContext<{ branch: string; path: string }>): Promise<AFSStatResult> {\n const entry = await this.readBranchHandler(ctx);\n if (!entry) {\n return { data: undefined };\n }\n // Return entry without content\n const { content: _content, ...rest } = entry;\n return { data: rest };\n }\n\n // ========== Explain Handlers ==========\n\n /**\n * Explain root → repo info, branch list, default branch\n */\n @Explain(\"/\")\n async explainRootHandler(_ctx: RouteContext): Promise<AFSExplainResult> {\n await this.ready();\n\n const format = (_ctx.options as AFSExplainOptions)?.format || \"markdown\";\n const branches = await this.getBranches();\n const currentBranch = await this.git.revparse([\"--abbrev-ref\", \"HEAD\"]).then((b) => b.trim());\n\n let remoteUrl: string | undefined;\n try {\n remoteUrl = await this.git.remote([\"get-url\", \"origin\"]).then((u) => u?.trim());\n } catch {\n // No remote configured\n }\n\n const lines: string[] = [];\n lines.push(\"# Git Repository\");\n lines.push(\"\");\n lines.push(`**Provider:** ${this.name}`);\n if (this.description) {\n lines.push(`**Description:** ${this.description}`);\n }\n lines.push(`**Default Branch:** ${currentBranch}`);\n if (remoteUrl) {\n lines.push(`**Remote:** ${remoteUrl}`);\n }\n lines.push(`**Branches:** ${branches.length}`);\n lines.push(\"\");\n lines.push(\"## Branches\");\n lines.push(\"\");\n for (const branch of branches) {\n lines.push(`- ${branch}`);\n }\n\n return { content: lines.join(\"\\n\"), format };\n }\n\n /**\n * Explain branch → branch name, HEAD commit, file count\n */\n @Explain(\"/:branch\")\n async explainBranchHandler(ctx: RouteContext<{ branch: string }>): Promise<AFSExplainResult> {\n await this.ready();\n\n const format = (ctx.options as AFSExplainOptions)?.format || \"markdown\";\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n\n const lastCommit = await this.getLastCommit(branch);\n const fileCount = await this.getTreeFileCount(branch, \"\");\n\n const lines: string[] = [];\n lines.push(`# Branch: ${branch}`);\n lines.push(\"\");\n lines.push(`**HEAD Commit:** ${lastCommit.shortHash} - ${lastCommit.message}`);\n lines.push(`**Author:** ${lastCommit.author}`);\n lines.push(`**Date:** ${lastCommit.date}`);\n lines.push(`**Files:** ${fileCount} entries in tree`);\n\n return { content: lines.join(\"\\n\"), format };\n }\n\n /**\n * Explain file or directory → path, size, last modified commit\n */\n @Explain(\"/:branch/:path+\")\n async explainPathHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n ): Promise<AFSExplainResult> {\n await this.ready();\n\n const format = (ctx.options as AFSExplainOptions)?.format || \"markdown\";\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const filePath = ctx.params.path;\n\n const objectType = await this.git\n .raw([\"cat-file\", \"-t\", `${branch}:${filePath}`])\n .then((t) => t.trim())\n .catch(() => null);\n\n if (objectType === null) {\n throw new AFSNotFoundError(this.buildBranchPath(branch, filePath));\n }\n\n const isDir = objectType === \"tree\";\n const lines: string[] = [];\n\n lines.push(`# ${basename(filePath)}`);\n lines.push(\"\");\n lines.push(`**Path:** ${filePath}`);\n lines.push(`**Type:** ${isDir ? \"directory\" : \"file\"}`);\n\n if (!isDir) {\n const size = await this.git\n .raw([\"cat-file\", \"-s\", `${branch}:${filePath}`])\n .then((s) => Number.parseInt(s.trim(), 10));\n lines.push(`**Size:** ${size} bytes`);\n }\n\n // Get last commit that modified this path\n try {\n const logOutput = await this.git.raw([\n \"log\",\n \"-1\",\n \"--format=%H%n%h%n%an%n%aI%n%s\",\n branch,\n \"--\",\n filePath,\n ]);\n const logLines = logOutput.trim().split(\"\\n\");\n if (logLines.length >= 5) {\n lines.push(\"\");\n lines.push(\"## Last Modified\");\n lines.push(`**Commit:** ${logLines[1]} - ${logLines[4]}`);\n lines.push(`**Author:** ${logLines[2]}`);\n lines.push(`**Date:** ${logLines[3]}`);\n }\n } catch {\n // Ignore log errors\n }\n\n return { content: lines.join(\"\\n\"), format };\n }\n\n // ========== Capabilities ==========\n\n @Read(\"/.meta/.capabilities\")\n async readCapabilitiesHandler(_ctx: RouteContext): Promise<AFSEntry | undefined> {\n const operations = [\"list\", \"read\", \"stat\", \"explain\", \"search\"];\n if (this.accessMode === \"readwrite\") {\n operations.push(\"write\", \"delete\", \"rename\");\n }\n\n const actionCatalogs: CapabilitiesManifest[\"actions\"] = [];\n\n // diff is available in both modes conceptually, but exec() enforces readwrite\n // Include all actions in readwrite mode\n if (this.accessMode === \"readwrite\") {\n actionCatalogs.push({\n description: \"Git workflow actions\",\n catalog: [\n {\n name: \"diff\",\n description: \"Compare two branches or refs\",\n inputSchema: {\n type: \"object\",\n properties: {\n from: { type: \"string\", description: \"Source ref\" },\n to: { type: \"string\", description: \"Target ref\" },\n path: { type: \"string\", description: \"Optional path filter\" },\n },\n required: [\"from\", \"to\"],\n },\n },\n {\n name: \"create-branch\",\n description: \"Create a new branch\",\n inputSchema: {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"New branch name\" },\n from: { type: \"string\", description: \"Source ref (defaults to current HEAD)\" },\n },\n required: [\"name\"],\n },\n },\n {\n name: \"commit\",\n description: \"Commit staged changes\",\n inputSchema: {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Commit message\" },\n author: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n email: { type: \"string\" },\n },\n },\n },\n required: [\"message\"],\n },\n },\n {\n name: \"merge\",\n description: \"Merge a branch into the current branch\",\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"Branch to merge\" },\n message: { type: \"string\", description: \"Custom merge message\" },\n },\n required: [\"branch\"],\n },\n },\n ],\n discovery: {\n pathTemplate: \"/:branch/.actions\",\n note: \"Git workflow actions (readwrite mode only)\",\n },\n });\n }\n\n const manifest: CapabilitiesManifest = {\n schemaVersion: 1,\n provider: this.name,\n description: this.description || \"Git repository provider\",\n tools: [],\n actions: actionCatalogs,\n operations: this.getOperationsDeclaration(),\n };\n\n return {\n id: \"/.meta/.capabilities\",\n path: \"/.meta/.capabilities\",\n content: manifest,\n meta: { kind: \"afs:capabilities\", operations },\n };\n }\n\n // ========== Git Actions ==========\n\n /**\n * List available actions for a branch\n */\n @Actions(\"/:branch\")\n async listBranchActions(ctx: RouteContext<{ branch: string }>): Promise<AFSListResult> {\n if (this.accessMode !== \"readwrite\") {\n return { data: [] };\n }\n\n const basePath = `/${ctx.params.branch}/.actions`;\n return {\n data: [\n {\n id: \"diff\",\n path: `${basePath}/diff`,\n summary: \"Compare two branches or refs\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n from: { type: \"string\", description: \"Source ref\" },\n to: { type: \"string\", description: \"Target ref\" },\n path: { type: \"string\", description: \"Optional path filter\" },\n },\n required: [\"from\", \"to\"],\n },\n },\n },\n {\n id: \"create-branch\",\n path: `${basePath}/create-branch`,\n summary: \"Create a new branch from this ref\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"New branch name\" },\n from: { type: \"string\", description: \"Source ref (defaults to current HEAD)\" },\n },\n required: [\"name\"],\n },\n },\n },\n {\n id: \"commit\",\n path: `${basePath}/commit`,\n summary: \"Commit staged changes\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Commit message\" },\n author: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n email: { type: \"string\" },\n },\n },\n },\n required: [\"message\"],\n },\n },\n },\n {\n id: \"merge\",\n path: `${basePath}/merge`,\n summary: \"Merge another branch into this branch\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"Branch to merge\" },\n message: { type: \"string\", description: \"Custom merge message\" },\n },\n required: [\"branch\"],\n },\n },\n },\n ],\n };\n }\n\n /**\n * diff action — compare two branches or refs\n */\n @Actions.Exec(\"/:branch\", \"diff\")\n async diffAction(\n _ctx: RouteContext<{ branch: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n await this.ready();\n\n const from = args.from as string;\n const to = args.to as string;\n const pathFilter = args.path as string | undefined;\n\n if (!from || !to) {\n return {\n success: false,\n error: { code: \"INVALID_ARGS\", message: \"from and to are required\" },\n };\n }\n\n try {\n // Get diff stat\n const diffArgs = [\"diff\", \"--stat\", \"--name-only\", `${from}...${to}`];\n if (pathFilter) {\n diffArgs.push(\"--\", pathFilter);\n }\n const statOutput = await this.git.raw(diffArgs);\n const fileLines = statOutput\n .trim()\n .split(\"\\n\")\n .filter((l) => l.trim());\n const files = fileLines.map((path) => ({ path }));\n\n // Get diff patch\n const patchArgs = [\"diff\", `${from}...${to}`];\n if (pathFilter) {\n patchArgs.push(\"--\", pathFilter);\n }\n const patch = await this.git.raw(patchArgs);\n\n return {\n success: true,\n data: {\n from,\n to,\n files,\n patch,\n filesChanged: files.length,\n },\n };\n } catch (error) {\n return {\n success: false,\n error: {\n code: \"DIFF_FAILED\",\n message: (error as Error).message.replace(this.repoPath, \"<repo>\"),\n },\n };\n }\n }\n\n /**\n * create-branch action — create a new branch\n */\n @Actions.Exec(\"/:branch\", \"create-branch\")\n async createBranchAction(\n _ctx: RouteContext<{ branch: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n await this.ready();\n\n const name = args.name as string;\n const from = args.from as string | undefined;\n\n if (!name) {\n return { success: false, error: { code: \"INVALID_ARGS\", message: \"name is required\" } };\n }\n\n // Validate branch name - reject path traversal\n if (name.includes(\"..\")) {\n return {\n success: false,\n error: { code: \"INVALID_NAME\", message: \"Branch name contains invalid characters\" },\n };\n }\n\n try {\n if (from) {\n await this.git.raw([\"branch\", name, from]);\n } else {\n await this.git.raw([\"branch\", name]);\n }\n\n // Get the hash of the new branch\n const hash = await this.git.revparse([name]).then((h) => h.trim());\n\n return {\n success: true,\n data: { branch: name, hash },\n };\n } catch (error) {\n return {\n success: false,\n error: {\n code: \"CREATE_BRANCH_FAILED\",\n message: (error as Error).message.replace(this.repoPath, \"<repo>\"),\n },\n };\n }\n }\n\n /**\n * commit action — commit staged changes\n */\n @Actions.Exec(\"/:branch\", \"commit\")\n async commitAction(\n _ctx: RouteContext<{ branch: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n await this.ready();\n\n const message = args.message as string;\n if (!message) {\n return { success: false, error: { code: \"INVALID_ARGS\", message: \"message is required\" } };\n }\n\n const author = args.author as { name?: string; email?: string } | undefined;\n\n try {\n const git = simpleGit(this.repoPath);\n\n // Check for staged changes\n const status = await git.status();\n if (\n status.staged.length === 0 &&\n status.files.filter((f) => f.index !== \" \" && f.index !== \"?\").length === 0\n ) {\n return {\n success: false,\n error: { code: \"NO_CHANGES\", message: \"No staged changes to commit\" },\n };\n }\n\n // Configure author if provided\n if (author?.name) {\n await git.addConfig(\"user.name\", author.name, undefined, \"local\");\n }\n if (author?.email) {\n await git.addConfig(\"user.email\", author.email, undefined, \"local\");\n }\n\n const result = await git.commit(message);\n\n return {\n success: true,\n data: {\n hash: result.commit || \"\",\n message,\n filesChanged: result.summary.changes,\n },\n };\n } catch (error) {\n return {\n success: false,\n error: {\n code: \"COMMIT_FAILED\",\n message: (error as Error).message.replace(this.repoPath, \"<repo>\"),\n },\n };\n }\n }\n\n /**\n * merge action — merge a branch into current branch\n */\n @Actions.Exec(\"/:branch\", \"merge\")\n async mergeAction(\n _ctx: RouteContext<{ branch: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n await this.ready();\n\n const branch = args.branch as string;\n if (!branch) {\n return { success: false, error: { code: \"INVALID_ARGS\", message: \"branch is required\" } };\n }\n\n const customMessage = args.message as string | undefined;\n\n try {\n const git = simpleGit(this.repoPath);\n\n // Verify the branch exists\n const branches = await git.branchLocal();\n if (!branches.all.includes(branch)) {\n return {\n success: false,\n error: { code: \"BRANCH_NOT_FOUND\", message: `Branch '${branch}' not found` },\n };\n }\n\n const mergeArgs = [branch];\n if (customMessage) {\n mergeArgs.push(\"-m\", customMessage);\n }\n\n const result = await git.merge(mergeArgs);\n\n // Get the resulting commit hash\n const hash = await git.revparse([\"HEAD\"]).then((h) => h.trim());\n\n return {\n success: true,\n data: {\n hash,\n merged: branch,\n conflicts: result.conflicts || [],\n },\n };\n } catch (error) {\n // Abort merge if there was a conflict\n try {\n const git = simpleGit(this.repoPath);\n await git.merge([\"--abort\"]);\n } catch {\n // Ignore abort errors\n }\n\n return {\n success: false,\n error: {\n code: \"MERGE_FAILED\",\n message: (error as Error).message.replace(this.repoPath, \"<repo>\"),\n },\n };\n }\n }\n\n // ========== .log/ Virtual Tree ==========\n\n /**\n * List .log/ → commit list with pagination\n */\n @List(\"/:branch/.log\")\n async listLogHandler(ctx: RouteContext<{ branch: string }>): Promise<AFSListResult> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n\n const options = ctx.options as { limit?: number; offset?: number };\n const limit = Math.min(options?.limit || LIST_MAX_LIMIT, LIST_MAX_LIMIT);\n const offset = options?.offset || 0;\n\n const commits = await this.getCommitList(branch, limit, offset);\n const branchEncoded = this.encodeBranchName(branch);\n\n const entries: AFSEntry[] = commits.map((commit, i) =>\n this.buildEntry(`/${branchEncoded}/.log/${offset + i}`, {\n meta: {\n hash: commit.hash,\n shortHash: commit.shortHash,\n author: commit.author,\n date: commit.date,\n message: commit.message,\n },\n }),\n );\n\n return { data: entries };\n }\n\n /**\n * Read .log/{index} → commit diff/patch content\n */\n @Read(\"/:branch/.log/:index\")\n async readLogEntryHandler(\n ctx: RouteContext<{ branch: string; index: string }>,\n ): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const index = Number.parseInt(ctx.params.index, 10);\n\n if (Number.isNaN(index) || index < 0) {\n throw new AFSNotFoundError(`/${this.encodeBranchName(branch)}/.log/${ctx.params.index}`);\n }\n\n const commits = await this.getCommitList(branch, 1, index);\n if (commits.length === 0) {\n throw new AFSNotFoundError(`/${this.encodeBranchName(branch)}/.log/${index}`);\n }\n\n const commit = commits[0]!;\n\n // Get diff for this commit\n let diff: string;\n try {\n diff = await this.git.raw([\"show\", \"--stat\", \"--patch\", commit.hash]);\n } catch {\n diff = \"\";\n }\n\n const branchEncoded = this.encodeBranchName(branch);\n return this.buildEntry(`/${branchEncoded}/.log/${index}`, {\n content: diff,\n meta: {\n hash: commit.hash,\n shortHash: commit.shortHash,\n author: commit.author,\n date: commit.date,\n message: commit.message,\n },\n });\n }\n\n /**\n * Read .log/{index}/.meta → commit metadata only (no diff)\n */\n @Read(\"/:branch/.log/:index/.meta\")\n async readLogEntryMetaHandler(\n ctx: RouteContext<{ branch: string; index: string }>,\n ): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const index = Number.parseInt(ctx.params.index, 10);\n\n if (Number.isNaN(index) || index < 0) {\n throw new AFSNotFoundError(\n `/${this.encodeBranchName(branch)}/.log/${ctx.params.index}/.meta`,\n );\n }\n\n const commits = await this.getCommitList(branch, 1, index);\n if (commits.length === 0) {\n throw new AFSNotFoundError(`/${this.encodeBranchName(branch)}/.log/${index}/.meta`);\n }\n\n const commit = commits[0]!;\n const branchEncoded = this.encodeBranchName(branch);\n return this.buildEntry(`/${branchEncoded}/.log/${index}/.meta`, {\n meta: {\n hash: commit.hash,\n shortHash: commit.shortHash,\n author: commit.author,\n date: commit.date,\n message: commit.message,\n },\n });\n }\n\n // ========== Private Helper Methods ==========\n\n /**\n * Decode branch name (replace ~ with /)\n */\n private decodeBranchName(encoded: string): string {\n return encoded.replace(/~/g, \"/\");\n }\n\n /**\n * Encode branch name (replace / with ~)\n */\n private encodeBranchName(branch: string): string {\n return branch.replace(/\\//g, \"~\");\n }\n\n /**\n * Parse AFS path into branch and file path\n * Branch names may contain slashes and are encoded with ~ in paths\n */\n private parsePath(path: string): { branch?: string; filePath: string } {\n const normalized = join(\"/\", path); // Ensure leading slash\n const segments = normalized.split(\"/\").filter(Boolean);\n\n if (segments.length === 0) {\n return { branch: undefined, filePath: \"\" };\n }\n\n // Decode branch name (first segment): replace ~ with /\n const branch = segments[0]!.replace(/~/g, \"/\");\n const filePath = segments.slice(1).join(\"/\");\n\n return { branch, filePath };\n }\n\n /**\n * Build AFS path with encoded branch name\n * Branch names with slashes are encoded by replacing / with ~\n */\n private buildBranchPath(branch: string, filePath?: string): string {\n const encodedBranch = this.encodeBranchName(branch);\n if (!filePath) {\n return `/${encodedBranch}`;\n }\n return `/${encodedBranch}/${filePath}`;\n }\n\n /**\n * Get list of available branches\n */\n private async getBranches(): Promise<string[]> {\n const branchSummary = await this.git.branchLocal();\n const allBranches = branchSummary.all;\n\n // Filter by allowed branches if specified\n if (this.options.branches && this.options.branches.length > 0) {\n return allBranches.filter((branch) => this.options.branches!.includes(branch));\n }\n\n return allBranches;\n }\n\n /**\n * Check if a branch exists, throw AFSNotFoundError if not\n */\n private async ensureBranchExists(branch: string): Promise<void> {\n const branches = await this.getBranches();\n if (!branches.includes(branch)) {\n throw new AFSNotFoundError(this.buildBranchPath(branch));\n }\n }\n\n /**\n * Get the number of children for a tree (directory) in git\n */\n private async getChildrenCount(branch: string, path: string): Promise<number> {\n try {\n const treeish = path ? `${branch}:${path}` : branch;\n const output = await this.git.raw([\"ls-tree\", treeish]);\n const lines = output.split(\"\\n\").filter((line) => line.trim());\n return lines.length;\n } catch {\n return 0;\n }\n }\n\n /**\n * Get the last commit on a branch\n */\n private async getLastCommit(\n branch: string,\n ): Promise<{ hash: string; shortHash: string; author: string; date: string; message: string }> {\n const output = await this.git.raw([\"log\", \"-1\", \"--format=%H%n%h%n%an%n%aI%n%s\", branch]);\n const lines = output.trim().split(\"\\n\");\n return {\n hash: lines[0] || \"\",\n shortHash: lines[1] || \"\",\n author: lines[2] || \"\",\n date: lines[3] || \"\",\n message: lines[4] || \"\",\n };\n }\n\n /**\n * Count total files in a tree (recursively)\n */\n private async getTreeFileCount(branch: string, path: string): Promise<number> {\n try {\n const treeish = path ? `${branch}:${path}` : branch;\n const output = await this.git.raw([\"ls-tree\", \"-r\", treeish]);\n const lines = output.split(\"\\n\").filter((line) => line.trim());\n return lines.length;\n } catch {\n return 0;\n }\n }\n\n /**\n * Get a list of commits on a branch with limit/offset\n */\n private async getCommitList(\n branch: string,\n limit: number,\n offset: number,\n ): Promise<{ hash: string; shortHash: string; author: string; date: string; message: string }[]> {\n try {\n const args = [\n \"log\",\n `--skip=${offset}`,\n `-${limit}`,\n \"--format=%H%n%h%n%an%n%aI%n%s%n---COMMIT_SEP---\",\n branch,\n ];\n const output = await this.git.raw(args);\n const blocks = output.split(\"---COMMIT_SEP---\").filter((b) => b.trim());\n\n return blocks.map((block) => {\n const lines = block.trim().split(\"\\n\");\n return {\n hash: lines[0] || \"\",\n shortHash: lines[1] || \"\",\n author: lines[2] || \"\",\n date: lines[3] || \"\",\n message: lines[4] || \"\",\n };\n });\n } catch {\n return [];\n }\n }\n\n /**\n * Ensure worktree exists for a branch (lazy creation)\n */\n private async ensureWorktree(branch: string): Promise<string> {\n if (this.worktrees.has(branch)) {\n return this.worktrees.get(branch)!;\n }\n\n // Check if this is the current branch in the main repo\n const currentBranch = await this.git.revparse([\"--abbrev-ref\", \"HEAD\"]);\n if (currentBranch.trim() === branch) {\n // Use the main repo path for the current branch\n this.worktrees.set(branch, this.repoPath);\n return this.repoPath;\n }\n\n const worktreePath = join(this.tempBase, branch);\n\n // Check if worktree directory already exists\n const exists = await stat(worktreePath)\n .then(() => true)\n .catch(() => false);\n\n if (!exists) {\n await mkdir(this.tempBase, { recursive: true });\n await this.git.raw([\"worktree\", \"add\", worktreePath, branch]);\n }\n\n this.worktrees.set(branch, worktreePath);\n return worktreePath;\n }\n\n /**\n * List files using git ls-tree (no worktree needed)\n * Note: list() returns only children, never the path itself (per new semantics)\n */\n private async listWithGitLsTree(\n branch: string,\n path: string,\n options?: { maxDepth?: number; limit?: number },\n ): Promise<AFSListResult> {\n const maxDepth = options?.maxDepth ?? 1;\n const limit = Math.min(options?.limit || LIST_MAX_LIMIT, LIST_MAX_LIMIT);\n\n const entries: AFSEntry[] = [];\n const targetPath = path || \"\";\n const treeish = targetPath ? `${branch}:${targetPath}` : branch;\n\n try {\n // Check if the path exists and is a directory\n const pathType = await this.git\n .raw([\"cat-file\", \"-t\", treeish])\n .then((t) => t.trim())\n .catch(() => null);\n\n if (pathType === null) {\n // Path doesn't exist\n throw new AFSNotFoundError(this.buildBranchPath(branch, path));\n }\n\n // If it's a file (blob), it has no children\n if (pathType === \"blob\") {\n return { data: [] };\n }\n\n // It's a directory\n // maxDepth: 0 means return no children\n if (maxDepth === 0) {\n return { data: [] };\n }\n\n // List directory contents via BFS\n interface QueueItem {\n path: string;\n depth: number;\n }\n\n const queue: QueueItem[] = [{ path: targetPath, depth: 0 }];\n\n while (queue.length > 0) {\n const item = queue.shift()!;\n const { path: itemPath, depth } = item;\n\n // List directory contents\n const itemTreeish = itemPath ? `${branch}:${itemPath}` : branch;\n const output = await this.git.raw([\"ls-tree\", \"-l\", itemTreeish]);\n\n const lines = output\n .split(\"\\n\")\n .filter((line) => line.trim())\n .slice(0, limit - entries.length);\n\n for (const line of lines) {\n // Format: <mode> <type> <hash> <size (with padding)> <name>\n const match = line.match(/^(\\d+)\\s+(blob|tree)\\s+(\\w+)\\s+(-|\\d+)\\s+(.+)$/);\n if (!match) continue;\n\n const type = match[2]!;\n const sizeStr = match[4]!;\n const name = match[5]!;\n const isDirectory = type === \"tree\";\n const size = sizeStr === \"-\" ? undefined : Number.parseInt(sizeStr, 10);\n\n const fullPath = itemPath ? `${itemPath}/${name}` : name;\n const afsPath = this.buildBranchPath(branch, fullPath);\n\n // For directories, get children count\n const childrenCount = isDirectory\n ? await this.getChildrenCount(branch, fullPath)\n : undefined;\n\n entries.push(\n this.buildEntry(afsPath, {\n meta: { kind: isDirectory ? \"git:directory\" : \"git:file\", size, childrenCount },\n }),\n );\n\n // Add to queue if it's a directory and we haven't reached max depth\n if (isDirectory && depth + 1 < maxDepth) {\n queue.push({ path: fullPath, depth: depth + 1 });\n }\n\n // Check limit\n if (entries.length >= limit) {\n return { data: entries };\n }\n }\n }\n\n return { data: entries };\n } catch (error) {\n // Re-throw AFSNotFoundError as-is\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw new Error(`Failed to list: ${(error as Error).message}`);\n }\n }\n\n /**\n * Detect MIME type based on file extension\n */\n private getMimeType(filePath: string): string {\n const ext = filePath.split(\".\").pop()?.toLowerCase();\n const mimeTypes: Record<string, string> = {\n // Images\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n bmp: \"image/bmp\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n ico: \"image/x-icon\",\n // Documents\n pdf: \"application/pdf\",\n txt: \"text/plain\",\n md: \"text/markdown\",\n // Code\n js: \"text/javascript\",\n ts: \"text/typescript\",\n json: \"application/json\",\n html: \"text/html\",\n css: \"text/css\",\n xml: \"text/xml\",\n };\n return mimeTypes[ext || \"\"] || \"application/octet-stream\";\n }\n\n /**\n * Check if file is likely binary based on extension\n */\n private isBinaryFile(filePath: string): boolean {\n const ext = filePath.split(\".\").pop()?.toLowerCase();\n const binaryExtensions = [\n \"png\",\n \"jpg\",\n \"jpeg\",\n \"gif\",\n \"bmp\",\n \"webp\",\n \"ico\",\n \"pdf\",\n \"zip\",\n \"tar\",\n \"gz\",\n \"exe\",\n \"dll\",\n \"so\",\n \"dylib\",\n \"wasm\",\n ];\n return binaryExtensions.includes(ext || \"\");\n }\n\n // ========== Public Git Operations ==========\n\n /**\n * Fetch latest changes from remote\n */\n async fetch(): Promise<void> {\n await this.ready();\n await this.git.fetch();\n }\n\n /**\n * Pull latest changes from remote for current branch\n */\n async pull(): Promise<void> {\n await this.ready();\n await this.git.pull();\n }\n\n /**\n * Push local changes to remote\n */\n async push(branch?: string): Promise<void> {\n await this.ready();\n if (branch) {\n await this.git.push(\"origin\", branch);\n } else {\n await this.git.push();\n }\n }\n\n /**\n * Cleanup all worktrees (useful when unmounting)\n */\n async cleanup(): Promise<void> {\n await this.ready();\n for (const [_branch, worktreePath] of this.worktrees) {\n try {\n await this.git.raw([\"worktree\", \"remove\", worktreePath, \"--force\"]);\n } catch (_error) {\n // Ignore errors during cleanup\n }\n }\n this.worktrees.clear();\n\n // Remove temp directory\n try {\n await rm(this.tempBase, { recursive: true, force: true });\n } catch {\n // Ignore errors\n }\n\n // Cleanup cloned repository if auto-cloned and autoCleanup enabled\n const autoCleanup = this.options.autoCleanup ?? true;\n if (this.isAutoCloned && autoCleanup && this.clonedPath) {\n try {\n await rm(this.clonedPath, { recursive: true, force: true });\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n}\n\nconst _typeCheck: AFSModuleClass<AFSGit, AFSGitOptions> = AFSGit;\n\nexport default AFSGit;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["afsPath","mimeType","isBinary","content","meta"],"sources":["../src/index.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { mkdir, readdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { basename, dirname, isAbsolute, join } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport {\n type AFSAccessMode,\n type AFSDeleteOptions,\n type AFSEntry,\n type AFSEntryMetadata,\n type AFSExecResult,\n type AFSExplainOptions,\n type AFSExplainResult,\n type AFSListResult,\n type AFSModuleClass,\n type AFSModuleLoadParams,\n AFSNotFoundError,\n type AFSRenameOptions,\n type AFSSearchOptions,\n type AFSStatResult,\n type AFSWriteEntryPayload,\n type AFSWriteOptions,\n type CapabilitiesManifest,\n type ProviderManifest,\n type ProviderTreeSchema,\n} from \"@aigne/afs\";\nimport {\n Actions,\n AFSBaseProvider,\n Delete,\n Explain,\n List,\n Meta,\n Read,\n Rename,\n type RouteContext,\n Search,\n Stat,\n Write,\n} from \"@aigne/afs/provider\";\nimport { camelize, optionalize, zodParse } from \"@aigne/afs/utils/zod\";\nimport { assertPathWithinRoot, getMimeType, isBinaryFile } from \"@aigne/afs-provider-utils\";\nimport { type SimpleGit, simpleGit } from \"simple-git\";\nimport { z } from \"zod\";\n\nconst LIST_MAX_LIMIT = 1000;\n\nconst execFileAsync = promisify(execFile);\n\nexport interface AFSGitOptions {\n name?: string;\n /**\n * Local path to git repository.\n * If remoteUrl is provided and repoPath doesn't exist, will clone to this path.\n * If remoteUrl is provided and repoPath is not specified, clones to temp directory.\n */\n repoPath?: string;\n /**\n * Remote repository URL (https or git protocol).\n * If provided, will clone the repository if repoPath doesn't exist.\n * Examples:\n * - https://github.com/user/repo.git\n * - git@github.com:user/repo.git\n */\n remoteUrl?: string;\n description?: string;\n /**\n * List of branches to expose/access.\n * Also used for clone optimization when cloning from remoteUrl:\n * - Single branch (e.g., ['main']): Uses --single-branch for faster clone\n * - Multiple branches: Clones all branches, filters access to specified ones\n * - Not specified: All branches are accessible\n */\n branches?: string[];\n /**\n * Access mode for this module.\n * - \"readonly\": Only read operations are allowed, uses git commands (no worktree)\n * - \"readwrite\": All operations are allowed, creates worktrees as needed\n * @default \"readonly\"\n */\n accessMode?: AFSAccessMode;\n /**\n * Automatically commit changes after write operations\n * @default false\n */\n autoCommit?: boolean;\n /**\n * Author information for commits when autoCommit is enabled\n */\n commitAuthor?: {\n name: string;\n email: string;\n };\n /**\n * Clone depth for shallow clone (only used when cloning from remoteUrl)\n * @default 1\n */\n depth?: number;\n /**\n * Automatically clean up cloned repository on cleanup()\n * Only applies when repository was auto-cloned to temp directory\n * @default true\n */\n autoCleanup?: boolean;\n /**\n * Git clone options (only used when cloning from remoteUrl)\n */\n cloneOptions?: {\n /**\n * Authentication credentials for private repositories\n */\n auth?: {\n username?: string;\n password?: string;\n };\n };\n}\n\nconst afsGitOptionsSchema = camelize(\n z\n .object({\n name: optionalize(z.string()),\n repoPath: optionalize(z.string().describe(\"The path to the git repository\")),\n remoteUrl: optionalize(z.string().describe(\"Remote repository URL (https or git protocol)\")),\n description: optionalize(z.string().describe(\"A description of the repository\")),\n branches: optionalize(z.array(z.string()).describe(\"List of branches to expose\")),\n accessMode: optionalize(\n z.enum([\"readonly\", \"readwrite\"]).describe(\"Access mode for this module\"),\n ),\n autoCommit: optionalize(\n z.boolean().describe(\"Automatically commit changes after write operations\"),\n ),\n commitAuthor: optionalize(\n z.object({\n name: z.string(),\n email: z.string(),\n }),\n ),\n depth: optionalize(z.number().describe(\"Clone depth for shallow clone\")),\n autoCleanup: optionalize(\n z.boolean().describe(\"Automatically clean up cloned repository on cleanup()\"),\n ),\n cloneOptions: optionalize(\n z.object({\n auth: optionalize(\n z.object({\n username: optionalize(z.string()),\n password: optionalize(z.string()),\n }),\n ),\n }),\n ),\n })\n .refine((data) => data.repoPath || data.remoteUrl, {\n message: \"Either repoPath or remoteUrl must be provided\",\n }),\n);\n\nexport class AFSGit extends AFSBaseProvider {\n static schema() {\n return afsGitOptionsSchema;\n }\n\n static manifest(): ProviderManifest {\n return {\n name: \"git\",\n description:\n \"Git repository browser with branch-based access.\\n- Browse branches, read files at any ref, search across repository\\n- Exec actions (readwrite): `diff`, `create-branch`, `commit`, `merge`\\n- Virtual `.log/` tree exposes commit history per branch\\n- Path structure: `/{branch}/{file-path}` (branch `/` encoded as `~`)\",\n uriTemplate: \"git://{localPath+}\",\n category: \"vcs\",\n schema: z.object({\n localPath: z.string(),\n branch: z.string().optional(),\n remoteUrl: z.string().optional(),\n }),\n tags: [\"git\", \"version-control\"],\n capabilityTags: [\"read-write\", \"search\", \"auth:none\", \"local\"],\n security: {\n riskLevel: \"local\",\n resourceAccess: [\"local-filesystem\"],\n requires: [\"git\"],\n dataSensitivity: [\"code\"],\n notes: [\"Accesses local git repositories; readwrite mode creates worktrees and can commit\"],\n },\n capabilities: {\n filesystem: { read: true, write: true },\n process: { spawn: true, allowedCommands: [\"git\"] },\n },\n };\n }\n\n static treeSchema(): ProviderTreeSchema {\n return {\n operations: [\"list\", \"read\", \"write\", \"delete\", \"search\", \"stat\", \"explain\"],\n tree: {\n \"/\": { kind: \"git:root\", operations: [\"list\", \"read\"] },\n \"/{branch}\": {\n kind: \"git:branch\",\n operations: [\"list\", \"read\", \"search\"],\n actions: [\"diff\", \"create-branch\", \"commit\", \"merge\"],\n },\n \"/{branch}/{path+}\": {\n kind: \"git:file\",\n operations: [\"list\", \"read\", \"write\", \"delete\", \"search\"],\n },\n \"/{branch}/.log\": { kind: \"git:log\", operations: [\"list\"] },\n \"/{branch}/.log/{index}\": { kind: \"git:commit\", operations: [\"read\"] },\n },\n auth: { type: \"none\" },\n bestFor: [\"code browsing\", \"version control\", \"branch comparison\"],\n notFor: [\"binary storage\", \"large files\"],\n };\n }\n\n static async load({ basePath, config }: AFSModuleLoadParams = {}) {\n const valid = await AFSGit.schema().parseAsync(config);\n const instance = new AFSGit({ ...valid, cwd: basePath });\n await instance.ready();\n return instance;\n }\n\n readonly name: string;\n readonly description?: string;\n readonly accessMode: AFSAccessMode;\n\n private initPromise: Promise<void>;\n private git: SimpleGit;\n private tempBase: string;\n private worktrees: Map<string, string> = new Map();\n private repoHash: string;\n private isAutoCloned = false;\n private clonedPath?: string;\n private repoPath: string;\n\n constructor(\n public options: AFSGitOptions & {\n cwd?: string;\n localPath?: string;\n branch?: string;\n uri?: string;\n },\n ) {\n super();\n\n // Normalize registry-passed template vars\n if ((options as any).localPath && !options.repoPath) {\n options.repoPath = (options as any).localPath;\n }\n if ((options as any).branch && !options.branches) {\n options.branches = [(options as any).branch];\n }\n\n zodParse(afsGitOptionsSchema, options);\n\n // Synchronously determine repoPath to initialize name\n let repoPath: string;\n let repoName: string;\n\n if (options.repoPath) {\n // Use provided repoPath\n repoPath = isAbsolute(options.repoPath)\n ? options.repoPath\n : join(options.cwd || process.cwd(), options.repoPath);\n repoName = basename(repoPath);\n } else if (options.remoteUrl) {\n // Extract repo name from URL for temporary name\n const urlParts = options.remoteUrl.split(\"/\");\n const lastPart = urlParts[urlParts.length - 1];\n repoName = lastPart?.replace(/\\.git$/, \"\") || \"git\";\n\n // Will be updated during async init, use temp path for now\n const repoHash = createHash(\"md5\").update(options.remoteUrl).digest(\"hex\").substring(0, 8);\n repoPath = join(tmpdir(), `afs-git-remote-${repoHash}`);\n } else {\n // This should never happen due to schema validation\n throw new Error(\"Either repoPath or remoteUrl must be provided\");\n }\n\n // Initialize basic properties immediately\n this.repoPath = repoPath;\n this.name = options.name || repoName;\n this.description = options.description;\n this.accessMode = options.accessMode ?? \"readonly\";\n\n // Calculate hash for temp directories\n this.repoHash = createHash(\"md5\").update(repoPath).digest(\"hex\").substring(0, 8);\n this.tempBase = join(tmpdir(), `afs-git-${this.repoHash}`);\n\n // Note: git and other properties will be initialized in initialize() after cloning\n // We need to delay simpleGit() initialization until the directory exists\n this.git = null as any; // Will be set in initialize()\n\n // Start async initialization (cloning if needed)\n this.initPromise = this.initialize();\n }\n\n /**\n * Wait for async initialization to complete\n */\n async ready(): Promise<void> {\n await this.initPromise;\n }\n\n /**\n * Async initialization logic (runs in constructor)\n * Handles cloning remote repositories if needed\n */\n private async initialize(): Promise<void> {\n const options = this.options;\n\n // Auto-create local repository if it doesn't exist (skip for remoteUrl — will be cloned)\n if (options.repoPath && !options.remoteUrl) {\n const { existsSync, mkdirSync } = await import(\"node:fs\");\n const { execSync } = await import(\"node:child_process\");\n if (!existsSync(this.repoPath)) {\n mkdirSync(this.repoPath, { recursive: true });\n }\n if (!existsSync(join(this.repoPath, \".git\"))) {\n execSync(\"git init -b main\", { cwd: this.repoPath, stdio: \"ignore\" });\n }\n }\n\n // If remoteUrl is provided, handle cloning\n if (options.remoteUrl) {\n const targetPath = options.repoPath\n ? isAbsolute(options.repoPath)\n ? options.repoPath\n : join(options.cwd || process.cwd(), options.repoPath)\n : this.repoPath; // Use temp path set in constructor\n\n // Mark as auto-cloned if we're using temp directory\n if (!options.repoPath) {\n this.isAutoCloned = true;\n }\n\n // Check if targetPath exists and is a valid git repository\n const exists = await stat(targetPath)\n .then(() => true)\n .catch(() => false);\n\n let needsClone = !exists;\n\n // If directory exists but is not a valid git repo, clean it up and re-clone\n if (exists) {\n const tempGit = simpleGit(targetPath);\n const isValidRepo = await tempGit.checkIsRepo().catch(() => false);\n if (!isValidRepo) {\n // Remove invalid directory and re-clone\n await rm(targetPath, { recursive: true, force: true });\n needsClone = true;\n }\n }\n\n if (needsClone) {\n // Determine if single-branch optimization should be used\n const singleBranch = options.branches?.length === 1 ? options.branches[0] : undefined;\n\n await AFSGit.cloneRepository(options.remoteUrl, targetPath, {\n depth: options.depth ?? 1,\n branch: singleBranch,\n auth: options.cloneOptions?.auth,\n });\n }\n\n // Update properties if targetPath differs from constructor initialization\n if (targetPath !== this.repoPath) {\n this.repoPath = targetPath;\n this.repoHash = createHash(\"md5\").update(targetPath).digest(\"hex\").substring(0, 8);\n this.tempBase = join(tmpdir(), `afs-git-${this.repoHash}`);\n }\n\n this.clonedPath = this.isAutoCloned ? targetPath : undefined;\n }\n\n // Now that the directory exists (either it was there or we cloned it), initialize git\n this.git = simpleGit(this.repoPath);\n\n // Validate that the directory is actually a git repository\n const isRepo = await this.git.checkIsRepo();\n if (!isRepo) {\n throw new Error(`Not a git repository: ${this.repoPath}`);\n }\n }\n\n /**\n * Clone a remote repository to local path\n */\n private static async cloneRepository(\n remoteUrl: string,\n targetPath: string,\n options: {\n depth?: number;\n branch?: string;\n auth?: { username?: string; password?: string };\n } = {},\n ): Promise<void> {\n const git = simpleGit();\n\n // Build clone options\n const cloneArgs: string[] = [];\n\n if (options.depth) {\n cloneArgs.push(\"--depth\", options.depth.toString());\n }\n\n if (options.branch) {\n cloneArgs.push(\"--branch\", options.branch, \"--single-branch\");\n }\n\n // Handle authentication in URL if provided\n let cloneUrl = remoteUrl;\n if (options.auth?.username && options.auth?.password) {\n // Insert credentials into HTTPS URL\n if (remoteUrl.startsWith(\"https://\")) {\n const url = new URL(remoteUrl);\n url.username = encodeURIComponent(options.auth.username);\n url.password = encodeURIComponent(options.auth.password);\n cloneUrl = url.toString();\n }\n }\n\n await git.clone(cloneUrl, targetPath, cloneArgs);\n }\n\n // ========== Route Handlers ==========\n\n /**\n * List root (branches)\n * Note: list() returns only children (branches), never the path itself (per new semantics)\n */\n @List(\"/\", { handleDepth: true })\n async listRootHandler(ctx: RouteContext): Promise<AFSListResult & { noExpand?: string[] }> {\n await this.ready();\n\n const options = ctx.options as { limit?: number; maxDepth?: number };\n const maxDepth = options?.maxDepth ?? 1;\n const limit = Math.min(options?.limit || LIST_MAX_LIMIT, LIST_MAX_LIMIT);\n\n // maxDepth: 0 means return no children\n if (maxDepth === 0) {\n return { data: [] };\n }\n\n const branches = await this.getBranches();\n const entries: AFSEntry[] = [];\n\n for (const name of branches) {\n if (entries.length >= limit) break;\n\n const encodedPath = this.buildBranchPath(name);\n\n // Get children count for this branch\n const branchChildrenCount = await this.getChildrenCount(name, \"\");\n\n entries.push(\n this.buildEntry(encodedPath, {\n meta: { kind: \"git:branch\", childrenCount: branchChildrenCount },\n }),\n );\n\n // If maxDepth > 1, also list contents of each branch\n if (maxDepth > 1) {\n const branchResult = await this.listWithGitLsTree(name, \"\", {\n maxDepth: maxDepth - 1,\n limit: limit - entries.length,\n });\n entries.push(...branchResult.data);\n }\n }\n\n return { data: entries };\n }\n\n /**\n * List branch root (matches /main, /develop, etc.)\n */\n @List(\"/:branch\", { handleDepth: true })\n async listBranchRootHandler(\n ctx: RouteContext<{ branch: string }>,\n ): Promise<AFSListResult & { noExpand?: string[] }> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n\n return this.listWithGitLsTree(branch, \"\", ctx.options as { maxDepth?: number; limit?: number });\n }\n\n /**\n * List files in branch with subpath (matches /main/src, /main/src/foo, etc.)\n */\n @List(\"/:branch/:path+\", { handleDepth: true })\n async listBranchHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n ): Promise<AFSListResult & { noExpand?: string[] }> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const filePath = ctx.params.path;\n\n return this.listWithGitLsTree(\n branch,\n filePath,\n ctx.options as { maxDepth?: number; limit?: number },\n );\n }\n\n // ========== Meta Handlers ==========\n // These handlers provide metadata access for all paths via .meta suffix\n\n /**\n * Read root metadata (introspection only, read-only)\n */\n @Meta(\"/\")\n async readRootMetaHandler(_ctx: RouteContext): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branches = await this.getBranches();\n return this.buildEntry(\"/.meta\", {\n meta: { childrenCount: branches.length, type: \"root\" },\n });\n }\n\n /**\n * Read branch root metadata (introspection only, read-only)\n */\n @Meta(\"/:branch\")\n async readBranchMetaHandler(\n ctx: RouteContext<{ branch: string }>,\n ): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const childrenCount = await this.getChildrenCount(branch, \"\");\n const branchPath = `/${this.encodeBranchName(branch)}`;\n const metaPath = `${branchPath}/.meta`;\n const lastCommit = await this.getLastCommit(branch);\n\n return this.buildEntry(metaPath, {\n meta: { childrenCount, type: \"branch\", lastCommit },\n });\n }\n\n /**\n * Read file or directory metadata in branch (introspection only, read-only)\n */\n @Meta(\"/:branch/:path+\")\n async readPathMetaHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n ): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const filePath = ctx.params.path;\n\n // Check if the path exists\n const objectType = await this.git\n .raw([\"cat-file\", \"-t\", `${branch}:${filePath}`])\n .then((t) => t.trim())\n .catch(() => null);\n\n if (objectType === null) {\n throw new AFSNotFoundError(this.buildBranchPath(branch, filePath));\n }\n\n const isDir = objectType === \"tree\";\n const metaPath = `/${this.encodeBranchName(branch)}/${filePath}/.meta`;\n\n let childrenCount: number | undefined;\n if (isDir) {\n childrenCount = await this.getChildrenCount(branch, filePath);\n }\n\n return this.buildEntry(metaPath, {\n meta: {\n childrenCount,\n type: isDir ? \"directory\" : \"file\",\n gitObjectType: objectType,\n },\n });\n }\n\n // ========== Regular Read Handlers ==========\n\n /**\n * Read root\n */\n @Read(\"/\")\n async readRootHandler(_ctx: RouteContext): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branches = await this.getBranches();\n return this.buildEntry(\"/\", {\n meta: { childrenCount: branches.length },\n });\n }\n\n /**\n * Read branch root\n */\n @Read(\"/:branch\")\n async readBranchRootHandler(\n ctx: RouteContext<{ branch: string }>,\n ): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const branchPath = this.buildBranchPath(branch);\n const childrenCount = await this.getChildrenCount(branch, \"\");\n const lastCommit = await this.getLastCommit(branch);\n return this.buildEntry(branchPath, {\n meta: { childrenCount, lastCommit },\n });\n }\n\n /**\n * Read file or directory in branch\n */\n @Read(\"/:branch/:path+\")\n async readBranchHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n ): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const filePath = ctx.params.path;\n\n // Check if there's an active worktree for this branch - read from there first\n // This handles files that were written but may be in a different state than git index\n const worktreePath = this.worktrees.get(branch);\n if (worktreePath) {\n try {\n const fullPath = join(worktreePath, filePath);\n await this.assertWithinWorktree(fullPath, worktreePath);\n const stats = await stat(fullPath);\n\n if (stats.isDirectory()) {\n // It's a directory - count children\n const files = await readdir(fullPath);\n const afsPath = this.buildBranchPath(branch, filePath);\n return this.buildEntry(afsPath, {\n meta: { childrenCount: files.length },\n });\n }\n\n // It's a file - read content\n const mimeType = getMimeType(filePath);\n const isBinary = isBinaryFile(filePath);\n\n let content: string;\n const meta: AFSEntryMetadata = {\n size: stats.size,\n mimeType,\n };\n\n if (isBinary) {\n const buffer = await readFile(fullPath);\n content = buffer.toString(\"base64\");\n meta.contentType = \"base64\";\n } else {\n content = await readFile(fullPath, \"utf8\");\n }\n\n const afsPath = this.buildBranchPath(branch, filePath);\n return this.buildEntry(afsPath, {\n content,\n meta,\n createdAt: stats.birthtime,\n updatedAt: stats.mtime,\n });\n } catch (err: unknown) {\n // Re-throw security errors — only fall through for \"not found\" / filesystem errors\n if (\n err instanceof Error &&\n \"code\" in err &&\n (err as { code: string }).code === \"AFS_PERMISSION_DENIED\"\n ) {\n throw err;\n }\n // File doesn't exist in worktree, fall through to git\n }\n }\n\n // Read from git repository\n // Check if path is a blob (file) or tree (directory)\n const objectType = await this.git\n .raw([\"cat-file\", \"-t\", `${branch}:${filePath}`])\n .then((t) => t.trim())\n .catch(() => null);\n\n if (objectType === null) {\n // Path doesn't exist in git\n throw new AFSNotFoundError(this.buildBranchPath(branch, filePath));\n }\n\n if (objectType === \"tree\") {\n // It's a directory\n const afsPath = this.buildBranchPath(branch, filePath);\n const childrenCount = await this.getChildrenCount(branch, filePath);\n return this.buildEntry(afsPath, {\n meta: { childrenCount },\n });\n }\n\n // It's a file, get content\n const size = await this.git\n .raw([\"cat-file\", \"-s\", `${branch}:${filePath}`])\n .then((s) => Number.parseInt(s.trim(), 10));\n\n // Determine mimeType based on file extension\n const mimeType = getMimeType(filePath);\n const isBinary = isBinaryFile(filePath);\n\n let content: string;\n const meta: AFSEntryMetadata = {\n size,\n mimeType,\n };\n\n if (isBinary) {\n // For binary files, use execFileAsync to get raw buffer\n const { stdout } = await execFileAsync(\"git\", [\"cat-file\", \"-p\", `${branch}:${filePath}`], {\n cwd: this.options.repoPath,\n encoding: \"buffer\",\n maxBuffer: 10 * 1024 * 1024, // 10MB max\n });\n // Store only base64 string without data URL prefix\n content = (stdout as Buffer).toString(\"base64\");\n // Mark content as base64 in metadata\n meta.contentType = \"base64\";\n } else {\n // For text files, use git.show\n content = await this.git.show([`${branch}:${filePath}`]);\n }\n\n const afsPath = this.buildBranchPath(branch, filePath);\n return this.buildEntry(afsPath, {\n content,\n meta,\n });\n }\n\n /**\n * Write to root is not allowed\n */\n @Write(\"/\")\n async writeRootHandler(): Promise<never> {\n throw new Error(\"Cannot write to root\");\n }\n\n /**\n * Write to branch root is not allowed\n */\n @Write(\"/:branch\")\n async writeBranchRootHandler(): Promise<never> {\n throw new Error(\"Cannot write to branch root\");\n }\n\n /**\n * Write file in branch\n */\n @Write(\"/:branch/:path+\")\n async writeHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n payload: AFSWriteEntryPayload,\n ): Promise<{ data: AFSEntry }> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n const filePath = ctx.params.path;\n const options = ctx.options as AFSWriteOptions | undefined;\n const mode = options?.mode ?? \"replace\";\n\n // Create worktree for write operations\n const worktreePath = await this.ensureWorktree(branch);\n const fullPath = join(worktreePath, filePath);\n await this.assertWithinWorktree(fullPath, worktreePath);\n\n // Ensure parent directory exists\n const parentDir = dirname(fullPath);\n await mkdir(parentDir, { recursive: true });\n\n // Write content\n if (payload.content !== undefined) {\n let contentToWrite: string;\n if (typeof payload.content === \"string\") {\n contentToWrite = payload.content;\n } else {\n contentToWrite = JSON.stringify(payload.content, null, 2);\n }\n await writeFile(fullPath, contentToWrite, {\n encoding: \"utf8\",\n flag: mode === \"append\" ? \"a\" : \"w\",\n });\n }\n\n // Auto commit if enabled\n if (this.options.autoCommit) {\n const gitInstance = simpleGit(worktreePath);\n await gitInstance.add(filePath);\n\n if (this.options.commitAuthor) {\n await gitInstance.addConfig(\n \"user.name\",\n this.options.commitAuthor.name,\n undefined,\n \"local\",\n );\n await gitInstance.addConfig(\n \"user.email\",\n this.options.commitAuthor.email,\n undefined,\n \"local\",\n );\n }\n\n await gitInstance.commit(`Update ${filePath}`);\n }\n\n // Get file stats\n const stats = await stat(fullPath);\n\n const afsPath = this.buildBranchPath(branch, filePath);\n const writtenEntry: AFSEntry = {\n id: afsPath,\n path: afsPath,\n content: payload.content,\n summary: payload.summary,\n createdAt: stats.birthtime,\n updatedAt: stats.mtime,\n meta: {\n ...payload.meta,\n size: stats.size,\n } as AFSEntryMetadata,\n userId: payload.userId,\n sessionId: payload.sessionId,\n linkTo: payload.linkTo,\n };\n\n return { data: writtenEntry };\n }\n\n /**\n * Delete root is not allowed\n */\n @Delete(\"/\")\n async deleteRootHandler(): Promise<never> {\n throw new Error(\"Cannot delete root\");\n }\n\n /**\n * Delete branch root is not allowed\n */\n @Delete(\"/:branch\")\n async deleteBranchRootHandler(ctx: RouteContext<{ branch: string }>): Promise<never> {\n await this.ready();\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n throw new Error(\"Cannot delete branch root\");\n }\n\n /**\n * Delete file in branch\n */\n @Delete(\"/:branch/:path+\")\n async deleteHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n ): Promise<{ message: string }> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n const filePath = ctx.params.path;\n const options = ctx.options as AFSDeleteOptions | undefined;\n const recursive = options?.recursive ?? false;\n\n // Create worktree for delete operations\n const worktreePath = await this.ensureWorktree(branch);\n const fullPath = join(worktreePath, filePath);\n await this.assertWithinWorktree(fullPath, worktreePath);\n\n let stats: Awaited<ReturnType<typeof stat>>;\n try {\n stats = await stat(fullPath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n throw new AFSNotFoundError(this.buildBranchPath(branch, filePath));\n }\n throw error;\n }\n\n if (stats.isDirectory() && !recursive) {\n throw new Error(\n `Cannot delete directory '/${ctx.params.branch}/${filePath}' without recursive option. Set recursive: true to delete directories.`,\n );\n }\n\n await rm(fullPath, { recursive, force: true });\n\n // Auto commit if enabled\n if (this.options.autoCommit) {\n const gitInstance = simpleGit(worktreePath);\n await gitInstance.add(filePath);\n\n if (this.options.commitAuthor) {\n await gitInstance.addConfig(\n \"user.name\",\n this.options.commitAuthor.name,\n undefined,\n \"local\",\n );\n await gitInstance.addConfig(\n \"user.email\",\n this.options.commitAuthor.email,\n undefined,\n \"local\",\n );\n }\n\n await gitInstance.commit(`Delete ${filePath}`);\n }\n\n return { message: `Successfully deleted: /${ctx.params.branch}/${filePath}` };\n }\n\n /**\n * Rename file in branch\n */\n @Rename(\"/:branch/:path+\")\n async renameHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n newPath: string,\n ): Promise<{ message: string }> {\n await this.ready();\n\n const oldBranch = this.decodeBranchName(ctx.params.branch);\n const oldFilePath = ctx.params.path;\n\n // Parse new path\n const { branch: newBranch, filePath: newFilePath } = this.parsePath(newPath);\n const options = ctx.options as AFSRenameOptions | undefined;\n const overwrite = options?.overwrite ?? false;\n\n if (!newBranch || !newFilePath) {\n throw new Error(\"Cannot rename to root or branch root\");\n }\n\n if (oldBranch !== newBranch) {\n throw new Error(\"Cannot rename across branches\");\n }\n\n // Create worktree for rename operations\n const worktreePath = await this.ensureWorktree(oldBranch);\n const oldFullPath = join(worktreePath, oldFilePath);\n const newFullPath = join(worktreePath, newFilePath);\n await this.assertWithinWorktree(oldFullPath, worktreePath);\n await this.assertWithinWorktree(newFullPath, worktreePath);\n\n // Check if source exists\n try {\n await stat(oldFullPath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n throw new AFSNotFoundError(this.buildBranchPath(oldBranch, oldFilePath));\n }\n throw error;\n }\n\n // Check if destination exists\n try {\n await stat(newFullPath);\n if (!overwrite) {\n throw new Error(\n `Destination '${newPath}' already exists. Set overwrite: true to replace it.`,\n );\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw error;\n }\n }\n\n // Ensure parent directory exists\n const newParentDir = dirname(newFullPath);\n await mkdir(newParentDir, { recursive: true });\n\n // Perform rename\n await rename(oldFullPath, newFullPath);\n\n // Auto commit if enabled\n if (this.options.autoCommit) {\n const gitInstance = simpleGit(worktreePath);\n await gitInstance.add([oldFilePath, newFilePath]);\n\n if (this.options.commitAuthor) {\n await gitInstance.addConfig(\n \"user.name\",\n this.options.commitAuthor.name,\n undefined,\n \"local\",\n );\n await gitInstance.addConfig(\n \"user.email\",\n this.options.commitAuthor.email,\n undefined,\n \"local\",\n );\n }\n\n await gitInstance.commit(`Rename ${oldFilePath} to ${newFilePath}`);\n }\n\n return {\n message: `Successfully renamed '/${ctx.params.branch}/${oldFilePath}' to '${newPath}'`,\n };\n }\n\n /**\n * Search files in branch root\n */\n @Search(\"/:branch\")\n async searchBranchRootHandler(\n ctx: RouteContext<{ branch: string }>,\n query: string,\n options?: AFSSearchOptions,\n ): Promise<{ data: AFSEntry[]; message?: string }> {\n return this.searchInBranch(ctx.params.branch, \"\", query, options);\n }\n\n /**\n * Search files in branch path\n */\n @Search(\"/:branch/:path+\")\n async searchHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n query: string,\n options?: AFSSearchOptions,\n ): Promise<{ data: AFSEntry[]; message?: string }> {\n return this.searchInBranch(ctx.params.branch, ctx.params.path, query, options);\n }\n\n /**\n * Internal search implementation\n */\n private async searchInBranch(\n encodedBranch: string,\n filePath: string,\n query: string,\n options?: AFSSearchOptions,\n ): Promise<{ data: AFSEntry[]; message?: string }> {\n await this.ready();\n\n const branch = this.decodeBranchName(encodedBranch);\n const limit = Math.min(options?.limit || LIST_MAX_LIMIT, LIST_MAX_LIMIT);\n\n try {\n // Use git grep for searching (no worktree needed)\n const args = [\"grep\", \"-n\", \"-I\"]; // -n for line numbers, -I to skip binary files\n\n if (options?.caseSensitive === false) {\n args.push(\"-i\");\n }\n\n args.push(query, branch);\n\n // Add path filter if specified\n if (filePath) {\n args.push(\"--\", filePath);\n }\n\n const output = await this.git.raw(args);\n const lines = output.split(\"\\n\").filter((line) => line.trim());\n\n const entries: AFSEntry[] = [];\n const processedFiles = new Set<string>();\n\n for (const line of lines) {\n // Format when searching in branch: branch:path:linenum:content\n // Try the format with branch prefix first\n let matchPath: string;\n let lineNum: string;\n let content: string;\n\n const matchWithBranch = line.match(/^[^:]+:([^:]+):(\\d+):(.+)$/);\n if (matchWithBranch) {\n matchPath = matchWithBranch[1]!;\n lineNum = matchWithBranch[2]!;\n content = matchWithBranch[3]!;\n } else {\n // Try format without branch: path:linenum:content\n const matchNoBranch = line.match(/^([^:]+):(\\d+):(.+)$/);\n if (!matchNoBranch) continue;\n matchPath = matchNoBranch[1]!;\n lineNum = matchNoBranch[2]!;\n content = matchNoBranch[3]!;\n }\n\n const afsPath = this.buildBranchPath(branch, matchPath);\n\n if (processedFiles.has(afsPath)) continue;\n processedFiles.add(afsPath);\n\n const entry = this.buildEntry(afsPath);\n entry.summary = `Line ${lineNum}: ${content}`;\n entries.push(entry);\n\n if (entries.length >= limit) {\n break;\n }\n }\n\n return {\n data: entries,\n message: entries.length >= limit ? `Results truncated to limit ${limit}` : undefined,\n };\n } catch (error) {\n // git grep returns exit code 1 if no matches found\n if ((error as Error).message.includes(\"did not match any file(s)\")) {\n return { data: [] };\n }\n return { data: [], message: (error as Error).message };\n }\n }\n\n /**\n * Stat root\n */\n @Stat(\"/\")\n async statRootHandler(_ctx: RouteContext): Promise<AFSStatResult> {\n const entry = await this.readRootHandler(_ctx);\n if (!entry) {\n return { data: undefined };\n }\n // Return entry without content\n const { content: _content, ...rest } = entry;\n return { data: rest };\n }\n\n /**\n * Stat branch root\n */\n @Stat(\"/:branch\")\n async statBranchRootHandler(ctx: RouteContext<{ branch: string }>): Promise<AFSStatResult> {\n const entry = await this.readBranchRootHandler(ctx);\n if (!entry) {\n return { data: undefined };\n }\n // Return entry without content\n const { content: _content, ...rest } = entry;\n return { data: rest };\n }\n\n /**\n * Stat file or directory in branch\n */\n @Stat(\"/:branch/:path+\")\n async statHandler(ctx: RouteContext<{ branch: string; path: string }>): Promise<AFSStatResult> {\n const entry = await this.readBranchHandler(ctx);\n if (!entry) {\n return { data: undefined };\n }\n // Return entry without content\n const { content: _content, ...rest } = entry;\n return { data: rest };\n }\n\n // ========== Explain Handlers ==========\n\n /**\n * Explain root → repo info, branch list, default branch\n */\n @Explain(\"/\")\n async explainRootHandler(_ctx: RouteContext): Promise<AFSExplainResult> {\n await this.ready();\n\n const format = (_ctx.options as AFSExplainOptions)?.format || \"markdown\";\n const branches = await this.getBranches();\n const currentBranch = await this.git.revparse([\"--abbrev-ref\", \"HEAD\"]).then((b) => b.trim());\n\n let remoteUrl: string | undefined;\n try {\n remoteUrl = await this.git.remote([\"get-url\", \"origin\"]).then((u) => u?.trim());\n } catch {\n // No remote configured\n }\n\n const lines: string[] = [];\n lines.push(\"# Git Repository\");\n lines.push(\"\");\n lines.push(`**Provider:** ${this.name}`);\n if (this.description) {\n lines.push(`**Description:** ${this.description}`);\n }\n lines.push(`**Default Branch:** ${currentBranch}`);\n if (remoteUrl) {\n lines.push(`**Remote:** ${remoteUrl}`);\n }\n lines.push(`**Branches:** ${branches.length}`);\n lines.push(\"\");\n lines.push(\"## Branches\");\n lines.push(\"\");\n for (const branch of branches) {\n lines.push(`- ${branch}`);\n }\n\n return { content: lines.join(\"\\n\"), format };\n }\n\n /**\n * Explain branch → branch name, HEAD commit, file count\n */\n @Explain(\"/:branch\")\n async explainBranchHandler(ctx: RouteContext<{ branch: string }>): Promise<AFSExplainResult> {\n await this.ready();\n\n const format = (ctx.options as AFSExplainOptions)?.format || \"markdown\";\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n\n const lastCommit = await this.getLastCommit(branch);\n const fileCount = await this.getTreeFileCount(branch, \"\");\n\n const lines: string[] = [];\n lines.push(`# Branch: ${branch}`);\n lines.push(\"\");\n lines.push(`**HEAD Commit:** ${lastCommit.shortHash} - ${lastCommit.message}`);\n lines.push(`**Author:** ${lastCommit.author}`);\n lines.push(`**Date:** ${lastCommit.date}`);\n lines.push(`**Files:** ${fileCount} entries in tree`);\n\n return { content: lines.join(\"\\n\"), format };\n }\n\n /**\n * Explain file or directory → path, size, last modified commit\n */\n @Explain(\"/:branch/:path+\")\n async explainPathHandler(\n ctx: RouteContext<{ branch: string; path: string }>,\n ): Promise<AFSExplainResult> {\n await this.ready();\n\n const format = (ctx.options as AFSExplainOptions)?.format || \"markdown\";\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const filePath = ctx.params.path;\n\n const objectType = await this.git\n .raw([\"cat-file\", \"-t\", `${branch}:${filePath}`])\n .then((t) => t.trim())\n .catch(() => null);\n\n if (objectType === null) {\n throw new AFSNotFoundError(this.buildBranchPath(branch, filePath));\n }\n\n const isDir = objectType === \"tree\";\n const lines: string[] = [];\n\n lines.push(`# ${basename(filePath)}`);\n lines.push(\"\");\n lines.push(`**Path:** ${filePath}`);\n lines.push(`**Type:** ${isDir ? \"directory\" : \"file\"}`);\n\n if (!isDir) {\n const size = await this.git\n .raw([\"cat-file\", \"-s\", `${branch}:${filePath}`])\n .then((s) => Number.parseInt(s.trim(), 10));\n lines.push(`**Size:** ${size} bytes`);\n }\n\n // Get last commit that modified this path\n try {\n const logOutput = await this.git.raw([\n \"log\",\n \"-1\",\n \"--format=%H%n%h%n%an%n%aI%n%s\",\n branch,\n \"--\",\n filePath,\n ]);\n const logLines = logOutput.trim().split(\"\\n\");\n if (logLines.length >= 5) {\n lines.push(\"\");\n lines.push(\"## Last Modified\");\n lines.push(`**Commit:** ${logLines[1]} - ${logLines[4]}`);\n lines.push(`**Author:** ${logLines[2]}`);\n lines.push(`**Date:** ${logLines[3]}`);\n }\n } catch {\n // Ignore log errors\n }\n\n return { content: lines.join(\"\\n\"), format };\n }\n\n // ========== Capabilities ==========\n\n @Read(\"/.meta/.capabilities\")\n async readCapabilitiesHandler(_ctx: RouteContext): Promise<AFSEntry | undefined> {\n const operations = [\"list\", \"read\", \"stat\", \"explain\", \"search\"];\n if (this.accessMode === \"readwrite\") {\n operations.push(\"write\", \"delete\", \"rename\");\n }\n\n const actionCatalogs: CapabilitiesManifest[\"actions\"] = [];\n\n // diff is available in both modes conceptually, but exec() enforces readwrite\n // Include all actions in readwrite mode\n if (this.accessMode === \"readwrite\") {\n actionCatalogs.push({\n description: \"Git workflow actions\",\n catalog: [\n {\n name: \"diff\",\n description: \"Compare two branches or refs\",\n inputSchema: {\n type: \"object\",\n properties: {\n from: { type: \"string\", description: \"Source ref\" },\n to: { type: \"string\", description: \"Target ref\" },\n path: { type: \"string\", description: \"Optional path filter\" },\n },\n required: [\"from\", \"to\"],\n },\n },\n {\n name: \"create-branch\",\n description: \"Create a new branch\",\n inputSchema: {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"New branch name\" },\n from: { type: \"string\", description: \"Source ref (defaults to current HEAD)\" },\n },\n required: [\"name\"],\n },\n },\n {\n name: \"commit\",\n description: \"Commit staged changes\",\n inputSchema: {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Commit message\" },\n author: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n email: { type: \"string\" },\n },\n },\n },\n required: [\"message\"],\n },\n },\n {\n name: \"merge\",\n description: \"Merge a branch into the current branch\",\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"Branch to merge\" },\n message: { type: \"string\", description: \"Custom merge message\" },\n },\n required: [\"branch\"],\n },\n },\n ],\n discovery: {\n pathTemplate: \"/:branch/.actions\",\n note: \"Git workflow actions (readwrite mode only)\",\n },\n });\n }\n\n const manifest: CapabilitiesManifest = {\n schemaVersion: 1,\n provider: this.name,\n description: this.description || \"Git repository provider\",\n tools: [],\n actions: actionCatalogs,\n operations: this.getOperationsDeclaration(),\n };\n\n return {\n id: \"/.meta/.capabilities\",\n path: \"/.meta/.capabilities\",\n content: manifest,\n meta: { kind: \"afs:capabilities\", operations },\n };\n }\n\n // ========== Git Actions ==========\n\n /**\n * List available actions for a branch\n */\n @Actions(\"/:branch\")\n async listBranchActions(ctx: RouteContext<{ branch: string }>): Promise<AFSListResult> {\n if (this.accessMode !== \"readwrite\") {\n return { data: [] };\n }\n\n const basePath = `/${ctx.params.branch}/.actions`;\n return {\n data: [\n {\n id: \"diff\",\n path: `${basePath}/diff`,\n summary: \"Compare two branches or refs\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n from: { type: \"string\", description: \"Source ref\" },\n to: { type: \"string\", description: \"Target ref\" },\n path: { type: \"string\", description: \"Optional path filter\" },\n },\n required: [\"from\", \"to\"],\n },\n },\n },\n {\n id: \"create-branch\",\n path: `${basePath}/create-branch`,\n summary: \"Create a new branch from this ref\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"New branch name\" },\n from: { type: \"string\", description: \"Source ref (defaults to current HEAD)\" },\n },\n required: [\"name\"],\n },\n },\n },\n {\n id: \"commit\",\n path: `${basePath}/commit`,\n summary: \"Commit staged changes\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Commit message\" },\n author: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n email: { type: \"string\" },\n },\n },\n },\n required: [\"message\"],\n },\n },\n },\n {\n id: \"merge\",\n path: `${basePath}/merge`,\n summary: \"Merge another branch into this branch\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"Branch to merge\" },\n message: { type: \"string\", description: \"Custom merge message\" },\n },\n required: [\"branch\"],\n },\n },\n },\n ],\n };\n }\n\n /**\n * diff action — compare two branches or refs\n */\n @Actions.Exec(\"/:branch\", \"diff\")\n async diffAction(\n _ctx: RouteContext<{ branch: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n await this.ready();\n\n const from = args.from as string;\n const to = args.to as string;\n const pathFilter = args.path as string | undefined;\n\n if (!from || !to) {\n return {\n success: false,\n error: { code: \"INVALID_ARGS\", message: \"from and to are required\" },\n };\n }\n\n try {\n // Get diff stat\n const diffArgs = [\"diff\", \"--stat\", \"--name-only\", `${from}...${to}`];\n if (pathFilter) {\n diffArgs.push(\"--\", pathFilter);\n }\n const statOutput = await this.git.raw(diffArgs);\n const fileLines = statOutput\n .trim()\n .split(\"\\n\")\n .filter((l) => l.trim());\n const files = fileLines.map((path) => ({ path }));\n\n // Get diff patch\n const patchArgs = [\"diff\", `${from}...${to}`];\n if (pathFilter) {\n patchArgs.push(\"--\", pathFilter);\n }\n const patch = await this.git.raw(patchArgs);\n\n return {\n success: true,\n data: {\n from,\n to,\n files,\n patch,\n filesChanged: files.length,\n },\n };\n } catch (error) {\n return {\n success: false,\n error: {\n code: \"DIFF_FAILED\",\n message: (error as Error).message.replace(this.repoPath, \"<repo>\"),\n },\n };\n }\n }\n\n /**\n * create-branch action — create a new branch\n */\n @Actions.Exec(\"/:branch\", \"create-branch\")\n async createBranchAction(\n _ctx: RouteContext<{ branch: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n await this.ready();\n\n const name = args.name as string;\n const from = args.from as string | undefined;\n\n if (!name) {\n return { success: false, error: { code: \"INVALID_ARGS\", message: \"name is required\" } };\n }\n\n // Validate branch name - reject path traversal\n if (name.includes(\"..\")) {\n return {\n success: false,\n error: { code: \"INVALID_NAME\", message: \"Branch name contains invalid characters\" },\n };\n }\n\n try {\n if (from) {\n await this.git.raw([\"branch\", name, from]);\n } else {\n await this.git.raw([\"branch\", name]);\n }\n\n // Get the hash of the new branch\n const hash = await this.git.revparse([name]).then((h) => h.trim());\n\n return {\n success: true,\n data: { branch: name, hash },\n };\n } catch (error) {\n return {\n success: false,\n error: {\n code: \"CREATE_BRANCH_FAILED\",\n message: (error as Error).message.replace(this.repoPath, \"<repo>\"),\n },\n };\n }\n }\n\n /**\n * commit action — commit staged changes\n */\n @Actions.Exec(\"/:branch\", \"commit\")\n async commitAction(\n _ctx: RouteContext<{ branch: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n await this.ready();\n\n const message = args.message as string;\n if (!message) {\n return { success: false, error: { code: \"INVALID_ARGS\", message: \"message is required\" } };\n }\n\n const author = args.author as { name?: string; email?: string } | undefined;\n\n try {\n const git = simpleGit(this.repoPath);\n\n // Check for staged changes\n const status = await git.status();\n if (\n status.staged.length === 0 &&\n status.files.filter((f) => f.index !== \" \" && f.index !== \"?\").length === 0\n ) {\n return {\n success: false,\n error: { code: \"NO_CHANGES\", message: \"No staged changes to commit\" },\n };\n }\n\n // Configure author if provided\n if (author?.name) {\n await git.addConfig(\"user.name\", author.name, undefined, \"local\");\n }\n if (author?.email) {\n await git.addConfig(\"user.email\", author.email, undefined, \"local\");\n }\n\n const result = await git.commit(message);\n\n return {\n success: true,\n data: {\n hash: result.commit || \"\",\n message,\n filesChanged: result.summary.changes,\n },\n };\n } catch (error) {\n return {\n success: false,\n error: {\n code: \"COMMIT_FAILED\",\n message: (error as Error).message.replace(this.repoPath, \"<repo>\"),\n },\n };\n }\n }\n\n /**\n * merge action — merge a branch into current branch\n */\n @Actions.Exec(\"/:branch\", \"merge\")\n async mergeAction(\n _ctx: RouteContext<{ branch: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n await this.ready();\n\n const branch = args.branch as string;\n if (!branch) {\n return { success: false, error: { code: \"INVALID_ARGS\", message: \"branch is required\" } };\n }\n\n const customMessage = args.message as string | undefined;\n\n try {\n const git = simpleGit(this.repoPath);\n\n // Verify the branch exists\n const branches = await git.branchLocal();\n if (!branches.all.includes(branch)) {\n return {\n success: false,\n error: { code: \"BRANCH_NOT_FOUND\", message: `Branch '${branch}' not found` },\n };\n }\n\n const mergeArgs = [branch];\n if (customMessage) {\n mergeArgs.push(\"-m\", customMessage);\n }\n\n const result = await git.merge(mergeArgs);\n\n // Get the resulting commit hash\n const hash = await git.revparse([\"HEAD\"]).then((h) => h.trim());\n\n return {\n success: true,\n data: {\n hash,\n merged: branch,\n conflicts: result.conflicts || [],\n },\n };\n } catch (error) {\n // Abort merge if there was a conflict\n try {\n const git = simpleGit(this.repoPath);\n await git.merge([\"--abort\"]);\n } catch {\n // Ignore abort errors\n }\n\n return {\n success: false,\n error: {\n code: \"MERGE_FAILED\",\n message: (error as Error).message.replace(this.repoPath, \"<repo>\"),\n },\n };\n }\n }\n\n // ========== .log/ Virtual Tree ==========\n\n /**\n * List .log/ → commit list with pagination\n */\n @List(\"/:branch/.log\")\n async listLogHandler(ctx: RouteContext<{ branch: string }>): Promise<AFSListResult> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n\n const options = ctx.options as { limit?: number; offset?: number };\n const limit = Math.min(options?.limit || LIST_MAX_LIMIT, LIST_MAX_LIMIT);\n const offset = options?.offset || 0;\n\n const commits = await this.getCommitList(branch, limit, offset);\n const branchEncoded = this.encodeBranchName(branch);\n\n const entries: AFSEntry[] = commits.map((commit, i) =>\n this.buildEntry(`/${branchEncoded}/.log/${offset + i}`, {\n meta: {\n hash: commit.hash,\n shortHash: commit.shortHash,\n author: commit.author,\n date: commit.date,\n message: commit.message,\n },\n }),\n );\n\n return { data: entries };\n }\n\n /**\n * Read .log/{index} → commit diff/patch content\n */\n @Read(\"/:branch/.log/:index\")\n async readLogEntryHandler(\n ctx: RouteContext<{ branch: string; index: string }>,\n ): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const index = Number.parseInt(ctx.params.index, 10);\n\n if (Number.isNaN(index) || index < 0) {\n throw new AFSNotFoundError(`/${this.encodeBranchName(branch)}/.log/${ctx.params.index}`);\n }\n\n const commits = await this.getCommitList(branch, 1, index);\n if (commits.length === 0) {\n throw new AFSNotFoundError(`/${this.encodeBranchName(branch)}/.log/${index}`);\n }\n\n const commit = commits[0]!;\n\n // Get diff for this commit\n let diff: string;\n try {\n diff = await this.git.raw([\"show\", \"--stat\", \"--patch\", commit.hash]);\n } catch {\n diff = \"\";\n }\n\n const branchEncoded = this.encodeBranchName(branch);\n return this.buildEntry(`/${branchEncoded}/.log/${index}`, {\n content: diff,\n meta: {\n hash: commit.hash,\n shortHash: commit.shortHash,\n author: commit.author,\n date: commit.date,\n message: commit.message,\n },\n });\n }\n\n /**\n * Read .log/{index}/.meta → commit metadata only (no diff)\n */\n @Read(\"/:branch/.log/:index/.meta\")\n async readLogEntryMetaHandler(\n ctx: RouteContext<{ branch: string; index: string }>,\n ): Promise<AFSEntry | undefined> {\n await this.ready();\n\n const branch = this.decodeBranchName(ctx.params.branch);\n await this.ensureBranchExists(branch);\n const index = Number.parseInt(ctx.params.index, 10);\n\n if (Number.isNaN(index) || index < 0) {\n throw new AFSNotFoundError(\n `/${this.encodeBranchName(branch)}/.log/${ctx.params.index}/.meta`,\n );\n }\n\n const commits = await this.getCommitList(branch, 1, index);\n if (commits.length === 0) {\n throw new AFSNotFoundError(`/${this.encodeBranchName(branch)}/.log/${index}/.meta`);\n }\n\n const commit = commits[0]!;\n const branchEncoded = this.encodeBranchName(branch);\n return this.buildEntry(`/${branchEncoded}/.log/${index}/.meta`, {\n meta: {\n hash: commit.hash,\n shortHash: commit.shortHash,\n author: commit.author,\n date: commit.date,\n message: commit.message,\n },\n });\n }\n\n // ========== Private Helper Methods ==========\n\n /**\n * Decode branch name (replace ~ with /)\n */\n private async assertWithinWorktree(fullPath: string, worktreeRoot: string): Promise<void> {\n return assertPathWithinRoot(fullPath, worktreeRoot);\n }\n\n private decodeBranchName(encoded: string): string {\n return encoded.replace(/~/g, \"/\");\n }\n\n /**\n * Encode branch name (replace / with ~)\n */\n private encodeBranchName(branch: string): string {\n return branch.replace(/\\//g, \"~\");\n }\n\n /**\n * Parse AFS path into branch and file path\n * Branch names may contain slashes and are encoded with ~ in paths\n */\n private parsePath(path: string): { branch?: string; filePath: string } {\n const normalized = join(\"/\", path); // Ensure leading slash\n const segments = normalized.split(\"/\").filter(Boolean);\n\n if (segments.length === 0) {\n return { branch: undefined, filePath: \"\" };\n }\n\n // Decode branch name (first segment): replace ~ with /\n const branch = segments[0]!.replace(/~/g, \"/\");\n const filePath = segments.slice(1).join(\"/\");\n\n return { branch, filePath };\n }\n\n /**\n * Build AFS path with encoded branch name\n * Branch names with slashes are encoded by replacing / with ~\n */\n private buildBranchPath(branch: string, filePath?: string): string {\n const encodedBranch = this.encodeBranchName(branch);\n if (!filePath) {\n return `/${encodedBranch}`;\n }\n return `/${encodedBranch}/${filePath}`;\n }\n\n /**\n * Get list of available branches\n */\n private async getBranches(): Promise<string[]> {\n const branchSummary = await this.git.branchLocal();\n const allBranches = branchSummary.all;\n\n // Filter by allowed branches if specified\n if (this.options.branches && this.options.branches.length > 0) {\n return allBranches.filter((branch) => this.options.branches!.includes(branch));\n }\n\n return allBranches;\n }\n\n /**\n * Check if a branch exists, throw AFSNotFoundError if not\n */\n private async ensureBranchExists(branch: string): Promise<void> {\n const branches = await this.getBranches();\n if (!branches.includes(branch)) {\n throw new AFSNotFoundError(this.buildBranchPath(branch));\n }\n }\n\n /**\n * Get the number of children for a tree (directory) in git\n */\n private async getChildrenCount(branch: string, path: string): Promise<number> {\n try {\n const treeish = path ? `${branch}:${path}` : branch;\n const output = await this.git.raw([\"ls-tree\", treeish]);\n const lines = output.split(\"\\n\").filter((line) => line.trim());\n return lines.length;\n } catch {\n return 0;\n }\n }\n\n /**\n * Get the last commit on a branch\n */\n private async getLastCommit(\n branch: string,\n ): Promise<{ hash: string; shortHash: string; author: string; date: string; message: string }> {\n const output = await this.git.raw([\"log\", \"-1\", \"--format=%H%n%h%n%an%n%aI%n%s\", branch]);\n const lines = output.trim().split(\"\\n\");\n return {\n hash: lines[0] || \"\",\n shortHash: lines[1] || \"\",\n author: lines[2] || \"\",\n date: lines[3] || \"\",\n message: lines[4] || \"\",\n };\n }\n\n /**\n * Count total files in a tree (recursively)\n */\n private async getTreeFileCount(branch: string, path: string): Promise<number> {\n try {\n const treeish = path ? `${branch}:${path}` : branch;\n const output = await this.git.raw([\"ls-tree\", \"-r\", treeish]);\n const lines = output.split(\"\\n\").filter((line) => line.trim());\n return lines.length;\n } catch {\n return 0;\n }\n }\n\n /**\n * Get a list of commits on a branch with limit/offset\n */\n private async getCommitList(\n branch: string,\n limit: number,\n offset: number,\n ): Promise<{ hash: string; shortHash: string; author: string; date: string; message: string }[]> {\n try {\n const args = [\n \"log\",\n `--skip=${offset}`,\n `-${limit}`,\n \"--format=%H%n%h%n%an%n%aI%n%s%n---COMMIT_SEP---\",\n branch,\n ];\n const output = await this.git.raw(args);\n const blocks = output.split(\"---COMMIT_SEP---\").filter((b) => b.trim());\n\n return blocks.map((block) => {\n const lines = block.trim().split(\"\\n\");\n return {\n hash: lines[0] || \"\",\n shortHash: lines[1] || \"\",\n author: lines[2] || \"\",\n date: lines[3] || \"\",\n message: lines[4] || \"\",\n };\n });\n } catch {\n return [];\n }\n }\n\n /**\n * Ensure worktree exists for a branch (lazy creation)\n */\n private async ensureWorktree(branch: string): Promise<string> {\n if (this.worktrees.has(branch)) {\n return this.worktrees.get(branch)!;\n }\n\n // Check if this is the current branch in the main repo\n const currentBranch = await this.git.revparse([\"--abbrev-ref\", \"HEAD\"]);\n if (currentBranch.trim() === branch) {\n // Use the main repo path for the current branch\n this.worktrees.set(branch, this.repoPath);\n return this.repoPath;\n }\n\n const worktreePath = join(this.tempBase, branch);\n\n // Check if worktree directory already exists\n const exists = await stat(worktreePath)\n .then(() => true)\n .catch(() => false);\n\n if (!exists) {\n await mkdir(this.tempBase, { recursive: true });\n await this.git.raw([\"worktree\", \"add\", worktreePath, branch]);\n }\n\n this.worktrees.set(branch, worktreePath);\n return worktreePath;\n }\n\n /**\n * List files using git ls-tree (no worktree needed)\n * Note: list() returns only children, never the path itself (per new semantics)\n */\n private async listWithGitLsTree(\n branch: string,\n path: string,\n options?: { maxDepth?: number; limit?: number },\n ): Promise<AFSListResult> {\n const maxDepth = options?.maxDepth ?? 1;\n const limit = Math.min(options?.limit || LIST_MAX_LIMIT, LIST_MAX_LIMIT);\n\n const entries: AFSEntry[] = [];\n const targetPath = path || \"\";\n const treeish = targetPath ? `${branch}:${targetPath}` : branch;\n\n try {\n // Check if the path exists and is a directory\n const pathType = await this.git\n .raw([\"cat-file\", \"-t\", treeish])\n .then((t) => t.trim())\n .catch(() => null);\n\n if (pathType === null) {\n // Path doesn't exist\n throw new AFSNotFoundError(this.buildBranchPath(branch, path));\n }\n\n // If it's a file (blob), it has no children\n if (pathType === \"blob\") {\n return { data: [] };\n }\n\n // It's a directory\n // maxDepth: 0 means return no children\n if (maxDepth === 0) {\n return { data: [] };\n }\n\n // List directory contents via BFS\n interface QueueItem {\n path: string;\n depth: number;\n }\n\n const queue: QueueItem[] = [{ path: targetPath, depth: 0 }];\n\n while (queue.length > 0) {\n const item = queue.shift()!;\n const { path: itemPath, depth } = item;\n\n // List directory contents\n const itemTreeish = itemPath ? `${branch}:${itemPath}` : branch;\n const output = await this.git.raw([\"ls-tree\", \"-l\", itemTreeish]);\n\n const lines = output\n .split(\"\\n\")\n .filter((line) => line.trim())\n .slice(0, limit - entries.length);\n\n for (const line of lines) {\n // Format: <mode> <type> <hash> <size (with padding)> <name>\n const match = line.match(/^(\\d+)\\s+(blob|tree)\\s+(\\w+)\\s+(-|\\d+)\\s+(.+)$/);\n if (!match) continue;\n\n const type = match[2]!;\n const sizeStr = match[4]!;\n const name = match[5]!;\n const isDirectory = type === \"tree\";\n const size = sizeStr === \"-\" ? undefined : Number.parseInt(sizeStr, 10);\n\n const fullPath = itemPath ? `${itemPath}/${name}` : name;\n const afsPath = this.buildBranchPath(branch, fullPath);\n\n // For directories, get children count\n const childrenCount = isDirectory\n ? await this.getChildrenCount(branch, fullPath)\n : undefined;\n\n entries.push(\n this.buildEntry(afsPath, {\n meta: { kind: isDirectory ? \"git:directory\" : \"git:file\", size, childrenCount },\n }),\n );\n\n // Add to queue if it's a directory and we haven't reached max depth\n if (isDirectory && depth + 1 < maxDepth) {\n queue.push({ path: fullPath, depth: depth + 1 });\n }\n\n // Check limit\n if (entries.length >= limit) {\n return { data: entries };\n }\n }\n }\n\n return { data: entries };\n } catch (error) {\n // Re-throw AFSNotFoundError as-is\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw new Error(`Failed to list: ${(error as Error).message}`);\n }\n }\n\n // ========== Public Git Operations ==========\n\n /**\n * Fetch latest changes from remote\n */\n async fetch(): Promise<void> {\n await this.ready();\n await this.git.fetch();\n }\n\n /**\n * Pull latest changes from remote for current branch\n */\n async pull(): Promise<void> {\n await this.ready();\n await this.git.pull();\n }\n\n /**\n * Push local changes to remote\n */\n async push(branch?: string): Promise<void> {\n await this.ready();\n if (branch) {\n await this.git.push(\"origin\", branch);\n } else {\n await this.git.push();\n }\n }\n\n /**\n * Cleanup all worktrees (useful when unmounting)\n */\n async cleanup(): Promise<void> {\n await this.ready();\n for (const [_branch, worktreePath] of this.worktrees) {\n try {\n await this.git.raw([\"worktree\", \"remove\", worktreePath, \"--force\"]);\n } catch (_error) {\n // Ignore errors during cleanup\n }\n }\n this.worktrees.clear();\n\n // Remove temp directory\n try {\n await rm(this.tempBase, { recursive: true, force: true });\n } catch {\n // Ignore errors\n }\n\n // Cleanup cloned repository if auto-cloned and autoCleanup enabled\n const autoCleanup = this.options.autoCleanup ?? true;\n if (this.isAutoCloned && autoCleanup && this.clonedPath) {\n try {\n await rm(this.clonedPath, { recursive: true, force: true });\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n}\n\nconst _typeCheck: AFSModuleClass<AFSGit, AFSGitOptions> = AFSGit;\n\nexport default AFSGit;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aigne/afs-git",
|
|
3
|
-
"version": "1.11.0-beta.
|
|
3
|
+
"version": "1.11.0-beta.13",
|
|
4
4
|
"description": "AIGNE AFS module for git storage",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"publishConfig": {
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"simple-git": "^3.27.0",
|
|
37
37
|
"zod": "^4.0.0",
|
|
38
|
-
"@aigne/afs": "^1.11.0-beta.
|
|
38
|
+
"@aigne/afs-provider-utils": "^1.11.0-beta.13",
|
|
39
|
+
"@aigne/afs": "^1.11.0-beta.13"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"@types/bun": "^1.3.6",
|
|
@@ -43,9 +44,9 @@
|
|
|
43
44
|
"rimraf": "^6.1.2",
|
|
44
45
|
"tsdown": "0.20.0-beta.3",
|
|
45
46
|
"typescript": "5.9.2",
|
|
46
|
-
"@aigne/scripts": "0.0.0",
|
|
47
47
|
"@aigne/typescript-config": "0.0.0",
|
|
48
|
-
"@aigne/
|
|
48
|
+
"@aigne/scripts": "0.0.0",
|
|
49
|
+
"@aigne/afs-testing": "1.11.0-beta.13"
|
|
49
50
|
},
|
|
50
51
|
"scripts": {
|
|
51
52
|
"build": "tsdown",
|