@garuda-agent/garuda 0.18.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/README.md +3 -2
  3. package/dist/{acpCommand-IUZPLPLI.js → acpCommand-JAYNMIMP.js} +17 -14
  4. package/dist/advise-HDKJGNBL.js +195 -0
  5. package/dist/brief-RWKESOPT.js +91 -0
  6. package/dist/buildCheck-HB46NWLX.js +19 -0
  7. package/dist/buildInfo-K73FFI5S.js +8 -0
  8. package/dist/chunk-2OYTNNUJ.js +216 -0
  9. package/dist/{chunk-OQ26W5L4.js → chunk-34WBDQBL.js} +134 -77
  10. package/dist/chunk-4QRTTRVK.js +88 -0
  11. package/dist/{chunk-NHWHG3SW.js → chunk-5UDYQ5MK.js} +354 -59
  12. package/dist/chunk-CWVXOSAA.js +894 -0
  13. package/dist/{chunk-FARS4R67.js → chunk-DQSCCE77.js} +18 -1
  14. package/dist/chunk-DUYLVGPY.js +821 -0
  15. package/dist/{chunk-Q4P3W657.js → chunk-GKU62MHQ.js} +10 -1124
  16. package/dist/chunk-H7LIBGZT.js +9 -0
  17. package/dist/{chunk-FOPBOB7W.js → chunk-IKPGHM54.js} +6 -4
  18. package/dist/chunk-IYGJWX65.js +9 -0
  19. package/dist/{chunk-65NUEOI7.js → chunk-JMH5W5DG.js} +1 -44
  20. package/dist/chunk-L55VUSX3.js +54 -0
  21. package/dist/{chunk-B4SJY7KT.js → chunk-LMKGSYJC.js} +77 -8
  22. package/dist/{chunk-23FXBNBB.js → chunk-MNE7HHX2.js} +2 -1
  23. package/dist/chunk-NH72AELP.js +48 -0
  24. package/dist/{chunk-UKIE4AAM.js → chunk-NLS7GMF7.js} +11 -105
  25. package/dist/{chunk-2FIV2VZP.js → chunk-QLBCM2YQ.js} +2 -2
  26. package/dist/{chunk-6XHON3Q6.js → chunk-QXTHEFKP.js} +3 -3
  27. package/dist/{chunk-4BVCYCQT.js → chunk-R7RCW4BQ.js} +9 -7
  28. package/dist/chunk-TLA2PDGM.js +156 -0
  29. package/dist/{chunk-5B5HDTUR.js → chunk-TWQ7V2L6.js} +2 -2
  30. package/dist/chunk-ULAJPMY7.js +107 -0
  31. package/dist/chunk-VISKX36R.js +306 -0
  32. package/dist/{chunk-55CFEEIN.js → chunk-WK5NE6RY.js} +2 -2
  33. package/dist/chunk-Y62J7PTD.js +90 -0
  34. package/dist/chunk-YAQ6QLKB.js +40 -0
  35. package/dist/{chunk-7TH3NHCQ.js → chunk-ZKT4P2PC.js} +2 -2
  36. package/dist/cli/index.js +252 -61
  37. package/dist/{create-W6BV75QX.js → create-OACSGJ37.js} +5 -5
  38. package/dist/{evalCommand-SKJQ6CW2.js → evalCommand-BLJJ4G76.js} +43 -33
  39. package/dist/{evalTune-CZRCPSZ3.js → evalTune-W53KAD4V.js} +25 -18
  40. package/dist/{export-SJ3DUP6W.js → export-E677HIQS.js} +27 -4
  41. package/dist/exportHtml-BMV73YRO.js +21 -0
  42. package/dist/{inkChat-WYYAFZXD.js → inkChat-EVLYDC3K.js} +258 -42
  43. package/dist/{install-TKWN5AYH.js → install-4ST5BNRG.js} +3 -3
  44. package/dist/{jobCommand-2ODQLHB3.js → jobCommand-ILOTYOX3.js} +8 -7
  45. package/dist/{launchd-3A3ONEW5.js → launchd-QG6R5RAK.js} +4 -4
  46. package/dist/{lspCommand-ZZ6R4PA2.js → lspCommand-X4FTTONR.js} +3 -3
  47. package/dist/{manager-TCYTA5XU.js → manager-L25IUXHW.js} +3 -3
  48. package/dist/{nightCommand-WJU6VZA6.js → nightCommand-XPWT6CQZ.js} +8 -7
  49. package/dist/receiptView-YFDHV253.js +19 -0
  50. package/dist/{remove-42WQNVPX.js → remove-STX5UO7X.js} +3 -3
  51. package/dist/{run-PTMRQCIU.js → run-W4TK52WT.js} +5 -5
  52. package/dist/{server-VEYIJHEU.js → server-ASM73QVY.js} +3 -3
  53. package/dist/{setupCommand-YMF6I3MD.js → setupCommand-OSOMFHDQ.js} +9 -8
  54. package/dist/tour-CEVMAC5S.js +18 -0
  55. package/dist/tune-VX2WQTHS.js +52 -0
  56. package/dist/uiState-YFJQ43D7.js +16 -0
  57. package/package.json +1 -1
  58. package/dist/chunk-4JSUUDFQ.js +0 -118
  59. package/dist/chunk-C57GHLKP.js +0 -9
  60. package/dist/chunk-DOCDCQVM.js +0 -484
  61. package/dist/tune-YVI2KWQF.js +0 -31
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/buildInfo.ts
4
+ var BUILD_COMMIT = true ? "820270c2d2acf39e101be31cdb9b8f9ed1b5839e" : void 0;
5
+
6
+ export {
7
+ BUILD_COMMIT
8
+ };
9
+ //# sourceMappingURL=chunk-H7LIBGZT.js.map
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ taskHash
4
+ } from "./chunk-DUYLVGPY.js";
2
5
  import {
3
6
  compareReports,
4
7
  costPerPass,
5
- formatComparison,
6
- taskHash
7
- } from "./chunk-DOCDCQVM.js";
8
+ formatComparison
9
+ } from "./chunk-CWVXOSAA.js";
8
10
 
