@garuda-agent/garuda 0.14.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -1
- package/README.md +78 -8
- package/dist/acpCommand-AJJBKAE5.js +191 -0
- package/dist/chunk-23FXBNBB.js +46 -0
- package/dist/chunk-3EQU2GUX.js +27 -0
- package/dist/{chunk-YIZWYUBG.js → chunk-44QN5SWZ.js} +2 -2
- package/dist/chunk-4JSUUDFQ.js +118 -0
- package/dist/{chunk-KWRJREUH.js → chunk-B25VL4RU.js} +1 -42
- package/dist/{chunk-U4OQVT2T.js → chunk-B6FA4I7Q.js} +2 -2
- package/dist/chunk-EIXUSVFX.js +116 -0
- package/dist/{chunk-YM6T4J5V.js → chunk-ENLDPN4S.js} +8 -111
- package/dist/{chunk-D5O5CIWK.js → chunk-GXZACJUI.js} +8 -25
- package/dist/{chunk-QOW5TK4L.js → chunk-HE2K657U.js} +9 -7
- package/dist/chunk-NP6RIC25.js +331 -0
- package/dist/chunk-NXJ4XLB5.js +19 -0
- package/dist/chunk-R7I2NBPX.js +71 -0
- package/dist/{chunk-ZGZ2ZADL.js → chunk-TUUEBHG2.js} +2 -2
- package/dist/{chunk-W7BPYQMW.js → chunk-ULAJPMY7.js} +1 -67
- package/dist/{chunk-35GMLLJW.js → chunk-USCUXCAU.js} +2 -2
- package/dist/{chunk-ITITLNAR.js → chunk-WIJL72ZE.js} +150 -234
- package/dist/chunk-XOK5OZOC.js +9 -0
- package/dist/cli/index.js +68 -39
- package/dist/{create-Y5SXJUXO.js → create-VXIH2GUZ.js} +6 -5
- package/dist/{evalCommand-5TG2CKZY.js → evalCommand-OX2LQYEK.js} +35 -18
- package/dist/{inkChat-3J26R24L.js → inkChat-YMMMGU7Y.js} +16 -10
- package/dist/{install-XWLU22OH.js → install-EWDSPFFZ.js} +4 -3
- package/dist/{jobCommand-IYWZWTSI.js → jobCommand-ACSGSBQ4.js} +9 -7
- package/dist/{launchd-2FL5UN2U.js → launchd-SSX362VG.js} +5 -4
- package/dist/{lspCommand-TJEZBV2E.js → lspCommand-JRXKRSL7.js} +4 -3
- package/dist/{manager-EBTYO2GR.js → manager-AWU4NHHK.js} +4 -3
- package/dist/{nightCommand-DUNWJASI.js → nightCommand-65MUEV32.js} +9 -7
- package/dist/{remove-AENULZZE.js → remove-6GDSTAIO.js} +4 -3
- package/dist/{run-66Y6R2KU.js → run-ML6GSPN4.js} +5 -3
- package/dist/server-KHHNHUYV.js +694 -0
- package/dist/setupCommand-VA7D5MZK.js +303 -0
- package/package.json +2 -1
- package/dist/chunk-6TWBYMY7.js +0 -9
- package/dist/chunk-V7WPGQLB.js +0 -207
|
@@ -1,32 +1,41 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadLspConfig
|
|
4
|
+
} from "./chunk-G2AX7MGS.js";
|
|
2
5
|
import {
|
|
3
6
|
hasServerBlocks,
|
|
4
7
|
serverCallText,
|
|
5
8
|
serverPairText,
|
|
6
9
|
withoutServerBlocks
|
|
7
10
|
} from "./chunk-A2QAIRQL.js";
|
|
11
|
+
import {
|
|
12
|
+
gateProjectSettings
|
|
13
|
+
} from "./chunk-ULAJPMY7.js";
|
|
14
|
+
import {
|
|
15
|
+
REDACTED,
|
|
16
|
+
Redactor,
|
|
17
|
+
writeFileAtomic
|
|
18
|
+
} from "./chunk-EIXUSVFX.js";
|
|
8
19
|
import {
|
|
9
20
|
aliasModel,
|
|
10
21
|
contextSize,
|
|
11
|
-
gateProjectSettings,
|
|
12
22
|
knownModels,
|
|
13
23
|
lookupModel,
|
|
14
24
|
responseCost,
|
|
15
25
|
totalTokens
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import {
|
|
18
|
-
loadLspConfig
|
|
19
|
-
} from "./chunk-G2AX7MGS.js";
|
|
26
|
+
} from "./chunk-R7I2NBPX.js";
|
|
20
27
|
import {
|
|
21
|
-
BUILTIN_COMMANDS,
|
|
22
28
|
MAX_RESULTS,
|
|
23
29
|
providerLabel,
|
|
24
30
|
saveSearchUse,
|
|
25
31
|
search
|
|
26
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-B25VL4RU.js";
|
|
33
|
+
import {
|
|
34
|
+
BUILTIN_COMMANDS
|
|
35
|
+
} from "./chunk-23FXBNBB.js";
|
|
27
36
|
import {
|
|
28
37
|
VERSION
|
|
29
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-XOK5OZOC.js";
|
|
30
39
|
import {
|
|
31
40
|
TrustStore
|
|
32
41
|
} from "./chunk-5DEWP3YX.js";
|
|
@@ -50,24 +59,26 @@ import {
|
|
|
50
59
|
} from "./chunk-OUCV3FCT.js";
|
|
51
60
|
import {
|
|
52
61
|
DEFAULT_ENV_ALLOWLIST,
|
|
53
|
-
EFFORTS,
|
|
54
62
|
NETWORK_PORTS,
|
|
55
63
|
PermissionEngine,
|
|
56
|
-
ZERO_USAGE,
|
|
57
|
-
addUsage,
|
|
58
64
|
createExecutor,
|
|
59
65
|
describeAllowlist,
|
|
60
66
|
expandAllowlist,
|
|
61
67
|
hostAllowed,
|
|
62
68
|
isSensitive,
|
|
63
69
|
loadSettings
|
|
64
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-GXZACJUI.js";
|
|
65
71
|
import {
|
|
66
72
|
PathOutsideRootError,
|
|
67
73
|
displayPath,
|
|
68
74
|
isInside,
|
|
69
75
|
resolveInRoot
|
|
70
76
|
} from "./chunk-WMOLXTBU.js";
|
|
77
|
+
import {
|
|
78
|
+
EFFORTS,
|
|
79
|
+
ZERO_USAGE,
|
|
80
|
+
addUsage
|
|
81
|
+
} from "./chunk-3EQU2GUX.js";
|
|
71
82
|
import {
|
|
72
83
|
assertModelAllowedByPolicy,
|
|
73
84
|
ignoredProjectPolicy,
|
|
@@ -84,71 +95,6 @@ import { randomBytes } from "crypto";
|
|
|
84
95
|
import { appendFileSync, mkdirSync } from "fs";
|
|
85
96
|
import { readdir, readFile, rm, stat, utimes } from "fs/promises";
|
|
86
97
|
import { basename, join } from "path";
|
|
87
|
-
|
|
88
|
-
// src/session/redact.ts
|
|
89
|
-
var REDACTED = "[REDACTED]";
|
|
90
|
-
var PATTERNS = [
|
|
91
|
-
/-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g,
|
|
92
|
-
/sk-ant-[A-Za-z0-9_-]{16,}/g,
|
|
93
|
-
/\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}/g,
|
|
94
|
-
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
95
|
-
/\bgh[pousr]_[A-Za-z0-9]{30,}\b/g,
|
|
96
|
-
/\bgithub_pat_[A-Za-z0-9_]{30,}\b/g,
|
|
97
|
-
/\bxox[abprs]-[A-Za-z0-9-]{10,}/g,
|
|
98
|
-
/\bAIza[0-9A-Za-z_-]{35}\b/g,
|
|
99
|
-
/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
100
|
-
];
|
|
101
|
-
var ASSIGNMENT = /\b([A-Za-z0-9_]*(?:api[_-]?key|secret|token|password|passwd)[A-Za-z0-9_]*)(["']?\s*[:=]\s*["']?)([^\s"'`,;]{8,})/gi;
|
|
102
|
-
var OPAQUE_KEYS = /* @__PURE__ */ new Set(["encrypted_content", "encrypted_index", "signature"]);
|
|
103
|
-
function looksLikeCode(name, value, next) {
|
|
104
|
-
if (/^[\d_.]+$/.test(value)) return !/pass|secret/i.test(name);
|
|
105
|
-
if (value.includes("(") || next === "(") return true;
|
|
106
|
-
return /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(value) && !/\d{4,}/.test(value);
|
|
107
|
-
}
|
|
108
|
-
var SECRET_ENV_NAME = /(KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL)/i;
|
|
109
|
-
var movedSecrets = /* @__PURE__ */ new Map();
|
|
110
|
-
function keepSecretForRedaction(name, value) {
|
|
111
|
-
movedSecrets.set(name, value);
|
|
112
|
-
}
|
|
113
|
-
var Redactor = class {
|
|
114
|
-
literals;
|
|
115
|
-
/**
|
|
116
|
-
* `env`: variables whose names look secret have their values removed wherever they appear.
|
|
117
|
-
* Default: this process's environment and the secrets moved out of it.
|
|
118
|
-
*/
|
|
119
|
-
constructor(env = { ...process.env, ...Object.fromEntries(movedSecrets) }) {
|
|
120
|
-
this.literals = Object.entries(env).filter(([name, value]) => SECRET_ENV_NAME.test(name) && (value?.length ?? 0) >= 8).map(([, value]) => value).sort((a, b) => b.length - a.length);
|
|
121
|
-
}
|
|
122
|
-
text(input12) {
|
|
123
|
-
let out = input12;
|
|
124
|
-
for (const literal of this.literals) out = out.split(literal).join(REDACTED);
|
|
125
|
-
for (const pattern of PATTERNS) out = out.replace(pattern, REDACTED);
|
|
126
|
-
return out.replace(
|
|
127
|
-
ASSIGNMENT,
|
|
128
|
-
(all, name, sep4, value, at) => looksLikeCode(name, value, out[at + all.length]) ? all : `${name}${sep4}${REDACTED}`
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
/** A copy of `value` with every string redacted. */
|
|
132
|
-
value(value) {
|
|
133
|
-
return this.walk(value);
|
|
134
|
-
}
|
|
135
|
-
walk(value) {
|
|
136
|
-
if (typeof value === "string") return this.text(value);
|
|
137
|
-
if (Array.isArray(value)) return value.map((item) => this.walk(item));
|
|
138
|
-
if (value !== null && typeof value === "object") {
|
|
139
|
-
const redactedThinking = value.type === "redacted_thinking";
|
|
140
|
-
return Object.fromEntries(
|
|
141
|
-
Object.entries(value).map(([k, v]) => [
|
|
142
|
-
k,
|
|
143
|
-
(OPAQUE_KEYS.has(k) || redactedThinking && k === "data") && typeof v === "string" ? v : this.walk(v)
|
|
144
|
-
])
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
return value;
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
// src/session/store.ts
|
|
152
98
|
var SESSIONS_DIR = join(".garuda", "sessions");
|
|
153
99
|
var FileSessionStore = class {
|
|
154
100
|
dir;
|
|
@@ -1392,15 +1338,22 @@ ${z.prettifyError(parsed.error)}`,
|
|
|
1392
1338
|
}
|
|
1393
1339
|
const readOnly = tool.readOnly && info?.mutates !== true;
|
|
1394
1340
|
const decision = await context.permissions.check(
|
|
1395
|
-
|
|
1341
|
+
{
|
|
1342
|
+
tool: tool.name,
|
|
1343
|
+
readOnly,
|
|
1344
|
+
...info === void 0 ? {} : { info },
|
|
1345
|
+
// The question names this call (0.15: ACP shows it next to the right tool call).
|
|
1346
|
+
callId: call.id
|
|
1347
|
+
},
|
|
1396
1348
|
context.signal
|
|
1397
1349
|
);
|
|
1398
1350
|
if (!decision.allowed) {
|
|
1399
1351
|
return { content: `Permission denied: ${decision.reason}`, isError: true, denied: true };
|
|
1400
1352
|
}
|
|
1401
1353
|
startTime = Date.now();
|
|
1402
|
-
const runContext = decision.hunks === void 0 ? context : {
|
|
1354
|
+
const runContext = decision.hunks === void 0 ? { ...context, callId: call.id } : {
|
|
1403
1355
|
...context,
|
|
1356
|
+
callId: call.id,
|
|
1404
1357
|
approvedHunks: decision.hunks,
|
|
1405
1358
|
...info?.preview === void 0 ? {} : { approvedPreview: info.preview }
|
|
1406
1359
|
};
|
|
@@ -1927,50 +1880,6 @@ var repoMapTool = {
|
|
|
1927
1880
|
// src/tools/editFile.ts
|
|
1928
1881
|
import { readFile as readFile3 } from "fs/promises";
|
|
1929
1882
|
import { z as z4 } from "zod";
|
|
1930
|
-
|
|
1931
|
-
// src/tools/atomicWrite.ts
|
|
1932
|
-
import { randomBytes as randomBytes3 } from "crypto";
|
|
1933
|
-
import { chmod, link, realpath, rename, stat as stat2, unlink, writeFile } from "fs/promises";
|
|
1934
|
-
import { basename as basename3, dirname, join as join4 } from "path";
|
|
1935
|
-
async function writeFileAtomic(path, content, { createOnly }) {
|
|
1936
|
-
const target = createOnly ? path : await realpath(path);
|
|
1937
|
-
const temp = join4(
|
|
1938
|
-
dirname(target),
|
|
1939
|
-
`.${basename3(target)}.garuda-${randomBytes3(4).toString("hex")}.tmp`
|
|
1940
|
-
);
|
|
1941
|
-
const mode = createOnly ? void 0 : (await stat2(target)).mode & 4095;
|
|
1942
|
-
await writeFile(temp, content, { flag: "wx", ...mode === void 0 ? {} : { mode } });
|
|
1943
|
-
try {
|
|
1944
|
-
if (createOnly) {
|
|
1945
|
-
try {
|
|
1946
|
-
await link(temp, target);
|
|
1947
|
-
} catch (error) {
|
|
1948
|
-
const code = error.code;
|
|
1949
|
-
if (code !== "EPERM" && code !== "ENOTSUP" && code !== "EXDEV") throw error;
|
|
1950
|
-
if (await exists(target)) throw Object.assign(new Error("EEXIST"), { code: "EEXIST" });
|
|
1951
|
-
await rename(temp, target);
|
|
1952
|
-
return;
|
|
1953
|
-
}
|
|
1954
|
-
} else {
|
|
1955
|
-
await chmod(temp, mode ?? 420);
|
|
1956
|
-
await rename(temp, target);
|
|
1957
|
-
return;
|
|
1958
|
-
}
|
|
1959
|
-
} finally {
|
|
1960
|
-
await unlink(temp).catch(() => {
|
|
1961
|
-
});
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
async function exists(path) {
|
|
1965
|
-
try {
|
|
1966
|
-
await stat2(path);
|
|
1967
|
-
return true;
|
|
1968
|
-
} catch {
|
|
1969
|
-
return false;
|
|
1970
|
-
}
|
|
1971
|
-
}
|
|
1972
|
-
|
|
1973
|
-
// src/tools/editFile.ts
|
|
1974
1883
|
var input2 = z4.object({
|
|
1975
1884
|
path: z4.string().min(1).describe("File path, relative to the working root."),
|
|
1976
1885
|
old_string: z4.string().min(1).describe("The exact text to replace. It must occur exactly once in the file."),
|
|
@@ -1994,6 +1903,7 @@ var editFileTool = {
|
|
|
1994
1903
|
return {
|
|
1995
1904
|
target: { kind: "path", path: edit.shown },
|
|
1996
1905
|
preview,
|
|
1906
|
+
change: { path: edit.absolute, oldText: edit.before, newText: edit.after },
|
|
1997
1907
|
// Hunk-by-hunk approval only when the user sees every hunk (U0).
|
|
1998
1908
|
...previewIsComplete(preview) ? { hunks: true } : {}
|
|
1999
1909
|
};
|
|
@@ -2075,11 +1985,11 @@ function countOccurrences(text, search3) {
|
|
|
2075
1985
|
}
|
|
2076
1986
|
|
|
2077
1987
|
// src/tools/glob.ts
|
|
2078
|
-
import { stat as
|
|
1988
|
+
import { stat as stat3 } from "fs/promises";
|
|
2079
1989
|
import { z as z5 } from "zod";
|
|
2080
1990
|
|
|
2081
1991
|
// src/tools/files.ts
|
|
2082
|
-
import { stat as
|
|
1992
|
+
import { stat as stat2 } from "fs/promises";
|
|
2083
1993
|
import { globby } from "globby";
|
|
2084
1994
|
async function listFiles(base, pattern, root) {
|
|
2085
1995
|
assertSafePattern(pattern);
|
|
@@ -2104,7 +2014,7 @@ function assertSafePattern(pattern) {
|
|
|
2104
2014
|
}
|
|
2105
2015
|
async function isDirectory(path) {
|
|
2106
2016
|
try {
|
|
2107
|
-
return (await
|
|
2017
|
+
return (await stat2(path)).isDirectory();
|
|
2108
2018
|
} catch {
|
|
2109
2019
|
return false;
|
|
2110
2020
|
}
|
|
@@ -2130,7 +2040,7 @@ var globTool = {
|
|
|
2130
2040
|
(p) => !permissions.deniedByPolicy(displayPath(root, p))
|
|
2131
2041
|
);
|
|
2132
2042
|
const withTimes = await Promise.all(
|
|
2133
|
-
paths.map(async (p) => ({ p, mtime: (await
|
|
2043
|
+
paths.map(async (p) => ({ p, mtime: (await stat3(p).catch(() => void 0))?.mtimeMs ?? 0 }))
|
|
2134
2044
|
);
|
|
2135
2045
|
withTimes.sort((a, b) => b.mtime - a.mtime || a.p.localeCompare(b.p));
|
|
2136
2046
|
return {
|
|
@@ -2148,7 +2058,7 @@ var globTool = {
|
|
|
2148
2058
|
};
|
|
2149
2059
|
|
|
2150
2060
|
// src/tools/grep.ts
|
|
2151
|
-
import { lstat, readFile as readFile4, realpath
|
|
2061
|
+
import { lstat, readFile as readFile4, realpath, stat as stat4 } from "fs/promises";
|
|
2152
2062
|
import { z as z6 } from "zod";
|
|
2153
2063
|
|
|
2154
2064
|
// src/tools/regexWorker.ts
|
|
@@ -2298,13 +2208,13 @@ async function search2(args, root, signal, permissions, matcher, mode, max, cont
|
|
|
2298
2208
|
const shownPath = displayPath(root, file2);
|
|
2299
2209
|
if (isSensitive(shownPath) || permissions.deniedByPolicy(shownPath)) continue;
|
|
2300
2210
|
if ((await lstat(file2).catch(() => void 0))?.isSymbolicLink() === true) {
|
|
2301
|
-
realRoot ??= await
|
|
2302
|
-
const real = await
|
|
2211
|
+
realRoot ??= await realpath(root);
|
|
2212
|
+
const real = await realpath(file2).catch(() => void 0);
|
|
2303
2213
|
if (real === void 0 || !isInside(realRoot, real)) continue;
|
|
2304
2214
|
const realShown = displayPath(realRoot, real);
|
|
2305
2215
|
if (isSensitive(realShown) || permissions.deniedByPolicy(realShown)) continue;
|
|
2306
2216
|
}
|
|
2307
|
-
const info = await
|
|
2217
|
+
const info = await stat4(file2).catch(() => void 0);
|
|
2308
2218
|
if (info === void 0 || !info.isFile() || info.size > LIMITS.grepFileBytes) continue;
|
|
2309
2219
|
const buffer = await readFile4(file2);
|
|
2310
2220
|
if (looksBinary(buffer)) continue;
|
|
@@ -2577,7 +2487,7 @@ var processManagerTool = {
|
|
|
2577
2487
|
};
|
|
2578
2488
|
|
|
2579
2489
|
// src/tools/readFile.ts
|
|
2580
|
-
import { readFile as readFile5, stat as
|
|
2490
|
+
import { readFile as readFile5, stat as stat5 } from "fs/promises";
|
|
2581
2491
|
import { z as z9 } from "zod";
|
|
2582
2492
|
var input6 = z9.object({
|
|
2583
2493
|
path: z9.string().min(1).describe("File path, relative to the working root."),
|
|
@@ -2599,7 +2509,7 @@ var readFileTool = {
|
|
|
2599
2509
|
async run({ path, offset = 1, limit = LIMITS.readLines }, { root, files }) {
|
|
2600
2510
|
const absolute = await resolveInRoot(root, path);
|
|
2601
2511
|
const shown = displayPath(root, absolute);
|
|
2602
|
-
const info = await
|
|
2512
|
+
const info = await stat5(absolute).catch(() => void 0);
|
|
2603
2513
|
if (info === void 0) throw new Error(`${shown} does not exist.`);
|
|
2604
2514
|
if (info.isDirectory()) throw new Error(`${shown} is a folder. Use glob to list its files.`);
|
|
2605
2515
|
if (info.size > LIMITS.readFileBytes) {
|
|
@@ -2632,23 +2542,23 @@ var readFileTool = {
|
|
|
2632
2542
|
|
|
2633
2543
|
// src/tools/remember.ts
|
|
2634
2544
|
import { mkdir as mkdir2, readFile as readFile7 } from "fs/promises";
|
|
2635
|
-
import { dirname
|
|
2545
|
+
import { dirname } from "path";
|
|
2636
2546
|
import { z as z10 } from "zod";
|
|
2637
2547
|
|
|
2638
2548
|
// src/context/instructions.ts
|
|
2639
|
-
import { readFile as readFile6, realpath as
|
|
2640
|
-
import { join as
|
|
2549
|
+
import { readFile as readFile6, realpath as realpath2 } from "fs/promises";
|
|
2550
|
+
import { join as join4 } from "path";
|
|
2641
2551
|
async function readInRoot(root, name) {
|
|
2642
|
-
const file2 =
|
|
2552
|
+
const file2 = join4(root, name);
|
|
2643
2553
|
let real;
|
|
2644
2554
|
try {
|
|
2645
|
-
real = await
|
|
2555
|
+
real = await realpath2(file2);
|
|
2646
2556
|
} catch (error) {
|
|
2647
2557
|
const code = error.code;
|
|
2648
2558
|
if (code === "ENOENT" || code === "ENOTDIR") return void 0;
|
|
2649
2559
|
throw error;
|
|
2650
2560
|
}
|
|
2651
|
-
if (!isInside(await
|
|
2561
|
+
if (!isInside(await realpath2(root), real)) return void 0;
|
|
2652
2562
|
try {
|
|
2653
2563
|
return await readFile6(real, "utf8");
|
|
2654
2564
|
} catch (error) {
|
|
@@ -2862,7 +2772,7 @@ var rememberTool = {
|
|
|
2862
2772
|
`The project memory is full (${MEMORY_MAX_CHARS} characters). Ask the user to clean up ${MEMORY_FILE}.`
|
|
2863
2773
|
);
|
|
2864
2774
|
}
|
|
2865
|
-
await mkdir2(
|
|
2775
|
+
await mkdir2(dirname(path), { recursive: true });
|
|
2866
2776
|
await writeFileAtomic(path, next, { createOnly: current === "" });
|
|
2867
2777
|
return `Saved to ${MEMORY_FILE}. It loads in the next session.`;
|
|
2868
2778
|
}
|
|
@@ -3183,7 +3093,8 @@ function createWebFetchTool(options = {}) {
|
|
|
3183
3093
|
target: { kind: "url", url: next.toString(), host: next.hostname },
|
|
3184
3094
|
preview: ` GET ${next.toString()}
|
|
3185
3095
|
(a redirect from ${new URL(key).host})`
|
|
3186
|
-
}
|
|
3096
|
+
},
|
|
3097
|
+
...context.callId === void 0 ? {} : { callId: context.callId }
|
|
3187
3098
|
},
|
|
3188
3099
|
context.signal
|
|
3189
3100
|
);
|
|
@@ -3224,7 +3135,7 @@ function pageText(page, start, maxChars) {
|
|
|
3224
3135
|
|
|
3225
3136
|
// src/tools/writeFile.ts
|
|
3226
3137
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
3227
|
-
import { dirname as
|
|
3138
|
+
import { dirname as dirname2 } from "path";
|
|
3228
3139
|
import { z as z13 } from "zod";
|
|
3229
3140
|
var input9 = z13.object({
|
|
3230
3141
|
path: z13.string().min(1).describe("Path of the new file, relative to the working root."),
|
|
@@ -3240,14 +3151,20 @@ var writeFileTool = {
|
|
|
3240
3151
|
inputSchema: input9,
|
|
3241
3152
|
readOnly: false,
|
|
3242
3153
|
async describe({ path, content }, { root }) {
|
|
3243
|
-
const
|
|
3244
|
-
|
|
3154
|
+
const absolute = await resolveInRoot(root, path);
|
|
3155
|
+
const shown = displayPath(root, absolute);
|
|
3156
|
+
return {
|
|
3157
|
+
target: { kind: "path", path: shown },
|
|
3158
|
+
preview: unifiedDiff(shown, "", content),
|
|
3159
|
+
// write_file only creates files, so there is no old text (0.15: ACP's diff view).
|
|
3160
|
+
change: { path: absolute, oldText: null, newText: content }
|
|
3161
|
+
};
|
|
3245
3162
|
},
|
|
3246
3163
|
async run({ path, content }, context) {
|
|
3247
3164
|
const { root, files } = context;
|
|
3248
3165
|
const absolute = await resolveInRoot(root, path);
|
|
3249
3166
|
const shown = displayPath(root, absolute);
|
|
3250
|
-
await mkdir3(
|
|
3167
|
+
await mkdir3(dirname2(absolute), { recursive: true });
|
|
3251
3168
|
try {
|
|
3252
3169
|
await writeFileAtomic(absolute, content, { createOnly: true });
|
|
3253
3170
|
} catch (error) {
|
|
@@ -3300,7 +3217,7 @@ function defaultTools({
|
|
|
3300
3217
|
// src/app/runtime.ts
|
|
3301
3218
|
import { readFile as readFile16 } from "fs/promises";
|
|
3302
3219
|
import { homedir as homedir4 } from "os";
|
|
3303
|
-
import { join as
|
|
3220
|
+
import { join as join17 } from "path";
|
|
3304
3221
|
|
|
3305
3222
|
// src/agents/agentTool.ts
|
|
3306
3223
|
import { z as z14 } from "zod";
|
|
@@ -3461,7 +3378,7 @@ function describeCall(call) {
|
|
|
3461
3378
|
// src/agents/custom.ts
|
|
3462
3379
|
import { createHash as createHash3 } from "crypto";
|
|
3463
3380
|
import { lstat as lstat2, readdir as readdir3, readFile as readFile8 } from "fs/promises";
|
|
3464
|
-
import { join as
|
|
3381
|
+
import { join as join5 } from "path";
|
|
3465
3382
|
|
|
3466
3383
|
// src/skills/frontmatter.ts
|
|
3467
3384
|
function parseFrontmatterBlock(text) {
|
|
@@ -3548,10 +3465,10 @@ var READ_ONLY_AGENT_TOOLS = [
|
|
|
3548
3465
|
];
|
|
3549
3466
|
function agentDirs(home, root) {
|
|
3550
3467
|
return [
|
|
3551
|
-
{ dir:
|
|
3552
|
-
{ dir:
|
|
3553
|
-
{ dir:
|
|
3554
|
-
{ dir:
|
|
3468
|
+
{ dir: join5(home, ".garuda", "agents"), shown: "~/.garuda/agents", source: "user" },
|
|
3469
|
+
{ dir: join5(home, ".claude", "agents"), shown: "~/.claude/agents", source: "user" },
|
|
3470
|
+
{ dir: join5(root, ".garuda", "agents"), shown: ".garuda/agents", source: "project" },
|
|
3471
|
+
{ dir: join5(root, ".claude", "agents"), shown: ".claude/agents", source: "project" }
|
|
3555
3472
|
];
|
|
3556
3473
|
}
|
|
3557
3474
|
async function loadAgents(options) {
|
|
@@ -3568,7 +3485,7 @@ async function loadAgents(options) {
|
|
|
3568
3485
|
continue;
|
|
3569
3486
|
}
|
|
3570
3487
|
for (const entry of entries) {
|
|
3571
|
-
const file2 =
|
|
3488
|
+
const file2 = join5(place.dir, entry);
|
|
3572
3489
|
const shown = `${place.shown}/${entry}`;
|
|
3573
3490
|
try {
|
|
3574
3491
|
const info = await lstat2(file2);
|
|
@@ -4151,16 +4068,16 @@ function createMoeDispatchTool(options) {
|
|
|
4151
4068
|
|
|
4152
4069
|
// src/commands/custom.ts
|
|
4153
4070
|
import { createHash as createHash4 } from "crypto";
|
|
4154
|
-
import { lstat as lstat3, readdir as readdir4, readFile as readFile9, stat as
|
|
4155
|
-
import { join as
|
|
4156
|
-
var COMMANDS_DIR =
|
|
4071
|
+
import { lstat as lstat3, readdir as readdir4, readFile as readFile9, stat as stat6 } from "fs/promises";
|
|
4072
|
+
import { join as join6, relative, sep } from "path";
|
|
4073
|
+
var COMMANDS_DIR = join6(".garuda", "commands");
|
|
4157
4074
|
var COMMAND_MAX_CHARS = 2e4;
|
|
4158
4075
|
var MAX_FILES = 200;
|
|
4159
4076
|
var NAME = /^[a-z0-9][a-z0-9_-]*(?::[a-z0-9][a-z0-9_-]*)*$/;
|
|
4160
4077
|
async function loadCommands(options) {
|
|
4161
4078
|
const problems = [];
|
|
4162
|
-
const user = await loadDir(
|
|
4163
|
-
const project =
|
|
4079
|
+
const user = await loadDir(join6(options.home, COMMANDS_DIR), "user", problems);
|
|
4080
|
+
const project = join6(options.home, COMMANDS_DIR) === join6(options.root, COMMANDS_DIR) ? [] : await loadDir(join6(options.root, COMMANDS_DIR), "project", problems);
|
|
4164
4081
|
const byName = /* @__PURE__ */ new Map();
|
|
4165
4082
|
for (const command of [...user, ...project]) {
|
|
4166
4083
|
if (options.builtins.includes(command.name)) {
|
|
@@ -4190,7 +4107,7 @@ async function loadDir(dir, source, problems) {
|
|
|
4190
4107
|
}
|
|
4191
4108
|
const out = [];
|
|
4192
4109
|
for (const rel of files) {
|
|
4193
|
-
const file2 =
|
|
4110
|
+
const file2 = join6(dir, rel);
|
|
4194
4111
|
const name = relative(dir, file2).slice(0, -3).split(sep).join(":").toLowerCase();
|
|
4195
4112
|
if (!NAME.test(name)) {
|
|
4196
4113
|
problems.push(`${file2}: a command name may hold only a\u2013z, 0\u20139, "-" and "_".`);
|
|
@@ -4201,7 +4118,7 @@ async function loadDir(dir, source, problems) {
|
|
|
4201
4118
|
problems.push(`${file2}: a project command may not be a symbolic link; ignored.`);
|
|
4202
4119
|
continue;
|
|
4203
4120
|
}
|
|
4204
|
-
if (!(await
|
|
4121
|
+
if (!(await stat6(file2)).isFile()) continue;
|
|
4205
4122
|
const text = await readFile9(file2, "utf8");
|
|
4206
4123
|
if (text.length > COMMAND_MAX_CHARS) {
|
|
4207
4124
|
problems.push(`${file2}: longer than ${COMMAND_MAX_CHARS} characters; ignored.`);
|
|
@@ -4293,18 +4210,18 @@ function commandConsent(command, changed, isolation) {
|
|
|
4293
4210
|
|
|
4294
4211
|
// src/format/formatters.ts
|
|
4295
4212
|
import { existsSync as existsSync2, readFileSync as readFileSync2, statSync } from "fs";
|
|
4296
|
-
import { delimiter, extname, join as
|
|
4213
|
+
import { delimiter, extname, join as join7 } from "path";
|
|
4297
4214
|
var JS_EXTENSIONS = ["js", "jsx", "mjs", "cjs", "ts", "tsx", "mts", "cts"];
|
|
4298
4215
|
function detectFormatters(root, path, settings = {}) {
|
|
4299
4216
|
const found = [];
|
|
4300
4217
|
const onPath = (name) => findOnPath(name, path);
|
|
4301
4218
|
const local = (name) => {
|
|
4302
|
-
const file2 =
|
|
4219
|
+
const file2 = join7(root, "node_modules", ".bin", name);
|
|
4303
4220
|
return isFile(file2) ? file2 : void 0;
|
|
4304
4221
|
};
|
|
4305
|
-
const venv = (name) => [
|
|
4222
|
+
const venv = (name) => [join7(root, ".venv", "bin", name), join7(root, "venv", "bin", name)].find(isFile);
|
|
4306
4223
|
const biome = local("biome");
|
|
4307
|
-
if (biome !== void 0 && ["biome.json", "biome.jsonc"].some((f) => isFile(
|
|
4224
|
+
if (biome !== void 0 && ["biome.json", "biome.jsonc"].some((f) => isFile(join7(root, f)))) {
|
|
4308
4225
|
found.push({
|
|
4309
4226
|
name: "biome",
|
|
4310
4227
|
extensions: [...JS_EXTENSIONS, "json", "jsonc", "css"],
|
|
@@ -4330,9 +4247,9 @@ function detectFormatters(root, path, settings = {}) {
|
|
|
4330
4247
|
command: [prettier, "--write", "$FILE"]
|
|
4331
4248
|
});
|
|
4332
4249
|
}
|
|
4333
|
-
const pyproject = readText(
|
|
4250
|
+
const pyproject = readText(join7(root, "pyproject.toml"));
|
|
4334
4251
|
const ruff = venv("ruff") ?? onPath("ruff");
|
|
4335
|
-
if (ruff !== void 0 && (pyproject.includes("[tool.ruff") || ["ruff.toml", ".ruff.toml"].some((f) => isFile(
|
|
4252
|
+
if (ruff !== void 0 && (pyproject.includes("[tool.ruff") || ["ruff.toml", ".ruff.toml"].some((f) => isFile(join7(root, f))))) {
|
|
4336
4253
|
found.push({ name: "ruff", extensions: ["py", "pyi"], command: [ruff, "format", "$FILE"] });
|
|
4337
4254
|
}
|
|
4338
4255
|
const black = venv("black") ?? onPath("black");
|
|
@@ -4340,11 +4257,11 @@ function detectFormatters(root, path, settings = {}) {
|
|
|
4340
4257
|
found.push({ name: "black", extensions: ["py", "pyi"], command: [black, "-q", "$FILE"] });
|
|
4341
4258
|
}
|
|
4342
4259
|
const gofmt = onPath("gofmt");
|
|
4343
|
-
if (gofmt !== void 0 && isFile(
|
|
4260
|
+
if (gofmt !== void 0 && isFile(join7(root, "go.mod"))) {
|
|
4344
4261
|
found.push({ name: "gofmt", extensions: ["go"], command: [gofmt, "-w", "$FILE"] });
|
|
4345
4262
|
}
|
|
4346
4263
|
const rustfmt = onPath("rustfmt");
|
|
4347
|
-
const cargo = readText(
|
|
4264
|
+
const cargo = readText(join7(root, "Cargo.toml"));
|
|
4348
4265
|
if (rustfmt !== void 0 && cargo !== "") {
|
|
4349
4266
|
const edition = /^\s*edition\s*=\s*"(\d{4})"/m.exec(cargo)?.[1] ?? "2021";
|
|
4350
4267
|
found.push({
|
|
@@ -4390,9 +4307,9 @@ function hasPrettierConfig(root) {
|
|
|
4390
4307
|
"prettier.config.cjs",
|
|
4391
4308
|
"prettier.config.mjs"
|
|
4392
4309
|
];
|
|
4393
|
-
if (files.some((f) => isFile(
|
|
4310
|
+
if (files.some((f) => isFile(join7(root, f)))) return true;
|
|
4394
4311
|
try {
|
|
4395
|
-
const pkg = JSON.parse(readText(
|
|
4312
|
+
const pkg = JSON.parse(readText(join7(root, "package.json")) || "{}");
|
|
4396
4313
|
return pkg.prettier !== void 0;
|
|
4397
4314
|
} catch {
|
|
4398
4315
|
return false;
|
|
@@ -4401,7 +4318,7 @@ function hasPrettierConfig(root) {
|
|
|
4401
4318
|
function findOnPath(name, path) {
|
|
4402
4319
|
for (const dir of (path ?? "").split(delimiter)) {
|
|
4403
4320
|
if (dir === "") continue;
|
|
4404
|
-
const file2 =
|
|
4321
|
+
const file2 = join7(dir, name);
|
|
4405
4322
|
if (isFile(file2)) return file2;
|
|
4406
4323
|
}
|
|
4407
4324
|
return void 0;
|
|
@@ -4424,9 +4341,9 @@ function readText(file2) {
|
|
|
4424
4341
|
// src/hooks/config.ts
|
|
4425
4342
|
import { createHash as createHash5 } from "crypto";
|
|
4426
4343
|
import { readFile as readFile10 } from "fs/promises";
|
|
4427
|
-
import { join as
|
|
4344
|
+
import { join as join8 } from "path";
|
|
4428
4345
|
import { z as z17 } from "zod";
|
|
4429
|
-
var HOOKS_FILE =
|
|
4346
|
+
var HOOKS_FILE = join8(".garuda", "hooks.json");
|
|
4430
4347
|
var HOOK_EVENTS = ["preToolUse", "postToolUse"];
|
|
4431
4348
|
var hookSchema = z17.strictObject({
|
|
4432
4349
|
command: z17.string().min(1),
|
|
@@ -4472,8 +4389,8 @@ async function readHooks(file2, source) {
|
|
|
4472
4389
|
return { hooks, problems };
|
|
4473
4390
|
}
|
|
4474
4391
|
async function loadHooks(home, root) {
|
|
4475
|
-
const user = await readHooks(
|
|
4476
|
-
const project = home === root ? { hooks: [], problems: [] } : await readHooks(
|
|
4392
|
+
const user = await readHooks(join8(home, HOOKS_FILE), "user");
|
|
4393
|
+
const project = home === root ? { hooks: [], problems: [] } : await readHooks(join8(root, HOOKS_FILE), "project");
|
|
4477
4394
|
return {
|
|
4478
4395
|
user: user.hooks,
|
|
4479
4396
|
project: project.hooks,
|
|
@@ -4486,10 +4403,10 @@ function hooksHash(hooks) {
|
|
|
4486
4403
|
}
|
|
4487
4404
|
|
|
4488
4405
|
// src/hooks/runner.ts
|
|
4489
|
-
import { randomBytes as
|
|
4490
|
-
import { rm as rm2, writeFile
|
|
4406
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
4407
|
+
import { rm as rm2, writeFile } from "fs/promises";
|
|
4491
4408
|
import { tmpdir } from "os";
|
|
4492
|
-
import { isAbsolute as isAbsolute2, join as
|
|
4409
|
+
import { isAbsolute as isAbsolute2, join as join9 } from "path";
|
|
4493
4410
|
var OUTPUT_BYTES = 8e3;
|
|
4494
4411
|
var FEEDBACK_CHARS = 4e3;
|
|
4495
4412
|
var HookRunner = class {
|
|
@@ -4560,7 +4477,7 @@ ${clean2(r.stderr)}
|
|
|
4560
4477
|
});
|
|
4561
4478
|
return { exitCode: null, signal: "refused by the team policy", timedOut: false, stderr: "" };
|
|
4562
4479
|
}
|
|
4563
|
-
const file2 =
|
|
4480
|
+
const file2 = join9(tmpdir(), `garuda-hook-${randomBytes3(6).toString("hex")}.json`);
|
|
4564
4481
|
const data = {
|
|
4565
4482
|
event: hook.event,
|
|
4566
4483
|
tool: call.tool,
|
|
@@ -4569,14 +4486,14 @@ ${clean2(r.stderr)}
|
|
|
4569
4486
|
...outcome === void 0 ? {} : { result: outcome },
|
|
4570
4487
|
root
|
|
4571
4488
|
};
|
|
4572
|
-
await
|
|
4489
|
+
await writeFile(file2, JSON.stringify(data), { mode: 384 });
|
|
4573
4490
|
const env = {
|
|
4574
4491
|
GARUDA_HOOK_EVENT: hook.event,
|
|
4575
4492
|
GARUDA_TOOL: call.tool,
|
|
4576
4493
|
GARUDA_HOOK_INPUT: file2
|
|
4577
4494
|
};
|
|
4578
4495
|
if (target?.kind === "path") {
|
|
4579
|
-
env.GARUDA_FILE = isAbsolute2(target.path) ? target.path :
|
|
4496
|
+
env.GARUDA_FILE = isAbsolute2(target.path) ? target.path : join9(root, target.path);
|
|
4580
4497
|
}
|
|
4581
4498
|
if (target?.kind === "command") env.GARUDA_COMMAND = target.command;
|
|
4582
4499
|
if (target?.kind === "url") env.GARUDA_URL = target.url;
|
|
@@ -4642,8 +4559,8 @@ function hooksConsent(hooks, file2, isolation, changed) {
|
|
|
4642
4559
|
|
|
4643
4560
|
// src/knowledge/index.ts
|
|
4644
4561
|
import { createHash as createHash7 } from "crypto";
|
|
4645
|
-
import { mkdir as mkdir4, readFile as readFile12, stat as
|
|
4646
|
-
import { dirname as
|
|
4562
|
+
import { mkdir as mkdir4, readFile as readFile12, stat as stat7, writeFile as writeFile2 } from "fs/promises";
|
|
4563
|
+
import { dirname as dirname4, extname as extname3, join as join12 } from "path";
|
|
4647
4564
|
|
|
4648
4565
|
// src/knowledge/java.ts
|
|
4649
4566
|
import { readFileSync as readFileSync3, statSync as statSync2 } from "fs";
|
|
@@ -5325,7 +5242,7 @@ function createRustExpert(root) {
|
|
|
5325
5242
|
// src/knowledge/plugins.ts
|
|
5326
5243
|
import { createHash as createHash6 } from "crypto";
|
|
5327
5244
|
import { readdir as readdir5, readFile as readFile11 } from "fs/promises";
|
|
5328
|
-
import { basename as
|
|
5245
|
+
import { basename as basename3, extname as extname2, join as join11 } from "path";
|
|
5329
5246
|
import { pathToFileURL } from "url";
|
|
5330
5247
|
|
|
5331
5248
|
// src/knowledge/python.ts
|
|
@@ -5568,7 +5485,7 @@ function openTripleQuote(line) {
|
|
|
5568
5485
|
|
|
5569
5486
|
// src/knowledge/typescript.ts
|
|
5570
5487
|
import { readFileSync as readFileSync7, statSync as statSync6 } from "fs";
|
|
5571
|
-
import { dirname as
|
|
5488
|
+
import { dirname as dirname3, isAbsolute as isAbsolute7, join as join10, relative as relative2, sep as sep2 } from "path";
|
|
5572
5489
|
async function createTypeScriptExpert(root) {
|
|
5573
5490
|
const ts = (await import("ts6")).default;
|
|
5574
5491
|
return new TypeScriptExpert(ts, root);
|
|
@@ -5734,7 +5651,7 @@ var TypeScriptExpert = class {
|
|
|
5734
5651
|
};
|
|
5735
5652
|
}
|
|
5736
5653
|
abs(path) {
|
|
5737
|
-
return isAbsolute7(path) ? path :
|
|
5654
|
+
return isAbsolute7(path) ? path : join10(this.root, path);
|
|
5738
5655
|
}
|
|
5739
5656
|
rel(file2) {
|
|
5740
5657
|
return relative2(this.root, file2).split(sep2).join("/");
|
|
@@ -5758,7 +5675,7 @@ function declarationKind(ts, node) {
|
|
|
5758
5675
|
function compilerOptions(ts, root) {
|
|
5759
5676
|
let base = {};
|
|
5760
5677
|
const configPath = ts.findConfigFile(root, (f) => ts.sys.fileExists(f));
|
|
5761
|
-
if (configPath !== void 0 &&
|
|
5678
|
+
if (configPath !== void 0 && dirname3(configPath) === root) {
|
|
5762
5679
|
const read = ts.readConfigFile(configPath, (f) => ts.sys.readFile(f));
|
|
5763
5680
|
if (read.config !== void 0) {
|
|
5764
5681
|
base = ts.parseJsonConfigFileContent(read.config, ts.sys, root).options;
|
|
@@ -5821,12 +5738,12 @@ async function discoverPlugins({
|
|
|
5821
5738
|
}) {
|
|
5822
5739
|
const plugins = includeBuiltins ? [...BUILTIN_PLUGINS] : [];
|
|
5823
5740
|
const warnings = [];
|
|
5824
|
-
const userDir =
|
|
5741
|
+
const userDir = join11(home, ".garuda", "languages");
|
|
5825
5742
|
const userFiles = await safeReaddir(userDir);
|
|
5826
5743
|
for (const filename of userFiles) {
|
|
5827
5744
|
if (!isPluginFile(filename)) continue;
|
|
5828
|
-
const fullPath =
|
|
5829
|
-
const id =
|
|
5745
|
+
const fullPath = join11(userDir, filename);
|
|
5746
|
+
const id = basename3(filename, extname2(filename));
|
|
5830
5747
|
try {
|
|
5831
5748
|
const plugin = await loadPluginModule(fullPath, id, "user", root);
|
|
5832
5749
|
if (plugin) {
|
|
@@ -5836,7 +5753,7 @@ async function discoverPlugins({
|
|
|
5836
5753
|
warnings.push(`Failed to load user language plugin "${id}": ${error.message}`);
|
|
5837
5754
|
}
|
|
5838
5755
|
}
|
|
5839
|
-
const projDir =
|
|
5756
|
+
const projDir = join11(root, ".garuda", "languages");
|
|
5840
5757
|
const projFiles = (await safeReaddir(projDir)).filter(isPluginFile);
|
|
5841
5758
|
if (!projectPlugins) {
|
|
5842
5759
|
if (projFiles.length > 0) {
|
|
@@ -5848,8 +5765,8 @@ async function discoverPlugins({
|
|
|
5848
5765
|
}
|
|
5849
5766
|
for (const filename of projFiles) {
|
|
5850
5767
|
if (!isPluginFile(filename)) continue;
|
|
5851
|
-
const fullPath =
|
|
5852
|
-
const id =
|
|
5768
|
+
const fullPath = join11(projDir, filename);
|
|
5769
|
+
const id = basename3(filename, extname2(filename));
|
|
5853
5770
|
try {
|
|
5854
5771
|
const content = await readFile11(fullPath, "utf8");
|
|
5855
5772
|
const hash2 = createHash6("sha256").update(content).digest("hex");
|
|
@@ -6084,7 +6001,7 @@ var KnowledgeIndex = class {
|
|
|
6084
6001
|
const callableSymbols = fileSymbols.filter(
|
|
6085
6002
|
(s) => s.kind === "function" || s.kind === "method" || s.kind === "constructor" || s.kind === "class"
|
|
6086
6003
|
);
|
|
6087
|
-
const content = await readFile12(
|
|
6004
|
+
const content = await readFile12(join12(this.root, filePath), "utf8").catch(() => "");
|
|
6088
6005
|
const lines = content.split("\n");
|
|
6089
6006
|
for (const site of sites) {
|
|
6090
6007
|
const enclosing = enclosingSymbol(filePath, content, lines, site.line, callableSymbols);
|
|
@@ -6274,7 +6191,7 @@ var KnowledgeIndex = class {
|
|
|
6274
6191
|
const prefix = dir.replace(/^\.?\/?/, "").replace(/\/?$/, dir === "" || dir === "." ? "" : "/");
|
|
6275
6192
|
for (const { expert, files } of await this.byExpert()) {
|
|
6276
6193
|
for (const path of files) {
|
|
6277
|
-
const content = await readFile12(
|
|
6194
|
+
const content = await readFile12(join12(this.root, path), "utf8").catch(() => void 0);
|
|
6278
6195
|
if (content === void 0) continue;
|
|
6279
6196
|
const hash2 = createHash7("sha256").update(content).digest("hex");
|
|
6280
6197
|
const cached = cache.files[path];
|
|
@@ -6346,7 +6263,7 @@ var KnowledgeIndex = class {
|
|
|
6346
6263
|
const ext = extname3(path);
|
|
6347
6264
|
const group = groups.find((g) => g.expert.extensions.includes(ext));
|
|
6348
6265
|
if (group === void 0) continue;
|
|
6349
|
-
const info = await
|
|
6266
|
+
const info = await stat7(absolute).catch(() => void 0);
|
|
6350
6267
|
if (info === void 0 || info.size > MAX_FILE_BYTES) continue;
|
|
6351
6268
|
group.files.push(path);
|
|
6352
6269
|
if (++count >= MAX_FILES2) break;
|
|
@@ -6355,7 +6272,7 @@ var KnowledgeIndex = class {
|
|
|
6355
6272
|
}
|
|
6356
6273
|
async loadCache() {
|
|
6357
6274
|
try {
|
|
6358
|
-
const cache = JSON.parse(await readFile12(
|
|
6275
|
+
const cache = JSON.parse(await readFile12(join12(this.root, GRAPH_FILE), "utf8"));
|
|
6359
6276
|
if (cache.version === GRAPH_VERSION) return cache;
|
|
6360
6277
|
} catch {
|
|
6361
6278
|
}
|
|
@@ -6363,10 +6280,10 @@ var KnowledgeIndex = class {
|
|
|
6363
6280
|
}
|
|
6364
6281
|
/** Best effort (0.14, review): a read-only checkout gives the map without a cache, not an error. */
|
|
6365
6282
|
async saveCache(cache) {
|
|
6366
|
-
const path =
|
|
6283
|
+
const path = join12(this.root, GRAPH_FILE);
|
|
6367
6284
|
try {
|
|
6368
|
-
await mkdir4(
|
|
6369
|
-
await
|
|
6285
|
+
await mkdir4(dirname4(path), { recursive: true });
|
|
6286
|
+
await writeFile2(path, JSON.stringify(cache));
|
|
6370
6287
|
} catch {
|
|
6371
6288
|
}
|
|
6372
6289
|
}
|
|
@@ -6603,8 +6520,8 @@ function repairServerBlocks(message) {
|
|
|
6603
6520
|
|
|
6604
6521
|
// src/skills/load.ts
|
|
6605
6522
|
import { createHash as createHash8 } from "crypto";
|
|
6606
|
-
import { lstat as lstat4, readdir as readdir6, readFile as readFile13, stat as
|
|
6607
|
-
import { join as
|
|
6523
|
+
import { lstat as lstat4, readdir as readdir6, readFile as readFile13, stat as stat8 } from "fs/promises";
|
|
6524
|
+
import { join as join13 } from "path";
|
|
6608
6525
|
var SKILL_FILE = "SKILL.md";
|
|
6609
6526
|
var SKILL_NAME = /^(?!-)(?!.*--)[a-z0-9-]{1,64}(?<!-)$/;
|
|
6610
6527
|
var SKILL_MAX_CHARS = 5e4;
|
|
@@ -6612,10 +6529,10 @@ var DESCRIPTION_MAX_CHARS2 = 1536;
|
|
|
6612
6529
|
var MAX_SKILLS = 100;
|
|
6613
6530
|
function skillDirs(home, root) {
|
|
6614
6531
|
return [
|
|
6615
|
-
{ dir:
|
|
6616
|
-
{ dir:
|
|
6617
|
-
{ dir:
|
|
6618
|
-
{ dir:
|
|
6532
|
+
{ dir: join13(home, ".garuda", "skills"), shown: "~/.garuda/skills", source: "user" },
|
|
6533
|
+
{ dir: join13(home, ".claude", "skills"), shown: "~/.claude/skills", source: "user" },
|
|
6534
|
+
{ dir: join13(root, ".garuda", "skills"), shown: ".garuda/skills", source: "project" },
|
|
6535
|
+
{ dir: join13(root, ".claude", "skills"), shown: ".claude/skills", source: "project" }
|
|
6619
6536
|
];
|
|
6620
6537
|
}
|
|
6621
6538
|
async function loadSkills(options) {
|
|
@@ -6655,11 +6572,11 @@ async function loadDir2(place, problems) {
|
|
|
6655
6572
|
const out = [];
|
|
6656
6573
|
for (const entry of entries) {
|
|
6657
6574
|
if (entry.startsWith(".")) continue;
|
|
6658
|
-
const dir =
|
|
6575
|
+
const dir = join13(place.dir, entry);
|
|
6659
6576
|
const shown = `${place.shown}/${entry}`;
|
|
6660
|
-
const file2 =
|
|
6577
|
+
const file2 = join13(dir, SKILL_FILE);
|
|
6661
6578
|
try {
|
|
6662
|
-
if (!(await
|
|
6579
|
+
if (!(await stat8(dir)).isDirectory()) continue;
|
|
6663
6580
|
if (place.source === "project" && ((await lstat4(dir)).isSymbolicLink() || (await lstat4(file2)).isSymbolicLink())) {
|
|
6664
6581
|
problems.push(`${shown}: a project skill may not be a symbolic link; ignored.`);
|
|
6665
6582
|
continue;
|
|
@@ -6757,8 +6674,8 @@ function skillConsent(skill, changed, isolation) {
|
|
|
6757
6674
|
}
|
|
6758
6675
|
|
|
6759
6676
|
// src/skills/tool.ts
|
|
6760
|
-
import { readdir as readdir7, readFile as readFile14, realpath as
|
|
6761
|
-
import { join as
|
|
6677
|
+
import { readdir as readdir7, readFile as readFile14, realpath as realpath3, stat as stat9 } from "fs/promises";
|
|
6678
|
+
import { join as join14, relative as relative3, resolve, sep as sep3 } from "path";
|
|
6762
6679
|
import { z as z18 } from "zod";
|
|
6763
6680
|
var input10 = z18.object({
|
|
6764
6681
|
name: z18.string().min(1).describe("The skill name from the list."),
|
|
@@ -6828,11 +6745,11 @@ async function skillText(skill, args, place) {
|
|
|
6828
6745
|
async function readSkillFile(skill, file2) {
|
|
6829
6746
|
const target = resolve(skill.dir, file2);
|
|
6830
6747
|
try {
|
|
6831
|
-
const [realDir, realTarget] = await Promise.all([
|
|
6748
|
+
const [realDir, realTarget] = await Promise.all([realpath3(skill.dir), realpath3(target)]);
|
|
6832
6749
|
if (!isInside(realDir, realTarget) || !isInside(skill.dir, target)) {
|
|
6833
6750
|
return { error: true, text: `"${file2}" is outside the skill folder.` };
|
|
6834
6751
|
}
|
|
6835
|
-
if (!(await
|
|
6752
|
+
if (!(await stat9(realTarget)).isFile()) {
|
|
6836
6753
|
return { error: true, text: `"${file2}" is not a file.` };
|
|
6837
6754
|
}
|
|
6838
6755
|
const buffer = await readFile14(realTarget);
|
|
@@ -6869,8 +6786,8 @@ async function listFiles2(dir) {
|
|
|
6869
6786
|
for (const name of entries) {
|
|
6870
6787
|
if (out.length >= MAX_FILES_LISTED) return;
|
|
6871
6788
|
if (name.startsWith(".")) continue;
|
|
6872
|
-
const full =
|
|
6873
|
-
const info = await
|
|
6789
|
+
const full = join14(current, name);
|
|
6790
|
+
const info = await stat9(full).catch(() => void 0);
|
|
6874
6791
|
if (info?.isDirectory()) {
|
|
6875
6792
|
if (depth < 3) await walk(full, depth + 1);
|
|
6876
6793
|
} else if (info?.isFile()) {
|
|
@@ -6960,9 +6877,9 @@ No results.
|
|
|
6960
6877
|
|
|
6961
6878
|
// src/undo/snapshots.ts
|
|
6962
6879
|
import { createHash as createHash9 } from "crypto";
|
|
6963
|
-
import { mkdir as mkdir5, writeFile as
|
|
6880
|
+
import { mkdir as mkdir5, writeFile as writeFile3 } from "fs/promises";
|
|
6964
6881
|
import { homedir as homedir3 } from "os";
|
|
6965
|
-
import { join as
|
|
6882
|
+
import { join as join15 } from "path";
|
|
6966
6883
|
var SNAPSHOT_EXCLUDES = [
|
|
6967
6884
|
".git",
|
|
6968
6885
|
".garuda/sessions/",
|
|
@@ -7059,7 +6976,7 @@ var SnapshotStore = class {
|
|
|
7059
6976
|
this.ready ??= (async () => {
|
|
7060
6977
|
await mkdir5(this.dir, { recursive: true, mode: 448 });
|
|
7061
6978
|
await this.git("init -q", signal, true);
|
|
7062
|
-
await
|
|
6979
|
+
await writeFile3(join15(this.dir, "info", "exclude"), `${SNAPSHOT_EXCLUDES.join("\n")}
|
|
7063
6980
|
`);
|
|
7064
6981
|
})().catch((error) => {
|
|
7065
6982
|
this.ready = void 0;
|
|
@@ -7113,7 +7030,7 @@ var SnapshotStore = class {
|
|
|
7113
7030
|
};
|
|
7114
7031
|
function storeDir(root, home = homedir3()) {
|
|
7115
7032
|
const id = createHash9("sha256").update(root).digest("hex").slice(0, 16);
|
|
7116
|
-
return
|
|
7033
|
+
return join15(home, ".garuda", "snapshots", id);
|
|
7117
7034
|
}
|
|
7118
7035
|
function shellQuote(text) {
|
|
7119
7036
|
return `'${text.replace(/'/g, "'\\''")}'`;
|
|
@@ -7124,7 +7041,7 @@ function tree(id) {
|
|
|
7124
7041
|
}
|
|
7125
7042
|
|
|
7126
7043
|
// src/app/mentions.ts
|
|
7127
|
-
import { readdir as readdir8, readFile as readFile15, stat as
|
|
7044
|
+
import { readdir as readdir8, readFile as readFile15, stat as stat10 } from "fs/promises";
|
|
7128
7045
|
var MAX_ATTACHMENTS = 10;
|
|
7129
7046
|
var MAX_TOTAL_CHARS = 15e4;
|
|
7130
7047
|
var MAX_FOLDER_ENTRIES = 200;
|
|
@@ -7150,7 +7067,7 @@ async function attachMentions(prompt, root, files, denial = async () => void 0)
|
|
|
7150
7067
|
}
|
|
7151
7068
|
continue;
|
|
7152
7069
|
}
|
|
7153
|
-
const info = await
|
|
7070
|
+
const info = await stat10(absolute).catch(() => void 0);
|
|
7154
7071
|
if (info === void 0) continue;
|
|
7155
7072
|
const shown = displayPath(root, absolute);
|
|
7156
7073
|
if (attachments.length >= MAX_ATTACHMENTS) {
|
|
@@ -7353,7 +7270,7 @@ var ModelState = class {
|
|
|
7353
7270
|
// src/app/network.ts
|
|
7354
7271
|
import { createHash as createHash10 } from "crypto";
|
|
7355
7272
|
import { existsSync as existsSync3 } from "fs";
|
|
7356
|
-
import { basename as
|
|
7273
|
+
import { basename as basename4, delimiter as delimiter2, join as join16 } from "path";
|
|
7357
7274
|
function networkHash(entries) {
|
|
7358
7275
|
const canonical = [...new Set(entries.map((e) => e.trim().toLowerCase()))].sort();
|
|
7359
7276
|
return createHash10("sha256").update(JSON.stringify(canonical)).digest("hex");
|
|
@@ -7388,10 +7305,10 @@ function networkConsent(entries, isolation, changed) {
|
|
|
7388
7305
|
};
|
|
7389
7306
|
}
|
|
7390
7307
|
function nodeBinary(execPath = process.execPath, path = process.env.PATH ?? "") {
|
|
7391
|
-
if (/^node(\.exe)?$/.test(
|
|
7308
|
+
if (/^node(\.exe)?$/.test(basename4(execPath))) return execPath;
|
|
7392
7309
|
for (const dir of path.split(delimiter2)) {
|
|
7393
7310
|
if (dir === "") continue;
|
|
7394
|
-
const candidate =
|
|
7311
|
+
const candidate = join16(dir, "node");
|
|
7395
7312
|
if (existsSync3(candidate)) return candidate;
|
|
7396
7313
|
}
|
|
7397
7314
|
return void 0;
|
|
@@ -8605,7 +8522,7 @@ ${neutralizeTags(output)}`
|
|
|
8605
8522
|
text: "There is no finished plan in this chat. Make one in plan mode (/plan), then type /schedule."
|
|
8606
8523
|
};
|
|
8607
8524
|
}
|
|
8608
|
-
const { createJob } = await import("./create-
|
|
8525
|
+
const { createJob } = await import("./create-VXIH2GUZ.js");
|
|
8609
8526
|
const created = await createJob({
|
|
8610
8527
|
root: this.root,
|
|
8611
8528
|
executor: this.executor,
|
|
@@ -8672,7 +8589,7 @@ ${result.stderr.text}`)
|
|
|
8672
8589
|
*/
|
|
8673
8590
|
async cancelJob(id, agent = {}) {
|
|
8674
8591
|
const { loadJob, saveJob } = await import("./job-3NYDMP2M.js");
|
|
8675
|
-
const { defaultAgentEnv, nightSpec, removeAgent, removeSpec } = await import("./launchd-
|
|
8592
|
+
const { defaultAgentEnv, nightSpec, removeAgent, removeSpec } = await import("./launchd-SSX362VG.js");
|
|
8676
8593
|
if (id === "night") {
|
|
8677
8594
|
const removed = await removeSpec(
|
|
8678
8595
|
agent.executor ?? this.executor,
|
|
@@ -8704,8 +8621,8 @@ ${result.stderr.text}`)
|
|
|
8704
8621
|
*/
|
|
8705
8622
|
async deleteJob(id, signal, agent = {}) {
|
|
8706
8623
|
const { loadJob } = await import("./job-3NYDMP2M.js");
|
|
8707
|
-
const { defaultAgentEnv, removeAgent } = await import("./launchd-
|
|
8708
|
-
const { jobLeftovers, removeJob } = await import("./remove-
|
|
8624
|
+
const { defaultAgentEnv, removeAgent } = await import("./launchd-SSX362VG.js");
|
|
8625
|
+
const { jobLeftovers, removeJob } = await import("./remove-6GDSTAIO.js");
|
|
8709
8626
|
let job;
|
|
8710
8627
|
try {
|
|
8711
8628
|
job = await loadJob(this.root, id);
|
|
@@ -8840,7 +8757,7 @@ ${result.stderr.text}`)
|
|
|
8840
8757
|
if (known === hash2) {
|
|
8841
8758
|
active = [...active, ...project];
|
|
8842
8759
|
} else {
|
|
8843
|
-
const file2 =
|
|
8760
|
+
const file2 = join17(this.root, HOOKS_FILE);
|
|
8844
8761
|
const request = hooksConsent(project, file2, this.executor.isolation, known !== void 0);
|
|
8845
8762
|
const choice = await this.approver.ask(request, signal);
|
|
8846
8763
|
if (choice !== "deny") active = [...active, ...project];
|
|
@@ -8877,7 +8794,7 @@ ${result.stderr.text}`)
|
|
|
8877
8794
|
* The files it writes are new only; the turn's own writes go through the normal approvals.
|
|
8878
8795
|
*/
|
|
8879
8796
|
async init(signal) {
|
|
8880
|
-
const { runInit } = await import("./run-
|
|
8797
|
+
const { runInit } = await import("./run-ML6GSPN4.js");
|
|
8881
8798
|
const result = await runInit({
|
|
8882
8799
|
root: this.root,
|
|
8883
8800
|
home: this.commandsHome,
|
|
@@ -9032,7 +8949,7 @@ ${result.stderr.text}`)
|
|
|
9032
8949
|
}
|
|
9033
8950
|
/** Install a language server into ~/.garuda/lsp (the user asked for it). */
|
|
9034
8951
|
async installLsp(language, signal) {
|
|
9035
|
-
const { installServer } = await import("./install-
|
|
8952
|
+
const { installServer } = await import("./install-EWDSPFFZ.js");
|
|
9036
8953
|
const result = await installServer(language, {
|
|
9037
8954
|
executor: this.executor,
|
|
9038
8955
|
home: this.lspHome,
|
|
@@ -9043,7 +8960,7 @@ ${result.stderr.text}`)
|
|
|
9043
8960
|
}
|
|
9044
8961
|
/** autoInstall (~/.garuda/lsp.json): ask before a managed install. */
|
|
9045
8962
|
async askInstall(language, signal) {
|
|
9046
|
-
const { installCommand } = await import("./install-
|
|
8963
|
+
const { installCommand } = await import("./install-EWDSPFFZ.js");
|
|
9047
8964
|
const { LANGUAGE_LABELS } = await import("./manager-4PMSXT47.js");
|
|
9048
8965
|
const { managedLabel, managedDir } = await import("./servers-6YDGWOZ6.js");
|
|
9049
8966
|
const dir = managedDir(language, this.lspHome);
|
|
@@ -9076,7 +8993,7 @@ ${result.stderr.text}`)
|
|
|
9076
8993
|
async startMcp(signal) {
|
|
9077
8994
|
if (this.mcpServers.length === 0) return;
|
|
9078
8995
|
this.mcpStarted ??= (async () => {
|
|
9079
|
-
const { McpManager } = await import("./manager-
|
|
8996
|
+
const { McpManager } = await import("./manager-AWU4NHHK.js");
|
|
9080
8997
|
const { AuthStore } = await import("./oauth-W6OK6XXO.js");
|
|
9081
8998
|
const manager = new McpManager({
|
|
9082
8999
|
root: this.root,
|
|
@@ -9303,7 +9220,6 @@ async function gatedProjectSettings(options) {
|
|
|
9303
9220
|
}
|
|
9304
9221
|
|
|
9305
9222
|
export {
|
|
9306
|
-
keepSecretForRedaction,
|
|
9307
9223
|
FileSessionStore,
|
|
9308
9224
|
parseRecords,
|
|
9309
9225
|
newSessionId,
|
|
@@ -9338,4 +9254,4 @@ export {
|
|
|
9338
9254
|
modelFacts,
|
|
9339
9255
|
Runtime
|
|
9340
9256
|
};
|
|
9341
|
-
//# sourceMappingURL=chunk-
|
|
9257
|
+
//# sourceMappingURL=chunk-WIJL72ZE.js.map
|