@ra3orblade/swarm 0.13.2 → 0.15.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.
@@ -1,9 +1,19 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
+ var __esm = (fn, res, err) => () => {
4
+ if (fn)
5
+ try {
6
+ res = fn(fn = 0);
7
+ } catch (e) {
8
+ err = [e];
9
+ }
10
+ if (err)
11
+ throw err[0];
12
+ return res;
13
+ };
3
14
 
4
- // packages/hook/src/bin.ts
5
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
6
- import { dirname, join as join2 } from "path";
15
+ // packages/client/src/bins.ts
16
+ var init_bins = () => {};
7
17
 
8
18
  // packages/client/src/daemon.ts
9
19
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
@@ -12,8 +22,6 @@ import { join } from "path";
12
22
  function swarmHome() {
13
23
  return process.env.SWARM_HOME ?? join(homedir(), ".swarm");
14
24
  }
15
- var DEFAULT_PORT = Number(process.env.SWARM_PORT ?? 7777);
16
- var infoFile = () => join(swarmHome(), "daemon.json");
17
25
  function readDaemonInfo() {
18
26
  const file = infoFile();
19
27
  if (!existsSync(file))
@@ -24,7 +32,6 @@ function readDaemonInfo() {
24
32
  return null;
25
33
  }
26
34
  }