9
11
  // src/evals/tune.ts
10
12
  import { readFile } from "fs/promises";
@@ -329,4 +331,4 @@ export {
329
331
  parseProposal,
330
332
  tune
331
333
  };
332
- //# sourceMappingURL=chunk-FOPBOB7W.js.map
334
+ //# sourceMappingURL=chunk-IKPGHM54.js.map
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/version.ts
4
+ var VERSION = "0.18.1";
5
+
6
+ export {
7
+ VERSION
8
+ };
9
+ //# sourceMappingURL=chunk-IYGJWX65.js.map
@@ -91,53 +91,10 @@ function prefixInfo(id) {
91
91
  return best?.info;
92
92
  }
93
93
 
94
- // src/tools/atomicWrite.ts
95
- import { randomBytes } from "crypto";
96
- import { chmod, link, realpath, rename, stat, unlink, writeFile as writeFile2 } from "fs/promises";
97
- import { basename, dirname as dirname2, join as join2 } from "path";
98
- async function writeFileAtomic(path, content, { createOnly }) {
99
- const target = createOnly ? path : await realpath(path);
100
- const temp = join2(
101
- dirname2(target),
102
- `.${basename(target)}.garuda-${randomBytes(4).toString("hex")}.tmp`
103
- );
104
- const mode = createOnly ? void 0 : (await stat(target)).mode & 4095;
105
- await writeFile2(temp, content, { flag: "wx", ...mode === void 0 ? {} : { mode } });
106
- try {
107
- if (createOnly) {
108
- try {
109
- await link(temp, target);
110
- } catch (error) {
111
- const code = error.code;
112
- if (code !== "EPERM" && code !== "ENOTSUP" && code !== "EXDEV") throw error;
113
- if (await exists(target)) throw Object.assign(new Error("EEXIST"), { code: "EEXIST" });
114
- await rename(temp, target);
115
- return;
116
- }
117
- } else {
118
- await chmod(temp, mode ?? 420);
119
- await rename(temp, target);
120
- return;
121
- }
122
- } finally {
123
- await unlink(temp).catch(() => {
124
- });
125
- }
126
- }
127
- async function exists(path) {
128
- try {
129
- await stat(path);
130
- return true;
131
- } catch {
132
- return false;
133
- }
134
- }
135
-
136
94
  export {
137
- writeFileAtomic,
138
95
  fetchCatalog,
139
96
  saveCatalog,
140
97
  loadCatalog,
141
98
  mergedModels
142
99
  };
