@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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.0 (2026-10-06)
|
|
4
|
+
|
|
5
|
+
Setup for editors and the ACP Registry (design: [docs/lld/setup.md](docs/lld/setup.md)).
|
|
6
|
+
|
|
7
|
+
- Stored provider keys: Garuda reads `~/.garuda/credentials` (JSON, key name → key) when no
|
|
8
|
+
environment variable sets the key. It reads the file only when it is a regular file (not a link),
|
|
9
|
+
owned by you, with mode 0600; else it ignores the file with a warning. The key never enters
|
|
10
|
+
Garuda's environment, and the redactor knows it. `garuda setup` writes this file.
|
|
11
|
+
- Commands in the OS sandbox cannot read `~/.garuda/credentials` (macOS and Linux).
|
|
12
|
+
- `"default"` in `~/.garuda/models.json`: the model when neither `--model` nor `GARUDA_MODEL` names
|
|
13
|
+
one.
|
|
14
|
+
- The redactor removes every provider key that Garuda keeps, also one whose variable name does not
|
|
15
|
+
look secret (a custom provider's `apiKeyEnv`).
|
|
16
|
+
- `garuda setup`: choose a provider and a model, enter the key once (hidden input), check it with
|
|
17
|
+
one free request to the provider, then store the model as the default and the key in
|
|
18
|
+
`~/.garuda/credentials`. `--show` shows where each key comes from (masked); `--forget [NAME]`
|
|
19
|
+
removes stored keys. With no terminal, it stops with a message.
|
|
20
|
+
- The messages for a missing model or key name `garuda setup`.
|
|
21
|
+
- `garuda acp`: Terminal Auth. An editor that can run it (`auth.terminal`) gets a "Set up Garuda"
|
|
22
|
+
method; it runs `garuda acp setup` in a terminal. A session with no model or no key fails with
|
|
23
|
+
ACP's "auth required", so the editor offers the setup. The setup is read again for each new
|
|
24
|
+
session: no restart after `garuda setup`.
|
|
25
|
+
- Native Windows: `garuda acp` sessions and `garuda setup` stop with a message to use WSL.
|
|
26
|
+
- Docs: `garuda setup` in the quick start, the landing page, the README and the Editors page; editor
|
|
27
|
+
entries need no API key in their settings. Tested live in Zed 1.22: with no model and no key, Zed
|
|
28
|
+
offered the setup, ran it in a terminal, and the next thread worked with no restart.
|
|
29
|
+
- `-p --output-format stream-json`: `apiKeySource` in `system/init` is `credentials` for a stored
|
|
30
|
+
Claude key.
|
|
31
|
+
|
|
32
|
+
## 0.15.0 (2026-10-06)
|
|
33
|
+
|
|
34
|
+
Garuda in your editor. Version 0.15.0 is not the old "0.15" branch label of the status table (code
|
|
35
|
+
intelligence), which shipped in 0.14.0.
|
|
36
|
+
|
|
37
|
+
### Editors
|
|
38
|
+
|
|
39
|
+
- `garuda acp`: Garuda as an agent for editors over the Agent Client Protocol (v1): VS Code (with
|
|
40
|
+
the "ACP Client" extension), Zed, JetBrains, Neovim, Emacs. One runtime per editor session, with
|
|
41
|
+
the same permission engine, OS sandbox, team policy, hooks and audit log as the terminal.
|
|
42
|
+
- Approval questions name their tool call (`callId`), and file writes carry the whole change, so an
|
|
43
|
+
editor shows each question next to the right call, with its own diff view.
|
|
44
|
+
- A stop in the editor names what it ended: each stopped tool call shows as failed, with a line
|
|
45
|
+
such as "Garuda: $ sleep 60 cancelled." (or "the turn was cancelled." when no call ran). A call
|
|
46
|
+
that has not reported within 3 s of the stop ends as failed, so none stays "running".
|
|
47
|
+
- Docs: the website's Editors page (VS Code with ACP Client, Zed, other ACP editors). Tested live
|
|
48
|
+
in VS Code and Zed on macOS.
|
|
49
|
+
|
|
3
50
|
## 0.14.1 (2026-10)
|
|
4
51
|
|
|
5
52
|
The first version on npm (`@garuda-agent/garuda`) and Homebrew (`madhu-sv/garuda/garuda`). It fixes
|
|
@@ -10,7 +57,8 @@ of the other areas are closed. Each fix has a test that fails without it.
|
|
|
10
57
|
|
|
11
58
|
- npm: `npm install -g @garuda-agent/garuda`. Published from the release workflow with npm trusted
|
|
12
59
|
publishing, with provenance.
|
|
13
|
-
- Homebrew: `brew
|
|
60
|
+
- Homebrew: `brew tap madhu-sv/garuda`, `brew trust --formula madhu-sv/garuda/garuda`, then
|
|
61
|
+
`brew install garuda`.
|
|
14
62
|
|
|
15
63
|
### Security
|
|
16
64
|
|
package/README.md
CHANGED
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
Website and docs: https://madhu-sv.github.io/garuda/
|
|
6
6
|
|
|
7
|
-
Garuda is a terminal coding agent. This is version 0.
|
|
8
|
-
|
|
7
|
+
Garuda is a terminal coding agent. This is version 0.16.0: `garuda setup` (choose a model and store
|
|
8
|
+
its key once) and the sign-in from editors, on top of 0.15.0 (Garuda in your editor over the Agent
|
|
9
|
+
Client Protocol, `garuda acp`) and 0.14.1 (the 0.14–0.17 branches after the merge gate and Garuda's own
|
|
10
|
+
review; see the notes under the status table). Release notes: [CHANGELOG.md](CHANGELOG.md).
|
|
9
11
|
Design documents: [docs/](docs/README.md) (architecture, high-level design, low-level design per component).
|
|
10
12
|
The requirements doc defines the scope. Code, tests and commits refer to its IDs (F1–F26, N1–N8).
|
|
11
13
|
|
|
@@ -34,6 +36,11 @@ The requirements doc defines the scope. Code, tests and commits refer to its IDs
|
|
|
34
36
|
| 0.15: multi-language AST code intelligence (Python, Java, TS/JS, call graph, blast radius, /callers, /defs, /impact, ast_query) | Done (0.14.0) |
|
|
35
37
|
| 0.16: language plugins (Go and Rust built in, user plugins in ~/.garuda/languages, /languages; project plugins not loaded) | Done (0.14.0) |
|
|
36
38
|
| 0.17: Mixture-of-Experts subagents (opt-in), team policy and audit log (W5) | Done (0.14.0) |
|
|
39
|
+
| Editors over the Agent Client Protocol (`garuda acp`: VS Code with an extension, Zed, JetBrains, Neovim, Emacs) | Done (0.15.0) |
|
|
40
|
+
| `garuda setup` (model and stored key) and the editor sign-in (ACP Terminal Auth) | Done (0.16.0) |
|
|
41
|
+
|
|
42
|
+
The rows labelled 0.14–0.17 are branch labels, not versions: the "0.15" and "0.16" branches (code
|
|
43
|
+
intelligence, language plugins) shipped in 0.14.0. The last two rows are versions 0.15.0 and 0.16.0.
|
|
37
44
|
|
|
38
45
|
The 0.14–0.17 rows were built on stacked branches that were never released. They ship together as
|
|
39
46
|
0.14.0 after the merge gate: the high findings of the branch review are fixed (policy source, audit
|
|
@@ -45,7 +52,7 @@ headings below keep the branch labels (0.14–0.17).
|
|
|
45
52
|
Before 0.14.0, Garuda reviewed its own code with Fable 5.1, one area at a time (knowledge,
|
|
46
53
|
permissions, tools, sandbox, audit, loop, agents), in plan mode. 0.14.1 adds the cli and extensions
|
|
47
54
|
areas and fixes the open findings of all nine areas (see [CHANGELOG.md](CHANGELOG.md)). Known limits
|
|
48
|
-
in 0.
|
|
55
|
+
in 0.16.0:
|
|
49
56
|
|
|
50
57
|
- G09: project language plugins are not loaded (no consent flow for their imports yet).
|
|
51
58
|
- Subagents: each child has its own step and token budget (capped by the team policy); it does not
|
|
@@ -57,14 +64,28 @@ in 0.14.1:
|
|
|
57
64
|
`~/.m2/wrapper`) hold programs that a build runs later outside the sandbox. They stay writable, because
|
|
58
65
|
`./gradlew` needs them in the sandbox; a sandboxed command could change them.
|
|
59
66
|
- The jdtls download is checked against the `.sha256` file of the same server, not a pinned hash.
|
|
67
|
+
- Editors (`garuda acp`): Garuda's session history is not in the editor (no `session/load`), the
|
|
68
|
+
editor's MCP servers are not started, Garuda reads saved files (not unsaved buffers), the terminal's
|
|
69
|
+
own commands (`/undo`, `/diff` …) are not available, and an edit is approved as a whole (no hunks).
|
|
70
|
+
- Stored keys are in a file (`~/.garuda/credentials`, mode 0600, hidden from the sandbox), not in the
|
|
71
|
+
OS keychain yet. Keep the file out of backups and dotfile sync.
|
|
72
|
+
- Native Windows is not supported; use WSL.
|
|
60
73
|
|
|
61
74
|
## Use
|
|
62
75
|
|
|
63
76
|
Install (0.14.1 and later) with Homebrew or npm. You need Node 22 or later; Homebrew installs it.
|
|
64
77
|
|
|
65
78
|
```sh
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
npm install -g @garuda-agent/garuda
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Or with Homebrew (macOS and Linux). Homebrew loads a formula from a third-party tap only after you
|
|
83
|
+
trust it, so trust the Garuda formula first:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
brew tap madhu-sv/garuda
|
|
87
|
+
brew trust --formula madhu-sv/garuda/garuda
|
|
88
|
+
brew install garuda
|
|
68
89
|
```
|
|
69
90
|
|
|
70
91
|
Or build from source. You need Node 22 or later, pnpm 10 and git. Node 25 and later do not include
|
|
@@ -82,8 +103,7 @@ pnpm link --global # optional: puts `garuda` on your PATH (run `pnpm setup` onc
|
|
|
82
103
|
Then:
|
|
83
104
|
|
|
84
105
|
```sh
|
|
85
|
-
|
|
86
|
-
export GARUDA_MODEL=<model-id>
|
|
106
|
+
garuda setup # once: pick a provider and model, enter the key
|
|
87
107
|
garuda # chat in the current folder
|
|
88
108
|
garuda -p "Explain what this repo does" # run one task and exit
|
|
89
109
|
echo "Explain this repo" | garuda # the same, from stdin
|
|
@@ -91,6 +111,13 @@ echo "Explain this repo" | garuda # the same, from stdin
|
|
|
91
111
|
|
|
92
112
|
From a source build without `pnpm link`, use `node dist/cli/index.js` in place of `garuda`.
|
|
93
113
|
|
|
114
|
+
`garuda setup` (0.16) asks for the provider, the model and the key (hidden input), checks the key
|
|
115
|
+
with one free request to the provider, and stores the model as `"default"` in
|
|
116
|
+
`~/.garuda/models.json` and the key in `~/.garuda/credentials` (mode 0600; commands in the sandbox
|
|
117
|
+
cannot read it). `garuda setup --show` shows the model and where each key comes from (masked);
|
|
118
|
+
`garuda setup --forget [NAME]` removes stored keys. Environment variables still work and win:
|
|
119
|
+
`ANTHROPIC_API_KEY`, `GARUDA_MODEL`. Do not sync or back up `~/.garuda/credentials` to other places.
|
|
120
|
+
|
|
94
121
|
Tools: read_file, glob and grep run with no question.
|
|
95
122
|
write_file, edit_file and bash show a diff or the command first. You pick: allow once, allow for this session, or deny.
|
|
96
123
|
Commands run in an OS sandbox when your machine has one (see Sandbox below). Without a sandbox,
|
|
@@ -134,13 +161,15 @@ Set the context window, price and output limit per model, and add providers, in
|
|
|
134
161
|
```json
|
|
135
162
|
{
|
|
136
163
|
"providers": { "lab": { "type": "openai-compatible", "baseUrl": "https://llm.example.com/v1", "apiKeyEnv": "LAB_KEY" } },
|
|
137
|
-
"models": { "ollama/qwen3-coder:30b": { "contextWindow": 65536, "maxTokens": 8192 } }
|
|
164
|
+
"models": { "ollama/qwen3-coder:30b": { "contextWindow": 65536, "maxTokens": 8192 } },
|
|
165
|
+
"default": "ollama/qwen3-coder:30b"
|
|
138
166
|
}
|
|
139
167
|
```
|
|
140
168
|
|
|
141
169
|
- Only this file in your home folder can define providers; a project cannot send your code elsewhere.
|
|
142
170
|
Keys come from environment variables. Plain http works only to this machine (or with
|
|
143
171
|
`"allowInsecureHttp": true`).
|
|
172
|
+
- `"default"` is the model when neither `--model` nor `GARUDA_MODEL` names one.
|
|
144
173
|
- Some small models write a tool call as JSON text. When the whole reply is such a call to a known
|
|
145
174
|
tool, Garuda runs it as a real call, with the usual approvals. For a model that also writes
|
|
146
175
|
sentences around the call (for example `qwen2.5-coder:7b`), set `"textToolCalls": "lines"` for that
|
|
@@ -236,6 +265,47 @@ Set the context window, price and output limit per model, and add providers, in
|
|
|
236
265
|
|
|
237
266
|
Model text goes to stdout; tool activity and notes go to stderr. So `garuda -p "…" > answer.md` keeps only the answer.
|
|
238
267
|
|
|
268
|
+
## Editors (ACP, 0.15; sign-in 0.16)
|
|
269
|
+
|
|
270
|
+
`garuda acp` runs Garuda as an agent for editors that speak the Agent Client Protocol. The editor
|
|
271
|
+
starts it, shows the conversation, the tool calls and the questions, and Garuda does the work with
|
|
272
|
+
the same permission engine, OS sandbox, team policy, hooks and audit log as in the terminal.
|
|
273
|
+
|
|
274
|
+
Run `garuda setup` once in a terminal (see Use): then the editor needs no key in its settings.
|
|
275
|
+
|
|
276
|
+
VS Code, with the extension "ACP Client" (`formulahendry.acp-client`), in the user settings
|
|
277
|
+
(Cmd+Shift+P, **Preferences: Open User Settings (JSON)**). Use the full path from `which garuda`:
|
|
278
|
+
VS Code started from the Dock does not get your shell's `PATH`.
|
|
279
|
+
|
|
280
|
+
```json
|
|
281
|
+
{
|
|
282
|
+
"acp.agents": {
|
|
283
|
+
"Garuda": { "command": "/opt/homebrew/bin/garuda", "args": ["acp"] }
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Zed: the same command and argument under `agent_servers` (with `"type": "custom"`). With no model
|
|
289
|
+
or key, Zed offers **Set up Garuda** (ACP Terminal Auth, 0.16): it runs `garuda acp setup` in a
|
|
290
|
+
terminal, and the next thread works. The website's
|
|
291
|
+
[Editors](https://madhu-sv.github.io/garuda/docs/editors/) page has the steps and what the editor
|
|
292
|
+
shows.
|
|
293
|
+
|
|
294
|
+
- Each edit asks in the editor, with the editor's diff view; "Allow for this session" lasts for this
|
|
295
|
+
session only. Keep an extension's auto-approve off: it answers Garuda's questions for you.
|
|
296
|
+
- The editor's stop button ends the turn: a running command stops, and a waiting question is a deny.
|
|
297
|
+
Each call that the stop ended shows as failed, with a line such as "Garuda: $ sleep 60 cancelled."
|
|
298
|
+
A command that exits with an error shows as done (the model reads the error); a refused call shows
|
|
299
|
+
as failed.
|
|
300
|
+
- Build and plan modes are in the editor's mode menu; custom commands and skills are in its `/` menu.
|
|
301
|
+
Built-in chat commands (`/undo`, `/diff` ...) are only in the terminal in this version.
|
|
302
|
+
- Garuda reads and writes files and runs commands itself; it does not use the editor's file or
|
|
303
|
+
terminal access, so it reads the saved file, not an unsaved buffer. It does not start MCP servers
|
|
304
|
+
that the editor sends (its own `~/.garuda/mcp.json` and the project's work as usual).
|
|
305
|
+
- Project settings that loosen safety apply only after a yes in a terminal (`garuda` in that folder).
|
|
306
|
+
|
|
307
|
+
Design: [docs/lld/acp.md](docs/lld/acp.md).
|
|
308
|
+
|
|
239
309
|
## Init: set up a folder
|
|
240
310
|
|
|
241
311
|
`garuda init` (or `/init` in the chat) sets up the current folder:
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
FileSessionStore,
|
|
4
|
+
Runtime,
|
|
5
|
+
auditDirFor
|
|
6
|
+
} from "./chunk-WIJL72ZE.js";
|
|
7
|
+
import "./chunk-G2AX7MGS.js";
|
|
8
|
+
import "./chunk-A2QAIRQL.js";
|
|
9
|
+
import "./chunk-ULAJPMY7.js";
|
|
10
|
+
import {
|
|
11
|
+
chooseModel,
|
|
12
|
+
keepProviderKeys,
|
|
13
|
+
loadModelsConfig,
|
|
14
|
+
providerKeySource,
|
|
15
|
+
resolveModel
|
|
16
|
+
} from "./chunk-NP6RIC25.js";
|
|
17
|
+
import "./chunk-EIXUSVFX.js";
|
|
18
|
+
import "./chunk-R7I2NBPX.js";
|
|
19
|
+
import {
|
|
20
|
+
loadSearchConfig
|
|
21
|
+
} from "./chunk-B25VL4RU.js";
|
|
22
|
+
import "./chunk-23FXBNBB.js";
|
|
23
|
+
import {
|
|
24
|
+
VERSION
|
|
25
|
+
} from "./chunk-XOK5OZOC.js";
|
|
26
|
+
import "./chunk-5DEWP3YX.js";
|
|
27
|
+
import "./chunk-ZAE25JM2.js";
|
|
28
|
+
import "./chunk-ATW7BNIG.js";
|
|
29
|
+
import "./chunk-PZS3V6P3.js";
|
|
30
|
+
import "./chunk-OUCV3FCT.js";
|
|
31
|
+
import "./chunk-GXZACJUI.js";
|
|
32
|
+
import "./chunk-WMOLXTBU.js";
|
|
33
|
+
import "./chunk-3EQU2GUX.js";
|
|
34
|
+
import "./chunk-HUIK3WXO.js";
|
|
35
|
+
import "./chunk-3NJNVZTK.js";
|
|
36
|
+
|
|
37
|
+
// src/cli/acpCommand.ts
|
|
38
|
+
import { homedir } from "os";
|
|
39
|
+
import { Readable } from "stream";
|
|
40
|
+
var WINDOWS_MESSAGE = "Garuda supports macOS and Linux. On Windows, run Garuda in WSL (Windows Subsystem for Linux).";
|
|
41
|
+
async function acpCommand(options) {
|
|
42
|
+
if (options.action !== void 0) {
|
|
43
|
+
if (options.action !== "setup") {
|
|
44
|
+
process.stderr.write(`Unknown argument "${options.action}". Use: garuda acp [setup]
|
|
45
|
+
`);
|
|
46
|
+
return 1;
|
|
47
|
+
}
|
|
48
|
+
const { setupCommand, terminalIO } = await import("./setupCommand-VA7D5MZK.js");
|
|
49
|
+
return setupCommand({}, { io: await terminalIO() });
|
|
50
|
+
}
|
|
51
|
+
const output = protocolOutput(process.stdout, process.stderr);
|
|
52
|
+
const input = Readable.toWeb(process.stdin);
|
|
53
|
+
const { acpServer, setupNeeded } = await import("./server-KHHNHUYV.js");
|
|
54
|
+
const server = acpServer({
|
|
55
|
+
version: VERSION,
|
|
56
|
+
// Not on native Windows: the setup there could not keep a key private (no 0600).
|
|
57
|
+
...process.platform === "win32" ? {} : { terminalAuth: { args: ["setup"] } },
|
|
58
|
+
createRuntime: async ({ root, approver, onEvent, onNotice }) => {
|
|
59
|
+
const setup = await prepare(options);
|
|
60
|
+
if ("problem" in setup) {
|
|
61
|
+
throw setup.setupNeeded ? setupNeeded(setup.problem) : new Error(setup.problem);
|
|
62
|
+
}
|
|
63
|
+
return createRuntime(setup, { root, approver, onEvent, onNotice });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
process.on("exit", () => server.shutdown());
|
|
67
|
+
const stop = () => {
|
|
68
|
+
server.shutdown();
|
|
69
|
+
process.exit(143);
|
|
70
|
+
};
|
|
71
|
+
process.once("SIGTERM", stop);
|
|
72
|
+
await server.serve(output, input);
|
|
73
|
+
await server.close();
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
function protocolOutput(stdout, stderr) {
|
|
77
|
+
const write = stdout.write.bind(stdout);
|
|
78
|
+
const toStderr = stderr.write.bind(stderr);
|
|
79
|
+
stdout.write = ((...args) => toStderr(...args));
|
|
80
|
+
return new WritableStream({
|
|
81
|
+
write: (chunk) => new Promise((resolve, reject) => {
|
|
82
|
+
write(chunk, (error) => error ? reject(error) : resolve());
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
var SETUP_HINT = "Run `garuda setup` in a terminal, or use the editor's sign-in for Garuda.";
|
|
87
|
+
async function prepare(options, from = {}) {
|
|
88
|
+
const fail = (problem, setupNeeded = false) => ({ problem, setupNeeded });
|
|
89
|
+
if ((from.platform ?? process.platform) === "win32") return fail(WINDOWS_MESSAGE);
|
|
90
|
+
const home = from.home ?? homedir();
|
|
91
|
+
const models = await loadModelsConfig(home);
|
|
92
|
+
if (models.problem !== void 0) return fail(models.problem);
|
|
93
|
+
const spec = chooseModel(models.config, options.model, process.env.GARUDA_MODEL);
|
|
94
|
+
if (!spec) return fail(`Garuda has no model. ${SETUP_HINT}`, true);
|
|
95
|
+
let resolved;
|
|
96
|
+
let sub;
|
|
97
|
+
try {
|
|
98
|
+
resolved = resolveModel(spec, models.config);
|
|
99
|
+
const subSpec = process.env.GARUDA_SUBAGENT_MODEL;
|
|
100
|
+
if (subSpec) sub = resolveModel(subSpec, models.config);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return fail(error.message);
|
|
103
|
+
}
|
|
104
|
+
const search = await loadSearchConfig(home);
|
|
105
|
+
const credentialsWarning = await keepProviderKeys(
|
|
106
|
+
[resolved.def.apiKeyEnv, sub?.def.apiKeyEnv, "ANTHROPIC_API_KEY"],
|
|
107
|
+
home
|
|
108
|
+
);
|
|
109
|
+
if (credentialsWarning !== void 0) process.stderr.write(`garuda acp: ${credentialsWarning}
|
|
110
|
+
`);
|
|
111
|
+
const keyName = resolved.def.type === "anthropic" ? resolved.def.apiKeyEnv ?? "ANTHROPIC_API_KEY" : resolved.def.apiKeyEnv;
|
|
112
|
+
const sdkToken = resolved.def.type === "anthropic" && (process.env.ANTHROPIC_AUTH_TOKEN ?? "") !== "";
|
|
113
|
+
if (keyName !== void 0 && providerKeySource(keyName) === void 0 && !sdkToken) {
|
|
114
|
+
const why = credentialsWarning === void 0 ? "" : ` ${credentialsWarning}`;
|
|
115
|
+
return fail(`Garuda has no key for ${resolved.spec} (${keyName}).${why} ${SETUP_HINT}`, true);
|
|
116
|
+
}
|
|
117
|
+
const { loadTeamPolicy } = await import("./policy-EJPLEEXP.js");
|
|
118
|
+
let team;
|
|
119
|
+
try {
|
|
120
|
+
team = await loadTeamPolicy("managed" in from ? { home, managed: from.managed } : { home });
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return fail(`Team policy: ${error.message}`);
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
resolved,
|
|
126
|
+
...sub === void 0 ? {} : { sub },
|
|
127
|
+
models: models.config,
|
|
128
|
+
...team === void 0 ? {} : { team: { policy: team.policy, sources: team.sources } },
|
|
129
|
+
search,
|
|
130
|
+
...credentialsWarning === void 0 ? {} : { credentialsWarning }
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function createRuntime(setup, input) {
|
|
134
|
+
const { resolved, sub, models, team, search } = setup;
|
|
135
|
+
return Runtime.create({
|
|
136
|
+
root: input.root,
|
|
137
|
+
modelId: resolved.spec,
|
|
138
|
+
...team === void 0 ? {} : { policy: team.policy, policySources: team.sources },
|
|
139
|
+
audit: { dir: auditDirFor(input.root) },
|
|
140
|
+
model: () => resolved.create(),
|
|
141
|
+
modelInfo: resolved.info,
|
|
142
|
+
...resolved.maxTokens === void 0 ? {} : { maxTokens: resolved.maxTokens },
|
|
143
|
+
...sub === void 0 ? {} : { subagentModel: { spec: sub.spec, model: () => sub.create(), info: sub.info } },
|
|
144
|
+
approver: input.approver,
|
|
145
|
+
store: new FileSessionStore(input.root),
|
|
146
|
+
undo: {},
|
|
147
|
+
skills: {},
|
|
148
|
+
languages: {},
|
|
149
|
+
// The editor cannot answer a question before session/new returns: only project settings that
|
|
150
|
+
// the user approved in a terminal apply; the rest is left out with a notice (docs/lld/acp.md).
|
|
151
|
+
projectSettings: { home: homedir(), ask: false },
|
|
152
|
+
...search.config === void 0 && search.claude === void 0 ? {} : {
|
|
153
|
+
search: {
|
|
154
|
+
...search.config === void 0 ? {} : { config: search.config },
|
|
155
|
+
...search.claude === void 0 ? {} : { claude: search.claude },
|
|
156
|
+
...search.use === void 0 ? {} : { use: search.use },
|
|
157
|
+
home: homedir()
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
agents: {
|
|
161
|
+
resolveModel: (spec) => {
|
|
162
|
+
const r = resolveModel(spec, models);
|
|
163
|
+
return { spec: r.spec, model: () => r.create(), info: r.info };
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
models: {
|
|
167
|
+
resolve: (spec) => {
|
|
168
|
+
const r = resolveModel(spec, models);
|
|
169
|
+
return {
|
|
170
|
+
spec: r.spec,
|
|
171
|
+
model: () => r.create(),
|
|
172
|
+
info: r.info,
|
|
173
|
+
...r.maxTokens === void 0 ? {} : { maxTokens: r.maxTokens }
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
configured: Object.keys(models.models)
|
|
177
|
+
},
|
|
178
|
+
...input.onEvent === void 0 ? {} : { onEvent: input.onEvent },
|
|
179
|
+
onNotice: input.onNotice
|
|
180
|
+
}).then((runtime) => {
|
|
181
|
+
if (setup.credentialsWarning !== void 0) input.onNotice(setup.credentialsWarning);
|
|
182
|
+
return runtime;
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
export {
|
|
186
|
+
WINDOWS_MESSAGE,
|
|
187
|
+
acpCommand,
|
|
188
|
+
prepare,
|
|
189
|
+
protocolOutput
|
|
190
|
+
};
|
|
191
|
+
//# sourceMappingURL=acpCommand-AJJBKAE5.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/commands/builtins.ts
|
|
4
|
+
var BUILTIN_COMMANDS = [
|
|
5
|
+
"help",
|
|
6
|
+
"usage",
|
|
7
|
+
"session",
|
|
8
|
+
"sessions",
|
|
9
|
+
"models",
|
|
10
|
+
"export",
|
|
11
|
+
"compact",
|
|
12
|
+
"diff",
|
|
13
|
+
"schedule",
|
|
14
|
+
"jobs",
|
|
15
|
+
"where",
|
|
16
|
+
"defs",
|
|
17
|
+
"refs",
|
|
18
|
+
"callers",
|
|
19
|
+
"impact",
|
|
20
|
+
"map",
|
|
21
|
+
"mcp",
|
|
22
|
+
"hooks",
|
|
23
|
+
"lsp",
|
|
24
|
+
"languages",
|
|
25
|
+
"experts",
|
|
26
|
+
"audit",
|
|
27
|
+
"commands",
|
|
28
|
+
"agents",
|
|
29
|
+
"plan",
|
|
30
|
+
"build",
|
|
31
|
+
"new",
|
|
32
|
+
"init",
|
|
33
|
+
"editor",
|
|
34
|
+
"details",
|
|
35
|
+
"thinking",
|
|
36
|
+
"search",
|
|
37
|
+
"undo",
|
|
38
|
+
"redo",
|
|
39
|
+
"exit",
|
|
40
|
+
"quit"
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
BUILTIN_COMMANDS
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=chunk-23FXBNBB.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/model/types.ts
|
|
4
|
+
var EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
5
|
+
var ZERO_USAGE = {
|
|
6
|
+
inputTokens: 0,
|
|
7
|
+
outputTokens: 0,
|
|
8
|
+
cacheReadTokens: 0,
|
|
9
|
+
cacheWriteTokens: 0
|
|
10
|
+
};
|
|
11
|
+
function addUsage(a, b) {
|
|
12
|
+
const searches = (a.webSearches ?? 0) + (b.webSearches ?? 0);
|
|
13
|
+
return {
|
|
14
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
15
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
16
|
+
cacheReadTokens: a.cacheReadTokens + b.cacheReadTokens,
|
|
17
|
+
cacheWriteTokens: a.cacheWriteTokens + b.cacheWriteTokens,
|
|
18
|
+
...searches === 0 ? {} : { webSearches: searches }
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
EFFORTS,
|
|
24
|
+
ZERO_USAGE,
|
|
25
|
+
addUsage
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=chunk-3EQU2GUX.js.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
JobGitError,
|
|
4
4
|
git
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-TUUEBHG2.js";
|
|
6
6
|
|
|
7
7
|
// src/jobs/worktree.ts
|
|
8
8
|
import { createHash } from "crypto";
|
|
@@ -135,4 +135,4 @@ export {
|
|
|
135
135
|
commitJob,
|
|
136
136
|
jobChanges
|
|
137
137
|
};
|
|
138
|
-
//# sourceMappingURL=chunk-
|
|
138
|
+
//# sourceMappingURL=chunk-44QN5SWZ.js.map
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/approver.ts
|
|
4
|
+
import { styleText } from "util";
|
|
5
|
+
var TerminalApprover = class {
|
|
6
|
+
/** Set by the CLI: it aborts the current turn. */
|
|
7
|
+
onInterrupt = () => {
|
|
8
|
+
};
|
|
9
|
+
/** One question at a time (0.14.1, review): two prompts at once mixed their keys. */
|
|
10
|
+
ask(request, signal) {
|
|
11
|
+
const next = this.asking === 0 ? this.askNow(request, signal) : this.queue.then(() => this.askNow(request, signal));
|
|
12
|
+
this.asking++;
|
|
13
|
+
this.queue = next.catch(() => {
|
|
14
|
+
}).finally(() => {
|
|
15
|
+
this.asking--;
|
|
16
|
+
});
|
|
17
|
+
return next;
|
|
18
|
+
}
|
|
19
|
+
asking = 0;
|
|
20
|
+
queue = Promise.resolve();
|
|
21
|
+
async askNow(request, signal) {
|
|
22
|
+
signal.throwIfAborted();
|
|
23
|
+
const out = process.stderr;
|
|
24
|
+
out.write(`
|
|
25
|
+
${header(request)}
|
|
26
|
+
${colorPreview(request)}
|
|
27
|
+
`);
|
|
28
|
+
if (!process.stdin.isTTY) {
|
|
29
|
+
out.write("No terminal to ask for approval, so Garuda denies this call.\n");
|
|
30
|
+
return "deny";
|
|
31
|
+
}
|
|
32
|
+
const sessionLabel = request.labels?.session ?? (request.target.kind === "command" ? "Yes, and allow this exact command for this session" : request.target.kind === "url" ? `Yes, and allow ${request.target.host} for this session` : `Yes, and allow all ${request.tool} calls for this session`);
|
|
33
|
+
const { select } = await import("@inquirer/prompts");
|
|
34
|
+
try {
|
|
35
|
+
return await select(
|
|
36
|
+
{
|
|
37
|
+
message: request.question ?? "Allow?",
|
|
38
|
+
choices: [
|
|
39
|
+
{ name: request.labels?.once ?? "Yes, once", value: "once" },
|
|
40
|
+
{ name: sessionLabel, value: "session" },
|
|
41
|
+
{ name: request.labels?.deny ?? "No, deny", value: "deny" }
|
|
42
|
+
].filter((c) => request.choices?.includes(c.value) ?? true)
|
|
43
|
+
},
|
|
44
|
+
{ signal, output: out }
|
|
45
|
+
);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error.name === "ExitPromptError") this.onInterrupt();
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var SwitchApprover = class {
|
|
53
|
+
constructor(current) {
|
|
54
|
+
this.current = current;
|
|
55
|
+
}
|
|
56
|
+
current;
|
|
57
|
+
/** Called before each question: the chat's notifier (0.6). */
|
|
58
|
+
onAsk = () => {
|
|
59
|
+
};
|
|
60
|
+
ask(request, signal) {
|
|
61
|
+
this.onAsk(request);
|
|
62
|
+
return this.current.ask(request, signal);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
function visible(text) {
|
|
66
|
+
return text.replace(/[\u0000-\u0008\u000b-\u001f]/g, (c) => String.fromCodePoint(9216 + c.charCodeAt(0))).replace(/\u007f/g, "\u2421").replace(
|
|
67
|
+
/[\u0080-\u009f\u200b-\u200f\u202a-\u202e\u2060-\u2064\u2066-\u2069\ufeff\u{e0000}-\u{e007f}]/gu,
|
|
68
|
+
(c) => `[U+${(c.codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, "0")}]`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
function hasHidden(text) {
|
|
72
|
+
return visible(text) !== text;
|
|
73
|
+
}
|
|
74
|
+
var HIDDEN_WARNING = "! This holds hidden or control characters, shown as \u240D, \u241B or [U+\u2026]. Read it with care.";
|
|
75
|
+
function headerText(request) {
|
|
76
|
+
const { tool, target, isolation } = request;
|
|
77
|
+
if (request.title !== void 0) return visible(request.title);
|
|
78
|
+
if (target.kind === "command") {
|
|
79
|
+
const where = isolation === "none" ? "on your machine, no sandbox" : target.outsideSandbox ? "OUTSIDE the sandbox: network on, writes anywhere" : `sandbox: ${isolation}`;
|
|
80
|
+
return `${tool} wants to run a command (${where}):`;
|
|
81
|
+
}
|
|
82
|
+
if (target.kind === "input") return `${tool} wants to run with this input:`;
|
|
83
|
+
if (target.kind === "url") return `${tool} wants to fetch from ${visible(target.host)}:`;
|
|
84
|
+
return `${tool} wants to change ${visible(target.path)}:`;
|
|
85
|
+
}
|
|
86
|
+
function header(request) {
|
|
87
|
+
return styleText("bold", headerText(request));
|
|
88
|
+
}
|
|
89
|
+
function colorPreview({ target, preview }) {
|
|
90
|
+
const warning = hasHidden(preview) ? `
|
|
91
|
+
${styleText("yellow", ` ${HIDDEN_WARNING}`)}` : "";
|
|
92
|
+
if (target.kind === "command") return `${styleText("cyan", ` $ ${visible(preview)}`)}${warning}`;
|
|
93
|
+
if (target.kind === "url") return `${visible(preview)}${warning}`;
|
|
94
|
+
return `${colorDiff(visible(preview))}${warning}`;
|
|
95
|
+
}
|
|
96
|
+
function colorDiff(text) {
|
|
97
|
+
return text.split("\n").map((line) => {
|
|
98
|
+
if (line.startsWith("+++") || line.startsWith("---") || line.startsWith("diff --git"))
|
|
99
|
+
return styleText("bold", line);
|
|
100
|
+
if (line.startsWith("+")) return styleText("green", line);
|
|
101
|
+
if (line.startsWith("-")) return styleText("red", line);
|
|
102
|
+
if (line.startsWith("@@")) return styleText("cyan", line);
|
|
103
|
+
return line;
|
|
104
|
+
}).join("\n");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
TerminalApprover,
|
|
109
|
+
SwitchApprover,
|
|
110
|
+
visible,
|
|
111
|
+
hasHidden,
|
|
112
|
+
HIDDEN_WARNING,
|
|
113
|
+
headerText,
|
|
114
|
+
header,
|
|
115
|
+
colorPreview,
|
|
116
|
+
colorDiff
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=chunk-4JSUUDFQ.js.map
|
|
@@ -3,46 +3,6 @@ import {
|
|
|
3
3
|
isLoopbackHost
|
|
4
4
|
} from "./chunk-ATW7BNIG.js";
|
|
5
5
|
|
|
6
|
-
// src/commands/builtins.ts
|
|
7
|
-
var BUILTIN_COMMANDS = [
|
|
8
|
-
"help",
|
|
9
|
-
"usage",
|
|
10
|
-
"session",
|
|
11
|
-
"sessions",
|
|
12
|
-
"models",
|
|
13
|
-
"export",
|
|
14
|
-
"compact",
|
|
15
|
-
"diff",
|
|
16
|
-
"schedule",
|
|
17
|
-
"jobs",
|
|
18
|
-
"where",
|
|
19
|
-
"defs",
|
|
20
|
-
"refs",
|
|
21
|
-
"callers",
|
|
22
|
-
"impact",
|
|
23
|
-
"map",
|
|
24
|
-
"mcp",
|
|
25
|
-
"hooks",
|
|
26
|
-
"lsp",
|
|
27
|
-
"languages",
|
|
28
|
-
"experts",
|
|
29
|
-
"audit",
|
|
30
|
-
"commands",
|
|
31
|
-
"agents",
|
|
32
|
-
"plan",
|
|
33
|
-
"build",
|
|
34
|
-
"new",
|
|
35
|
-
"init",
|
|
36
|
-
"editor",
|
|
37
|
-
"details",
|
|
38
|
-
"thinking",
|
|
39
|
-
"search",
|
|
40
|
-
"undo",
|
|
41
|
-
"redo",
|
|
42
|
-
"exit",
|
|
43
|
-
"quit"
|
|
44
|
-
];
|
|
45
|
-
|
|
46
6
|
// src/web/search.ts
|
|
47
7
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
48
8
|
import { homedir } from "os";
|
|
@@ -273,11 +233,10 @@ async function readCapped(response, max) {
|
|
|
273
233
|
}
|
|
274
234
|
|
|
275
235
|
export {
|
|
276
|
-
BUILTIN_COMMANDS,
|
|
277
236
|
MAX_RESULTS,
|
|
278
237
|
providerLabel,
|
|
279
238
|
loadSearchConfig,
|
|
280
239
|
saveSearchUse,
|
|
281
240
|
search
|
|
282
241
|
};
|
|
283
|
-
//# sourceMappingURL=chunk-
|
|
242
|
+
//# sourceMappingURL=chunk-B25VL4RU.js.map
|