27
- var tokenFile = (home = swarmHome()) => join(home, "token");
28
35
  function readToken(home = swarmHome()) {
29
36
  try {
30
37
  const t = readFileSync(tokenFile(home), "utf8").trim();
@@ -33,153 +40,329 @@ function readToken(home = swarmHome()) {
33
40
  return null;
34
41
  }
35
42
  }
36
- var authedFetch = (input, init) => {
43
+ function resolveBaseUrl(explicit) {
44
+ const raw = explicit ?? process.env.SWARM_URL ?? readDaemonInfo()?.url ?? `http://127.0.0.1:${DEFAULT_PORT}`;
45
+ return raw.replace(/\/$/, "");
46
+ }
47
+ var DEFAULT_PORT, infoFile = () => join(swarmHome(), "daemon.json"), tokenFile = (home = swarmHome()) => join(home, "token"), authedFetch = (input, init) => {
37
48
  const h = new Headers(init?.headers);
38
49
  const t = readToken();
39
50
  if (t && !h.has("authorization"))
40
51
  h.set("authorization", `Bearer ${t}`);
41
52
  return fetch(input, { ...init, headers: h });
42
53
  };
43
- function resolveBaseUrl(explicit) {
44
- const raw = explicit ?? process.env.SWARM_URL ?? readDaemonInfo()?.url ?? `http://127.0.0.1:${DEFAULT_PORT}`;
45
- return raw.replace(/\/$/, "");
46
- }
54
+ var init_daemon = __esm(() => {
55
+ init_bins();
56
+ DEFAULT_PORT = Number(process.env.SWARM_PORT ?? 7777);
57
+ });
58
+
59
+ // packages/client/src/index.ts
60
+ var init_src = __esm(() => {
61
+ init_daemon();
62
+ init_bins();
63
+ init_daemon();
64
+ });
47
65
  // packages/core/src/actor.ts
48
- var HUMAN_ALIASES = new Set(["cli", "dashboard", "me", "desktop", "human"]);
49
- var DAEMON_ALIASES = new Set(["daemon", "system", "swarm"]);
50
- // packages/core/src/art.ts
51
- var ROBOT = [
52
- " MM MM ",
53
- " LL LL ",
54
- " LL LL ",
55
- " LM MM ",
56
- " MD MS ",
57
- " LE LE ",
58
- " ME ME ",
59
- " MS MS ",
60
- " KK KK ",
61
- " SSDDDSDDDDDDDDDDDDDDDDDDDDDDDDDDDDS ",
62
- " SSDSDDDDDDDDDDDDDDDDDDDDDDDDDDDDDSS ",
63
- " SSDOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKSS ",
64
- " SSOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMOSS ",
65
- " DDKMMLLLMLLMMMMMMMMMMMMMMMMMMMMMODD ",
66
- " DDOMMLMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
67
- " SSSSODDOMMLMMMMMMMMMMMMMMMMMMMMMMMMMMODDOSSSS ",
68
- " SSSSODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOSSSS ",
69
- " SMMMMODDOMMLMMMMMMMMMMMMMMMMMMMMMMMMMMODDOSMMMS ",
70
- " DMSSSODDOMMMMOOOOOOMMMMMMMMMOOOOOOMMMMODDOSSSED ",
71
- " DSSSSODDOMMMMODDDDDMMMMMMMMMODDDDDMMMMODDOSSSSK ",
72
- " DSMMSODDOMMMMDDDDDDMMMMMMMMMKDDDDDMMMMODDOSMMSK ",
73
- " DSSSSODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDOSSSSK ",
74
- " DSSSSODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDODSSSK ",
75
- " DSSSSODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDODSSSK ",
76
- " DSSSKODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDODSSSD ",
77
- " OKKKKODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOKKKKO ",
78
- " KKKKODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOOKKK ",
79
- " KKKOODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOOKKK ",
80
- " DDOMMMMDDDDDDDDDDDDDDDDDDDDDMMMMODD ",
81
- " DDOMMMMDDDDDDDDDDDDDDDDDDDDDMMMMODD ",
82
- " DDOMMMMDDDDDDDDDDDDDDDDDDDDDMMMMODD ",
83
- " DDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
84
- " DDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
85
- " DDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
86
- " DDDOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKDD ",
87
- " DDDDDDDDDDDDDDDDDDDDDDDDDDDDDKDDDDD ",
88
- " DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ",
89
- " OOOOOOOOOOO ",
90
- " OODDDDDKKOO ",
91
- " DSMMMMMMSDD ",
92
- " OOOOOOOOOOO ",
93
- " KDSMMMMMESSKK ",
94
- " MMMMMMSSKOOKDSMMMMMSSSDKOODSEMMMMMM ",
95
- " SSSSDO MMMMMMSSKKOOOOOOOOOOOOOOOOOKSSSMMMMMM ODSSSS ",
96
- " EEESSSSOOLLLLMMMMMESSSSSSSSSSSSSSSSSSSSMMMMMLLLLOOSSSSMME ",
97
- " SMMSSSSKOEELLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLEEOOSSSSEMS ",
98
- " DSSSSSSSOOSEEKSMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMSOEEDOOSSSSSSSK ",
99
- " DSSSSSSDOOSEOSKEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMEOSDEDOODSSSSSSK ",
100
- " KSSSSSSKOODSSOSEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEESOEEDOOKSSSSSSK ",
101
- " DKSSSSKKOODSMEEMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMSEMSDOOKKSSSSDK ",
102
- " OKDDKDKKOOKSMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMSKOOKKKKKKKO ",
103
- " OOOOOKKKOOKSMMMMESSSSSSSSSSSSSMMMMEEEEEESSSSEMMMMSKOOKKKOOOOO ",
104
- " OOOOOOKKOOKSMMMSSKKKKKKKKKKKKKKMMMDKOOOOOOOODDMMMSKOOKKOOOOOO ",
105
- " OKSSKOOOOOKSMMMKKMLLLLLLLLLLLMKMMMDDSSSSSSSSDDMMMSKOOOOODSSKO ",
106
- " SDOKKK KSMMMKDLLLLLLLLLLLLLKMMMKDKKKDDKKKDKMMMSK KKKKDS ",
107
- " DMLLMKO KSMMMKDLLLLLLLLLLLLLKMMMDDDDDDDDDSKDMMMSK ODMLLMD ",
108
- " DELLMSD KSMMMKKMLLLLLLLLLLLLKMMMDKKKKKKKKKDDMMMSK DSMLLEO ",
109
- " KSKKKKDO KSMMMEDDDDKKDDDKKKDKSMMMDDDDDDDDDSDDMMMSK ODDKKKSK ",
110
- " EMLLMDO KSMMMMLLLLLLLLLLLLLLLMMMDOOOOOOOOODDMMMSK ODMLLME ",
111
- " KMLLMSD KSMMMMMMMMMMMMMMMMMMMMMMDDSSDDSSSSDDMMMSK SSMLLEK ",
112
- " KSKKKKSO KSMMMMMOOMMOOMKOEMOOMMMMDOOOOOOOOODDMMMSK OSKKKKSO ",
113
- " SMLLMDO KSMMMMMKKMMKKMDKEMKKMMMMKDDDDSSSSDDDMMMSK ODMLLMS ",
114
- " SMMMMSD KSMMMMMEEMMEEMEEMMEEMMMMDSOOOOOOOODDMMSSK DSMMMMD ",
115
- " OOOOOOO KSSMMMMMMMMMMMMMMMMMMMMMDDDDDDDDDDDDMSSSK OOOOOOO ",
116
- " KSMLLLMMSKO KSSSSMMMMMMMMMMMMMMMMMMMSOOOOOOOOOOSSSSSK OKSMLLLLMSO ",
117
- " DSMMMEEESKK KSSKSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSOSSK KDSMMMMMESD ",
118
- " DSMMMMMESKK KSOOKSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSOOKSK KKSMMMMMMSD ",
119
- " DSMMMMMESKK OKSKSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSDSKO KDSMMMMMMSD ",
120
- " KDDDDDDDDKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKDDDDDDDDO ",
121
- " OOOOOOOOOOO OOOOOOOOO OOOOOOOOO OOOOOOOOOOO ",
122
- "DSDO OSSO ODKKKKKKK OKKKKKKKO KEDO ODEK",
123
- "SMDO OSMD KDSSSSSSK KDSSSSSKO SMDO ODMS",
124
- "DEKO OOED OOKKKOOO OOOKKOOO SSOO OKMS",
125
- "DEKO OOED SEMMMMSK OSMMMMED SSKO OKES",
126
- "DSSO OESK SEMMMMSK OSMMMMED KSSO OSSK",
127
- "KSMS EEDO OOKKKKKO OOKKKKKO OSEE EMSO",
128
- " DSS SSK OSEEESDO ODSEEESO KSS SSD ",
129
- " OOO OO SEMMMMSK OSMMMMED OO OOO ",
130
- " KSSSSSDO OKSSESSK ",
131
- " DKKDDDDDDKKDK KDKDDDDDDDKDK ",
132
- " SSMLLMMMMMMLMSK DSSMMMMMMMLLMSS ",
133
- " SMLMMMMMMMMLLED SEMMMMMMMMMMLMS ",
134
- " DMMMMMMMMMMMMMMD SMMMMMMMMMMMMMSK ",
135
- " ODSSSSSSSSSSSSSSDO ODSSSSSSSSSSSSSSKO ",
136
- " OOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOO ",
137
- " OKSSSSSSSSSSSSSSK KSSSSSSSSSSSSSSKO ",
138
- " OKDDDDDDDDDDDDDDK KDDDDDDDDDDDDDDOO "
139
- ];
140
- var HEAD = ROBOT.slice(0, 37);
141
- // packages/core/src/audit.ts
142
- var AUDIT_TYPES = new Set([
143
- "session.started",
144
- "session.ended",
145
- "tool.denied",
146
- "claim.acquired",
147
- "claim.renewed",
148
- "claim.released",
149
- "claim.expired",
150
- "claim.orphaned",
151
- "claim.denied",
152
- "rules.changed",
153
- "worktree.reclaimed",
154
- "worktree.created",
155
- "worktree.removed",
156
- "worktree.bootstrapped",
157
- "pr.opened",
158
- "question.asked",
159
- "question.answered",
160
- "dispatch.queued",
161
- "dispatch.started",
162
- "dispatch.finished",
163
- "resource.acquired",
164
- "resource.released",
165
- "resource.reaped",
166
- "process.started",
167
- "process.exited",
168
- "gate.recorded",
169
- "handoff.recorded",
170
- "permission.requested",
171
- "permission.resolved",
172
- "incident.opened",
173
- "incident.acked",
174
- "run.result",
175
- "workflow.started",
176
- "workflow.finished"
177
- ]);
178
- var AUDIT_TYPES_SQL = [...AUDIT_TYPES].map((t) => `'${t}'`).join(", ");
179
- // packages/core/src/budget.ts
180
- var BUDGET_ASK_TOOLS = new Set(["Bash", "Edit", "Write", "MultiEdit", "NotebookEdit"]);
66
+ var HUMAN_ALIASES, DAEMON_ALIASES;
67
+ var init_actor = __esm(() => {
68
+ HUMAN_ALIASES = new Set(["cli", "dashboard", "me", "desktop", "human"]);
69
+ DAEMON_ALIASES = new Set(["daemon", "system", "swarm"]);
70
+ });
71
+
72
+ // packages/core/src/adapters/aider/history.ts
73
+ var init_history = () => {};
74
+ // packages/core/src/adapters/claude-code/adapter.ts
75
+ var init_adapter = () => {};
76
+
77
+ // packages/core/src/adapters/codex/rollout.ts
78
+ var init_rollout = () => {};
79
+
80
+ // packages/core/src/adapters/gemini/chats.ts
81
+ var init_chats = () => {};
82
+
83
+ // packages/core/src/adapters/grok/updates.ts
84
+ var init_updates = () => {};
85
+
86
+ // packages/core/src/adapters/opencode/db.ts
87
+ var init_db = () => {};
88
+ // packages/core/src/adapters/index.ts
89
+ var init_adapters = __esm(() => {
90
+ init_history();
91
+ init_adapter();
92
+ init_rollout();
93
+ init_chats();
94
+ init_updates();
95
+ init_db();
96
+ init_history();
97
+ init_adapter();
98
+ init_rollout();
99
+ init_chats();
100
+ init_updates();
101
+ init_db();
102
+ });
103
+ // packages/core/src/prompt.ts
104
+ var init_prompt = () => {};
105
+
106
+ // packages/core/src/adapters/claude-code/hooks.ts
107
+ var OWN_SUMMARY;
108
+ var init_hooks = __esm(() => {
109
+ init_prompt();
110
+ OWN_SUMMARY = new Set([
111
+ "Bash",
112
+ "Read",
113
+ "Edit",
114
+ "Write",
115
+ "MultiEdit",
116
+ "NotebookEdit",
117
+ "Glob",
118
+ "Grep",
119
+ "Agent",
120
+ "Task",
121
+ "WebFetch",
122
+ "WebSearch",
123
+ "AskUserQuestion"
124
+ ]);
125
+ });
126
+
127
+ // packages/core/src/guards.ts
128
+ function expandHome(s, home) {
129
+ return s.replace(/(^|[\s="'`:])(?:~|\$HOME|\$\{HOME\})(?=\/|$|[\s"'`])/g, `$1${home}`);
130
+ }
131
+ function segments(cmd) {
132
+ return cmd.split(/\s*(?:&&|\|\||;|\n)\s*/).map((s) => s.trim()).filter(Boolean);
133
+ }
134
+ function words(seg) {
135
+ return (seg.match(/"[^"]*"|'[^']*'|\S+/g) ?? []).map((w) => w.replace(/^["']|["']$/g, ""));
136
+ }
137
+ function dangerousTarget(target, home, toplevel) {
138
+ if (/^\$\{?\w+\}?(\/|$)/.test(target) && !target.startsWith(home))
139
+ return `starts with ${target.match(/^\$\{?\w+\}?/)?.[0]} \u2014 if that is ever unset, it is /`;
140
+ const t = trimSlash(target.replace(/\/\*$/, "/").replace(/\/\.$/, "/"));
141
+ if (t === "*" || target === "/*")
142
+ return "removes everything the glob matches, from the top";
143
+ if (t === "." || t === ".." || t.startsWith("../"))
144
+ return "removes the working directory or its parent";
145
+ if (t === home || t === trimSlash(home))
146
+ return "removes the home directory";
147
+ if (!t.startsWith("/"))
148
+ return null;
149
+ if (SYSTEM_DIRS.includes(t))
150
+ return `removes ${t}`;
151
+ if (t.split("/").length <= 3 && /^\/(Users|home)\//.test(t))
152
+ return `removes the home directory ${t}`;
153
+ if (SCRATCH.test(t))
154
+ return null;
155
+ if (toplevel && !(t === toplevel || t.startsWith(`${trimSlash(toplevel)}/`)))
156
+ return `removes ${t}, outside this repository`;
157
+ return null;
158
+ }
159
+ function destructiveFs(cmd, home, toplevel) {
160
+ const c = expandHome(cmd, home);
161
+ for (const seg of segments(c)) {
162
+ const w = words(seg.replace(/^sudo\s+/, ""));
163
+ if (w[0] === "rm") {
164
+ const flags = w.filter((x) => x.startsWith("-"));
165
+ const recursive = flags.some((f) => /^-[a-zA-Z]*[rR]/.test(f) || f === "--recursive");
166
+ if (!recursive)
167
+ continue;
168
+ for (const target of w.slice(1).filter((x) => !x.startsWith("-"))) {
169
+ const why = dangerousTarget(target, home, toplevel);
170
+ if (why)
171
+ return { what: `\`rm -r ${target}\` ${why}` };
172
+ }
173
+ }
174
+ if (w[0] === "find" && (w.includes("-delete") || /-exec\s+rm\b/.test(seg))) {
175
+ const root = w[1] ?? ".";
176
+ const why = dangerousTarget(root, home, toplevel);
177
+ if (why && root !== ".")
178
+ return { what: `\`find ${root} \u2026 -delete\` ${why}` };
179
+ }
180
+ if (/^(mkfs(\.\w+)?|newfs\w*|wipefs)$/.test(w[0] ?? ""))
181
+ return { what: `\`${w[0]}\` formats a disk` };
182
+ if (w[0] === "dd" && w.some((x) => /^of=\/dev\//.test(x)))
183
+ return { what: "`dd` writes over a device" };
184
+ if (w[0] === "diskutil" && /^(erase|zero|secureErase|reformat)/i.test(w[1] ?? ""))
185
+ return { what: `\`diskutil ${w[1]}\` erases a disk` };
186
+ if ((w[0] === "chmod" || w[0] === "chown") && w.some((x) => /^-[a-zA-Z]*R/.test(x))) {
187
+ const target = w[w.length - 1] ?? "";
188
+ if (target.startsWith("/") || target === home) {
189
+ const why = dangerousTarget(target, home, toplevel);
190
+ if (why)
191
+ return { what: `\`${w[0]} -R ${target}\` ${why.replace(/^removes/, "changes")}` };
192
+ }
193
+ }
194
+ }
195
+ return null;
196
+ }
197
+ function destructiveInfra(cmd) {
198
+ for (const r of INFRA)
199
+ if (r.re.test(cmd) && (!r.needs || r.needs.test(cmd)))
200
+ return { what: r.what };
201
+ return null;
202
+ }
203
+ function pipeToShell(cmd) {
204
+ if (new RegExp(`${FETCHER}[^|;&]*\\|\\s*(${INTERP})\\b`).test(cmd))
205
+ return { what: "pipes a downloaded script straight into an interpreter" };
206
+ if (/\b((ba|z)?sh|source|\.)\s+<\(\s*(curl|wget)\b/.test(cmd))
207
+ return { what: "runs a downloaded script via process substitution" };
208
+ if (/\b(ba|z)?sh\s+-c\s+["']?\$\(\s*(curl|wget)\b/.test(cmd))
209
+ return { what: "runs a downloaded script via command substitution" };
210
+ return null;
211
+ }
212
+ function secretFileIn(text) {
213
+ return SECRET_FILES.find((s) => s.re.test(text))?.what ?? null;
214
+ }
215
+ function secretCommand(cmd) {
216
+ if (/\bsecurity\s+find-(generic|internet)-password\b[^|;&]*\s-[a-z]*w/.test(cmd))
217
+ return { what: "prints a password from the macOS keychain" };
218
+ if (/\b(printenv|env)\s*(\||>|$)/.test(cmd) && /\|\s*(curl|nc|ncat)\b/.test(cmd))
219
+ return { what: "sends the environment over the network" };
220
+ for (const seg of segments(cmd)) {
221
+ if (!READERS.test(seg))
222
+ continue;
223
+ const w = words(seg);
224
+ const copy = /^(cp|scp|rsync)$/.test(w[0] ?? "");
225
+ const text = copy ? w.slice(1, -1).filter((x) => !x.startsWith("-")).join(" ") : seg;
226
+ const what = secretFileIn(text);
227
+ if (what)
228
+ return { what: `${copy ? "copies" : "reads"} ${what}` };
229
+ }
230
+ return null;
231
+ }
232
+ function secretFile(tool, path) {
233
+ const what = secretFileIn(path);
234
+ if (!what)
235
+ return null;
236
+ if (tool === "Read")
237
+ return { what: `reads ${what}` };
238
+ if (/\.env(\.[\w-]+)?$|\.(pem|p12|pfx|key)$/i.test(path))
239
+ return { what: `writes ${what}` };
240
+ return null;
241
+ }
242
+ function guardConfigFile(path, home) {
243
+ const p = expandHome(path, home);
244
+ const h = trimSlash(home);
245
+ if (/(^|\/)\.claude\/settings(\.local)?\.json$/.test(p))
246
+ return "Claude Code settings (hooks and permissions)";
247
+ if (p === `${h}/.claude.json`)
248
+ return "Claude Code's MCP registration";
249
+ if (p.startsWith(`${h}/.swarm/`) || p === `${h}/.swarm`)
250
+ return "Swarm's config and ledger";
251
+ if (/(^|\/)\.swarm\.toml$/.test(p))
252
+ return "this repo's Swarm rules";
253
+ if (p === `${h}/.codex/config.toml` || p === `${h}/.gemini/settings.json` || p === `${h}/.cursor/hooks.json`)
254
+ return "another agent's hook config";
255
+ return null;
256
+ }
257
+ function configTamperCommand(cmd, home) {
258
+ if (/(^|[\s;&|])swarm\s+uninstall\b/.test(cmd))
259
+ return { what: "uninstalls Swarm's hooks" };
260
+ for (const seg of segments(expandHome(cmd, home))) {
261
+ if (!WRITERS.test(seg))
262
+ continue;
263
+ for (const w of words(seg)) {
264
+ const target = w.replace(/^>+/, "");
265
+ if (!target.includes("/") && !target.startsWith("."))
266
+ continue;
267
+ const what = guardConfigFile(target, home);
268
+ if (what)
269
+ return { what: `changes ${what} (${target})` };
270
+ }
271
+ }
272
+ return null;
273
+ }
274
+ var trimSlash = (p) => p.length > 1 ? p.replace(/\/+$/, "") : p, SYSTEM_DIRS, SCRATCH, DB_CLIENT, INFRA, FETCHER, INTERP, SECRET_FILES, READERS, WRITERS;
275
+ var init_guards = __esm(() => {
276
+ SYSTEM_DIRS = [
277
+ "/",
278
+ "/bin",
279
+ "/boot",
280
+ "/dev",
281
+ "/etc",
282
+ "/home",
283
+ "/lib",
284
+ "/opt",
285
+ "/private",
286
+ "/root",
287
+ "/sbin",
288
+ "/System",
289
+ "/Library",
290
+ "/Applications",
291
+ "/Users",
292
+ "/usr",
293
+ "/var"
294
+ ];
295
+ SCRATCH = /^(\/tmp|\/private\/tmp|\/var\/folders|\/private\/var\/folders)(\/|$)/;
296
+ DB_CLIENT = /\b(psql|mysql|mariadb|sqlite3|sqlcmd|clickhouse(-client)?|cockroach|mongo(sh)?|duckdb)\b/i;
297
+ INFRA = [
298
+ {
299
+ what: "`terraform destroy` tears the stack down",
300
+ re: /\b(terraform|tofu)\s+(\S+\s+)*destroy\b/
301
+ },
302
+ { what: "`terraform state rm` forgets live resources", re: /\b(terraform|tofu)\s+state\s+rm\b/ },
303
+ {
304
+ what: "`kubectl delete` of a namespace or everything",
305
+ re: /\bkubectl\s+(\S+\s+)*delete\s+(\S+\s+)*(ns|namespaces?|pvc?|persistentvolume(claim)?s?|--all\b|-A\b|--all-namespaces\b)/
306
+ },
307
+ { what: "`kubectl drain` evicts a node's workloads", re: /\bkubectl\s+(\S+\s+)*drain\b/ },
308
+ { what: "`helm uninstall` removes a release", re: /\bhelm\s+(\S+\s+)*(uninstall|delete)\b/ },
309
+ {
310
+ what: "`aws s3 rm --recursive` empties a bucket path",
311
+ re: /\baws\s+s3\s+rm\b[^|;&]*--recursive/
312
+ },
313
+ { what: "`aws s3 rb` deletes a bucket", re: /\baws\s+s3\s+rb\b/ },
314
+ {
315
+ what: "an AWS delete / terminate call",
316
+ re: /\baws\s+[\w-]+\s+(terminate-instances|delete-(db-instance|db-cluster|stack|table|bucket|cluster|function|user|role|repository))\b/
317
+ },
318
+ { what: "a `gcloud \u2026 delete`", re: /\bgcloud\s+(\S+\s+)+delete\b/ },
319
+ { what: "an `az \u2026 delete`", re: /\baz\s+(\S+\s+)+delete\b/ },
320
+ {
321
+ what: "`docker system prune` removes images and volumes",
322
+ re: /\bdocker\s+system\s+prune\b[^|;&]*(-a\b|--all\b|--volumes\b)/
323
+ },
324
+ { what: "`docker volume rm/prune` deletes data volumes", re: /\bdocker\s+volume\s+(rm|prune)\b/ },
325
+ { what: "a SQL `DROP`", re: /\bdrop\s+(database|schema|table)\b/i, needs: DB_CLIENT },
326
+ { what: "a SQL `TRUNCATE`", re: /\btruncate\s+(table\s+)?\w/i, needs: DB_CLIENT },
327
+ {
328
+ what: "an unbounded SQL `DELETE`",
329
+ re: /\bdelete\s+from\s+[\w."]+\s*(;|"|'|$)/i,
330
+ needs: DB_CLIENT
331
+ },
332
+ { what: "`dropdb` deletes a database", re: /(^|[\s;&|])dropdb\s/ },
333
+ {
334
+ what: "a database reset",
335
+ re: /\b(prisma\s+migrate\s+reset|rails\s+db:(drop|reset)|rake\s+db:(drop|reset)|supabase\s+db\s+reset)\b/
336
+ },
337
+ {
338
+ what: "`redis-cli FLUSHALL/FLUSHDB` wipes the store",
339
+ re: /\bredis-cli\b[^|;&]*\bflush(all|db)\b/i
340
+ },
341
+ { what: "`dropDatabase()` in the Mongo shell", re: /\bdropDatabase\s*\(/, needs: DB_CLIENT }
342
+ ];
343
+ FETCHER = String.raw`\b(curl|wget|fetch|http|xh)\b`;
344
+ INTERP = String.raw`(sudo\s+(-\S+\s+)*)?(env\s+(\w+=\S+\s+)*)?(ba|z|k|da|fi)?sh|python[0-9.]*|node|perl|ruby|php|iex`;
345
+ SECRET_FILES = [
346
+ {
347
+ what: "a .env file",
348
+ re: /(^|[/\s"'`=@])\.env(\.(?!example\b|sample\b|template\b|dist\b|defaults?\b)[\w-]+)?(?=$|[\s"'`;|&)])/
349
+ },
350
+ {
351
+ what: "an SSH private key",
352
+ re: /(^|[/\s"'`])(id_rsa|id_ed25519|id_ecdsa|id_dsa)(?=$|[\s"'`;|&)])/
353
+ },
354
+ { what: "AWS credentials", re: /\.aws\/credentials\b/ },
355
+ { what: "an npm token file", re: /(^|[/\s"'`])\.npmrc\b/ },
356
+ { what: "a kubeconfig", re: /\.kube\/config\b/ },
357
+ { what: "Google Cloud credentials", re: /gcloud\/[\w-]*credential/i },
358
+ { what: "a private key file", re: /[\w-]\.(pem|p12|pfx|key)(?=$|[\s"'`;|&)])/i },
359
+ { what: "a netrc", re: /(^|[/\s"'`])\.netrc\b/ }
360
+ ];
361
+ READERS = /(^|[\s;&|(])(cat|less|more|head|tail|bat|strings|base64|xxd|od|hexdump|grep|egrep|rg|ag|awk|nl|tac|cp|scp|rsync|openssl|gpg|pbcopy)\b|curl\b[^|;&]*(-d|--data[\w-]*|-F|--form|-T|--upload-file)\s*@/;
362
+ WRITERS = /(^|[\s;&|(])(>>?|tee|rm|mv|cp|truncate|ln|chmod|chown|unlink|shred)\b|>>?\s*\S|\bsed\s+(-[a-zA-Z]*\s+)*-i|\bperl\s+-[a-zA-Z]*i|\bsqlite3\b[^|;&]*\b(delete|update|drop|insert)\b/i;
363
+ });
364
+
181
365
  // packages/core/src/rules.ts
182
- var LIVE_WINDOW_MS = 10 * 60000;
183
366
  function otherLiveInSameTree(current, sessions, now, withinMs = LIVE_WINDOW_MS) {
184
367
  if (!current.toplevel)
185
368
  return null;
@@ -196,7 +379,6 @@ function otherLiveInSameTree(current, sessions, now, withinMs = LIVE_WINDOW_MS)
196
379
  }
197
380
  return null;
198
381
  }
199
- var GIT_GLOBAL_OPTS = "(?:\\s+(?:-C\\s+\\S+|-c\\s+\\S+|--git-dir(?:=|\\s+)\\S+|--work-tree(?:=|\\s+)\\S+|--namespace(?:=|\\s+)\\S+|--no-pager|--paginate|--bare|--literal-pathspecs|--exec-path(?:=\\S+)?))*";
200
382
  function gitVerb(verb, tail) {
201
383
  return new RegExp(`\\bgit${GIT_GLOBAL_OPTS}\\s+${verb}${tail}`);
202
384
  }
@@ -230,21 +412,149 @@ function killedPorts(cmd) {
230
412
  ports.add(Number(m[1]));
231
413
  return [...ports];
232
414
  }
233
- var DEFAULT_MODES = {
234
- shared_tree: "ask",
235
- destructive_git: "ask",
236
- pattern_kill: "ask",
237
- protected_ports: "ask",
238
- no_foreign_worktree: "ask",
239
- claim_required_to_write: "off",
240
- protected: { ports: [] }
241
- };
242
- function guardBash(cmd, current, sessions, now, modes = DEFAULT_MODES) {
415
+ function familyHitsBash(cmd, home, toplevel) {
416
+ const out = [];
417
+ const add = (rule, h) => h && out.push({ rule, what: h.what });
418
+ add("destructive_fs", destructiveFs(cmd, home, toplevel));
419
+ add("destructive_infra", destructiveInfra(cmd));
420
+ add("pipe_to_shell", pipeToShell(cmd));
421
+ add("secrets", secretCommand(cmd));
422
+ add("config_tamper", configTamperCommand(cmd, home));
423
+ return out;
424
+ }
425
+ function familyHitsFile(tool, path, home) {
426
+ const out = [];
427
+ const secret = secretFile(tool, path);
428
+ if (secret)
429
+ out.push({ rule: "secrets", what: secret.what });
430
+ if (WRITE_TOOLS.has(tool)) {
431
+ const what = guardConfigFile(path, home);
432
+ if (what)
433
+ out.push({ rule: "config_tamper", what: `changes ${what} (${path})` });
434
+ }
435
+ return out;
436
+ }
437
+ function familyDecision(hits, modes) {
438
+ for (const h of hits) {
439
+ const mode = modes[h.rule] ?? "off";
440
+ if (mode !== "off")
441
+ return { action: mode, rule: h.rule, reason: FAMILY_REASON[h.rule](h.what) };
442
+ }
443
+ return null;
444
+ }
445
+ function guardFile(tool, path, modes = DEFAULT_MODES, home = process.env.HOME ?? "") {
446
+ if ((modes.secrets ?? "off") === "off" && (modes.config_tamper ?? "off") === "off")
447
+ return { action: "allow" };
448
+ return familyDecision(familyHitsFile(tool, path, home), modes) ?? { action: "allow" };
449
+ }
450
+ function isSingleCommand(cmd) {
451
+ return !/[&|;<>`\n]/.test(cmd) && !cmd.includes("$(");
452
+ }
453
+ function stripFlagsOutsideQuotes(cmd, flags) {
454
+ let out = "";
455
+ let quote = null;
456
+ let i = 0;
457
+ while (i < cmd.length) {
458
+ const ch = cmd[i];
459
+ if (quote) {
460
+ out += ch;
461
+ if (ch === quote && cmd[i - 1] !== "\\")
462
+ quote = null;
463
+ i++;
464
+ continue;
465
+ }
466
+ if (ch === '"' || ch === "'") {
467
+ quote = ch;
468
+ out += ch;
469
+ i++;
470
+ continue;
471
+ }
472
+ const rest = cmd.slice(i);
473
+ const hit = flags.find((f) => rest.startsWith(f) && /^(\s|$)/.test(rest.slice(f.length)));
474
+ if (hit) {
475
+ out = out.replace(/[ \t]+$/, "");
476
+ i += hit.length;
477
+ continue;
478
+ }
479
+ out += ch;
480
+ i++;
481
+ }
482
+ return out;
483
+ }
484
+ function rewriteNoVerify(cmd) {
485
+ if (!isSingleCommand(cmd) || !/^\s*git\s/.test(cmd))
486
+ return null;
487
+ if (!/--no-(verify|gpg-sign)\b/.test(cmd))
488
+ return null;
489
+ const out = stripFlagsOutsideQuotes(cmd, ["--no-verify", "--no-gpg-sign"]);
490
+ return out === cmd ? null : out;
491
+ }
492
+ function rewriteDryRun(cmd) {
493
+ if (!isSingleCommand(cmd))
494
+ return null;
495
+ const key = cmd.replace(/\s+/g, " ").trim();
496
+ if (/^\s*terraform\s+apply\b/.test(cmd)) {
497
+ const tail = cmd.replace(/^\s*terraform\s+apply\b/, "").trim();
498
+ if (tail.length > 0 && tail.split(/\s+/).some((a) => !a.startsWith("-")))
499
+ return null;
500
+ return {
501
+ key,
502
+ command: cmd.replace(/^(\s*)terraform\s+apply\b/, "$1terraform plan").replace(/\s+-auto-approve\b/g, "").replace(/ {2,}/g, " ").trimEnd()
503
+ };
504
+ }
505
+ if (/^\s*kubectl\s+delete\b/.test(cmd) && !/--dry-run\b/.test(cmd))
506
+ return { key, command: `${cmd.trimEnd()} --dry-run=client` };
507
+ if (/^\s*helm\s+(uninstall|delete)\b/.test(cmd) && !/--dry-run\b/.test(cmd))
508
+ return { key, command: `${cmd.trimEnd()} --dry-run` };
509
+ return null;
510
+ }
511
+ function compiled(rule) {
512
+ const hit = customRe.get(rule);
513
+ if (hit !== undefined)
514
+ return hit;
515
+ let re = null;
516
+ try {
517
+ re = new RegExp(rule.match, rule.action === "rewrite" ? "g" : "");
518
+ } catch {
519
+ re = null;
520
+ }
521
+ customRe.set(rule, re);
522
+ return re;
523
+ }
524
+ function applyCustomRule(rule, cmd) {
525
+ const re = compiled(rule);
526
+ if (!re)
527
+ return null;
528
+ if (rule.action === "off" || !re.test(cmd))
529
+ return null;
530
+ const id = `custom:${rule.name}`;
531
+ const reason = rule.reason ?? `matches custom rule "${rule.name}" (${rule.match})`;
532
+ if (rule.action !== "rewrite")
533
+ return { action: rule.action, rule: id, reason };
534
+ if (!isSingleCommand(cmd))
535
+ return {
536
+ action: "ask",
537
+ rule: id,
538
+ reason: `${reason} \u2014 not rewritten: the command chains or redirects`
539
+ };
540
+ re.lastIndex = 0;
541
+ const out = cmd.replace(re, rule.replace ?? "").trim();
542
+ return out === cmd ? null : { action: "rewrite", rule: id, reason, command: out };
543
+ }
544
+ function guardBash(cmd, current, sessions, now, modes = DEFAULT_MODES, ctx = {}) {
243
545
  const other = () => otherLiveInSameTree(current, sessions, now);
244
546
  const hit = (rule, reason) => {
245
547
  const mode = modes[rule];
246
548
  return mode === "off" ? { action: "allow" } : { action: mode, rule, reason };
247
549
  };
550
+ const fix = (rule, reason, command, key) => {
551
+ const mode = modes[rule] ?? "off";
552
+ if (mode === "off")
553
+ return { action: "allow" };
554
+ if (mode === "rewrite")
555
+ return { action: "rewrite", rule, reason, command, ...key ? { key } : {} };
556
+ return { action: mode, rule, reason };
557
+ };
248
558
  if (modes.protected_ports !== "off" && modes.protected.ports.length) {
249
559
  const target = killedPorts(cmd).filter((p) => modes.protected.ports.includes(p));
250
560
  if (target.length) {
@@ -261,7 +571,7 @@ function guardBash(cmd, current, sessions, now, modes = DEFAULT_MODES) {
261
571
  if (modes.shared_tree !== "off" && isBroadStage(cmd)) {
262
572
  const o = other();
263
573
  if (o) {
264
- const d = hit("shared_tree", `Another session (${o.id.slice(0, 8)}) is active in this same checkout. \`git add -A\` / \`git commit -a\` will sweep its uncommitted changes into your commit. Stage explicit paths (\`git add <path>\`), or give each session its own git worktree.`);
574
+ const d = hit("shared_tree", sharedTreeReason(o.id));
265
575
  if (d.action !== "allow")
266
576
  return d;
267
577
  }
@@ -274,6 +584,28 @@ function guardBash(cmd, current, sessions, now, modes = DEFAULT_MODES) {
274
584
  return d;
275
585
  }
276
586
  }
587
+ if (FAMILY_RULES.some((r) => (modes[r] ?? "off") !== "off")) {
588
+ const d = familyDecision(familyHitsBash(cmd, ctx.home ?? process.env.HOME ?? "", current.toplevel), modes);
589
+ if (d)
590
+ return d;
591
+ }
592
+ for (const rule of modes.custom ?? []) {
593
+ const d = applyCustomRule(rule, cmd);
594
+ if (d)
595
+ return d;
596
+ }
597
+ const nv = rewriteNoVerify(cmd);
598
+ if (nv) {
599
+ const d = fix("no_verify", NO_VERIFY_REASON, nv);
600
+ if (d.action !== "allow")
601
+ return d;
602
+ }
603
+ const dr = rewriteDryRun(cmd);
604
+ if (dr && !ctx.rewritesDone?.has(dr.key)) {
605
+ const d = fix("dry_run_first", `The first \`${dr.key}\` in a session runs as a dry run so you can read what it would change; run the real one next.`, dr.command, dr.key);
606
+ if (d.action !== "allow")
607
+ return d;
608
+ }
277
609
  return { action: "allow" };
278
610
  }
279
611
  function norm(p) {
@@ -302,7 +634,6 @@ function absolutePath(path, cwd) {
302
634
  return path;
303
635
  return `${cwd.replace(/\/+$/, "")}/${path}`;
304
636
  }
305
- var WRITE_TOOLS = new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
306
637
  function guardWrite(target, current, claims, modes = DEFAULT_MODES, kind = "file") {
307
638
  const hit = (rule, reason) => {
308
639
  const mode = modes[rule];
@@ -328,22 +659,47 @@ function guardWrite(target, current, claims, modes = DEFAULT_MODES, kind = "file
328
659
  }
329
660
  return { action: "allow" };
330
661
  }
331
- // packages/core/src/hygiene.ts
332
- var HYGIENE_DEFAULTS = {
333
- staleDays: 2,
334
- abandonedDays: 30,
335
- hungryRssKb: 1024 * 1024,
336
- heavyKb: 2 * 1024 * 1024
337
- };
338
- // packages/core/src/ledger.ts
339
- var EDIT_TOOLS = new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
340
- // packages/core/src/outcomes.ts
341
- var DEFAULT_BRANCHES = new Set(["main", "master", "develop", "trunk"]);
662
+ var LIVE_WINDOW_MS, GIT_GLOBAL_OPTS = "(?:\\s+(?:-C\\s+\\S+|-c\\s+\\S+|--git-dir(?:=|\\s+)\\S+|--work-tree(?:=|\\s+)\\S+|--namespace(?:=|\\s+)\\S+|--no-pager|--paginate|--bare|--literal-pathspecs|--exec-path(?:=\\S+)?))*", DEFAULT_MODES, FAMILY_RULES, FAMILY_REASON, customRe, sharedTreeReason = (sessionId) => `Another session (${sessionId.slice(0, 8)}) is active in this same checkout. \`git add -A\` / \`git commit -a\` will sweep its uncommitted changes into your commit. Stage explicit paths (\`git add <path>\`), or give each session its own git worktree.`, NO_VERIFY_REASON = "Hooks and signing exist for a reason: `--no-verify` / `--no-gpg-sign` was dropped. If a hook is wrong, fix the hook.", WRITE_TOOLS;
663
+ var init_rules = __esm(() => {
664
+ init_guards();
665
+ LIVE_WINDOW_MS = 10 * 60000;
666
+ DEFAULT_MODES = {
667
+ shared_tree: "ask",
668
+ destructive_git: "ask",
669
+ pattern_kill: "ask",
670
+ protected_ports: "ask",
671
+ no_foreign_worktree: "ask",
672
+ claim_required_to_write: "off",
673
+ no_verify: "off",
674
+ dry_run_first: "off",
675
+ custom: [],
676
+ destructive_fs: "off",
677
+ destructive_infra: "off",
678
+ pipe_to_shell: "off",
679
+ secrets: "off",
680
+ config_tamper: "off",
681
+ protected: { ports: [] }
682
+ };
683
+ FAMILY_RULES = [
684
+ "destructive_fs",
685
+ "destructive_infra",
686
+ "pipe_to_shell",
687
+ "secrets",
688
+ "config_tamper"
689
+ ];
690
+ FAMILY_REASON = {
691
+ destructive_fs: (w) => `This ${w}. Nothing gets that back \u2014 name the exact paths you mean, inside this repository.`,
692
+ destructive_infra: (w) => `This is ${w} \u2014 outside this checkout, and not something git can undo. Confirm with the owner first.`,
693
+ pipe_to_shell: (w) => `This ${w}. Download it to a file, read it, then run it.`,
694
+ secrets: (w) => `This ${w}. Credentials stay out of the transcript: use the variable by name, or ask the owner.`,
695
+ config_tamper: (w) => `This ${w} \u2014 the settings that decide what agents may do. Ask the owner to change them.`
696
+ };
697
+ customRe = new WeakMap;
698
+ WRITE_TOOLS = new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
699
+ });
700
+
342
701
  // packages/core/src/policy.ts
343
702
  import { createHash } from "crypto";
344
- var POLICY_CACHE_VERSION = 1;
345
- var POLICY_CACHE_FILE = "policy.cache.json";
346
- var digest = (body) => createHash("sha256").update(JSON.stringify(body)).digest("hex");
347
703
  function verifyPolicyCache(raw) {
348
704
  if (!raw || typeof raw !== "object")
349
705
  return null;
@@ -362,10 +718,15 @@ function evaluateOffline(cache, raw, toplevel, now = Date.now()) {
362
718
  const cwd = typeof raw.cwd === "string" ? raw.cwd : "";
363
719
  const isWrite = WRITE_TOOLS.has(tool) && typeof input.file_path === "string";
364
720
  const cmd = tool === "Bash" && typeof input.command === "string" ? input.command : null;
721
+ const modes = cache.modes;
722
+ if (typeof input.file_path === "string" && (isWrite || tool === "Read")) {
723
+ const f = guardFile(tool, absolutePath(input.file_path, cwd), modes);
724
+ if (f.action !== "allow")
725
+ return f;
726
+ }
365
727
  if (!isWrite && !cmd)
366
728
  return { action: "allow" };
367
729
  const current = { id, cwd, toplevel: toplevel(cwd) };
368
- const modes = cache.modes;
369
730
  if (modes.no_foreign_worktree !== "off" || modes.claim_required_to_write !== "off") {
370
731
  const target = isWrite ? absolutePath(input.file_path, cwd) : cwd;
371
732
  const w = guardWrite(target, current, cache.worktrees, modes, isWrite ? "file" : "bash");
@@ -376,10 +737,617 @@ function evaluateOffline(cache, raw, toplevel, now = Date.now()) {
376
737
  return { action: "allow" };
377
738
  return guardBash(cmd, current, cache.sessions, now, modes);
378
739
  }
740
+ var POLICY_CACHE_VERSION = 1, POLICY_CACHE_FILE = "policy.cache.json", digest = (body) => createHash("sha256").update(JSON.stringify(body)).digest("hex");
741
+ var init_policy = __esm(() => {
742
+ init_hooks();
743
+ init_rules();
744
+ });
745
+
746
+ // packages/core/src/agenthooks.ts
747
+ function detectAgent(raw) {
748
+ if (typeof raw.cursor_version === "string" || typeof raw.conversation_id === "string")
749
+ return "cursor";
750
+ if (raw.hook_event_name === "BeforeTool")
751
+ return "gemini";
752
+ if (typeof raw.turn_id === "string")
753
+ return "codex";
754
+ return "claude-code";
755
+ }
756
+ function patchPaths(patch) {
757
+ const out = [];
758
+ for (const m of patch.matchAll(/^\*\*\* (?:Add File|Update File|Delete File|Move to): (.+)$/gm)) {
759
+ const p = m[1]?.trim();
760
+ if (p && !out.includes(p))
761
+ out.push(p);
762
+ }
763
+ return out;
764
+ }
765
+ function toToolRequests(agent, raw) {
766
+ const input = raw.tool_input && typeof raw.tool_input === "object" ? raw.tool_input : {};
767
+ const tool = str(raw.tool_name) ?? "";
768
+ const cwd = str(raw.cwd) ?? "";
769
+ const sessionId = str(raw.session_id) ?? str(raw.conversation_id) ?? "";
770
+ const req = (t, i, at = cwd) => ({
771
+ tool: t,
772
+ input: i,
773
+ sessionId,
774
+ cwd: at
775
+ });
776
+ switch (agent) {
777
+ case "claude-code": {
778
+ const command = str(input.command);
779
+ const file_path = str(input.file_path);
780
+ return [req(tool, { ...command ? { command } : {}, ...file_path ? { file_path } : {} })];
781
+ }
782
+ case "codex": {
783
+ const command = str(input.command);
784
+ if (tool === "Bash" && command)
785
+ return [req("Bash", { command })];
786
+ if (tool === "apply_patch" && command)
787
+ return patchPaths(command).map((p) => req("Write", { file_path: p }));
788
+ return [];
789
+ }
790
+ case "gemini": {
791
+ const shell = str(input.command);
792
+ if (tool === "run_shell_command" && shell)
793
+ return [req("Bash", { command: shell }, str(input.dir_path) ?? cwd)];
794
+ const fp = str(input.file_path);
795
+ if (!fp)
796
+ return [];
797
+ if (tool === "read_file")
798
+ return [req("Read", { file_path: fp })];
799
+ if (tool === "write_file" || tool === "replace")
800
+ return [req("Write", { file_path: fp })];
801
+ return [];
802
+ }
803
+ case "cursor": {
804
+ const shell = str(input.command);
805
+ if (tool === "Shell" && shell)
806
+ return [req("Bash", { command: shell }, str(input.working_directory) ?? cwd)];
807
+ const fp = str(input.file_path) ?? str(input.path) ?? str(input.target_file);
808
+ if (!fp)
809
+ return [];
810
+ if (tool === "Read")
811
+ return [req("Read", { file_path: fp })];
812
+ if (tool === "Write" || tool === "Delete")
813
+ return [req("Write", { file_path: fp })];
814
+ return [];
815
+ }
816
+ }
817
+ }
818
+ function effectiveDecision(agent, d) {
819
+ if (d.action === "ask" && !CAN_ASK.has(agent))
820
+ return {
821
+ ...d,
822
+ action: "deny",
823
+ reason: `${d.reason ?? ""} (Swarm would ask the user here; ${AGENT_LABEL[agent]}'s hooks cannot ask, so the call is refused \u2014 ask the user to run it, or to turn the rule off.)`.trim()
824
+ };
825
+ if (d.action === "rewrite" && agent !== "claude-code")
826
+ return {
827
+ ...d,
828
+ action: "deny",
829
+ reason: `${d.reason ?? ""} Run this instead: ${d.command ?? ""}`.trim()
830
+ };
831
+ return d;
832
+ }
833
+ function renderAgentDecision(agent, d) {
834
+ const e = effectiveDecision(agent, d);
835
+ if (e.action === "allow")
836
+ return "{}";
837
+ const reason = `[swarm] ${e.reason ?? ""}`.trim();
838
+ if (agent === "gemini")
839
+ return JSON.stringify({ decision: "deny", reason });
840
+ return JSON.stringify({
841
+ hookSpecificOutput: {
842
+ hookEventName: "PreToolUse",
843
+ permissionDecision: e.action === "ask" ? "ask" : "deny",
844
+ permissionDecisionReason: reason
845
+ }
846
+ });
847
+ }
848
+ var AGENT_LABEL, CAN_ASK, str = (v) => typeof v === "string" && v ? v : undefined;
849
+ var init_agenthooks = __esm(() => {
850
+ init_policy();
851
+ AGENT_LABEL = {
852
+ "claude-code": "Claude Code",
853
+ codex: "Codex",
854
+ gemini: "Gemini CLI",
855
+ cursor: "Cursor"
856
+ };
857
+ CAN_ASK = new Set(["claude-code"]);
858
+ });
859
+
860
+ // packages/core/src/art.ts
861
+ var ROBOT, HEAD;
862
+ var init_art = __esm(() => {
863
+ ROBOT = [
864
+ " MM MM ",
865
+ " LL LL ",
866
+ " LL LL ",
867
+ " LM MM ",
868
+ " MD MS ",
869
+ " LE LE ",
870
+ " ME ME ",
871
+ " MS MS ",
872
+ " KK KK ",
873
+ " SSDDDSDDDDDDDDDDDDDDDDDDDDDDDDDDDDS ",
874
+ " SSDSDDDDDDDDDDDDDDDDDDDDDDDDDDDDDSS ",
875
+ " SSDOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKSS ",
876
+ " SSOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMOSS ",
877
+ " DDKMMLLLMLLMMMMMMMMMMMMMMMMMMMMMODD ",
878
+ " DDOMMLMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
879
+ " SSSSODDOMMLMMMMMMMMMMMMMMMMMMMMMMMMMMODDOSSSS ",
880
+ " SSSSODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOSSSS ",
881
+ " SMMMMODDOMMLMMMMMMMMMMMMMMMMMMMMMMMMMMODDOSMMMS ",
882
+ " DMSSSODDOMMMMOOOOOOMMMMMMMMMOOOOOOMMMMODDOSSSED ",
883
+ " DSSSSODDOMMMMODDDDDMMMMMMMMMODDDDDMMMMODDOSSSSK ",
884
+ " DSMMSODDOMMMMDDDDDDMMMMMMMMMKDDDDDMMMMODDOSMMSK ",
885
+ " DSSSSODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDOSSSSK ",
886
+ " DSSSSODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDODSSSK ",
887
+ " DSSSSODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDODSSSK ",
888
+ " DSSSKODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDODSSSD ",
889
+ " OKKKKODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOKKKKO ",
890
+ " KKKKODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOOKKK ",
891
+ " KKKOODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOOKKK ",
892
+ " DDOMMMMDDDDDDDDDDDDDDDDDDDDDMMMMODD ",
893
+ " DDOMMMMDDDDDDDDDDDDDDDDDDDDDMMMMODD ",
894
+ " DDOMMMMDDDDDDDDDDDDDDDDDDDDDMMMMODD ",
895
+ " DDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
896
+ " DDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
897
+ " DDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
898
+ " DDDOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKDD ",
899
+ " DDDDDDDDDDDDDDDDDDDDDDDDDDDDDKDDDDD ",
900
+ " DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ",
901
+ " OOOOOOOOOOO ",
902
+ " OODDDDDKKOO ",
903
+ " DSMMMMMMSDD ",
904
+ " OOOOOOOOOOO ",
905
+ " KDSMMMMMESSKK ",
906
+ " MMMMMMSSKOOKDSMMMMMSSSDKOODSEMMMMMM ",
907
+ " SSSSDO MMMMMMSSKKOOOOOOOOOOOOOOOOOKSSSMMMMMM ODSSSS ",
908
+ " EEESSSSOOLLLLMMMMMESSSSSSSSSSSSSSSSSSSSMMMMMLLLLOOSSSSMME ",
909
+ " SMMSSSSKOEELLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLEEOOSSSSEMS ",
910
+ " DSSSSSSSOOSEEKSMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMSOEEDOOSSSSSSSK ",
911
+ " DSSSSSSDOOSEOSKEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMEOSDEDOODSSSSSSK ",
912
+ " KSSSSSSKOODSSOSEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEESOEEDOOKSSSSSSK ",
913
+ " DKSSSSKKOODSMEEMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMSEMSDOOKKSSSSDK ",
914
+ " OKDDKDKKOOKSMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMSKOOKKKKKKKO ",
915
+ " OOOOOKKKOOKSMMMMESSSSSSSSSSSSSMMMMEEEEEESSSSEMMMMSKOOKKKOOOOO ",
916
+ " OOOOOOKKOOKSMMMSSKKKKKKKKKKKKKKMMMDKOOOOOOOODDMMMSKOOKKOOOOOO ",
917
+ " OKSSKOOOOOKSMMMKKMLLLLLLLLLLLMKMMMDDSSSSSSSSDDMMMSKOOOOODSSKO ",
918
+ " SDOKKK KSMMMKDLLLLLLLLLLLLLKMMMKDKKKDDKKKDKMMMSK KKKKDS ",
919
+ " DMLLMKO KSMMMKDLLLLLLLLLLLLLKMMMDDDDDDDDDSKDMMMSK ODMLLMD ",
920
+ " DELLMSD KSMMMKKMLLLLLLLLLLLLKMMMDKKKKKKKKKDDMMMSK DSMLLEO ",
921
+ " KSKKKKDO KSMMMEDDDDKKDDDKKKDKSMMMDDDDDDDDDSDDMMMSK ODDKKKSK ",
922
+ " EMLLMDO KSMMMMLLLLLLLLLLLLLLLMMMDOOOOOOOOODDMMMSK ODMLLME ",
923
+ " KMLLMSD KSMMMMMMMMMMMMMMMMMMMMMMDDSSDDSSSSDDMMMSK SSMLLEK ",
924
+ " KSKKKKSO KSMMMMMOOMMOOMKOEMOOMMMMDOOOOOOOOODDMMMSK OSKKKKSO ",
925
+ " SMLLMDO KSMMMMMKKMMKKMDKEMKKMMMMKDDDDSSSSDDDMMMSK ODMLLMS ",
926
+ " SMMMMSD KSMMMMMEEMMEEMEEMMEEMMMMDSOOOOOOOODDMMSSK DSMMMMD ",
927
+ " OOOOOOO KSSMMMMMMMMMMMMMMMMMMMMMDDDDDDDDDDDDMSSSK OOOOOOO ",
928
+ " KSMLLLMMSKO KSSSSMMMMMMMMMMMMMMMMMMMSOOOOOOOOOOSSSSSK OKSMLLLLMSO ",
929
+ " DSMMMEEESKK KSSKSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSOSSK KDSMMMMMESD ",
930
+ " DSMMMMMESKK KSOOKSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSOOKSK KKSMMMMMMSD ",
931
+ " DSMMMMMESKK OKSKSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSDSKO KDSMMMMMMSD ",
932
+ " KDDDDDDDDKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKDDDDDDDDO ",
933
+ " OOOOOOOOOOO OOOOOOOOO OOOOOOOOO OOOOOOOOOOO ",
934
+ "DSDO OSSO ODKKKKKKK OKKKKKKKO KEDO ODEK",
935
+ "SMDO OSMD KDSSSSSSK KDSSSSSKO SMDO ODMS",
936
+ "DEKO OOED OOKKKOOO OOOKKOOO SSOO OKMS",
937
+ "DEKO OOED SEMMMMSK OSMMMMED SSKO OKES",
938
+ "DSSO OESK SEMMMMSK OSMMMMED KSSO OSSK",
939
+ "KSMS EEDO OOKKKKKO OOKKKKKO OSEE EMSO",
940
+ " DSS SSK OSEEESDO ODSEEESO KSS SSD ",
941
+ " OOO OO SEMMMMSK OSMMMMED OO OOO ",
942
+ " KSSSSSDO OKSSESSK ",
943
+ " DKKDDDDDDKKDK KDKDDDDDDDKDK ",
944
+ " SSMLLMMMMMMLMSK DSSMMMMMMMLLMSS ",
945
+ " SMLMMMMMMMMLLED SEMMMMMMMMMMLMS ",
946
+ " DMMMMMMMMMMMMMMD SMMMMMMMMMMMMMSK ",
947
+ " ODSSSSSSSSSSSSSSDO ODSSSSSSSSSSSSSSKO ",
948
+ " OOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOO ",
949
+ " OKSSSSSSSSSSSSSSK KSSSSSSSSSSSSSSKO ",
950
+ " OKDDDDDDDDDDDDDDK KDDDDDDDDDDDDDDOO "
951
+ ];
952
+ HEAD = ROBOT.slice(0, 37);
953
+ });
954
+
955
+ // packages/core/src/audit.ts
956
+ var AUDIT_TYPES, AUDIT_TYPES_SQL;
957
+ var init_audit = __esm(() => {
958
+ AUDIT_TYPES = new Set([
959
+ "session.started",
960
+ "session.ended",
961
+ "tool.denied",
962
+ "claim.acquired",
963
+ "claim.renewed",
964
+ "claim.released",
965
+ "claim.expired",
966
+ "claim.orphaned",
967
+ "claim.denied",
968
+ "rules.changed",
969
+ "worktree.reclaimed",
970
+ "worktree.created",
971
+ "worktree.removed",
972
+ "worktree.bootstrapped",
973
+ "pr.opened",
974
+ "question.asked",
975
+ "question.answered",
976
+ "dispatch.queued",
977
+ "dispatch.started",
978
+ "dispatch.finished",
979
+ "resource.acquired",
980
+ "resource.released",
981
+ "resource.reaped",
982
+ "process.started",
983
+ "process.exited",
984
+ "gate.recorded",
985
+ "gate.blocked",
986
+ "codify.applied",
987
+ "handoff.recorded",
988
+ "permission.requested",
989
+ "permission.resolved",
990
+ "incident.opened",
991
+ "incident.acked",
992
+ "run.result",
993
+ "workflow.started",
994
+ "workflow.finished"
995
+ ]);
996
+ AUDIT_TYPES_SQL = [...AUDIT_TYPES].map((t) => `'${t}'`).join(", ");
997
+ });
998
+
999
+ // packages/core/src/budget.ts
1000
+ var BUDGET_ASK_TOOLS;
1001
+ var init_budget = __esm(() => {
1002
+ BUDGET_ASK_TOOLS = new Set(["Bash", "Edit", "Write", "MultiEdit", "NotebookEdit"]);
1003
+ });
1004
+ // packages/core/src/workflows.ts
1005
+ var init_workflows = () => {};
1006
+
1007
+ // packages/core/src/config.ts
1008
+ var init_config = __esm(() => {
1009
+ init_audit();
1010
+ init_workflows();
1011
+ });
1012
+
1013
+ // packages/core/src/context.ts
1014
+ var init_context = () => {};
1015
+
1016
+ // packages/core/src/dag.ts
1017
+ var init_dag = () => {};
1018
+ // packages/core/src/dryrun.ts
1019
+ var init_dryrun = __esm(() => {
1020
+ init_rules();
1021
+ });
1022
+ // packages/core/src/gatehealth.ts
1023
+ var init_gatehealth = () => {};
1024
+
1025
+ // packages/core/src/gates.ts
1026
+ var init_gates = () => {};
1027
+
1028
+ // packages/core/src/graphs.ts
1029
+ var init_graphs = __esm(() => {
1030
+ init_rules();
1031
+ });
1032
+
1033
+ // packages/core/src/heat.ts
1034
+ var init_heat = __esm(() => {
1035
+ init_rules();
1036
+ });
1037
+
1038
+ // packages/core/src/worktree.ts
1039
+ var init_worktree = __esm(() => {
1040
+ init_config();
1041
+ });
1042
+
1043
+ // packages/core/src/hygiene.ts
1044
+ var HYGIENE_DEFAULTS;
1045
+ var init_hygiene = __esm(() => {
1046
+ init_worktree();
1047
+ HYGIENE_DEFAULTS = {
1048
+ staleDays: 2,
1049
+ abandonedDays: 30,
1050
+ hungryRssKb: 1024 * 1024,
1051
+ heavyKb: 2 * 1024 * 1024
1052
+ };
1053
+ });
1054
+
1055
+ // packages/core/src/ledger.ts
1056
+ var EDIT_TOOLS;
1057
+ var init_ledger = __esm(() => {
1058
+ init_prompt();
1059
+ EDIT_TOOLS = new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
1060
+ });
1061
+ // packages/core/src/lessons-apply.ts
1062
+ var init_lessons_apply = () => {};
1063
+
1064
+ // packages/core/src/lineage.ts
1065
+ var init_lineage = __esm(() => {
1066
+ init_dag();
1067
+ });
1068
+
1069
+ // packages/core/src/mcphealth.ts
1070
+ var init_mcphealth = __esm(() => {
1071
+ init_gatehealth();
1072
+ });
1073
+ // packages/core/src/memory.ts
1074
+ var init_memory = () => {};
1075
+ // packages/core/src/otel.ts
1076
+ var init_otel = () => {};
1077
+
1078
+ // packages/core/src/outcomes.ts
1079
+ var DEFAULT_BRANCHES;
1080
+ var init_outcomes = __esm(() => {
1081
+ DEFAULT_BRANCHES = new Set(["main", "master", "develop", "trunk"]);
1082
+ });
1083
+
1084
+ // packages/core/src/pricing.ts
1085
+ var init_pricing = () => {};
1086
+ // packages/core/src/provenance.ts
1087
+ var init_provenance = () => {};
1088
+ // packages/core/src/quota.ts
1089
+ var BURN_MIN_SPAN_MS, BURN_LOOKBACK_MS;
1090
+ var init_quota = __esm(() => {
1091
+ BURN_MIN_SPAN_MS = 10 * 60000;
1092
+ BURN_LOOKBACK_MS = 60 * 60000;
1093
+ });
1094
+ // packages/core/src/security.ts
1095
+ var init_security = __esm(() => {
1096
+ init_rules();
1097
+ });
1098
+
1099
+ // packages/core/src/stall.ts
1100
+ var init_stall = () => {};
1101
+
1102
+ // packages/core/src/statusline.ts
1103
+ function parseStatuslinePayload(text) {
1104
+ try {
1105
+ const v = JSON.parse(text);
1106
+ return v && typeof v === "object" && !Array.isArray(v) ? v : null;
1107
+ } catch {
1108
+ return null;
1109
+ }
1110
+ }
1111
+ function pct(color, label, value) {
1112
+ const s = `${label} ${Math.round(value)}%`;
1113
+ if (value >= 90)
1114
+ return red(color, s);
1115
+ if (value >= 70)
1116
+ return yellow(color, s);
1117
+ return s;
1118
+ }
1119
+ function money(usd) {
1120
+ return usd >= 10 ? `$${usd.toFixed(0)}` : `$${usd.toFixed(2)}`;
1121
+ }
1122
+ function renderStatusline(payload, state, opts = {}) {
1123
+ const color = opts.color ?? false;
1124
+ const local = [];
1125
+ const model = payload.model?.display_name ?? payload.model?.id;
1126
+ if (model)
1127
+ local.push(bold(color, model));
1128
+ const ctx = payload.context_window?.used_percentage;
1129
+ if (typeof ctx === "number")
1130
+ local.push(pct(color, "ctx", ctx));
1131
+ const cost = payload.cost?.total_cost_usd;
1132
+ if (typeof cost === "number")
1133
+ local.push(money(cost));
1134
+ const five = payload.rate_limits?.five_hour?.used_percentage;
1135
+ if (typeof five === "number")
1136
+ local.push(pct(color, "5h", five));
1137
+ const week = payload.rate_limits?.seven_day?.used_percentage;
1138
+ if (typeof week === "number")
1139
+ local.push(pct(color, "7d", week));
1140
+ const swarm = [];
1141
+ if (state) {
1142
+ if (state.task)
1143
+ swarm.push(`${state.task.id} ${dim(color, `${state.task.leftMin}m`)}`);
1144
+ if (state.budget && state.budget.level !== "ok")
1145
+ swarm.push((state.budget.level === "exceeded" ? red : yellow)(color, `budget ${state.budget.level} ${Math.round(state.budget.pct * 100)}%`));
1146
+ if (state.incidents > 0)
1147
+ swarm.push(yellow(color, `${state.incidents} incident${state.incidents === 1 ? "" : "s"}`));
1148
+ if (state.waitingOn > 0)
1149
+ swarm.push(red(color, `waiting on you${state.waitingOn > 1 ? ` \xD7${state.waitingOn}` : ""}`));
1150
+ if (state.inbox > 0)
1151
+ swarm.push(`inbox ${state.inbox}`);
1152
+ if (state.quota) {
1153
+ const short = { five_hour: "5h", seven_day: "7d", spend_limit: "spend" }[state.quota.window];
1154
+ const h = state.quota.hoursToLimit;
1155
+ if (h == null)
1156
+ swarm.push(red(color, `${short} limit reached`));
1157
+ else {
1158
+ const s = `${short} limit in ${h < 1 ? `${Math.max(1, Math.round(h * 60))}m` : `${Math.round(h)}h`}`;
1159
+ swarm.push(h < 1 ? red(color, s) : h < 3 ? yellow(color, s) : s);
1160
+ }
1161
+ }
1162
+ }
1163
+ const sep = dim(color, " \xB7 ");
1164
+ const bar = dim(color, " \u2502 ");
1165
+ const left = local.join(sep);
1166
+ if (!swarm.length)
1167
+ return left;
1168
+ const right = swarm.join(sep);
1169
+ return left ? `${left}${bar}${right}` : right;
1170
+ }
1171
+ var ESC = "\x1B[", paint = (color, code, s) => color ? `${ESC}${code}m${s}${ESC}0m` : s, dim = (c, s) => paint(c, "2", s), yellow = (c, s) => paint(c, "33", s), red = (c, s) => paint(c, "31", s), bold = (c, s) => paint(c, "1", s);
1172
+
1173
+ // packages/core/src/tasks.ts
1174
+ var init_tasks = () => {};
1175
+
1176
+ // packages/core/src/team.ts
1177
+ var init_team = () => {};
1178
+
1179
+ // packages/core/src/teamsetup.ts
1180
+ var init_teamsetup = () => {};
1181
+
1182
+ // packages/core/src/transitions.ts
1183
+ var init_transitions = () => {};
1184
+
1185
+ // packages/core/src/types.ts
1186
+ var init_types = () => {};
1187
+
1188
+ // packages/core/src/waiting.ts
1189
+ var init_waiting = () => {};
1190
+
1191
+ // packages/core/src/index.ts
1192
+ var init_src2 = __esm(() => {
1193
+ init_actor();
1194
+ init_adapters();
1195
+ init_hooks();
1196
+ init_agenthooks();
1197
+ init_art();
1198
+ init_audit();
1199
+ init_budget();
1200
+ init_config();
1201
+ init_context();
1202
+ init_dag();
1203
+ init_dryrun();
1204
+ init_gatehealth();
1205
+ init_gates();
1206
+ init_graphs();
1207
+ init_heat();
1208
+ init_hygiene();
1209
+ init_ledger();
1210
+ init_lessons_apply();
1211
+ init_lineage();
1212
+ init_mcphealth();
1213
+ init_memory();
1214
+ init_otel();
1215
+ init_outcomes();
1216
+ init_policy();
1217
+ init_pricing();
1218
+ init_prompt();
1219
+ init_provenance();
1220
+ init_quota();
1221
+ init_rules();
1222
+ init_security();
1223
+ init_stall();
1224
+ init_tasks();
1225
+ init_team();
1226
+ init_teamsetup();
1227
+ init_transitions();
1228
+ init_types();
1229
+ init_waiting();
1230
+ init_workflows();
1231
+ init_worktree();
1232
+ });
1233
+
1234
+ // packages/hook/src/wait.ts
1235
+ async function waitForWake(sessionId, opts = {}) {
1236
+ const maxMs = opts.maxMs ?? 55 * 60000;
1237
+ const pollMs = opts.pollMs ?? 11 * 60000;
1238
+ const started = Date.now();
1239
+ let base = opts.baseUrl ?? resolveBaseUrl();
1240
+ const fallback = `http://127.0.0.1:${DEFAULT_PORT}`;
1241
+ while (Date.now() - started < maxMs) {
1242
+ let body = null;
1243
+ try {
1244
+ const r = await authedFetch(`${base}/v1/wake`, {
1245
+ method: "POST",
1246
+ headers: { "content-type": "application/json" },
1247
+ body: JSON.stringify({ session_id: sessionId }),
1248
+ signal: AbortSignal.timeout(pollMs)
1249
+ });
1250
+ if (r.ok)
1251
+ body = await r.json();
1252
+ } catch {
1253
+ if (base !== fallback && !opts.baseUrl) {
1254
+ base = fallback;
1255
+ continue;
1256
+ }
1257
+ return { exit: 0, reason: "daemon unreachable" };
1258
+ }
1259
+ if (!body)
1260
+ return { exit: 0, reason: "daemon refused" };
1261
+ if (body.wake && body.text)
1262
+ return { exit: 2, text: body.text };
1263
+ if (body.reason && body.reason !== "timeout")
1264
+ return { exit: 0, reason: body.reason };
1265
+ }
1266
+ return { exit: 0, reason: "gave up" };
1267
+ }
1268
+ var init_wait = __esm(() => {
1269
+ init_src();
1270
+ });
1271
+
1272
+ // packages/hook/src/statusline.ts
1273
+ async function fetchStatuslineState(payload, opts = {}) {
1274
+ const timeout = opts.timeoutMs ?? Number(process.env.SWARM_HOOK_TIMEOUT_MS ?? 400);
1275
+ const post = async (base) => {
1276
+ const r = await authedFetch(`${base}/v1/statusline`, {
1277
+ method: "POST",
1278
+ headers: { "content-type": "application/json" },
1279
+ body: JSON.stringify(payload),
1280
+ signal: AbortSignal.timeout(timeout)
1281
+ });
1282
+ return r.ok ? await r.json() : null;
1283
+ };
1284
+ const base = opts.baseUrl ?? resolveBaseUrl();
1285
+ const fallback = `http://127.0.0.1:${DEFAULT_PORT}`;
1286
+ try {
1287
+ const s = await post(base);
1288
+ if (s)
1289
+ return s;
1290
+ } catch {}
1291
+ if (base !== fallback && !opts.baseUrl) {
1292
+ try {
1293
+ return await post(fallback);
1294
+ } catch {}
1295
+ }
1296
+ return null;
1297
+ }
1298
+ async function runStatusline(input, opts = {}) {
1299
+ const payload = parseStatuslinePayload(input) ?? {};
1300
+ const state = await fetchStatuslineState(payload, opts);
1301
+ const color = opts.color ?? !process.env.NO_COLOR;
1302
+ return renderStatusline(payload, state, { color });
1303
+ }
1304
+ var init_statusline = __esm(() => {
1305
+ init_src();
1306
+ init_src2();
1307
+ });
1308
+
379
1309
  // packages/hook/src/bin.ts
1310
+ init_src();
1311
+ init_src2();
1312
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
1313
+ import { dirname, join as join2 } from "path";
380
1314
  var event = process.argv[2] ?? "Unknown";
381
1315
  var input = await Bun.stdin.text();
382
- var timeout = Number(process.env.SWARM_HOOK_TIMEOUT_MS ?? 400);
1316
+ if (event === "wait") {
1317
+ await Promise.resolve().then(() => init_wait());
1318
+ let sid = "";
1319
+ try {
1320
+ sid = String(JSON.parse(input || "{}").session_id ?? "");
1321
+ } catch {}
1322
+ if (!sid)
1323
+ process.exit(0);
1324
+ const out = await waitForWake(sid);
1325
+ if (out.exit === 2)
1326
+ process.stderr.write(`${out.text}
1327
+ `);
1328
+ process.exit(out.exit);
1329
+ }
1330
+ if (event === "statusline") {
1331
+ await Promise.resolve().then(() => init_statusline());
1332
+ process.stdout.write(`${await runStatusline(input)}
1333
+ `);
1334
+ process.exit(0);
1335
+ }
1336
+ var fast = Number(process.env.SWARM_HOOK_TIMEOUT_MS ?? 400);
1337
+ async function repairArmed() {
1338
+ try {
1339
+ const cwd = String(JSON.parse(input || "{}").cwd ?? "");
1340
+ if (!cwd)
1341
+ return false;
1342
+ const r = await authedFetch(`${resolveBaseUrl()}/v1/repair?cwd=${encodeURIComponent(cwd)}`, {
1343
+ signal: AbortSignal.timeout(fast)
1344
+ });
1345
+ return r.ok && (await r.json()).block === true;
1346
+ } catch {
1347
+ return false;
1348
+ }
1349
+ }
1350
+ var timeout = event === "Stop" ? await repairArmed() ? Number(process.env.SWARM_STOP_TIMEOUT_MS ?? 330000) : fast : event === "PermissionRequest" ? Number(process.env.SWARM_PERMISSION_TIMEOUT_MS ?? 130000) : fast;
383
1351
  var post = async (base) => {
384
1352
  const r = await authedFetch(`${base}/v1/hook/${event}`, {
385
1353
  method: "POST",
@@ -400,7 +1368,7 @@ if (out === null && base !== fallback) {
400
1368
  out = await post(fallback);
401
1369
  } catch {}
402
1370
  }
403
- if (out === null && event === "PreToolUse")
1371
+ if (out === null && (event === "PreToolUse" || event === "BeforeTool"))
404
1372
  out = offline();
405
1373
  process.stdout.write(`${out ?? "{}"}
406
1374
  `);
@@ -413,6 +1381,18 @@ function offline() {
413
1381
  if (!cache)
414
1382
  return null;
415
1383
  const raw = input ? JSON.parse(input) : {};
1384
+ const agent = detectAgent(raw);
1385
+ if (agent !== "claude-code") {
1386
+ for (const r of toToolRequests(agent, raw)) {
1387
+ const d = evaluateOffline(cache, { tool_name: r.tool, tool_input: r.input, session_id: r.sessionId, cwd: r.cwd }, gitToplevel);
1388
+ if (d.action !== "allow")
1389
+ return renderAgentDecision(agent, {
1390
+ ...d,
1391
+ reason: `(daemon unreachable, policy-locked rule) ${d.reason}`
1392
+ });
1393
+ }
1394
+ return null;
1395
+ }
416
1396
  const d = evaluateOffline(cache, raw, gitToplevel);
417
1397
  if (d.action === "allow")
418
1398
  return null;