143
- //# sourceMappingURL=chunk-65NUEOI7.js.map
100
+ //# sourceMappingURL=chunk-JMH5W5DG.js.map
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/uiState.ts
4
+ import { mkdir, readFile, writeFile } from "fs/promises";
5
+ import { join } from "path";
6
+ var KEY_HINT_CHATS = 5;
7
+ async function countChat(home) {
8
+ const dir = join(home, ".garuda");
9
+ const file = join(dir, "ui.json");
10
+ let chats = 0;
11
+ let data = {};
12
+ try {
13
+ const read = JSON.parse(await readFile(file, "utf8"));
14
+ if (typeof read === "object" && read !== null && !Array.isArray(read)) {
15
+ data = read;
16
+ }
17
+ if (typeof data.chats === "number" && Number.isInteger(data.chats) && data.chats >= 0) {
18
+ chats = data.chats;
19
+ }
20
+ } catch {
21
+ }
22
+ chats++;
23
+ try {
24
+ await mkdir(dir, { recursive: true, mode: 448 });
25
+ await writeFile(file, `${JSON.stringify({ ...data, chats })}
26
+ `, { mode: 384 });
27
+ } catch {
28
+ }
29
+ return chats;
30
+ }
31
+ var KEYS_HELP = [
32
+ "Keys:",
33
+ " Enter sends \xB7 \\ then Enter, or Alt+Enter: a new line \xB7 \u2191\u2193 earlier inputs",
34
+ " Tab completes /commands and @paths \xB7 Shift+Tab plan mode \xB7 Ctrl-P commands",
35
+ " Ctrl-G edits the prompt in your editor \xB7 Ctrl-O shows the last output in full",
36
+ " Esc or Ctrl-C stops a turn \xB7 Ctrl-C clears the line \xB7 Ctrl-D exits",
37
+ " !command runs a command yourself \xB7 @path attaches a file"
38
+ ].join("\n");
39
+ function contextBar(percent) {
40
+ const full = percent <= 0 ? 0 : Math.max(1, Math.min(10, Math.round(percent / 10)));
41
+ return `${"\u2593".repeat(full)}${"\u2591".repeat(10 - full)}`;
42
+ }
43
+ function contextLevel(percent) {
44
+ return percent >= 80 ? "full" : percent >= 70 ? "high" : "ok";
45
+ }
46
+
47
+ export {
48
+ KEY_HINT_CHATS,
49
+ countChat,
50
+ KEYS_HELP,
51
+ contextBar,
52
+ contextLevel
53
+ };
54
+ //# sourceMappingURL=chunk-L55VUSX3.js.map
@@ -42,6 +42,10 @@ var NETWORK_PRESETS = {
42
42
  ]
43
43
  };
44
44
  var PRESET_NAMES = Object.keys(NETWORK_PRESETS);
45
+ function presetFor(host) {
46
+ const bare = host.toLowerCase();
47
+ return PRESET_NAMES.find((name) => NETWORK_PRESETS[name]?.includes(bare));
48
+ }
45
49
  var NETWORK_PORTS = [80, 443];
46
50
  var HOST = /^(\*\.)?([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,63}$/;
47
51
  function expandAllowlist(entries) {
@@ -74,9 +78,9 @@ function describeAllowlist(entries) {
74
78
  }
75
79
 
76
80
  // src/permissions/sandboxPaths.ts
77
- import { realpathSync } from "fs";
81
+ import { readdirSync, realpathSync } from "fs";
78
82
  import { homedir, tmpdir } from "os";
79
- import { isAbsolute, join, resolve } from "path";
83
+ import { isAbsolute, join, resolve, sep } from "path";
80
84
  import { globbySync } from "globby";
81
85
  var DENY_READ_IN_HOME = [
82
86
  ".ssh",
@@ -101,6 +105,49 @@ var DENY_READ_IN_HOME = [
101
105
  ".claude.json",
102
106
  ".claude/.credentials.json"
103
107
  ];
108
+ var PERSONAL_IN_HOME = [
109
+ "Desktop",
110
+ "Documents",
111
+ "Downloads",
112
+ "Movies",
113
+ "Music",
114
+ "Pictures",
115
+ "Public",
116
+ "Library/Mail",
117
+ "Library/Messages",
118
+ "Library/Safari",
119
+ "Library/Calendars",
120
+ "Library/Application Support/AddressBook",
121
+ "Library/Mobile Documents",
122
+ "Library/CloudStorage"
123
+ ];
124
+ function denyAround(base, keep, keepHidden = false) {
125
+ let names;
126
+ try {
127
+ names = readdirSync(base);
128
+ } catch {
129
+ return [];
130
+ }
131
+ const out = [];
132
+ for (const name of names) {
133
+ const path = join(base, name);
134
+ if (keep.includes(path)) continue;
135
+ if (keep.some((k) => k.startsWith(`${path}${sep}`))) {
136
+ out.push(...denyAround(path, keep));
137
+ continue;
138
+ }
139
+ if (keepHidden && name.startsWith(".")) continue;
140
+ out.push(path);
141
+ }
142
+ return out;
143
+ }
144
+ function personalDenials(home, root) {
145
+ return PERSONAL_IN_HOME.flatMap((name) => {
146
+ const folder = join(home, name);
147
+ if (root === folder) return [];
148
+ return root.startsWith(`${folder}${sep}`) ? denyAround(folder, [root]) : [folder];
149
+ });
150
+ }
104
151
  var PROTECTED_IN_ROOT = [".git/hooks", ".git/config", ".garuda"];
105
152
  var WRITE_IN_HOME = [
106
153
  ".cache",
@@ -122,6 +169,7 @@ function sandboxPaths(root, settings = {}, home = homedir()) {
122
169
  denyWritePaths: PROTECTED_IN_ROOT.map((p) => join(root, p)),
123
170
  denyReadPaths: unique([
124
171
  ...DENY_READ_IN_HOME.map((p) => join(home, p)),
172
+ ...personalDenials(home, root),
125
173
  ...(settings.denyRead ?? []).map(expand)
126
174
  ])
127
175
  };
@@ -817,10 +865,10 @@ function seatbeltProfile(policy, extra = []) {
817
865
  }
818
866
  }
819
867
  if (policy.denyWritePaths.length > 0) {
820
- lines.push(`(deny file-write* ${paths(policy.denyWritePaths)})`);
868
+ lines.push(`(deny file-write* ${paths(withDataVolume(policy.denyWritePaths))})`);
821
869
  }
822
870
  if (policy.denyReadPaths.length > 0) {
823
- lines.push(`(deny file-read* ${paths(policy.denyReadPaths)})`);
871
+ lines.push(`(deny file-read* ${paths(withDataVolume(policy.denyReadPaths))})`);
824
872
  }
825
873
  lines.push("(deny lsopen)", "(deny appleevent-send)");
826
874
  if (!policy.network) {
@@ -836,6 +884,14 @@ function seatbeltProfile(policy, extra = []) {
836
884
  lines.push(...extra);
837
885
  return lines.join("\n");
838
886
  }
887
+ function withDataVolume(list) {
888
+ const out = [];
889
+ for (const path of list) {
890
+ out.push(path);
891
+ if (path.startsWith("/Users/")) out.push(`/System/Volumes/Data${path}`);
892
+ }
893
+ return out;
894
+ }
839
895
  function quote2(path) {
840
896
  return `"${path.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
841
897
  }
@@ -976,6 +1032,8 @@ var schema = z.strictObject({
976
1032
  }).optional(),
977
1033
  /** Language server diagnostics after edits (0.4). enabled: default false. */
978
1034
  lsp: z.strictObject({ enabled: z.boolean().optional() }).optional(),
1035
+ /** The chat's look (0.18.1). clearOnStart: default true (the Ink chat starts on a clear screen). */
1036
+ ui: z.strictObject({ clearOnStart: z.boolean().optional() }).optional(),
979
1037
  /**
980
1038
  * The project's formatter after edits (0.10). enabled: default false (A/B first). commands: by
981
1039
  * name, false turns a detected formatter off; a command adds one or replaces a detected one.
@@ -1065,6 +1123,7 @@ function parseSettings(json, source = SETTINGS_FILE) {
1065
1123
  skills,
1066
1124
  agents,
1067
1125
  notifications,
1126
+ ui,
1068
1127
  thinking,
1069
1128
  formatters,
1070
1129
  network
@@ -1102,6 +1161,7 @@ function parseSettings(json, source = SETTINGS_FILE) {
1102
1161
  ...commands?.mode === void 0 ? {} : { commands: { mode: commands.mode } },
1103
1162
  ...todo?.enabled === void 0 ? {} : { todo: { enabled: todo.enabled } },
1104
1163
  ...lsp?.enabled === void 0 ? {} : { lsp: { enabled: lsp.enabled } },
1164
+ ...ui?.clearOnStart === void 0 ? {} : { ui: { clearOnStart: ui.clearOnStart } },
1105
1165
  ...undo?.enabled === void 0 ? {} : { undo: { enabled: undo.enabled } },
1106
1166
  ...skills?.enabled === void 0 ? {} : { skills: { enabled: skills.enabled } },
1107
1167
  ...agents?.enabled === void 0 ? {} : { agents: { enabled: agents.enabled } },
@@ -1160,7 +1220,7 @@ async function loadSettings(root) {
1160
1220
  }
1161
1221
 
1162
1222
  // src/permissions/engine.ts
1163
- import { sep } from "path";
1223
+ import { sep as sep2 } from "path";
1164
1224
 
1165
1225
  // src/permissions/sensitive.ts
1166
1226
  var SENSITIVE_PATTERNS = [
@@ -1430,7 +1490,14 @@ var PermissionEngine = class {
1430
1490
  if (choice === "once" && accepted !== void 0) {
1431
1491
  return { decision: { allowed: true, by: "user", hunks: accepted }, userChoice: choice };
1432
1492
  }
1433
- return { decision: { allowed: true, by: "user" }, userChoice: choice };
1493
+ return {
1494
+ decision: {
1495
+ allowed: true,
1496
+ by: "user",
1497
+ ...choice === "session" ? { remembered: true } : {}
1498
+ },
1499
+ userChoice: choice
1500
+ };
1434
1501
  }
1435
1502
  /**
1436
1503
  * Plan mode: file changes and memory are always denied; a command runs only in the OS sandbox
@@ -1518,7 +1585,7 @@ var PermissionEngine = class {
1518
1585
  const denied = policyDeniedPaths(this.root, this.policy?.denyPaths ?? []);
1519
1586
  let writePaths = [.../* @__PURE__ */ new Set([...paths.writePaths, ...this.access.writePaths])];
1520
1587
  let denyWritePaths = [...paths.denyWritePaths, ...denied];
1521
- const inRoot = (p) => p === this.root || p.startsWith(`${this.root}${sep}`);
1588
+ const inRoot = (p) => p === this.root || p.startsWith(`${this.root}${sep2}`);
1522
1589
  if (readOnlyRoot) {
1523
1590
  writePaths = writePaths.filter((p) => !inRoot(p));
1524
1591
  denyWritePaths = [this.root, ...denyWritePaths];
@@ -1588,10 +1655,12 @@ function describeTarget(target) {
1588
1655
 
1589
1656
  export {
1590
1657
  isSensitive,
1658
+ presetFor,
1591
1659
  NETWORK_PORTS,
1592
1660
  expandAllowlist,
1593
1661
  hostAllowed,
1594
1662
  describeAllowlist,
1663
+ denyAround,
1595
1664
  sandboxPaths,
1596
1665
  HostExecutor,
1597
1666
  EXECUTOR_NAMES,
@@ -1602,4 +1671,4 @@ export {
1602
1671
  JOB_DENIAL,
1603
1672
  PermissionEngine
1604
1673
  };
1605
- //# sourceMappingURL=chunk-B4SJY7KT.js.map
1674
+ //# sourceMappingURL=chunk-LMKGSYJC.js.map
@@ -36,6 +36,7 @@ var BUILTIN_COMMANDS = [
36
36
  "search",
37
37
  "undo",
38
38
  "redo",
39
+ "rewind",
39
40
  "exit",
40
41
  "quit"
41
42
  ];
@@ -43,4 +44,4 @@ var BUILTIN_COMMANDS = [
43
44
  export {
44
45
  BUILTIN_COMMANDS
45
46
  };
46
- //# sourceMappingURL=chunk-23FXBNBB.js.map
47
+ //# sourceMappingURL=chunk-MNE7HHX2.js.map
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/tools/atomicWrite.ts
4
+ import { randomBytes } from "crypto";
5
+ import { chmod, link, realpath, rename, stat, unlink, writeFile } from "fs/promises";
6
+ import { basename, dirname, join } from "path";
7
+ async function writeFileAtomic(path, content, { createOnly }) {
8
+ const target = createOnly ? path : await realpath(path);
9
+ const temp = join(
10
+ dirname(target),
11
+ `.${basename(target)}.garuda-${randomBytes(4).toString("hex")}.tmp`
12
+ );
13
+ const mode = createOnly ? void 0 : (await stat(target)).mode & 4095;
14
+ await writeFile(temp, content, { flag: "wx", ...mode === void 0 ? {} : { mode } });
15
+ try {
16
+ if (createOnly) {
17
+ try {
18
+ await link(temp, target);
19
+ } catch (error) {
20
+ const code = error.code;
21
+ if (code !== "EPERM" && code !== "ENOTSUP" && code !== "EXDEV") throw error;
22
+ if (await exists(target)) throw Object.assign(new Error("EEXIST"), { code: "EEXIST" });
23
+ await rename(temp, target);
24
+ return;
25
+ }
26
+ } else {
27
+ await chmod(temp, mode ?? 420);
28
+ await rename(temp, target);
29
+ return;
30
+ }
31
+ } finally {
32
+ await unlink(temp).catch(() => {
33
+ });
34
+ }
35
+ }
36
+ async function exists(path) {
37
+ try {
38
+ await stat(path);
39
+ return true;
40
+ } catch {
41
+ return false;
42
+ }
43
+ }
44
+
45
+ export {
46
+ writeFileAtomic
47
+ };
48
+ //# sourceMappingURL=chunk-NH72AELP.js.map
@@ -4,17 +4,11 @@ import {
4
4
  contextSize,
5
5
  totalTokens
6
6
  } from "./chunk-7KBOIPUT.js";
7
- import {
8
- cleanLine
9
- } from "./chunk-PZS3V6P3.js";
10
7
  import {
11
8
  ZERO_USAGE,
12
9
  addUsage,
13
10
  reportedUsage
14
11
  } from "./chunk-BHZ6G65V.js";
15
- import {
16
- formatRule
17
- } from "./chunk-3NJNVZTK.js";
18
12
 
19
13
  // src/session/store.ts
20
14
  import { randomBytes } from "crypto";
@@ -227,7 +221,15 @@ function addSnapshot(session, tree, prompt, durationMs) {
227
221
  const messages = session.messages.length;
228
222
  pushPoint(session.undo, tree, messages, prompt);
229
223
  const point = session.undo.points.at(-1);
230
- session.journal?.write({ type: "snapshot", tree, messages, prompt: point.prompt, durationMs });
224
+ point.at = Date.now();
225
+ session.journal?.write({
226
+ type: "snapshot",
227
+ tree,
228
+ messages,
229
+ prompt: point.prompt,
230
+ durationMs,
231
+ at: point.at
232
+ });
231
233
  }
232
234
  function undoTurn(session, after) {
233
235
  const point = popPoint(session.undo, session.messages, after);
@@ -343,101 +345,6 @@ function addSideUsage(session, purpose, response, costUsd) {
343
345
  });
344
346
  }
345
347
 
346
- // src/permissions/projectSettings.ts
347
- import { createHash as createHash2 } from "crypto";
348
- function settingsRisk(settings) {
349
- const risk = {
350
- executorHost: settings.executor === "host",
351
- allow: settings.allow.map(formatRule),
352
- writePaths: [...settings.sandbox?.writePaths ?? []],
353
- envAllow: [...settings.envAllow],
354
- allowLocalhost: settings.web?.allowLocalhost === true
355
- };
356
- const formatterCommands = Object.entries(settings.formatters?.commands ?? {}).flatMap(
357
- ([name, f]) => f === false ? [] : [`${name}: ${f.command.join(" ")}`]
358
- );
359
- if (formatterCommands.length > 0) risk.formatterCommands = formatterCommands;
360
- const none = !risk.executorHost && risk.allow.length === 0 && risk.writePaths.length === 0 && risk.envAllow.length === 0 && !risk.allowLocalhost && risk.formatterCommands === void 0;
361
- return none ? void 0 : risk;
362
- }
363
- function settingsRiskHash(risk) {
364
- return createHash2("sha256").update(JSON.stringify(risk)).digest("hex");
365
- }
366
- function settingsRiskLines(risk) {
367
- const lines = [];
368
- if (risk.executorHost) lines.push('executor: "host" (commands run with no OS sandbox)');
369
- if (risk.allow.length > 0) {
370
- lines.push(`permissions.allow (no question for these): ${risk.allow.join(", ")}`);
371
- }
372
- if (risk.writePaths.length > 0) {
373
- lines.push(`sandbox.writePaths (commands may write there): ${risk.writePaths.join(", ")}`);
374
- }
375
- if (risk.envAllow.length > 0) {
376
- lines.push(`env.allow (commands see these variables): ${risk.envAllow.join(", ")}`);
377
- }
378
- if (risk.allowLocalhost) lines.push("web.allowLocalhost (web_fetch may reach this machine)");
379
- if (risk.formatterCommands !== void 0) {
380
- lines.push(
381
- `formatters.commands (run after each edit): ${risk.formatterCommands.map(cleanLine).join("; ")}`
382
- );
383
- }
384
- return lines;
385
- }
386
- function withoutRisk(settings) {
387
- const { sandbox, web, formatters, ...rest } = settings;
388
- const { writePaths: _writePaths, ...sandboxRest } = sandbox ?? {};
389
- const keptCommands = Object.fromEntries(
390
- Object.entries(formatters?.commands ?? {}).filter(([, f]) => f === false)
391
- );
392
- return {
393
- ...rest,
394
- ...formatters === void 0 ? {} : { formatters: { ...formatters, commands: keptCommands } },
395
- executor: settings.executor === "host" ? "auto" : settings.executor,
396
- allow: [],
397
- envAllow: [],
398
- ...sandbox === void 0 ? {} : { sandbox: sandboxRest },
399
- ...web === void 0 ? {} : { web: { ...web, allowLocalhost: false } }
400
- };
401
- }
402
- async function gateProjectSettings(options) {
403
- const risk = settingsRisk(options.settings);
404
- if (risk === void 0) return { settings: options.settings };
405
- const hash2 = settingsRiskHash(risk);
406
- const known = options.trust?.settingsHash(options.root);
407
- if (known === hash2) return { settings: options.settings };
408
- const lines = settingsRiskLines(risk);
409
- if (options.ask !== void 0) {
410
- const choice = await options.ask({
411
- tool: "settings",
412
- target: { kind: "input", json: "" },
413
- title: "Project settings that loosen safety",
414
- preview: [
415
- `This project's .garuda/settings.json ${known === void 0 ? "asks for" : "changed and now asks for"}:`,
416
- ...lines.map((line) => ` - ${line}`),
417
- "A cloned repository can ship this file. Without your yes, Garuda uses safe defaults for these",
418
- "parts; the rest of the file applies."
419
- ].join("\n"),
420
- isolation: options.isolation ?? "os",
421
- question: "Use these project settings?",
422
- choices: ["once", "session", "deny"],
423
- labels: {
424
- once: "Yes, for this run",
425
- session: "Yes, and remember (asks again when they change)",
426
- deny: "No, use safe defaults"
427
- }
428
- });
429
- if (choice === "session") await options.trust?.setSettingsHash(options.root, hash2);
430
- if (choice !== "deny") return { settings: options.settings };
431
- }
432
- return {
433
- settings: withoutRisk(options.settings),
434
- notice: [
435
- `Project settings not applied (not approved): ${lines.join("; ")}.`,
436
- "Start the chat in a terminal and answer yes to use them."
437
- ].join(" ")
438
- };
439
- }
440
-
441
348
  export {
442
349
  FileTracker,
443
350
  FileSessionStore,
@@ -466,7 +373,6 @@ export {
466
373
  addChildDebit,
467
374
  addChildReport,
468
375
  settleChildDebits,
469
- addSideUsage,
470
- gateProjectSettings
376
+ addSideUsage
471
377
  };
472
- //# sourceMappingURL=chunk-UKIE4AAM.js.map
378
+ //# sourceMappingURL=chunk-NLS7GMF7.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  writeFileAtomic
4
- } from "./chunk-65NUEOI7.js";
4
+ } from "./chunk-NH72AELP.js";
5
5
  import {
6
6
  keepSecretForRedaction,
7
7
  lookupModel
@@ -332,4 +332,4 @@ export {
332
332
  providerKeyValue,
333
333
  resolveModel
334
334
  };
335
- //# sourceMappingURL=chunk-2FIV2VZP.js.map
335
+ //# sourceMappingURL=chunk-QLBCM2YQ.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  DEFAULT_ENV_ALLOWLIST
4
- } from "./chunk-B4SJY7KT.js";
4
+ } from "./chunk-LMKGSYJC.js";
5
5
 
6
6
  // src/jobs/git.ts
7
7
  var GIT_TIMEOUT_MS = 12e4;
@@ -28,7 +28,7 @@ async function hostCommand(executor, cwd, argv, options = {}) {
28
28
  const command = argv.map(shellWord).join(" ");
29
29
  const result = await executor.run(command, policy, {
30
30
  ...options.signal === void 0 ? {} : { signal: options.signal },
31
- env: { GIT_TERMINAL_PROMPT: "0" }
31
+ env: { ...options.env, GIT_TERMINAL_PROMPT: "0" }
32
32
  });
33
33
  if (result.aborted) throw options.signal?.reason ?? new Error("stopped");
34
34
  const out = { exitCode: result.exitCode, stdout: result.stdout.text, stderr: result.stderr.text };
@@ -46,4 +46,4 @@ export {
46
46
  git,
47
47
  hostCommand
48
48
  };
49
- //# sourceMappingURL=chunk-6XHON3Q6.js.map
49
+ //# sourceMappingURL=chunk-QXTHEFKP.js.map
@@ -4,9 +4,11 @@ import {
4
4
  pickChannel
5
5
  } from "./chunk-N4I6YS3E.js";
6
6
  import {
7
- gateProjectSettings,
7
+ gateProjectSettings
8
+ } from "./chunk-ULAJPMY7.js";
9
+ import {
8
10
  isCostIncomplete
9
- } from "./chunk-UKIE4AAM.js";
11
+ } from "./chunk-NLS7GMF7.js";
10
12
  import {
11
13
  totalTokens
12
14
  } from "./chunk-7KBOIPUT.js";
@@ -19,11 +21,11 @@ import {
19
21
  jobChanges,
20
22
  outsideScope,
21
23
  prepareWorktree
22
- } from "./chunk-55CFEEIN.js";
24
+ } from "./chunk-WK5NE6RY.js";
23
25
  import {
24
26
  defaultAgentEnv,
25
27
  removeAgent
26
- } from "./chunk-5B5HDTUR.js";
28
+ } from "./chunk-TWQ7V2L6.js";
27
29
  import {
28
30
  jobReport
29
31
  } from "./chunk-3MRQJKUO.js";
@@ -40,11 +42,11 @@ import {
40
42
  JobGitError,
41
43
  git,
42
44
  hostCommand
43
- } from "./chunk-6XHON3Q6.js";
45
+ } from "./chunk-QXTHEFKP.js";
44
46
  import {
45
47
  createExecutor,
46
48
  loadSettings
47
- } from "./chunk-B4SJY7KT.js";
49
+ } from "./chunk-LMKGSYJC.js";
48
50
  import {
49
51
  JOBS_DIR,
50
52
  loadJob,
@@ -350,4 +352,4 @@ export {
350
352
  msUntil,
351
353
  finishJob
352
354
  };
353
- //# sourceMappingURL=chunk-4BVCYCQT.js.map
355
+ //# sourceMappingURL=chunk-R7RCW4BQ.js.map