brass-runtime 1.12.0 β 1.13.2
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/README.md +58 -188
- package/dist/agent/cli/main.cjs +16 -0
- package/dist/agent/cli/main.d.mts +1 -0
- package/dist/agent/cli/main.d.ts +1 -0
- package/dist/agent/cli/main.js +16 -0
- package/dist/agent/index.cjs +1 -0
- package/dist/agent/index.d.mts +688 -0
- package/dist/agent/index.d.ts +688 -0
- package/dist/agent/index.js +1 -0
- package/dist/chunk-6OUI6UGZ.cjs +25 -0
- package/dist/chunk-JAQUSSUV.js +1 -0
- package/dist/chunk-K47BP5A2.cjs +2 -0
- package/dist/chunk-LTHJNW5A.js +2 -0
- package/dist/chunk-W6TVAMYC.js +25 -0
- package/dist/chunk-XPOARCND.cjs +1 -0
- package/dist/effect-ISvXPLgc.d.mts +797 -0
- package/dist/effect-ISvXPLgc.d.ts +797 -0
- package/dist/http/index.cjs +1 -0
- package/dist/http/index.d.mts +2 -1
- package/dist/http/index.d.ts +2 -1
- package/dist/http/index.js +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.d.mts +159 -39
- package/dist/index.d.ts +159 -39
- package/dist/index.js +1 -1
- package/dist/stream-BvukHxCv.d.ts +66 -0
- package/dist/stream-C0-LWnUP.d.mts +66 -0
- package/package.json +52 -14
- package/dist/chunk-5PIZGM6Q.js +0 -1
- package/dist/chunk-CNA5AQHG.mjs +0 -1
- package/dist/http/index.mjs +0 -1
- package/dist/index.mjs +0 -1
- package/dist/stream-DVfZWtYZ.d.mts +0 -430
- package/dist/stream-DVfZWtYZ.d.ts +0 -430
package/README.md
CHANGED
|
@@ -1,188 +1,58 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
```ts
|
|
60
|
-
import { withScope } from "brass-runtime/scope";
|
|
61
|
-
import { Runtime } from "brass-runtime/runtime";
|
|
62
|
-
|
|
63
|
-
const runtime = new Runtime({ env: {} });
|
|
64
|
-
|
|
65
|
-
withScope(runtime, (scope) => {
|
|
66
|
-
const f = scope.fork(/* Async effect */);
|
|
67
|
-
// later...
|
|
68
|
-
scope.close(); // interrupts child fibers + runs finalizers
|
|
69
|
-
});
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
> `toPromise` is just a convenience bridge for examples/DX. The runtime semantics remain explicit.
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
## Modules built on top of brass-runtime
|
|
77
|
-
|
|
78
|
-
These are optional layers, implemented using the runtime primitives.
|
|
79
|
-
|
|
80
|
-
### π HTTP client (brass-http layer)
|
|
81
|
-
|
|
82
|
-
A ZIO-style HTTP client built on top of fibers and `Async`.
|
|
83
|
-
|
|
84
|
-
- Lazy & cancelable HTTP requests
|
|
85
|
-
- Explicit wire/content separation
|
|
86
|
-
- Middleware-friendly (logging, retry, timeout, etc.)
|
|
87
|
-
- Integrated with fiber interruption via `AbortController`
|
|
88
|
-
|
|
89
|
-
π **Docs:** [HTTP module](./docs/http.md)
|
|
90
|
-
|
|
91
|
-
Example:
|
|
92
|
-
|
|
93
|
-
```ts
|
|
94
|
-
import { httpClientStream } from "brass-runtime/http";
|
|
95
|
-
import { toPromise, Runtime } from "brass-runtime/runtime";
|
|
96
|
-
|
|
97
|
-
type Post = { id: number; title: string; body: string };
|
|
98
|
-
|
|
99
|
-
const runtime = new Runtime({ env: {} });
|
|
100
|
-
|
|
101
|
-
const client = httpClientStream({ baseUrl: "https://jsonplaceholder.typicode.com" });
|
|
102
|
-
|
|
103
|
-
const res = await toPromise(client.getJson<Post>("/posts/1"), runtime.env);
|
|
104
|
-
console.log(res.status, res.value.title);
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
### π Streams (ZStream-like)
|
|
110
|
-
|
|
111
|
-
Pull-based, resource-aware streams with backpressure.
|
|
112
|
-
|
|
113
|
-
- `ZStream<R, E, A>`
|
|
114
|
-
- `Pull` semantics
|
|
115
|
-
- Bounded buffers
|
|
116
|
-
- Deterministic resource cleanup
|
|
117
|
-
|
|
118
|
-
Examples:
|
|
119
|
-
- `src/examples/fromPromise.ts`
|
|
120
|
-
- `src/examples/mergeStreamSync.ts`
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
## Docs
|
|
125
|
-
|
|
126
|
-
- [Getting Started](./docs/getting-started.md)
|
|
127
|
-
- [Architecture](./docs/ARCHITECTURE.md)
|
|
128
|
-
- [Cancellation & Interruption](./docs/cancellation.md)
|
|
129
|
-
- [Observability: Hooks & Tracing](./docs/observability.md)
|
|
130
|
-
- [HTTP module](./docs/http.md)
|
|
131
|
-
- [Modules overview](./docs/modules.md)
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
## Whatβs new (recent changes)
|
|
136
|
-
|
|
137
|
-
- Stream buffering with backpressure (`buffer`)
|
|
138
|
-
- Abortable async integration (`fromPromiseAbortable`)
|
|
139
|
-
- Fiber-safe `toPromise` for examples & DX
|
|
140
|
-
- HTTP client module built on top of the runtime
|
|
141
|
-
|
|
142
|
-
---
|
|
143
|
-
|
|
144
|
-
## Features (status)
|
|
145
|
-
|
|
146
|
-
### Runtime (core)
|
|
147
|
-
- [x] Sync core: `Effect`
|
|
148
|
-
- [x] Async algebra: `Async`
|
|
149
|
-
- [x] Cooperative `Scheduler`
|
|
150
|
-
- [x] Fibers with interruption & finalizers
|
|
151
|
-
- [x] Structured `Scope`
|
|
152
|
-
- [x] Resource safety (`acquireRelease`)
|
|
153
|
-
|
|
154
|
-
### Concurrency & Streams
|
|
155
|
-
- [x] `race`, `zipPar`, `collectAllPar`
|
|
156
|
-
- [x] ZStream-like core
|
|
157
|
-
- [x] Bounded buffers & backpressure
|
|
158
|
-
- [x] Stream merge / zip
|
|
159
|
-
- [x] Hubs / Broadcast
|
|
160
|
-
- [x] Pipelines (`ZPipeline`-style)
|
|
161
|
-
|
|
162
|
-
### Libraries
|
|
163
|
-
- [x] HTTP client
|
|
164
|
-
- [ ] Retry / timeout middleware
|
|
165
|
-
- [ ] Logging / metrics layers
|
|
166
|
-
|
|
167
|
-
---
|
|
168
|
-
|
|
169
|
-
## Design notes
|
|
170
|
-
|
|
171
|
-
- **No hidden Promises**: async is always modeled explicitly
|
|
172
|
-
- **Deterministic execution**: scheduler is observable & testable
|
|
173
|
-
- **Resource safety is structural**: scopes guarantee cleanup
|
|
174
|
-
- **Libraries compose via functions**: middleware, not inheritance
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
## Contributing
|
|
179
|
-
|
|
180
|
-
- Runtime invariants matter β avoid sneaking Promises into semantics
|
|
181
|
-
- Prefer libraries on top of the runtime over changes in the core
|
|
182
|
-
- Small, focused PRs are welcome (your repo may enforce PR-only changes)
|
|
183
|
-
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
## License
|
|
187
|
-
|
|
188
|
-
MIT License Β© 2025
|
|
1
|
+
# Documentation
|
|
2
|
+
|
|
3
|
+
Start here:
|
|
4
|
+
|
|
5
|
+
- **[Getting started](./getting-started.md)** β installation, first effects, and running examples
|
|
6
|
+
- **[Architecture](./ARCHITECTURE.md)** β how the runtime is structured
|
|
7
|
+
- **[Cancellation & interruption](./cancellation.md)** β interruption semantics, scopes, and cancellable `Async`
|
|
8
|
+
- **[Observability](./observability.md)** β hooks, events, sinks, and tracing
|
|
9
|
+
- **[HTTP client](./http.md)** β ZIO-style HTTP built on brass-runtime
|
|
10
|
+
- **[Modules overview](./modules.md)** β map of core modules and where things live
|
|
11
|
+
- **[Agent module boundaries](./agent-boundaries.md)** β rules for keeping `brass-agent` isolated from the core runtime
|
|
12
|
+
- **[Brass Agent install and configure](./agent-install-and-configure.md)** β end-to-end local setup for CLI, config, providers, and VS Code
|
|
13
|
+
|
|
14
|
+
- [Agent LLM adapters](./agent-llm-adapters.md)
|
|
15
|
+
- [Agent env files](./agent-env-files.md)
|
|
16
|
+
- [Agent global usage and workspace discovery](./agent-global-usage.md)
|
|
17
|
+
- [Agent apply mode](./agent-apply-mode.md)
|
|
18
|
+
- [Brass Agent CLI](./agent-cli.md)
|
|
19
|
+
- [Agent init](./agent-init.md)
|
|
20
|
+
- [Agent observability](./agent-observability.md)
|
|
21
|
+
- [Agent approvals](./agent-approvals.md)
|
|
22
|
+
- [Agent config and policy files](./agent-config.md)
|
|
23
|
+
- [Agent project command discovery](./agent-project-commands.md)
|
|
24
|
+
- [Agent project intelligence](./agent-project-intelligence.md)
|
|
25
|
+
- [Declarative optimized planning roadmap](./agent-declarative-optimized-planning.md)
|
|
26
|
+
- [Agent context discovery](./agent-context-discovery.md)
|
|
27
|
+
- [Agent patch quality loop](./agent-patch-quality-loop.md)
|
|
28
|
+
- [Agent automatic rollback safety](./agent-rollback-safety.md)
|
|
29
|
+
- [Agent DX surfaces](./agent-dx.md)
|
|
30
|
+
|
|
31
|
+
- [VS Code patch preview](./agent-vscode-patch-preview.md)
|
|
32
|
+
- [VS Code enhanced diff preview](./agent-vscode-diff-preview.md)
|
|
33
|
+
- [VS Code run history](./agent-vscode-run-history.md)
|
|
34
|
+
- [VS Code batch runner](./agent-vscode-batch-runner.md)
|
|
35
|
+
- [VS Code UX](./agent-vscode-ux.md)
|
|
36
|
+
- [VS Code Project dashboard](./agent-vscode-project-dashboard.md)
|
|
37
|
+
- [VS Code Chat layout / focus mode](./agent-vscode-chat-layout.md)
|
|
38
|
+
- [Copilot-like VS Code DX](./agent-copilot-like-dx.md)
|
|
39
|
+
- [Chat sessions and slash commands](./agent-chat-sessions.md)
|
|
40
|
+
- [Agent follow-up context](./agent-follow-up-context.md)
|
|
41
|
+
- [VS Code code actions](./agent-vscode-code-actions.md)
|
|
42
|
+
- [VS Code problems-aware chat](./agent-vscode-problems.md)
|
|
43
|
+
- [VS Code inline assist](./agent-vscode-inline-assist.md)
|
|
44
|
+
- [Agent release readiness](./agent-release-readiness.md)
|
|
45
|
+
- [VS Code local install](./agent-vscode-install.md)
|
|
46
|
+
- [VS Code auto-discovery](./agent-vscode-auto-discovery.md)
|
|
47
|
+
- [VS Code model setup](./agent-vscode-model-setup.md)
|
|
48
|
+
- [Agent local install and doctor](./agent-local-install.md)
|
|
49
|
+
- [Agent local tests](./agent-local-tests.md)
|
|
50
|
+
|
|
51
|
+
- [Agent rollback patches](./agent-rollback.md)
|
|
52
|
+
- [Agent redaction](./agent-redaction.md)
|
|
53
|
+
- [Agent run artifacts](./agent-run-artifacts.md)
|
|
54
|
+
- [Agent CI mode](./agent-ci.md)
|
|
55
|
+
- [Agent presets](./agent-presets.md)
|
|
56
|
+
- [Agent batch runs](./agent-batch.md)
|
|
57
|
+
- [VS Code full clean and reinstall](./agent-vscode-clean-install.md)
|
|
58
|
+
- [Agent language and workspace setup UX](agent-language-workspace-ux.md)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';var chunk6OUI6UGZ_cjs=require('../../chunk-6OUI6UGZ.cjs'),chunkK47BP5A2_cjs=require('../../chunk-K47BP5A2.cjs'),fs=require('fs'),promises=require('fs/promises'),path=require('path'),child_process=require('child_process');var Xe=new Function("specifier","return import(specifier)"),Ze=e=>["y","yes","approve","approved","si","s\xED","s"].includes(e),en=e=>["n","no","reject","rejected","deny","denied"].includes(e),nn=(e,n)=>{let t=e.trim().toLowerCase();return t?Ze(t)?{type:"approved"}:en(t)?{type:"rejected",reason:"Rejected by user."}:{type:"rejected",reason:`Unrecognized approval answer: ${e}`}:n.defaultAnswer==="approve"?{type:"approved"}:{type:"rejected",reason:"Rejected by default answer."}},Pe=(e={})=>({request:n=>chunkK47BP5A2_cjs.j((t,o)=>{let r=false,a;return Xe("node:readline/promises").then(({createInterface:l})=>{if(r)return;let s=e.input??process.stdin,c=e.output??process.stderr??process.stdout,h=n.defaultAnswer==="approve"?"Y/n":"y/N";return a=l({input:s,output:c}),c?.write?.(`
|
|
3
|
+
Approval required (${n.risk})
|
|
4
|
+
`),c?.write?.(`Action: ${chunk6OUI6UGZ_cjs.T(n.action)}
|
|
5
|
+
`),c?.write?.(`Reason: ${n.reason}
|
|
6
|
+
`),a.question(`Approve? [${h}] `)}).then(l=>{l===void 0||r||(r=true,a?.close?.(),o(chunkK47BP5A2_cjs.u.succeed(nn(l,n))));}).catch(l=>{r||(r=true,a?.close?.(),o(chunkK47BP5A2_cjs.u.failCause(chunkK47BP5A2_cjs.t.fail({_tag:"AgentLoopError",message:`Approval prompt failed: ${String(l)}`}))));}),()=>{r||(r=true,a?.close?.());}})});var _=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),N=e=>{try{return JSON.parse(fs.readFileSync(e,"utf8"))}catch{return}},Le=(e,n)=>{let t=path.resolve(e);for(;;){let o=path.join(t,"package.json"),r=N(o);if(_(r)&&r.name===n)return t;let a=path.resolve(t,"..");if(a===t)return;t=a;}},M=(e,n=["--version"],t)=>{let o=child_process.spawnSync(e,[...n],{cwd:t,encoding:"utf8",shell:false,timeout:1e4});if(!(o.error||(o.status??0)!==0))return `${o.stdout??""}${o.stderr??""}`.trim().split(/\r?\n/g)[0]},L=e=>!!(e&&process.env[e]),ln=()=>process.env.BRASS_AGENT_VSCODE_EXTENSION==="1",cn=e=>{let n=N(path.join(e,"package.json")),t=_(n)&&typeof n.packageManager=="string"?n.packageManager.split("@")[0]:void 0;return t?{manager:t,source:"package.json packageManager"}:fs.existsSync(path.join(e,"pnpm-lock.yaml"))?{manager:"pnpm",source:"pnpm-lock.yaml"}:fs.existsSync(path.join(e,"yarn.lock"))?{manager:"yarn",source:"yarn.lock"}:fs.existsSync(path.join(e,"bun.lockb"))||fs.existsSync(path.join(e,"bun.lock"))?{manager:"bun",source:"bun lockfile"}:fs.existsSync(path.join(e,"package-lock.json"))||fs.existsSync(path.join(e,"npm-shrinkwrap.json"))?{manager:"npm",source:"npm lockfile"}:{manager:"npm",source:"fallback"}},pn=e=>_(e)&&_(e.scripts)?Object.entries(e.scripts).filter(([,n])=>typeof n=="string").map(([n])=>n).sort():[],dn=(e,n)=>e.find(t=>n.some(o=>o.test(t))),un=(e,n)=>{let t=pn(n),o=["Cargo.toml","Cargo.lock","src-tauri/tauri.conf.json","apps/desktop/package.json","apps/desktop/src-tauri/tauri.conf.json","bridges/whatsmeow-bridge/Cargo.toml","apps","packages","bridges","turbo.json","nx.json","pnpm-workspace.yaml"].filter(s=>fs.existsSync(path.join(e,s))),r=[];_(n)&&r.push("node"),o.some(s=>s==="Cargo.toml"||s==="Cargo.lock"||s.endsWith("Cargo.toml"))&&r.push("rust"),(o.some(s=>s.includes("tauri"))||t.some(s=>s.includes("tauri")))&&r.push("tauri"),(o.some(s=>s.startsWith("apps"))||t.some(s=>s.includes("desktop")))&&r.push("desktop"),(o.some(s=>s.startsWith("bridges"))||t.some(s=>s.includes("bridge")))&&r.push("bridge"),o.some(s=>["apps","packages","bridges","turbo.json","nx.json","pnpm-workspace.yaml"].includes(s))&&r.push("monorepo");let a=dn(t,[/^repo:check$/,/^check$/,/(^|:)check($|:)/,/(^|:)doctor($|:)/,/(^|:)health($|:)/,/(^|:)verify($|:)/,/(^|:)validate($|:)/,/(^|:)ci($|:)/]),l=[`stacks: ${r.length>0?[...new Set(r)].join(", "):"none detected"}`,`markers: ${o.length>0?o.slice(0,8).join(", "):"none"}`,a?`likely validation: npm run ${a}`:"likely validation: none detected"];return {id:"workspace.projectProfile",label:"Workspace project profile",status:r.length>0?"ok":"skip",message:l.join(". ")}},gn=e=>{if(!e)return {id:"envFile",label:"Agent env file",status:"skip",message:"Env file loading did not run."};if(e.disabled)return {id:"envFile",label:"Agent env file",status:"skip",message:"Env file loading disabled by --no-env-file."};if(e.errors.length>0)return {id:"envFile",label:"Agent env file",status:"fail",message:e.errors.join("; ")};if(e.paths.length===0)return {id:"envFile",label:"Agent env file",status:"skip",message:"No .brass-agent.env, .env.local, or .env found in workspace; using exported shell environment only."};let n=[`Loaded ${e.paths.join(", ")}`];return e.loadedKeys.length>0&&n.push(`keys: ${e.loadedKeys.join(", ")}`),e.alreadySetKeys.length>0&&n.push(`already set by shell: ${e.alreadySetKeys.join(", ")}`),e.emptyKeys.length>0&&n.push(`empty keys skipped: ${e.emptyKeys.join(", ")}`),e.ignoredKeys.length>0&&n.push(`non-agent keys ignored: ${e.ignoredKeys.slice(0,8).join(", ")}${e.ignoredKeys.length>8?", ...":""}`),e.invalidLines.length>0&&n.push(`invalid lines: ${e.invalidLines.join(", ")}`),{id:"envFile",label:"Agent env file",status:e.loadedKeys.length>0||e.alreadySetKeys.length>0?"ok":"warn",message:n.join(". ")}},fn=e=>{let n=(process.env.BRASS_LLM_PROVIDER??e?.llm?.provider)?.trim().toLowerCase(),t=e?.llm?.apiKeyEnv;if(n==="fake")return {id:"llm",label:"LLM provider",status:"ok",message:"Fake LLM provider is selected."};if(n==="google"||n==="gemini"){let o=L(t)||L("BRASS_GOOGLE_API_KEY")||L("GOOGLE_API_KEY")||L("GEMINI_API_KEY");return {id:"llm",label:"LLM provider",status:o?"ok":"fail",message:o?`Google/Gemini provider is configured (${n}).`:"Google/Gemini provider is selected but no API key env var is set. Export GEMINI_API_KEY or put it in .env/.brass-agent.env."}}if(n==="openai"||n==="openai-compatible"){let o=!!(process.env.BRASS_LLM_ENDPOINT??e?.llm?.endpoint),r=L(t)||L("BRASS_LLM_API_KEY");return {id:"llm",label:"LLM provider",status:o&&r?"ok":"fail",message:o&&r?`OpenAI-compatible provider is configured (${n}).`:"OpenAI-compatible provider is selected but endpoint or API key env var is missing. Export BRASS_LLM_API_KEY or put it in .env/.brass-agent.env."}}return L("BRASS_GOOGLE_API_KEY")||L("GOOGLE_API_KEY")||L("GEMINI_API_KEY")?{id:"llm",label:"LLM provider",status:"ok",message:"Google/Gemini credentials are available and will be auto-detected."}:(process.env.BRASS_LLM_ENDPOINT??e?.llm?.endpoint)&&L("BRASS_LLM_API_KEY")?{id:"llm",label:"LLM provider",status:"ok",message:"OpenAI-compatible credentials are available and will be auto-detected."}:{id:"llm",label:"LLM provider",status:"warn",message:"No real LLM credentials found. The CLI will fall back to the fake provider unless config selects a real provider."}},mn=e=>{let n=N(path.join(e,".vscode","settings.json"));if(!_(n))return;let t=n["brassAgent.command"];return typeof t=="string"?t:void 0},yn=e=>{try{return fs.readdirSync(e).filter(n=>n.endsWith(".vsix")).sort().at(-1)}catch{return}},y=(e,n)=>{e.push(n);},_e=async e=>{let n=path.resolve(e.cwd),t=[],o=Le(n,"brass-runtime")??Le(process.cwd(),"brass-runtime"),r=Number(process.versions.node.split(".")[0]??"0");y(t,{id:"node",label:"Node.js",status:r>=18?"ok":"fail",message:`Node ${process.versions.node}${r>=18?"":" is too old; use Node 18 or newer."}`});let a=M("npm");y(t,{id:"npm",label:"npm",status:a?"ok":"fail",message:a?`npm ${a}`:"npm is not available on PATH."});let l=M("git");y(t,{id:"git",label:"git",status:l?"ok":"warn",message:l||"git is not available; patch apply/rollback uses git apply."});let s=M("rg");if(y(t,{id:"ripgrep",label:"ripgrep",status:s?"ok":"warn",message:s||"rg is not available; context discovery search will be limited."}),y(t,{id:"workspace",label:"Workspace",status:fs.existsSync(n)?"ok":"fail",message:fs.existsSync(n)?n:`Workspace does not exist: ${n}`}),e.workspaceDiscovery){let p=e.workspaceDiscovery;y(t,{id:"workspace.discovery",label:"Workspace discovery",status:p.disabled?"skip":p.marker?"ok":"warn",message:p.disabled?"Workspace discovery disabled by --no-discover-workspace.":p.marker?`${p.changed?`Resolved ${p.inputCwd} -> ${p.cwd}`:`Using ${p.cwd}`} via ${p.marker}.`:`No workspace marker found upward from ${p.inputCwd}; using input cwd.`});}let c=path.join(n,"package.json"),h=N(c);if(y(t,{id:"workspace.packageJson",label:"Workspace package.json",status:_(h)?"ok":"warn",message:_(h)?`Found ${c}`:"No package.json found in workspace; project command discovery may use fallbacks."}),_(h)){let p=_(h.scripts)?Object.keys(h.scripts):[];y(t,{id:"workspace.scripts",label:"Workspace scripts",status:p.length?"ok":"warn",message:p.length?`Scripts: ${p.slice(0,12).join(", ")}${p.length>12?", ...":""}`:"No package scripts found."});let g=cn(n),m=M(g.manager);y(t,{id:"workspace.packageManager",label:"Workspace package manager",status:m?"ok":"warn",message:m?`${g.manager} available (${g.source}): ${m}`:`${g.manager} inferred from ${g.source}, but command is not available on PATH.`});}if(y(t,un(n,h)),y(t,gn(e.envFileLoad)),y(t,fn(e.config)),y(t,{id:"config",label:"Agent config",status:e.configPath?"ok":"skip",message:e.configPath?`Loaded ${e.configPath}`:"No .brass-agent.json / brass-agent.config.json loaded; using built-in defaults and VS Code/CLI settings."}),e.includeVsCode!==false){let p=M(process.env.BRASS_CODE_CMD??"code");y(t,{id:"vscode.code",label:"VS Code CLI",status:p?"ok":"warn",message:p?`code ${p}`:"VS Code CLI `code` is not available on PATH; .vsix install needs it unless using the VS Code UI."});let g=mn(n),m=ln();y(t,{id:"vscode.settings",label:"VS Code extension setting",status:g?"ok":m?"skip":"warn",message:g?`brassAgent.command = ${g}`:m?`No workspace brassAgent.command needed; launched by the VS Code extension (${process.env.BRASS_AGENT_VSCODE_CLI_SOURCE??"auto"}).`:"No workspace .vscode/settings.json brassAgent.command found."});}if(o){let p=path.join(o,"src","agent","cli","main.ts"),g=path.join(o,"dist","agent","cli","main.cjs"),m=path.join(o,"extensions","vscode-brass-agent");y(t,{id:"repo.root",label:"brass-runtime repo",status:"ok",message:o}),y(t,{id:"repo.cliSource",label:"CLI source",status:fs.existsSync(p)?"ok":"fail",message:fs.existsSync(p)?`Found ${p}`:`Missing ${p}`}),y(t,{id:"repo.cliBuild",label:"CLI build",status:fs.existsSync(g)?"ok":"warn",message:fs.existsSync(g)?`Found ${g}`:"dist/agent/cli/main.cjs is missing; run npm run build."}),y(t,{id:"repo.extensionDir",label:"VS Code extension source",status:fs.existsSync(path.join(m,"package.json"))?"ok":"warn",message:fs.existsSync(path.join(m,"package.json"))?m:"extensions/vscode-brass-agent was not found."}),y(t,{id:"repo.extensionBuild",label:"VS Code extension build",status:fs.existsSync(path.join(m,"out","extension.js"))?"ok":"warn",message:fs.existsSync(path.join(m,"out","extension.js"))?"out/extension.js exists.":"Extension output missing; run npm run agent:vscode:package or compile the extension."});let S=yn(m);y(t,{id:"repo.extensionVsix",label:"VSIX package",status:S?"ok":"warn",message:S?`Found ${S}`:"No .vsix found in extensions/vscode-brass-agent."});let R=await promises.access(g,promises.constants.R_OK).then(()=>true).catch(()=>false);y(t,{id:"repo.cliReadable",label:"CLI artifact readable",status:R?"ok":"warn",message:R?"Built CLI artifact is readable.":"Built CLI artifact is not readable yet."});}else y(t,{id:"repo.root",label:"brass-runtime repo",status:"skip",message:"Not running inside a brass-runtime checkout; local source/build checks skipped."});let E=t.some(p=>p.status==="fail")?"fail":t.some(p=>p.status==="warn")?"warn":"ok";return {generatedAt:new Date().toISOString(),cwd:n,...e.configPath?{configPath:e.configPath}:{},...o?{repoRoot:o}:{},status:E,checks:t}},hn=e=>{switch(e){case "ok":return "\u2713";case "warn":return "!";case "fail":return "\u2717";case "skip":return "-"}},xe=e=>{console.log("brass-agent doctor"),console.log(`workspace: ${e.cwd}`),e.configPath&&console.log(`config: ${e.configPath}`),e.repoRoot&&console.log(`repo: ${e.repoRoot}`),console.log(`status: ${e.status}`),console.log("");for(let n of e.checks)console.log(`${hn(n.status)} ${n.label}: ${n.message}`);};var kn=[".brass-agent.env",".env.local",".env"],wn=new Set(["BRASS_LLM_PROVIDER","BRASS_FAKE_LLM_RESPONSE","BRASS_GOOGLE_API_KEY","GOOGLE_API_KEY","GEMINI_API_KEY","BRASS_GOOGLE_MODEL","BRASS_GOOGLE_API_VERSION","BRASS_GOOGLE_BASE_URL","BRASS_GOOGLE_ENDPOINT","BRASS_GOOGLE_SYSTEM_INSTRUCTION","BRASS_GOOGLE_TEMPERATURE","BRASS_GOOGLE_TOP_P","BRASS_GOOGLE_TOP_K","BRASS_GOOGLE_MAX_OUTPUT_TOKENS","BRASS_LLM_ENDPOINT","BRASS_LLM_API_KEY","BRASS_LLM_MODEL","BRASS_AGENT_APPROVAL","BRASS_AGENT_AUTO_APPROVE","BRASS_CODE_CMD"]),C=e=>Array.from(new Set(e)),Sn=e=>{if(e.length<2)return;let n=e[0];if(n!=='"'&&n!=="'"||e[e.length-1]!==n)return;let t=e.slice(1,-1);return n==="'"?t:t.replace(/\\n/g,`
|
|
7
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")},En=e=>{let n=e.trim();if(!n||n.startsWith("#"))return {type:"skip"};let t=n.startsWith("export ")?n.slice(7).trimStart():n,o=/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(t);if(!o)return {type:"invalid"};let r=o[1]??"",l=(o[2]??"").trim(),c=Sn(l)??l.replace(/\s+#.*$/u,"").trim();return {type:"assignment",key:r,value:c}},Rn=(e,n)=>n?[path.isAbsolute(n)?n:path.resolve(e,n)]:kn.map(t=>path.join(e,t)),Ce=e=>{let n=path.resolve(e.cwd),t=e.allowedExtraKeys?.filter(Boolean)??[],o=new Set([...wn,...t]),r=e.noEnvFile?[]:Rn(n,e.envFile),a=[],l=[],s=[],c=[],h=[],E=[],p=[];if(e.noEnvFile)return {cwd:n,disabled:true,filesChecked:[],paths:[],loadedKeys:[],alreadySetKeys:[],emptyKeys:[],ignoredKeys:[],invalidLines:[],errors:[]};for(let g of r){if(!fs.existsSync(g)){e.envFile&&p.push(`Env file does not exist: ${g}`);continue}a.push(g);let m;try{m=fs.readFileSync(g,"utf8").replace(/^\uFEFF/u,"");}catch(S){p.push(`Could not read env file ${g}: ${S instanceof Error?S.message:String(S)}`);continue}for(let[S,R]of m.split(/\r?\n/gu).entries()){let b=En(R);if(b.type!=="skip"){if(b.type==="invalid"){E.push(`${g}:${S+1}`);continue}if(!o.has(b.key)){h.push(b.key);continue}if(!b.value){c.push(b.key);continue}if(process.env[b.key]!==void 0){s.push(b.key);continue}process.env[b.key]=b.value,l.push(b.key);}}}return {cwd:n,disabled:false,...e.envFile?{explicitPath:path.isAbsolute(e.envFile)?e.envFile:path.resolve(n,e.envFile)}:{},filesChecked:r,paths:a,loadedKeys:C(l),alreadySetKeys:C(s),emptyKeys:C(c),ignoredKeys:C(h),invalidLines:C(E),errors:C(p)}};var Fe=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),On=e=>{try{return JSON.parse(fs.readFileSync(e,"utf8"))}catch{return}},Me=e=>{let n=On(path.join(e,"package.json"));return Fe(n)?n:void 0},$e=e=>{let n=e?.scripts;return Fe(n)?Object.entries(n).filter(([,t])=>typeof t=="string").map(([t])=>t).sort():[]},$=(e,n)=>n.some(t=>e.includes(t)),Cn=(e,n)=>{let t=typeof n?.packageManager=="string"?n.packageManager.split("@")[0]:void 0;return t==="npm"||t==="pnpm"||t==="yarn"||t==="bun"?t:fs.existsSync(path.join(e,"pnpm-lock.yaml"))?"pnpm":fs.existsSync(path.join(e,"yarn.lock"))?"yarn":fs.existsSync(path.join(e,"bun.lockb"))||fs.existsSync(path.join(e,"bun.lock"))?"bun":fs.existsSync(path.join(e,"package-lock.json"))||fs.existsSync(path.join(e,"npm-shrinkwrap.json"))?"npm":"auto"},In=e=>{switch(e){case "default":return;case "google":return {provider:"google",model:"gemini-2.5-flash",apiKeyEnv:"GEMINI_API_KEY",temperature:.2,maxOutputTokens:4096};case "openai-compatible":return {provider:"openai-compatible",endpoint:"https://api.openai.com/v1/chat/completions",model:"gpt-4.1",apiKeyEnv:"BRASS_LLM_API_KEY",temperature:.2};case "fake":return {provider:"fake",fakeResponse:"Fake plan from brass-agent init. Configure a real provider when ready."}}},Fn=(e,n)=>{let t=Me(e),o=$e(t),r=$(o,["typecheck","type-check","check-types","tsc","check"]),a=$(o,["lint","lint:ci"]),l=In(n);return {mode:"propose",approval:"auto",...l?{llm:l}:{},project:{packageManager:Cn(e,t),testScriptNames:["test","test:ci","test:unit"],includeTypecheck:r,includeLint:a,maxValidationCommands:2},context:{enabled:true,maxSearchQueries:3,maxFiles:4,maxSearchResults:40,globs:["*.ts","*.tsx","*.js","*.jsx","*.mjs","*.cjs","*.json","*.md","*.yml","*.yaml"],excludeGlobs:[".env*","**/.env*","**/node_modules/**","**/dist/**","**/build/**","**/.git/**","**/*.pem","**/*.key","**/secrets/**"]},patchQuality:{enabled:true,maxRepairAttempts:1},rollback:{enabled:true,onFinalValidationFailure:true,strategy:"all",maxRollbackDepth:8,runValidationAfterRollback:true,allowForSuppliedPatches:false},redaction:{enabled:true,additionalPatterns:[]},language:{response:"auto"},permissions:{shell:{inheritDefaults:true,ask:[{pattern:"npm run build",reason:"Build commands can be slow and may produce large outputs.",risk:"medium",defaultAnswer:"approve"},{pattern:"pnpm run build",reason:"Build commands can be slow and may produce large outputs.",risk:"medium",defaultAnswer:"approve"},{pattern:"yarn run build",reason:"Build commands can be slow and may produce large outputs.",risk:"medium",defaultAnswer:"approve"},{pattern:"bun run build",reason:"Build commands can be slow and may produce large outputs.",risk:"medium",defaultAnswer:"approve"}],deny:["rm *","git push *","git reset *","git clean *"]},patchApply:{decision:"ask",reason:"Apply the generated unified diff to the workspace.",risk:"high",defaultAnswer:"reject"}},tools:{"fs.readFile":{timeoutMs:1e4,retries:1},"fs.exists":{timeoutMs:5e3,retries:0},"fs.searchText":{timeoutMs:1e4,retries:1},"shell.exec":{timeoutMs:18e4,retries:0},"llm.complete":{timeoutMs:9e4,retries:2},"patch.apply":{timeoutMs:3e4,retries:0},"patch.rollback":{timeoutMs:3e4,retries:0}}}},Mn=e=>{let n=$e(Me(e)),t=[{preset:"inspect",mode:"read-only"}];return $(n,["typecheck","type-check","check-types","tsc","check"])&&t.push({preset:"typecheck",mode:"propose"}),$(n,["lint","lint:ci"])&&t.push({preset:"lint",mode:"propose"}),$(n,["test","test:ci","test:unit"])&&t.push({preset:"fix-tests",mode:"propose"}),t},$n=e=>`${JSON.stringify({stopOnFailure:false,goals:Mn(e)},null,2)}
|
|
8
|
+
`,jn=e=>{let n=["# Brass Agent environment variables","# Copy this file to .env or .brass-agent.env, or export the variables in your shell.","# brass-agent auto-loads supported agent env keys from --cwd.","# Do not commit real API keys.",""];return e==="fake"&&n.push("BRASS_LLM_PROVIDER=fake","BRASS_FAKE_LLM_RESPONSE=Fake plan from .env.example",""),n.push("# Google / Gemini","# Used when .brass-agent.json selects provider google, or when auto-detected.","GEMINI_API_KEY=","BRASS_GOOGLE_MODEL=gemini-2.5-flash","","# OpenAI-compatible providers","# BRASS_LLM_PROVIDER=openai-compatible","BRASS_LLM_ENDPOINT=https://api.openai.com/v1/chat/completions","BRASS_LLM_API_KEY=","BRASS_LLM_MODEL=gpt-4.1","","# Approval behavior: auto | interactive | approve | deny","# BRASS_AGENT_APPROVAL=auto",""),e==="google"?n.splice(4,0,"BRASS_LLM_PROVIDER=google",""):e==="openai-compatible"&&n.splice(4,0,"BRASS_LLM_PROVIDER=openai-compatible",""),`${n.join(`
|
|
9
|
+
`)}
|
|
10
|
+
`},Bn=()=>["# Brass Agent","","This workspace was initialized with `brass-agent --init`.","","Generated files:","","- `.brass-agent.json` \u2014 local policy/config for Brass Agent.","- `brass-agent.batch.json` \u2014 sample multi-goal batch workflow.","- `.env.example` \u2014 example environment variables. Copy to `.env` or `.brass-agent.env`; keep real secrets out of git.","","Recommended first commands:","","```bash","brass-agent --doctor","brass-agent --preset inspect","brass-agent --batch-file brass-agent.batch.json","```","","Apply mode is intentionally approval-gated:","","```bash",'brass-agent --apply "fix the failing tests"',"```",""].join(`
|
|
11
|
+
`),Dn=e=>{let n=["Review .brass-agent.json and adjust permissions/context budgets for this repo.","Run: brass-agent --doctor","Run: brass-agent --preset inspect","Run: brass-agent --batch-file brass-agent.batch.json"];return e==="google"?["Set GEMINI_API_KEY in your shell, `.env`, or `.brass-agent.env`.",...n]:e==="openai-compatible"?["Set BRASS_LLM_API_KEY and BRASS_LLM_ENDPOINT in your shell, `.env`, or `.brass-agent.env`.",...n]:e==="default"?["Set an LLM provider env var in your shell, `.env`, or `.brass-agent.env` when ready, or let the CLI fall back to fake mode.",...n]:n},T=async e=>{let n=path.resolve(e.cwd,e.relativePath),o=fs.existsSync(n)?e.force?"overwritten":"skipped":"created";return !e.dryRun&&o!=="skipped"&&(await promises.mkdir(path.dirname(n),{recursive:true}),await promises.writeFile(n,e.content,"utf8")),{path:n,relativePath:e.relativePath,status:o,bytes:Buffer.byteLength(e.content,"utf8")}},je=async e=>{let n=path.resolve(e.cwd),t=e.profile??"default",o=e.force??false,r=e.dryRun??false,a=Fn(n,t),l=await Promise.all([T({cwd:n,relativePath:".brass-agent.json",content:`${JSON.stringify(a,null,2)}
|
|
12
|
+
`,force:o,dryRun:r}),T({cwd:n,relativePath:"brass-agent.batch.json",content:$n(n),force:o,dryRun:r}),T({cwd:n,relativePath:".env.example",content:jn(t),force:o,dryRun:r}),T({cwd:n,relativePath:"BRASS_AGENT.md",content:Bn(),force:o,dryRun:r})]);return {cwd:n,profile:t,dryRun:r,files:l,nextSteps:Dn(t)}},Gn=e=>{switch(e){case "created":return "\u2713";case "overwritten":return "!";case "skipped":return "-"}},Be=e=>{console.log(`brass-agent init${e.dryRun?" (dry run)":""}`),console.log(`workspace: ${e.cwd}`),console.log(`profile: ${e.profile}`),console.log("");for(let n of e.files)console.log(`${Gn(n.status)} ${n.status} ${n.relativePath}`);console.log(""),console.log("next steps:");for(let n of e.nextSteps)console.log(`- ${n}`);};var F=new Function("specifier","return import(specifier)"),Nn=async(e,n)=>{let t=await F("node:path");return (await F("node:fs/promises")).readFile(t.resolve(e,n),"utf8")},K=e=>{if(e===void 0||e.trim()==="")return;let n=Number(e);return Number.isFinite(n)?n:void 0},Ye=e=>e==="read-only"||e==="propose"||e==="write"||e==="autonomous",Je=e=>e==="auto"||e==="interactive"||e==="approve"||e==="deny",De=e=>e==="default"||e==="google"||e==="openai-compatible"||e==="fake",Tn=e=>["auto","match-user","en","es","pt","fr","de","it","custom"].includes(e),k=(e,n,t)=>{let o=e[n],r=o.startsWith(`${t}=`)?o.slice(t.length+1):void 0;if(r!==void 0&&r!=="")return [r,n];let a=e[n+1];if(!a)throw new Error(`${t} requires a value`);return [a,n+1]},Kn=e=>{let n=process.cwd(),t=true,o=false,r="propose",a=false,l=false,s="human",c="auto",h=false,E,p=false,g,m=false,S=false,R,b="apply",V,re=false,se=false,W,Y,D,ae=false,ie=false,le=false,G="default",ce=false,J,U=[];for(let u=0;u<e.length;u+=1){let i=e[u];if(i==="--"){U.push(...e.slice(u+1));break}if(i==="--help"||i==="-h"){l=true;continue}if(i==="--doctor"){ae=true;continue}if(i==="--where"||i==="--print-workspace"){o=true;continue}if(i==="--no-discover-workspace"){t=false;continue}if(i==="--init"){ie=true;continue}if(i==="--force"||i==="--init-force"){le=true;continue}if(i==="--init-dry-run"){ce=true;continue}if(i==="--init-profile"||i.startsWith("--init-profile=")){let[d,f]=k(e,u,"--init-profile");if(!De(d))throw new Error("--init-profile requires one of: default, google, openai-compatible, fake");G=d,u=f;continue}if(i==="--init-provider"||i.startsWith("--init-provider=")){let[d,f]=k(e,u,"--init-provider");if(d==="auto")G="default";else if(De(d))G=d;else throw new Error("--init-provider requires one of: auto, fake, google, openai-compatible");u=f;continue}if(i==="--language"||i.startsWith("--language=")){let[d,f]=k(e,u,"--language");if(!Tn(d))throw new Error("--language requires one of: auto, match-user, en, es, pt, fr, de, it, custom");J=d,u=f;continue}if(i==="--json"){s="json";continue}if(i==="--events-json"){s="events-json";continue}if(i==="--protocol-json"){s="protocol-json";continue}if(i==="--protocol-full-patches"){S=true;continue}if(i==="--preset"||i.startsWith("--preset=")){let[d,f]=k(e,u,"--preset");if(!chunk6OUI6UGZ_cjs.ga(d))throw new Error("--preset requires one of: fix-tests, inspect, typecheck, lint");W=d,u=f;continue}if(i==="--batch-file"||i.startsWith("--batch-file=")){let[d,f]=k(e,u,"--batch-file");Y=d,u=f;continue}if(i==="--batch-stop-on-failure"){D=true;continue}if(i==="--batch-continue-on-failure"){D=false;continue}if(i==="--ci"){re=true;continue}if(i==="--fail-on-patch-proposed"){se=true;continue}if(i==="--yes"||i==="-y"){c="approve",h=true;continue}if(i==="--no-input"){c="deny",h=true;continue}if(i==="--approval"||i.startsWith("--approval=")){let[d,f]=k(e,u,"--approval");if(!Je(d))throw new Error("--approval requires one of: auto, interactive, approve, deny");c=d,h=true,u=f;continue}if(i==="--apply"){r="write",a=true;continue}if(i==="--patch-file"||i.startsWith("--patch-file=")){let[d,f]=k(e,u,"--patch-file");R=d,u=f;continue}if(i==="--apply-patch-file"||i.startsWith("--apply-patch-file=")){let[d,f]=k(e,u,"--apply-patch-file");R=d,b="apply",r="write",a=true,u=f;continue}if(i==="--rollback-patch-file"||i.startsWith("--rollback-patch-file=")){let[d,f]=k(e,u,"--rollback-patch-file");R=d,b="rollback",r="write",a=true,u=f;continue}if(i==="--mode"||i.startsWith("--mode=")){let[d,f]=k(e,u,"--mode");if(!Ye(d))throw new Error("--mode requires one of: read-only, propose, write, autonomous");r=d,a=true,u=f;continue}if(i==="--cwd"||i.startsWith("--cwd=")){let[d,f]=k(e,u,"--cwd");n=d,u=f;continue}if(i==="--save-run"||i.startsWith("--save-run=")){let[d,f]=k(e,u,"--save-run");V=d,u=f;continue}if(i==="--config"||i.startsWith("--config=")){let[d,f]=k(e,u,"--config");E=d,p=false,u=f;continue}if(i==="--no-config"){E=void 0,p=true;continue}if(i==="--env-file"||i.startsWith("--env-file=")){let[d,f]=k(e,u,"--env-file");g=d,m=false,u=f;continue}if(i==="--no-env-file"){g=void 0,m=true;continue}if(i.startsWith("--"))throw new Error(`Unknown option: ${i}`);U.push(i);}return {cwd:n,discoverWorkspace:t,where:o,goalText:U.join(" ").trim(),mode:r,modeSpecified:a,showHelp:l,output:s,approval:c,approvalSpecified:h,...E?{configPath:E}:{},noConfig:p,...g?{envFile:g}:{},noEnvFile:m,protocolFullPatches:S,patchFileMode:b,ci:re,failOnPatchProposed:se,...W?{preset:W}:{},...Y?{batchFile:Y}:{},...D!==void 0?{batchStopOnFailure:D}:{},doctor:ae,init:ie,initForce:le,initProfile:G,initDryRun:ce,...J?{language:J}:{},...V?{saveRunDir:V}:{},...R?{patchFile:R}:{}}},Ue=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),Vn=(e,n)=>{if(typeof e=="string")return e;if(!Ue(e))throw new Error(`${n} must be a string or object.`);let t=e.goal,o=e.preset,r=e.mode,a=e.cwd,l=e.patchFile,s=e.patchFileMode,c=e.saveRunDir;if(t!==void 0&&typeof t!="string")throw new Error(`${n}.goal must be a string.`);if(a!==void 0&&typeof a!="string")throw new Error(`${n}.cwd must be a string.`);if(l!==void 0&&typeof l!="string")throw new Error(`${n}.patchFile must be a string.`);if(c!==void 0&&typeof c!="string")throw new Error(`${n}.saveRunDir must be a string.`);if(o!==void 0&&(typeof o!="string"||!chunk6OUI6UGZ_cjs.ga(o)))throw new Error(`${n}.preset must be one of: fix-tests, inspect, typecheck, lint.`);if(r!==void 0&&(typeof r!="string"||!Ye(r)))throw new Error(`${n}.mode must be one of: read-only, propose, write, autonomous.`);if(s!==void 0&&s!=="apply"&&s!=="rollback")throw new Error(`${n}.patchFileMode must be apply or rollback.`);if(t===void 0&&o===void 0&&l===void 0)throw new Error(`${n} must include goal, preset, or patchFile.`);return {...t?{goal:t}:{},...o?{preset:o}:{},...r?{mode:r}:{},...a?{cwd:a}:{},...l?{patchFile:l}:{},...s?{patchFileMode:s}:{},...c?{saveRunDir:c}:{}}},Wn=e=>{let n=Array.isArray(e)?e:Ue(e)&&Array.isArray(e.goals)?e.goals:void 0;if(!n)throw new Error("Batch file must be a JSON array or an object with a goals array.");return n.map((t,o)=>Vn(t,`goals[${o}]`))},Yn=async(e,n)=>{let t=await F("node:path"),o=await F("node:fs/promises"),r=t.isAbsolute(n)?n:t.resolve(e,n),a=String(await o.readFile(r,"utf8")).replace(/^\uFEFF/,"");try{return Wn(JSON.parse(a))}catch(l){if(!(l instanceof SyntaxError))throw l;let s=a.split(/\r?\n/g).map(c=>c.trim()).filter(c=>c&&!c.startsWith("#"));if(s.length===0)throw new Error(`Batch file has no goals: ${r}`);return s}},Jn=(e,n)=>typeof e=="string"?e:e.goal?e.goal:e.preset?chunk6OUI6UGZ_cjs.ha(e.preset):e.patchFile??n?"apply supplied patch":"",Un=(e,n,t)=>typeof e!="string"&&e.mode?e.mode:typeof e!="string"&&e.preset==="inspect"&&!n.modeSpecified?"read-only":n.modeSpecified?n.mode:t.mode??n.mode,qn=(e,n,t)=>e.map((o,r)=>{let a=Jn(o,n.patchFile),l=typeof o=="string"?n.cwd:o.cwd??n.cwd,s=typeof o=="string"?n.patchFile:o.patchFile??n.patchFile,c=typeof o=="string"?n.patchFileMode:o.patchFileMode??n.patchFileMode,h=typeof o=="string"?n.saveRunDir:o.saveRunDir??n.saveRunDir;if(!a)throw new Error(`Batch goal ${r+1} resolved to an empty goal.`);return {index:r,cwd:l,goalText:a,mode:Un(o,n,t),patchFileMode:c,...s?{patchFile:s}:{},...h?{saveRunDir:h}:{}}}),Hn=async e=>{let n=chunk6OUI6UGZ_cjs.ta(e.cwd,{enabled:e.discoverWorkspace}),t=n.cwd,o={...e,cwd:t},r=await chunk6OUI6UGZ_cjs.pa({cwd:t,configPath:e.configPath,noConfig:e.noConfig}),a=Ce({cwd:t,envFile:e.envFile,noEnvFile:e.noEnvFile,allowedExtraKeys:r.config.llm?.apiKeyEnv?[r.config.llm.apiKeyEnv]:[]}),l=!e.goalText&&!e.preset&&!e.patchFile,s=e.batchFile?await Yn(t,e.batchFile):l?r.config.batch?.goals??[]:[],c=qn(s,o,r.config);return {...o,goalText:e.goalText||(e.preset?chunk6OUI6UGZ_cjs.ha(e.preset):e.patchFile?"apply supplied patch":e.goalText),mode:e.modeSpecified?e.mode:e.preset==="inspect"?"read-only":r.config.mode??e.mode,approval:e.approvalSpecified?e.approval:r.config.approval??e.approval,config:r.config,workspaceDiscovery:n,batchRuns:c,batchStopOnFailureResolved:e.batchStopOnFailure??r.config.batch?.stopOnFailure??e.ci,envFileLoad:a,...r.path?{resolvedConfigPath:r.path}:{}}},Ge=()=>{console.log(['Usage: brass-agent [options] "goal"',"","Options:"," --mode read-only|propose|write|autonomous"," Agent permission mode. Default: propose, or config.mode if present."," --preset fix-tests|inspect|typecheck|lint"," Use a built-in goal preset when no explicit goal text is provided."," --apply"," Alias for --mode write."," --cwd PATH"," Starting directory for workspace discovery. Default: current directory."," --no-discover-workspace"," Use --cwd exactly instead of searching upward for package.json, .brass-agent.json, or .git."," --where, --print-workspace"," Print the resolved workspace root and exit."," --config PATH"," Load a specific .brass-agent.json policy/config file."," --save-run DIR"," Write final run JSON and Markdown artifacts to DIR."," --batch-file PATH"," Run multiple goals sequentially from a JSON or line-based file."," --batch-stop-on-failure"," Stop a batch after the first failed run."," --batch-continue-on-failure"," Continue a batch even when a run fails."," --doctor"," Check local CLI, workspace, VS Code, package manager, and LLM setup."," --init"," Initialize this workspace with .brass-agent.json, brass-agent.batch.json, .env.example, and BRASS_AGENT.md."," --force, --init-force"," Overwrite files generated by --init when they already exist."," --init-profile default|google|openai-compatible|fake"," Initialization profile. Default: default, which leaves provider auto-detection enabled."," --init-provider auto|fake|google|openai-compatible"," Alias for choosing an LLM-oriented init profile. auto maps to default."," --init-dry-run"," Preview generated files without writing them."," --no-config"," Do not discover or load an agent config file."," --language auto|match-user|en|es|pt|fr|de|it"," Response language for LLM summaries. Default: config.language or auto-match the user goal."," --env-file PATH"," Load Brass Agent environment variables from a specific env file."," --no-env-file"," Do not auto-load .brass-agent.env, .env.local, or .env from --cwd."," --json"," Print the full final AgentState JSON. Suppresses live event output."," --ci"," Preserve output mode but set process exit codes from the final run status."," --fail-on-patch-proposed"," In --ci mode, exit 2 when a patch was proposed but not applied."," --events-json"," Stream AgentEvent objects as JSON Lines. Does not print the final AgentState."," --protocol-json"," Stream Brass Agent protocol JSON Lines, including events and a final-state message."," --protocol-full-patches"," Keep patch payloads untruncated in protocol/event JSON output for trusted local integrations."," --patch-file PATH"," Supply a precomputed unified diff to the agent. Respects --mode."," --apply-patch-file PATH"," Supply and apply a precomputed unified diff. Alias for --patch-file PATH --mode write."," --rollback-patch-file PATH"," Reverse-apply a precomputed unified diff through PatchService. Requires write mode approvals."," --yes, -y"," Auto-approve approval prompts. Useful for CI and smoke tests."," --no-input"," Do not prompt; reject any action that requires approval."," --approval auto|interactive|approve|deny"," Approval strategy. Default: auto, or config.approval if present."," --help, -h"," Show this help message.","","Config files:"," brass-agent first resolves a workspace root by searching upward from --cwd."," It looks for .brass-agent.json, brass-agent.config.json, package.json, workspace markers, or .git."," brass-agent then searches upward from that workspace root for .brass-agent.json or brass-agent.config.json."," config.batch.goals can define a default batch when --batch-file is not provided.","","Examples:",' brass-agent "fix the failing tests"'," brass-agent --preset fix-tests"," brass-agent --preset inspect"," brass-agent --batch-file ./brass-agent.batch.json --ci"," brass-agent --where"," brass-agent --doctor"," brass-agent --doctor --json"," brass-agent --env-file .env --doctor"," brass-agent --init"," brass-agent --init --init-profile google"," brass-agent --init --init-profile fake --init-dry-run",' brass-agent --config ./agent.policy.json "fix the failing tests"',' brass-agent --no-config "fix the failing tests"',' brass-agent --json "fix the failing tests"',' brass-agent --events-json "fix the failing tests"',' brass-agent --protocol-json "fix the failing tests"',' brass-agent --protocol-json --protocol-full-patches "fix the failing tests"',' brass-agent --apply-patch-file ./approved.diff --yes "apply approved patch"',' brass-agent --apply "fix the failing tests"',' brass-agent --apply --yes "fix the failing tests"',' brass-agent --mode read-only --cwd ./repo "inspect the test failure"',"","LLM providers:"," BRASS_LLM_PROVIDER=fake"," BRASS_LLM_PROVIDER=google GEMINI_API_KEY=..."," BRASS_LLM_PROVIDER=openai-compatible BRASS_LLM_ENDPOINT=... BRASS_LLM_API_KEY=..."].join(`
|
|
13
|
+
`));},qe=e=>e?process.env[e]:void 0,Ne=e=>{let n=qe(e?.apiKeyEnv)??process.env.BRASS_GOOGLE_API_KEY??process.env.GOOGLE_API_KEY??process.env.GEMINI_API_KEY;if(n)return chunk6OUI6UGZ_cjs.ra({apiKey:n,model:process.env.BRASS_GOOGLE_MODEL??process.env.BRASS_LLM_MODEL??e?.model??"gemini-2.5-flash",apiVersion:process.env.BRASS_GOOGLE_API_VERSION??e?.apiVersion??"v1beta",baseUrl:process.env.BRASS_GOOGLE_BASE_URL??e?.baseUrl,endpoint:process.env.BRASS_GOOGLE_ENDPOINT??e?.endpoint,systemInstruction:process.env.BRASS_GOOGLE_SYSTEM_INSTRUCTION??e?.systemInstruction,temperature:K(process.env.BRASS_GOOGLE_TEMPERATURE)??e?.temperature,topP:K(process.env.BRASS_GOOGLE_TOP_P)??e?.topP,topK:K(process.env.BRASS_GOOGLE_TOP_K)??e?.topK,maxOutputTokens:K(process.env.BRASS_GOOGLE_MAX_OUTPUT_TOKENS)??e?.maxOutputTokens})},Te=e=>{let n=process.env.BRASS_LLM_ENDPOINT??e?.endpoint,t=qe(e?.apiKeyEnv)??process.env.BRASS_LLM_API_KEY,o=process.env.BRASS_LLM_MODEL??e?.model??"gpt-4.1";if(!(!n||!t))return chunk6OUI6UGZ_cjs.qa({endpoint:n,apiKey:t,model:o})},zn=e=>{let n=(process.env.BRASS_LLM_PROVIDER??e?.provider)?.trim().toLowerCase(),t=process.env.BRASS_FAKE_LLM_RESPONSE??e?.fakeResponse;if(n==="fake")return chunk6OUI6UGZ_cjs.sa({content:t});if(n==="google"||n==="gemini"){let o=Ne(e);if(!o)throw new Error("Google LLM provider requires BRASS_GOOGLE_API_KEY, GOOGLE_API_KEY, GEMINI_API_KEY, or config.llm.apiKeyEnv.");return o}if(n==="openai"||n==="openai-compatible"){let o=Te(e);if(!o)throw new Error("OpenAI-compatible LLM provider requires BRASS_LLM_ENDPOINT/config.llm.endpoint and BRASS_LLM_API_KEY/config.llm.apiKeyEnv.");return o}if(n)throw new Error(`Unsupported LLM provider: ${n}`);return Ne(e)??Te(e)??chunk6OUI6UGZ_cjs.sa({content:t})},Qn=()=>{let e=process.env.BRASS_AGENT_APPROVAL?.trim().toLowerCase();if(e){if(Je(e))return e;throw new Error("BRASS_AGENT_APPROVAL must be one of: auto, interactive, approve, deny")}},Xn=e=>e==="1"||e?.toLowerCase()==="true"||e?.toLowerCase()==="yes",Zn=()=>!!(process.stdin?.isTTY&&(process.stderr?.isTTY??process.stdout?.isTTY)),et=e=>{if(e.approvalSpecified&&e.approval!=="auto")return e.approval;if(Xn(process.env.BRASS_AGENT_AUTO_APPROVE))return "approve";let n=Qn();return n&&n!=="auto"?n:e.approval!=="auto"?e.approval:e.output==="human"&&Zn()?"interactive":"deny"},nt=e=>{switch(et(e)){case "approve":return chunk6OUI6UGZ_cjs.ka;case "deny":return chunk6OUI6UGZ_cjs.la("Approval rejected because the CLI is running without interactive input. Use --yes to auto-approve.");case "interactive":return Pe()}},Ke=(e,n=2e3)=>e.length<=n?e:`${e.slice(0,n)}
|
|
14
|
+
\u2026 truncated ${e.length-n} chars`,I=(e,n=1e3)=>e.length<=n?e:`${e.slice(0,n)}\u2026 truncated ${e.length-n} chars`,B=(e,n)=>n.fullPatches?e:I(e),He=(e,n)=>({...e,...e.initialPatch?{initialPatch:B(e.initialPatch,n)}:{}}),j=(e,n={})=>{switch(e.type){case "llm.complete":return {...e,prompt:I(e.prompt)};case "patch.apply":case "patch.rollback":case "patch.propose":return {...e,patch:B(e.patch,n)};default:return e}},ne=(e,n={})=>{switch(e.type){case "fs.fileRead":return {...e,content:I(e.content)};case "llm.response":return {...e,content:I(e.content)};case "shell.result":return {...e,stdout:I(e.stdout),stderr:I(e.stderr)};case "fs.searchResult":return {...e,matches:e.matches.slice(0,30),omittedMatches:Math.max(0,e.matches.length-30)};case "patch.proposed":return {...e,patch:B(e.patch,n)};case "patch.applied":case "patch.rolledBack":return e.patch?{...e,patch:B(e.patch,n)}:e;default:return e}},ze=(e,n={})=>{switch(e.type){case "agent.run.started":case "agent.run.completed":return {...e,goal:He(e.goal,n)};case "agent.action.started":return {...e,action:j(e.action,n)};case "agent.action.completed":return {...e,action:j(e.action,n),observation:ne(e.observation,n)};case "agent.action.failed":return {...e,action:j(e.action,n)};case "agent.observation.recorded":return {...e,observation:ne(e.observation,n)};case "agent.tool.timeout":case "agent.permission.denied":case "agent.approval.requested":case "agent.approval.resolved":return {...e,action:j(e.action,n)};default:return e}},te=(e,n={})=>({...e,goal:He(e.goal,n),observations:e.observations.map(t=>ne(t,n)),errors:e.errors.map(t=>{switch(t._tag){case "PermissionDenied":case "ApprovalRejected":return {...t,action:j(t.action,n)};case "PatchError":return {...t,cause:String(t.cause),...t.patch?{patch:B(t.patch,n)}:{}};case "FsError":case "ShellError":case "LLMError":return {...t,cause:String(t.cause)};default:return t}})}),oe=e=>({protocol:"brass-agent",version:1,...e}),tt=e=>{switch(e){case "ok":return "\u2713";case "warn":return "!";case "fail":return "\u2717"}},Ve=e=>`${Math.max(0,e)}ms`,w=(e,n)=>[...e.observations].reverse().find(t=>t.type===n),ot=e=>({emit(n){switch(n.type){case "agent.run.started":console.log(`brass-agent ${n.goal.mode}`),console.log(`workspace: ${n.goal.cwd}`),e&&console.log(`config: ${e}`),console.log(`goal: ${n.goal.text}`),console.log("");break;case "agent.action.started":console.log(`\u2192 ${chunk6OUI6UGZ_cjs.T(n.action)}`);break;case "agent.action.completed":{let t=chunk6OUI6UGZ_cjs.V(n.observation);console.log(`${tt(t)} ${chunk6OUI6UGZ_cjs.U(n.observation)} ${Ve(n.durationMs)}`);break}case "agent.action.failed":n.error._tag!=="ToolTimeout"&&n.error._tag!=="PermissionDenied"&&n.error._tag!=="ApprovalRejected"&&console.log(`\u2717 ${chunk6OUI6UGZ_cjs.T(n.action)} failed with ${n.error._tag} ${Ve(n.durationMs)}`);break;case "agent.tool.timeout":console.log(`! ${chunk6OUI6UGZ_cjs.T(n.action)} timed out after ${n.timeoutMs}ms`);break;case "agent.permission.denied":console.log(`\u2717 ${chunk6OUI6UGZ_cjs.T(n.action)} denied: ${n.reason}`);break;case "agent.approval.requested":console.log(`? approval required for ${chunk6OUI6UGZ_cjs.T(n.action)} (${n.risk})`);break;case "agent.approval.resolved":n.approved?console.log(`\u2713 approval granted for ${chunk6OUI6UGZ_cjs.T(n.action)}`):console.log(`\u2717 approval rejected for ${chunk6OUI6UGZ_cjs.T(n.action)}${n.reason?`: ${n.reason}`:""}`);break;case "agent.patch.applied":break;case "agent.patch.rolledBack":n.automatic&&console.log(`\u2713 automatic rollback completed (${n.changedFiles.join(", ")||"no files reported"})`);break;}}}),rt=(e={})=>({emit(n){console.log(JSON.stringify(ze(n,e)));}}),st=(e={})=>({emit(n){console.log(JSON.stringify(oe({type:"event",event:ze(n,e)})));}}),We=e=>e.toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"run",ee=e=>e.replace(/\\/g,"\\\\").replace(/`/g,"\\`"),at=async(e,n,t)=>{let o=await F("node:path"),r=await F("node:fs/promises"),a=o.isAbsolute(n)?n:o.resolve(e.goal.cwd,n);await r.mkdir(a,{recursive:true});let s=`${We(e.goal.id)}-${We(e.goal.text)}`,c=o.join(a,`${s}.json`),h=o.join(a,`${s}.md`),E=w(e,"agent.done"),p=w(e,"agent.error"),g=w(e,"patch.applied"),m=w(e,"patch.rolledBack");await r.writeFile(c,`${JSON.stringify(te(e,t),null,2)}
|
|
15
|
+
`,"utf8"),await r.writeFile(h,[`# Brass Agent Run ${ee(e.goal.id)}`,"",`- Goal: ${ee(e.goal.text)}`,`- Workspace: ${ee(e.goal.cwd)}`,`- Mode: ${e.goal.mode}`,`- Phase: ${e.phase}`,`- Steps: ${e.steps}`,g?`- Changed files: ${g.changedFiles.join(", ")||"none reported"}`:void 0,m?`- Rolled back files: ${m.changedFiles.join(", ")||"none reported"}`:void 0,"","## Summary","",E?.summary?.trim()||(p?`Error: ${p.error._tag}`:"No summary recorded.")].filter(Boolean).join(`
|
|
16
|
+
`),"utf8"),process.stderr?.isTTY&&console.error(`saved run artifacts: ${c} ${h}`);},it=e=>w(e,"shell.result"),lt=(e,n)=>{if(w(e,"agent.error"))return 1;let t=it(e);return t&&t.exitCode!==0?1:n.failOnPatchProposed&&w(e,"patch.proposed")&&!w(e,"patch.applied")?2:0},ct=e=>{let n=w(e,"agent.done"),t=w(e,"agent.error"),o=w(e,"patch.proposed"),r=w(e,"patch.applied"),a=w(e,"patch.rolledBack"),l=w(e,"llm.response");console.log(""),console.log(`phase: ${e.phase}`),console.log(`steps: ${e.steps}`),a?console.log(`rolled back files: ${a.changedFiles.join(", ")||"(none reported)"}`):r?console.log(`changed files: ${r.changedFiles.join(", ")||"(none reported)"}`):o&&console.log("patch: proposed only; rerun with --apply to apply it"),n?(console.log(""),console.log("summary:"),console.log(Ke(n.summary.trim()||"Agent completed."))):t?(console.log(""),console.log("error:"),console.log(JSON.stringify(t.error,null,2))):l&&(console.log(""),console.log("llm response:"),console.log(Ke(l.content.trim())));},pt=(e,n)=>e.output==="human"?ot(e.resolvedConfigPath):e.output==="events-json"?rt(n):e.output==="protocol-json"?st(n):void 0,dt=(e,n)=>{let t=chunk6OUI6UGZ_cjs.ma;return {shell:t,fs:chunk6OUI6UGZ_cjs.na(t),patch:chunk6OUI6UGZ_cjs.oa(t),llm:zn(e.config.llm),permissions:chunk6OUI6UGZ_cjs.ia(e.config.permissions),approvals:nt(e),...n?{events:n}:{},...e.config.tools?{toolPolicies:e.config.tools}:{}}},ut=e=>({index:0,cwd:e.cwd,goalText:e.goalText,mode:e.mode,patchFileMode:e.patchFileMode,...e.patchFile?{patchFile:e.patchFile}:{},...e.saveRunDir?{saveRunDir:e.saveRunDir}:{}}),gt=async(e,n,t,o)=>{let r=dt(e,o),a=new chunkK47BP5A2_cjs.$({env:r}),l=n.patchFile?await Nn(n.cwd,n.patchFile):void 0,s=await a.toPromise(chunk6OUI6UGZ_cjs.ba(a,{id:`agent-${Date.now()}-${n.index+1}`,cwd:n.cwd,text:n.goalText,mode:n.mode,...e.config.project?{project:e.config.project}:{},...e.config.context?{context:e.config.context}:{},...e.config.patchQuality?{patchQuality:e.config.patchQuality}:{},...e.config.rollback?{rollback:e.config.rollback}:{},...e.config.redaction?{redaction:e.config.redaction}:{},...e.language?{language:{response:e.language}}:e.config.language?{language:e.config.language}:{},...l?{initialPatch:l,initialPatchMode:n.patchFileMode}:{}}));return n.saveRunDir&&await at(s,n.saveRunDir,t),{run:n,state:s,exitCode:lt(s,{failOnPatchProposed:e.failOnPatchProposed})}},ft=e=>e.some(n=>n.exitCode===1)?1:e.some(n=>n.exitCode===2)?2:0,Qe=(e,n)=>({total:e.length,completed:n.length,failed:n.filter(t=>t.exitCode!==0).length,exitCode:ft(n),stoppedEarly:n.length<e.length}),mt=(e,n)=>{let t=Qe(e,n);console.log(""),console.log("batch summary:"),console.log(`completed: ${t.completed}/${t.total}`),console.log(`failed: ${t.failed}`),t.stoppedEarly&&console.log("stopped early: yes"),console.log(`exit code: ${t.exitCode}`);},yt=e=>{let n={cwd:e.cwd,inputCwd:e.workspaceDiscovery.inputCwd,changed:e.workspaceDiscovery.changed,disabled:!!e.workspaceDiscovery.disabled,marker:e.workspaceDiscovery.marker,markerPath:e.workspaceDiscovery.markerPath,configPath:e.resolvedConfigPath,envFiles:e.envFileLoad.paths};if(e.output==="json"||e.output==="protocol-json"){console.log(JSON.stringify(n,null,2));return}console.log("brass-agent workspace"),console.log(`input: ${n.inputCwd}`),console.log(`workspace: ${n.cwd}`),n.disabled?console.log("discovery: disabled"):n.marker?console.log(`marker: ${n.marker} (${n.markerPath})`):console.log("marker: none found; using input cwd"),n.configPath&&console.log(`config: ${n.configPath}`),n.envFiles.length>0&&console.log(`env: ${n.envFiles.join(", ")}`);},ht=async()=>{let e=await Hn(Kn(process.argv.slice(2))),n=e.batchRuns.length>0;if(e.showHelp&&(Ge(),process.exit(0)),e.where){yt(e);return}if(e.init){let s=await je({cwd:e.cwd,force:e.initForce,dryRun:e.initDryRun,profile:e.initProfile});e.output==="json"?console.log(JSON.stringify(s,null,2)):Be(s);return}if(e.doctor){let s=await _e({cwd:e.cwd,config:e.config,configPath:e.resolvedConfigPath,envFileLoad:e.envFileLoad,workspaceDiscovery:e.workspaceDiscovery});e.output==="json"?console.log(JSON.stringify(s,null,2)):xe(s),process.exitCode=s.status==="fail"?1:0;return}!e.goalText&&!n&&(Ge(),process.exit(1));let t={fullPatches:e.protocolFullPatches},o=pt(e,t),r=n?e.batchRuns:[ut(e)],a=[];for(let s of r){let c=await gt(e,s,t,o);if(a.push(c),e.output==="protocol-json"?console.log(JSON.stringify(oe({type:"final-state",state:te(c.state,t)}))):e.output==="human"&&ct(c.state),n&&e.batchStopOnFailureResolved&&c.exitCode!==0)break}if(n){let s=Qe(r,a);e.output==="json"?console.log(JSON.stringify({type:"batch",summary:s,results:a.map(c=>({index:c.run.index,goal:c.run.goalText,cwd:c.run.cwd,mode:c.run.mode,exitCode:c.exitCode,state:te(c.state,t)}))},null,2)):e.output==="protocol-json"?console.log(JSON.stringify(oe({type:"batch-summary",summary:s}))):e.output==="human"&&mt(r,a),e.ci&&(process.exitCode=s.exitCode);return}let l=a[0];if(!l)throw new Error("Agent run did not produce a result.");e.output==="json"&&console.log(JSON.stringify(l.state,null,2)),e.ci&&(process.exitCode=l.exitCode);};ht().catch(e=>{console.error(e),process.exit(1);});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {ta,pa,ha,ga,ba,T as T$1,V,U,ma,ia,oa,na,la,ka,sa,ra,qa}from'../../chunk-W6TVAMYC.js';import {$ as $$1,j as j$1,u,t}from'../../chunk-LTHJNW5A.js';import {existsSync,readFileSync,readdirSync}from'fs';import {access,constants,mkdir,writeFile}from'fs/promises';import {resolve,join,isAbsolute,dirname}from'path';import {spawnSync}from'child_process';var Xe=new Function("specifier","return import(specifier)"),Ze=e=>["y","yes","approve","approved","si","s\xED","s"].includes(e),en=e=>["n","no","reject","rejected","deny","denied"].includes(e),nn=(e,n)=>{let t=e.trim().toLowerCase();return t?Ze(t)?{type:"approved"}:en(t)?{type:"rejected",reason:"Rejected by user."}:{type:"rejected",reason:`Unrecognized approval answer: ${e}`}:n.defaultAnswer==="approve"?{type:"approved"}:{type:"rejected",reason:"Rejected by default answer."}},Pe=(e={})=>({request:n=>j$1((t$1,o)=>{let r=false,a;return Xe("node:readline/promises").then(({createInterface:l})=>{if(r)return;let s=e.input??process.stdin,c=e.output??process.stderr??process.stdout,h=n.defaultAnswer==="approve"?"Y/n":"y/N";return a=l({input:s,output:c}),c?.write?.(`
|
|
3
|
+
Approval required (${n.risk})
|
|
4
|
+
`),c?.write?.(`Action: ${T$1(n.action)}
|
|
5
|
+
`),c?.write?.(`Reason: ${n.reason}
|
|
6
|
+
`),a.question(`Approve? [${h}] `)}).then(l=>{l===void 0||r||(r=true,a?.close?.(),o(u.succeed(nn(l,n))));}).catch(l=>{r||(r=true,a?.close?.(),o(u.failCause(t.fail({_tag:"AgentLoopError",message:`Approval prompt failed: ${String(l)}`}))));}),()=>{r||(r=true,a?.close?.());}})});var _=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),N=e=>{try{return JSON.parse(readFileSync(e,"utf8"))}catch{return}},Le=(e,n)=>{let t=resolve(e);for(;;){let o=join(t,"package.json"),r=N(o);if(_(r)&&r.name===n)return t;let a=resolve(t,"..");if(a===t)return;t=a;}},M=(e,n=["--version"],t)=>{let o=spawnSync(e,[...n],{cwd:t,encoding:"utf8",shell:false,timeout:1e4});if(!(o.error||(o.status??0)!==0))return `${o.stdout??""}${o.stderr??""}`.trim().split(/\r?\n/g)[0]},L=e=>!!(e&&process.env[e]),ln=()=>process.env.BRASS_AGENT_VSCODE_EXTENSION==="1",cn=e=>{let n=N(join(e,"package.json")),t=_(n)&&typeof n.packageManager=="string"?n.packageManager.split("@")[0]:void 0;return t?{manager:t,source:"package.json packageManager"}:existsSync(join(e,"pnpm-lock.yaml"))?{manager:"pnpm",source:"pnpm-lock.yaml"}:existsSync(join(e,"yarn.lock"))?{manager:"yarn",source:"yarn.lock"}:existsSync(join(e,"bun.lockb"))||existsSync(join(e,"bun.lock"))?{manager:"bun",source:"bun lockfile"}:existsSync(join(e,"package-lock.json"))||existsSync(join(e,"npm-shrinkwrap.json"))?{manager:"npm",source:"npm lockfile"}:{manager:"npm",source:"fallback"}},pn=e=>_(e)&&_(e.scripts)?Object.entries(e.scripts).filter(([,n])=>typeof n=="string").map(([n])=>n).sort():[],dn=(e,n)=>e.find(t=>n.some(o=>o.test(t))),un=(e,n)=>{let t=pn(n),o=["Cargo.toml","Cargo.lock","src-tauri/tauri.conf.json","apps/desktop/package.json","apps/desktop/src-tauri/tauri.conf.json","bridges/whatsmeow-bridge/Cargo.toml","apps","packages","bridges","turbo.json","nx.json","pnpm-workspace.yaml"].filter(s=>existsSync(join(e,s))),r=[];_(n)&&r.push("node"),o.some(s=>s==="Cargo.toml"||s==="Cargo.lock"||s.endsWith("Cargo.toml"))&&r.push("rust"),(o.some(s=>s.includes("tauri"))||t.some(s=>s.includes("tauri")))&&r.push("tauri"),(o.some(s=>s.startsWith("apps"))||t.some(s=>s.includes("desktop")))&&r.push("desktop"),(o.some(s=>s.startsWith("bridges"))||t.some(s=>s.includes("bridge")))&&r.push("bridge"),o.some(s=>["apps","packages","bridges","turbo.json","nx.json","pnpm-workspace.yaml"].includes(s))&&r.push("monorepo");let a=dn(t,[/^repo:check$/,/^check$/,/(^|:)check($|:)/,/(^|:)doctor($|:)/,/(^|:)health($|:)/,/(^|:)verify($|:)/,/(^|:)validate($|:)/,/(^|:)ci($|:)/]),l=[`stacks: ${r.length>0?[...new Set(r)].join(", "):"none detected"}`,`markers: ${o.length>0?o.slice(0,8).join(", "):"none"}`,a?`likely validation: npm run ${a}`:"likely validation: none detected"];return {id:"workspace.projectProfile",label:"Workspace project profile",status:r.length>0?"ok":"skip",message:l.join(". ")}},gn=e=>{if(!e)return {id:"envFile",label:"Agent env file",status:"skip",message:"Env file loading did not run."};if(e.disabled)return {id:"envFile",label:"Agent env file",status:"skip",message:"Env file loading disabled by --no-env-file."};if(e.errors.length>0)return {id:"envFile",label:"Agent env file",status:"fail",message:e.errors.join("; ")};if(e.paths.length===0)return {id:"envFile",label:"Agent env file",status:"skip",message:"No .brass-agent.env, .env.local, or .env found in workspace; using exported shell environment only."};let n=[`Loaded ${e.paths.join(", ")}`];return e.loadedKeys.length>0&&n.push(`keys: ${e.loadedKeys.join(", ")}`),e.alreadySetKeys.length>0&&n.push(`already set by shell: ${e.alreadySetKeys.join(", ")}`),e.emptyKeys.length>0&&n.push(`empty keys skipped: ${e.emptyKeys.join(", ")}`),e.ignoredKeys.length>0&&n.push(`non-agent keys ignored: ${e.ignoredKeys.slice(0,8).join(", ")}${e.ignoredKeys.length>8?", ...":""}`),e.invalidLines.length>0&&n.push(`invalid lines: ${e.invalidLines.join(", ")}`),{id:"envFile",label:"Agent env file",status:e.loadedKeys.length>0||e.alreadySetKeys.length>0?"ok":"warn",message:n.join(". ")}},fn=e=>{let n=(process.env.BRASS_LLM_PROVIDER??e?.llm?.provider)?.trim().toLowerCase(),t=e?.llm?.apiKeyEnv;if(n==="fake")return {id:"llm",label:"LLM provider",status:"ok",message:"Fake LLM provider is selected."};if(n==="google"||n==="gemini"){let o=L(t)||L("BRASS_GOOGLE_API_KEY")||L("GOOGLE_API_KEY")||L("GEMINI_API_KEY");return {id:"llm",label:"LLM provider",status:o?"ok":"fail",message:o?`Google/Gemini provider is configured (${n}).`:"Google/Gemini provider is selected but no API key env var is set. Export GEMINI_API_KEY or put it in .env/.brass-agent.env."}}if(n==="openai"||n==="openai-compatible"){let o=!!(process.env.BRASS_LLM_ENDPOINT??e?.llm?.endpoint),r=L(t)||L("BRASS_LLM_API_KEY");return {id:"llm",label:"LLM provider",status:o&&r?"ok":"fail",message:o&&r?`OpenAI-compatible provider is configured (${n}).`:"OpenAI-compatible provider is selected but endpoint or API key env var is missing. Export BRASS_LLM_API_KEY or put it in .env/.brass-agent.env."}}return L("BRASS_GOOGLE_API_KEY")||L("GOOGLE_API_KEY")||L("GEMINI_API_KEY")?{id:"llm",label:"LLM provider",status:"ok",message:"Google/Gemini credentials are available and will be auto-detected."}:(process.env.BRASS_LLM_ENDPOINT??e?.llm?.endpoint)&&L("BRASS_LLM_API_KEY")?{id:"llm",label:"LLM provider",status:"ok",message:"OpenAI-compatible credentials are available and will be auto-detected."}:{id:"llm",label:"LLM provider",status:"warn",message:"No real LLM credentials found. The CLI will fall back to the fake provider unless config selects a real provider."}},mn=e=>{let n=N(join(e,".vscode","settings.json"));if(!_(n))return;let t=n["brassAgent.command"];return typeof t=="string"?t:void 0},yn=e=>{try{return readdirSync(e).filter(n=>n.endsWith(".vsix")).sort().at(-1)}catch{return}},y=(e,n)=>{e.push(n);},_e=async e=>{let n=resolve(e.cwd),t=[],o=Le(n,"brass-runtime")??Le(process.cwd(),"brass-runtime"),r=Number(process.versions.node.split(".")[0]??"0");y(t,{id:"node",label:"Node.js",status:r>=18?"ok":"fail",message:`Node ${process.versions.node}${r>=18?"":" is too old; use Node 18 or newer."}`});let a=M("npm");y(t,{id:"npm",label:"npm",status:a?"ok":"fail",message:a?`npm ${a}`:"npm is not available on PATH."});let l=M("git");y(t,{id:"git",label:"git",status:l?"ok":"warn",message:l||"git is not available; patch apply/rollback uses git apply."});let s=M("rg");if(y(t,{id:"ripgrep",label:"ripgrep",status:s?"ok":"warn",message:s||"rg is not available; context discovery search will be limited."}),y(t,{id:"workspace",label:"Workspace",status:existsSync(n)?"ok":"fail",message:existsSync(n)?n:`Workspace does not exist: ${n}`}),e.workspaceDiscovery){let p=e.workspaceDiscovery;y(t,{id:"workspace.discovery",label:"Workspace discovery",status:p.disabled?"skip":p.marker?"ok":"warn",message:p.disabled?"Workspace discovery disabled by --no-discover-workspace.":p.marker?`${p.changed?`Resolved ${p.inputCwd} -> ${p.cwd}`:`Using ${p.cwd}`} via ${p.marker}.`:`No workspace marker found upward from ${p.inputCwd}; using input cwd.`});}let c=join(n,"package.json"),h=N(c);if(y(t,{id:"workspace.packageJson",label:"Workspace package.json",status:_(h)?"ok":"warn",message:_(h)?`Found ${c}`:"No package.json found in workspace; project command discovery may use fallbacks."}),_(h)){let p=_(h.scripts)?Object.keys(h.scripts):[];y(t,{id:"workspace.scripts",label:"Workspace scripts",status:p.length?"ok":"warn",message:p.length?`Scripts: ${p.slice(0,12).join(", ")}${p.length>12?", ...":""}`:"No package scripts found."});let g=cn(n),m=M(g.manager);y(t,{id:"workspace.packageManager",label:"Workspace package manager",status:m?"ok":"warn",message:m?`${g.manager} available (${g.source}): ${m}`:`${g.manager} inferred from ${g.source}, but command is not available on PATH.`});}if(y(t,un(n,h)),y(t,gn(e.envFileLoad)),y(t,fn(e.config)),y(t,{id:"config",label:"Agent config",status:e.configPath?"ok":"skip",message:e.configPath?`Loaded ${e.configPath}`:"No .brass-agent.json / brass-agent.config.json loaded; using built-in defaults and VS Code/CLI settings."}),e.includeVsCode!==false){let p=M(process.env.BRASS_CODE_CMD??"code");y(t,{id:"vscode.code",label:"VS Code CLI",status:p?"ok":"warn",message:p?`code ${p}`:"VS Code CLI `code` is not available on PATH; .vsix install needs it unless using the VS Code UI."});let g=mn(n),m=ln();y(t,{id:"vscode.settings",label:"VS Code extension setting",status:g?"ok":m?"skip":"warn",message:g?`brassAgent.command = ${g}`:m?`No workspace brassAgent.command needed; launched by the VS Code extension (${process.env.BRASS_AGENT_VSCODE_CLI_SOURCE??"auto"}).`:"No workspace .vscode/settings.json brassAgent.command found."});}if(o){let p=join(o,"src","agent","cli","main.ts"),g=join(o,"dist","agent","cli","main.cjs"),m=join(o,"extensions","vscode-brass-agent");y(t,{id:"repo.root",label:"brass-runtime repo",status:"ok",message:o}),y(t,{id:"repo.cliSource",label:"CLI source",status:existsSync(p)?"ok":"fail",message:existsSync(p)?`Found ${p}`:`Missing ${p}`}),y(t,{id:"repo.cliBuild",label:"CLI build",status:existsSync(g)?"ok":"warn",message:existsSync(g)?`Found ${g}`:"dist/agent/cli/main.cjs is missing; run npm run build."}),y(t,{id:"repo.extensionDir",label:"VS Code extension source",status:existsSync(join(m,"package.json"))?"ok":"warn",message:existsSync(join(m,"package.json"))?m:"extensions/vscode-brass-agent was not found."}),y(t,{id:"repo.extensionBuild",label:"VS Code extension build",status:existsSync(join(m,"out","extension.js"))?"ok":"warn",message:existsSync(join(m,"out","extension.js"))?"out/extension.js exists.":"Extension output missing; run npm run agent:vscode:package or compile the extension."});let S=yn(m);y(t,{id:"repo.extensionVsix",label:"VSIX package",status:S?"ok":"warn",message:S?`Found ${S}`:"No .vsix found in extensions/vscode-brass-agent."});let R=await access(g,constants.R_OK).then(()=>true).catch(()=>false);y(t,{id:"repo.cliReadable",label:"CLI artifact readable",status:R?"ok":"warn",message:R?"Built CLI artifact is readable.":"Built CLI artifact is not readable yet."});}else y(t,{id:"repo.root",label:"brass-runtime repo",status:"skip",message:"Not running inside a brass-runtime checkout; local source/build checks skipped."});let E=t.some(p=>p.status==="fail")?"fail":t.some(p=>p.status==="warn")?"warn":"ok";return {generatedAt:new Date().toISOString(),cwd:n,...e.configPath?{configPath:e.configPath}:{},...o?{repoRoot:o}:{},status:E,checks:t}},hn=e=>{switch(e){case "ok":return "\u2713";case "warn":return "!";case "fail":return "\u2717";case "skip":return "-"}},xe=e=>{console.log("brass-agent doctor"),console.log(`workspace: ${e.cwd}`),e.configPath&&console.log(`config: ${e.configPath}`),e.repoRoot&&console.log(`repo: ${e.repoRoot}`),console.log(`status: ${e.status}`),console.log("");for(let n of e.checks)console.log(`${hn(n.status)} ${n.label}: ${n.message}`);};var kn=[".brass-agent.env",".env.local",".env"],wn=new Set(["BRASS_LLM_PROVIDER","BRASS_FAKE_LLM_RESPONSE","BRASS_GOOGLE_API_KEY","GOOGLE_API_KEY","GEMINI_API_KEY","BRASS_GOOGLE_MODEL","BRASS_GOOGLE_API_VERSION","BRASS_GOOGLE_BASE_URL","BRASS_GOOGLE_ENDPOINT","BRASS_GOOGLE_SYSTEM_INSTRUCTION","BRASS_GOOGLE_TEMPERATURE","BRASS_GOOGLE_TOP_P","BRASS_GOOGLE_TOP_K","BRASS_GOOGLE_MAX_OUTPUT_TOKENS","BRASS_LLM_ENDPOINT","BRASS_LLM_API_KEY","BRASS_LLM_MODEL","BRASS_AGENT_APPROVAL","BRASS_AGENT_AUTO_APPROVE","BRASS_CODE_CMD"]),C=e=>Array.from(new Set(e)),Sn=e=>{if(e.length<2)return;let n=e[0];if(n!=='"'&&n!=="'"||e[e.length-1]!==n)return;let t=e.slice(1,-1);return n==="'"?t:t.replace(/\\n/g,`
|
|
7
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")},En=e=>{let n=e.trim();if(!n||n.startsWith("#"))return {type:"skip"};let t=n.startsWith("export ")?n.slice(7).trimStart():n,o=/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(t);if(!o)return {type:"invalid"};let r=o[1]??"",l=(o[2]??"").trim(),c=Sn(l)??l.replace(/\s+#.*$/u,"").trim();return {type:"assignment",key:r,value:c}},Rn=(e,n)=>n?[isAbsolute(n)?n:resolve(e,n)]:kn.map(t=>join(e,t)),Ce=e=>{let n=resolve(e.cwd),t=e.allowedExtraKeys?.filter(Boolean)??[],o=new Set([...wn,...t]),r=e.noEnvFile?[]:Rn(n,e.envFile),a=[],l=[],s=[],c=[],h=[],E=[],p=[];if(e.noEnvFile)return {cwd:n,disabled:true,filesChecked:[],paths:[],loadedKeys:[],alreadySetKeys:[],emptyKeys:[],ignoredKeys:[],invalidLines:[],errors:[]};for(let g of r){if(!existsSync(g)){e.envFile&&p.push(`Env file does not exist: ${g}`);continue}a.push(g);let m;try{m=readFileSync(g,"utf8").replace(/^\uFEFF/u,"");}catch(S){p.push(`Could not read env file ${g}: ${S instanceof Error?S.message:String(S)}`);continue}for(let[S,R]of m.split(/\r?\n/gu).entries()){let b=En(R);if(b.type!=="skip"){if(b.type==="invalid"){E.push(`${g}:${S+1}`);continue}if(!o.has(b.key)){h.push(b.key);continue}if(!b.value){c.push(b.key);continue}if(process.env[b.key]!==void 0){s.push(b.key);continue}process.env[b.key]=b.value,l.push(b.key);}}}return {cwd:n,disabled:false,...e.envFile?{explicitPath:isAbsolute(e.envFile)?e.envFile:resolve(n,e.envFile)}:{},filesChecked:r,paths:a,loadedKeys:C(l),alreadySetKeys:C(s),emptyKeys:C(c),ignoredKeys:C(h),invalidLines:C(E),errors:C(p)}};var Fe=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),On=e=>{try{return JSON.parse(readFileSync(e,"utf8"))}catch{return}},Me=e=>{let n=On(join(e,"package.json"));return Fe(n)?n:void 0},$e=e=>{let n=e?.scripts;return Fe(n)?Object.entries(n).filter(([,t])=>typeof t=="string").map(([t])=>t).sort():[]},$=(e,n)=>n.some(t=>e.includes(t)),Cn=(e,n)=>{let t=typeof n?.packageManager=="string"?n.packageManager.split("@")[0]:void 0;return t==="npm"||t==="pnpm"||t==="yarn"||t==="bun"?t:existsSync(join(e,"pnpm-lock.yaml"))?"pnpm":existsSync(join(e,"yarn.lock"))?"yarn":existsSync(join(e,"bun.lockb"))||existsSync(join(e,"bun.lock"))?"bun":existsSync(join(e,"package-lock.json"))||existsSync(join(e,"npm-shrinkwrap.json"))?"npm":"auto"},In=e=>{switch(e){case "default":return;case "google":return {provider:"google",model:"gemini-2.5-flash",apiKeyEnv:"GEMINI_API_KEY",temperature:.2,maxOutputTokens:4096};case "openai-compatible":return {provider:"openai-compatible",endpoint:"https://api.openai.com/v1/chat/completions",model:"gpt-4.1",apiKeyEnv:"BRASS_LLM_API_KEY",temperature:.2};case "fake":return {provider:"fake",fakeResponse:"Fake plan from brass-agent init. Configure a real provider when ready."}}},Fn=(e,n)=>{let t=Me(e),o=$e(t),r=$(o,["typecheck","type-check","check-types","tsc","check"]),a=$(o,["lint","lint:ci"]),l=In(n);return {mode:"propose",approval:"auto",...l?{llm:l}:{},project:{packageManager:Cn(e,t),testScriptNames:["test","test:ci","test:unit"],includeTypecheck:r,includeLint:a,maxValidationCommands:2},context:{enabled:true,maxSearchQueries:3,maxFiles:4,maxSearchResults:40,globs:["*.ts","*.tsx","*.js","*.jsx","*.mjs","*.cjs","*.json","*.md","*.yml","*.yaml"],excludeGlobs:[".env*","**/.env*","**/node_modules/**","**/dist/**","**/build/**","**/.git/**","**/*.pem","**/*.key","**/secrets/**"]},patchQuality:{enabled:true,maxRepairAttempts:1},rollback:{enabled:true,onFinalValidationFailure:true,strategy:"all",maxRollbackDepth:8,runValidationAfterRollback:true,allowForSuppliedPatches:false},redaction:{enabled:true,additionalPatterns:[]},language:{response:"auto"},permissions:{shell:{inheritDefaults:true,ask:[{pattern:"npm run build",reason:"Build commands can be slow and may produce large outputs.",risk:"medium",defaultAnswer:"approve"},{pattern:"pnpm run build",reason:"Build commands can be slow and may produce large outputs.",risk:"medium",defaultAnswer:"approve"},{pattern:"yarn run build",reason:"Build commands can be slow and may produce large outputs.",risk:"medium",defaultAnswer:"approve"},{pattern:"bun run build",reason:"Build commands can be slow and may produce large outputs.",risk:"medium",defaultAnswer:"approve"}],deny:["rm *","git push *","git reset *","git clean *"]},patchApply:{decision:"ask",reason:"Apply the generated unified diff to the workspace.",risk:"high",defaultAnswer:"reject"}},tools:{"fs.readFile":{timeoutMs:1e4,retries:1},"fs.exists":{timeoutMs:5e3,retries:0},"fs.searchText":{timeoutMs:1e4,retries:1},"shell.exec":{timeoutMs:18e4,retries:0},"llm.complete":{timeoutMs:9e4,retries:2},"patch.apply":{timeoutMs:3e4,retries:0},"patch.rollback":{timeoutMs:3e4,retries:0}}}},Mn=e=>{let n=$e(Me(e)),t=[{preset:"inspect",mode:"read-only"}];return $(n,["typecheck","type-check","check-types","tsc","check"])&&t.push({preset:"typecheck",mode:"propose"}),$(n,["lint","lint:ci"])&&t.push({preset:"lint",mode:"propose"}),$(n,["test","test:ci","test:unit"])&&t.push({preset:"fix-tests",mode:"propose"}),t},$n=e=>`${JSON.stringify({stopOnFailure:false,goals:Mn(e)},null,2)}
|
|
8
|
+
`,jn=e=>{let n=["# Brass Agent environment variables","# Copy this file to .env or .brass-agent.env, or export the variables in your shell.","# brass-agent auto-loads supported agent env keys from --cwd.","# Do not commit real API keys.",""];return e==="fake"&&n.push("BRASS_LLM_PROVIDER=fake","BRASS_FAKE_LLM_RESPONSE=Fake plan from .env.example",""),n.push("# Google / Gemini","# Used when .brass-agent.json selects provider google, or when auto-detected.","GEMINI_API_KEY=","BRASS_GOOGLE_MODEL=gemini-2.5-flash","","# OpenAI-compatible providers","# BRASS_LLM_PROVIDER=openai-compatible","BRASS_LLM_ENDPOINT=https://api.openai.com/v1/chat/completions","BRASS_LLM_API_KEY=","BRASS_LLM_MODEL=gpt-4.1","","# Approval behavior: auto | interactive | approve | deny","# BRASS_AGENT_APPROVAL=auto",""),e==="google"?n.splice(4,0,"BRASS_LLM_PROVIDER=google",""):e==="openai-compatible"&&n.splice(4,0,"BRASS_LLM_PROVIDER=openai-compatible",""),`${n.join(`
|
|
9
|
+
`)}
|
|
10
|
+
`},Bn=()=>["# Brass Agent","","This workspace was initialized with `brass-agent --init`.","","Generated files:","","- `.brass-agent.json` \u2014 local policy/config for Brass Agent.","- `brass-agent.batch.json` \u2014 sample multi-goal batch workflow.","- `.env.example` \u2014 example environment variables. Copy to `.env` or `.brass-agent.env`; keep real secrets out of git.","","Recommended first commands:","","```bash","brass-agent --doctor","brass-agent --preset inspect","brass-agent --batch-file brass-agent.batch.json","```","","Apply mode is intentionally approval-gated:","","```bash",'brass-agent --apply "fix the failing tests"',"```",""].join(`
|
|
11
|
+
`),Dn=e=>{let n=["Review .brass-agent.json and adjust permissions/context budgets for this repo.","Run: brass-agent --doctor","Run: brass-agent --preset inspect","Run: brass-agent --batch-file brass-agent.batch.json"];return e==="google"?["Set GEMINI_API_KEY in your shell, `.env`, or `.brass-agent.env`.",...n]:e==="openai-compatible"?["Set BRASS_LLM_API_KEY and BRASS_LLM_ENDPOINT in your shell, `.env`, or `.brass-agent.env`.",...n]:e==="default"?["Set an LLM provider env var in your shell, `.env`, or `.brass-agent.env` when ready, or let the CLI fall back to fake mode.",...n]:n},T=async e=>{let n=resolve(e.cwd,e.relativePath),o=existsSync(n)?e.force?"overwritten":"skipped":"created";return !e.dryRun&&o!=="skipped"&&(await mkdir(dirname(n),{recursive:true}),await writeFile(n,e.content,"utf8")),{path:n,relativePath:e.relativePath,status:o,bytes:Buffer.byteLength(e.content,"utf8")}},je=async e=>{let n=resolve(e.cwd),t=e.profile??"default",o=e.force??false,r=e.dryRun??false,a=Fn(n,t),l=await Promise.all([T({cwd:n,relativePath:".brass-agent.json",content:`${JSON.stringify(a,null,2)}
|
|
12
|
+
`,force:o,dryRun:r}),T({cwd:n,relativePath:"brass-agent.batch.json",content:$n(n),force:o,dryRun:r}),T({cwd:n,relativePath:".env.example",content:jn(t),force:o,dryRun:r}),T({cwd:n,relativePath:"BRASS_AGENT.md",content:Bn(),force:o,dryRun:r})]);return {cwd:n,profile:t,dryRun:r,files:l,nextSteps:Dn(t)}},Gn=e=>{switch(e){case "created":return "\u2713";case "overwritten":return "!";case "skipped":return "-"}},Be=e=>{console.log(`brass-agent init${e.dryRun?" (dry run)":""}`),console.log(`workspace: ${e.cwd}`),console.log(`profile: ${e.profile}`),console.log("");for(let n of e.files)console.log(`${Gn(n.status)} ${n.status} ${n.relativePath}`);console.log(""),console.log("next steps:");for(let n of e.nextSteps)console.log(`- ${n}`);};var F=new Function("specifier","return import(specifier)"),Nn=async(e,n)=>{let t=await F("node:path");return (await F("node:fs/promises")).readFile(t.resolve(e,n),"utf8")},K=e=>{if(e===void 0||e.trim()==="")return;let n=Number(e);return Number.isFinite(n)?n:void 0},Ye=e=>e==="read-only"||e==="propose"||e==="write"||e==="autonomous",Je=e=>e==="auto"||e==="interactive"||e==="approve"||e==="deny",De=e=>e==="default"||e==="google"||e==="openai-compatible"||e==="fake",Tn=e=>["auto","match-user","en","es","pt","fr","de","it","custom"].includes(e),k=(e,n,t)=>{let o=e[n],r=o.startsWith(`${t}=`)?o.slice(t.length+1):void 0;if(r!==void 0&&r!=="")return [r,n];let a=e[n+1];if(!a)throw new Error(`${t} requires a value`);return [a,n+1]},Kn=e=>{let n=process.cwd(),t=true,o=false,r="propose",a=false,l=false,s="human",c="auto",h=false,E,p=false,g,m=false,S=false,R,b="apply",V,re=false,se=false,W,Y,D,ae=false,ie=false,le=false,G="default",ce=false,J,U=[];for(let u=0;u<e.length;u+=1){let i=e[u];if(i==="--"){U.push(...e.slice(u+1));break}if(i==="--help"||i==="-h"){l=true;continue}if(i==="--doctor"){ae=true;continue}if(i==="--where"||i==="--print-workspace"){o=true;continue}if(i==="--no-discover-workspace"){t=false;continue}if(i==="--init"){ie=true;continue}if(i==="--force"||i==="--init-force"){le=true;continue}if(i==="--init-dry-run"){ce=true;continue}if(i==="--init-profile"||i.startsWith("--init-profile=")){let[d,f]=k(e,u,"--init-profile");if(!De(d))throw new Error("--init-profile requires one of: default, google, openai-compatible, fake");G=d,u=f;continue}if(i==="--init-provider"||i.startsWith("--init-provider=")){let[d,f]=k(e,u,"--init-provider");if(d==="auto")G="default";else if(De(d))G=d;else throw new Error("--init-provider requires one of: auto, fake, google, openai-compatible");u=f;continue}if(i==="--language"||i.startsWith("--language=")){let[d,f]=k(e,u,"--language");if(!Tn(d))throw new Error("--language requires one of: auto, match-user, en, es, pt, fr, de, it, custom");J=d,u=f;continue}if(i==="--json"){s="json";continue}if(i==="--events-json"){s="events-json";continue}if(i==="--protocol-json"){s="protocol-json";continue}if(i==="--protocol-full-patches"){S=true;continue}if(i==="--preset"||i.startsWith("--preset=")){let[d,f]=k(e,u,"--preset");if(!ga(d))throw new Error("--preset requires one of: fix-tests, inspect, typecheck, lint");W=d,u=f;continue}if(i==="--batch-file"||i.startsWith("--batch-file=")){let[d,f]=k(e,u,"--batch-file");Y=d,u=f;continue}if(i==="--batch-stop-on-failure"){D=true;continue}if(i==="--batch-continue-on-failure"){D=false;continue}if(i==="--ci"){re=true;continue}if(i==="--fail-on-patch-proposed"){se=true;continue}if(i==="--yes"||i==="-y"){c="approve",h=true;continue}if(i==="--no-input"){c="deny",h=true;continue}if(i==="--approval"||i.startsWith("--approval=")){let[d,f]=k(e,u,"--approval");if(!Je(d))throw new Error("--approval requires one of: auto, interactive, approve, deny");c=d,h=true,u=f;continue}if(i==="--apply"){r="write",a=true;continue}if(i==="--patch-file"||i.startsWith("--patch-file=")){let[d,f]=k(e,u,"--patch-file");R=d,u=f;continue}if(i==="--apply-patch-file"||i.startsWith("--apply-patch-file=")){let[d,f]=k(e,u,"--apply-patch-file");R=d,b="apply",r="write",a=true,u=f;continue}if(i==="--rollback-patch-file"||i.startsWith("--rollback-patch-file=")){let[d,f]=k(e,u,"--rollback-patch-file");R=d,b="rollback",r="write",a=true,u=f;continue}if(i==="--mode"||i.startsWith("--mode=")){let[d,f]=k(e,u,"--mode");if(!Ye(d))throw new Error("--mode requires one of: read-only, propose, write, autonomous");r=d,a=true,u=f;continue}if(i==="--cwd"||i.startsWith("--cwd=")){let[d,f]=k(e,u,"--cwd");n=d,u=f;continue}if(i==="--save-run"||i.startsWith("--save-run=")){let[d,f]=k(e,u,"--save-run");V=d,u=f;continue}if(i==="--config"||i.startsWith("--config=")){let[d,f]=k(e,u,"--config");E=d,p=false,u=f;continue}if(i==="--no-config"){E=void 0,p=true;continue}if(i==="--env-file"||i.startsWith("--env-file=")){let[d,f]=k(e,u,"--env-file");g=d,m=false,u=f;continue}if(i==="--no-env-file"){g=void 0,m=true;continue}if(i.startsWith("--"))throw new Error(`Unknown option: ${i}`);U.push(i);}return {cwd:n,discoverWorkspace:t,where:o,goalText:U.join(" ").trim(),mode:r,modeSpecified:a,showHelp:l,output:s,approval:c,approvalSpecified:h,...E?{configPath:E}:{},noConfig:p,...g?{envFile:g}:{},noEnvFile:m,protocolFullPatches:S,patchFileMode:b,ci:re,failOnPatchProposed:se,...W?{preset:W}:{},...Y?{batchFile:Y}:{},...D!==void 0?{batchStopOnFailure:D}:{},doctor:ae,init:ie,initForce:le,initProfile:G,initDryRun:ce,...J?{language:J}:{},...V?{saveRunDir:V}:{},...R?{patchFile:R}:{}}},Ue=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),Vn=(e,n)=>{if(typeof e=="string")return e;if(!Ue(e))throw new Error(`${n} must be a string or object.`);let t=e.goal,o=e.preset,r=e.mode,a=e.cwd,l=e.patchFile,s=e.patchFileMode,c=e.saveRunDir;if(t!==void 0&&typeof t!="string")throw new Error(`${n}.goal must be a string.`);if(a!==void 0&&typeof a!="string")throw new Error(`${n}.cwd must be a string.`);if(l!==void 0&&typeof l!="string")throw new Error(`${n}.patchFile must be a string.`);if(c!==void 0&&typeof c!="string")throw new Error(`${n}.saveRunDir must be a string.`);if(o!==void 0&&(typeof o!="string"||!ga(o)))throw new Error(`${n}.preset must be one of: fix-tests, inspect, typecheck, lint.`);if(r!==void 0&&(typeof r!="string"||!Ye(r)))throw new Error(`${n}.mode must be one of: read-only, propose, write, autonomous.`);if(s!==void 0&&s!=="apply"&&s!=="rollback")throw new Error(`${n}.patchFileMode must be apply or rollback.`);if(t===void 0&&o===void 0&&l===void 0)throw new Error(`${n} must include goal, preset, or patchFile.`);return {...t?{goal:t}:{},...o?{preset:o}:{},...r?{mode:r}:{},...a?{cwd:a}:{},...l?{patchFile:l}:{},...s?{patchFileMode:s}:{},...c?{saveRunDir:c}:{}}},Wn=e=>{let n=Array.isArray(e)?e:Ue(e)&&Array.isArray(e.goals)?e.goals:void 0;if(!n)throw new Error("Batch file must be a JSON array or an object with a goals array.");return n.map((t,o)=>Vn(t,`goals[${o}]`))},Yn=async(e,n)=>{let t=await F("node:path"),o=await F("node:fs/promises"),r=t.isAbsolute(n)?n:t.resolve(e,n),a=String(await o.readFile(r,"utf8")).replace(/^\uFEFF/,"");try{return Wn(JSON.parse(a))}catch(l){if(!(l instanceof SyntaxError))throw l;let s=a.split(/\r?\n/g).map(c=>c.trim()).filter(c=>c&&!c.startsWith("#"));if(s.length===0)throw new Error(`Batch file has no goals: ${r}`);return s}},Jn=(e,n)=>typeof e=="string"?e:e.goal?e.goal:e.preset?ha(e.preset):e.patchFile??n?"apply supplied patch":"",Un=(e,n,t)=>typeof e!="string"&&e.mode?e.mode:typeof e!="string"&&e.preset==="inspect"&&!n.modeSpecified?"read-only":n.modeSpecified?n.mode:t.mode??n.mode,qn=(e,n,t)=>e.map((o,r)=>{let a=Jn(o,n.patchFile),l=typeof o=="string"?n.cwd:o.cwd??n.cwd,s=typeof o=="string"?n.patchFile:o.patchFile??n.patchFile,c=typeof o=="string"?n.patchFileMode:o.patchFileMode??n.patchFileMode,h=typeof o=="string"?n.saveRunDir:o.saveRunDir??n.saveRunDir;if(!a)throw new Error(`Batch goal ${r+1} resolved to an empty goal.`);return {index:r,cwd:l,goalText:a,mode:Un(o,n,t),patchFileMode:c,...s?{patchFile:s}:{},...h?{saveRunDir:h}:{}}}),Hn=async e=>{let n=ta(e.cwd,{enabled:e.discoverWorkspace}),t=n.cwd,o={...e,cwd:t},r=await pa({cwd:t,configPath:e.configPath,noConfig:e.noConfig}),a=Ce({cwd:t,envFile:e.envFile,noEnvFile:e.noEnvFile,allowedExtraKeys:r.config.llm?.apiKeyEnv?[r.config.llm.apiKeyEnv]:[]}),l=!e.goalText&&!e.preset&&!e.patchFile,s=e.batchFile?await Yn(t,e.batchFile):l?r.config.batch?.goals??[]:[],c=qn(s,o,r.config);return {...o,goalText:e.goalText||(e.preset?ha(e.preset):e.patchFile?"apply supplied patch":e.goalText),mode:e.modeSpecified?e.mode:e.preset==="inspect"?"read-only":r.config.mode??e.mode,approval:e.approvalSpecified?e.approval:r.config.approval??e.approval,config:r.config,workspaceDiscovery:n,batchRuns:c,batchStopOnFailureResolved:e.batchStopOnFailure??r.config.batch?.stopOnFailure??e.ci,envFileLoad:a,...r.path?{resolvedConfigPath:r.path}:{}}},Ge=()=>{console.log(['Usage: brass-agent [options] "goal"',"","Options:"," --mode read-only|propose|write|autonomous"," Agent permission mode. Default: propose, or config.mode if present."," --preset fix-tests|inspect|typecheck|lint"," Use a built-in goal preset when no explicit goal text is provided."," --apply"," Alias for --mode write."," --cwd PATH"," Starting directory for workspace discovery. Default: current directory."," --no-discover-workspace"," Use --cwd exactly instead of searching upward for package.json, .brass-agent.json, or .git."," --where, --print-workspace"," Print the resolved workspace root and exit."," --config PATH"," Load a specific .brass-agent.json policy/config file."," --save-run DIR"," Write final run JSON and Markdown artifacts to DIR."," --batch-file PATH"," Run multiple goals sequentially from a JSON or line-based file."," --batch-stop-on-failure"," Stop a batch after the first failed run."," --batch-continue-on-failure"," Continue a batch even when a run fails."," --doctor"," Check local CLI, workspace, VS Code, package manager, and LLM setup."," --init"," Initialize this workspace with .brass-agent.json, brass-agent.batch.json, .env.example, and BRASS_AGENT.md."," --force, --init-force"," Overwrite files generated by --init when they already exist."," --init-profile default|google|openai-compatible|fake"," Initialization profile. Default: default, which leaves provider auto-detection enabled."," --init-provider auto|fake|google|openai-compatible"," Alias for choosing an LLM-oriented init profile. auto maps to default."," --init-dry-run"," Preview generated files without writing them."," --no-config"," Do not discover or load an agent config file."," --language auto|match-user|en|es|pt|fr|de|it"," Response language for LLM summaries. Default: config.language or auto-match the user goal."," --env-file PATH"," Load Brass Agent environment variables from a specific env file."," --no-env-file"," Do not auto-load .brass-agent.env, .env.local, or .env from --cwd."," --json"," Print the full final AgentState JSON. Suppresses live event output."," --ci"," Preserve output mode but set process exit codes from the final run status."," --fail-on-patch-proposed"," In --ci mode, exit 2 when a patch was proposed but not applied."," --events-json"," Stream AgentEvent objects as JSON Lines. Does not print the final AgentState."," --protocol-json"," Stream Brass Agent protocol JSON Lines, including events and a final-state message."," --protocol-full-patches"," Keep patch payloads untruncated in protocol/event JSON output for trusted local integrations."," --patch-file PATH"," Supply a precomputed unified diff to the agent. Respects --mode."," --apply-patch-file PATH"," Supply and apply a precomputed unified diff. Alias for --patch-file PATH --mode write."," --rollback-patch-file PATH"," Reverse-apply a precomputed unified diff through PatchService. Requires write mode approvals."," --yes, -y"," Auto-approve approval prompts. Useful for CI and smoke tests."," --no-input"," Do not prompt; reject any action that requires approval."," --approval auto|interactive|approve|deny"," Approval strategy. Default: auto, or config.approval if present."," --help, -h"," Show this help message.","","Config files:"," brass-agent first resolves a workspace root by searching upward from --cwd."," It looks for .brass-agent.json, brass-agent.config.json, package.json, workspace markers, or .git."," brass-agent then searches upward from that workspace root for .brass-agent.json or brass-agent.config.json."," config.batch.goals can define a default batch when --batch-file is not provided.","","Examples:",' brass-agent "fix the failing tests"'," brass-agent --preset fix-tests"," brass-agent --preset inspect"," brass-agent --batch-file ./brass-agent.batch.json --ci"," brass-agent --where"," brass-agent --doctor"," brass-agent --doctor --json"," brass-agent --env-file .env --doctor"," brass-agent --init"," brass-agent --init --init-profile google"," brass-agent --init --init-profile fake --init-dry-run",' brass-agent --config ./agent.policy.json "fix the failing tests"',' brass-agent --no-config "fix the failing tests"',' brass-agent --json "fix the failing tests"',' brass-agent --events-json "fix the failing tests"',' brass-agent --protocol-json "fix the failing tests"',' brass-agent --protocol-json --protocol-full-patches "fix the failing tests"',' brass-agent --apply-patch-file ./approved.diff --yes "apply approved patch"',' brass-agent --apply "fix the failing tests"',' brass-agent --apply --yes "fix the failing tests"',' brass-agent --mode read-only --cwd ./repo "inspect the test failure"',"","LLM providers:"," BRASS_LLM_PROVIDER=fake"," BRASS_LLM_PROVIDER=google GEMINI_API_KEY=..."," BRASS_LLM_PROVIDER=openai-compatible BRASS_LLM_ENDPOINT=... BRASS_LLM_API_KEY=..."].join(`
|
|
13
|
+
`));},qe=e=>e?process.env[e]:void 0,Ne=e=>{let n=qe(e?.apiKeyEnv)??process.env.BRASS_GOOGLE_API_KEY??process.env.GOOGLE_API_KEY??process.env.GEMINI_API_KEY;if(n)return ra({apiKey:n,model:process.env.BRASS_GOOGLE_MODEL??process.env.BRASS_LLM_MODEL??e?.model??"gemini-2.5-flash",apiVersion:process.env.BRASS_GOOGLE_API_VERSION??e?.apiVersion??"v1beta",baseUrl:process.env.BRASS_GOOGLE_BASE_URL??e?.baseUrl,endpoint:process.env.BRASS_GOOGLE_ENDPOINT??e?.endpoint,systemInstruction:process.env.BRASS_GOOGLE_SYSTEM_INSTRUCTION??e?.systemInstruction,temperature:K(process.env.BRASS_GOOGLE_TEMPERATURE)??e?.temperature,topP:K(process.env.BRASS_GOOGLE_TOP_P)??e?.topP,topK:K(process.env.BRASS_GOOGLE_TOP_K)??e?.topK,maxOutputTokens:K(process.env.BRASS_GOOGLE_MAX_OUTPUT_TOKENS)??e?.maxOutputTokens})},Te=e=>{let n=process.env.BRASS_LLM_ENDPOINT??e?.endpoint,t=qe(e?.apiKeyEnv)??process.env.BRASS_LLM_API_KEY,o=process.env.BRASS_LLM_MODEL??e?.model??"gpt-4.1";if(!(!n||!t))return qa({endpoint:n,apiKey:t,model:o})},zn=e=>{let n=(process.env.BRASS_LLM_PROVIDER??e?.provider)?.trim().toLowerCase(),t=process.env.BRASS_FAKE_LLM_RESPONSE??e?.fakeResponse;if(n==="fake")return sa({content:t});if(n==="google"||n==="gemini"){let o=Ne(e);if(!o)throw new Error("Google LLM provider requires BRASS_GOOGLE_API_KEY, GOOGLE_API_KEY, GEMINI_API_KEY, or config.llm.apiKeyEnv.");return o}if(n==="openai"||n==="openai-compatible"){let o=Te(e);if(!o)throw new Error("OpenAI-compatible LLM provider requires BRASS_LLM_ENDPOINT/config.llm.endpoint and BRASS_LLM_API_KEY/config.llm.apiKeyEnv.");return o}if(n)throw new Error(`Unsupported LLM provider: ${n}`);return Ne(e)??Te(e)??sa({content:t})},Qn=()=>{let e=process.env.BRASS_AGENT_APPROVAL?.trim().toLowerCase();if(e){if(Je(e))return e;throw new Error("BRASS_AGENT_APPROVAL must be one of: auto, interactive, approve, deny")}},Xn=e=>e==="1"||e?.toLowerCase()==="true"||e?.toLowerCase()==="yes",Zn=()=>!!(process.stdin?.isTTY&&(process.stderr?.isTTY??process.stdout?.isTTY)),et=e=>{if(e.approvalSpecified&&e.approval!=="auto")return e.approval;if(Xn(process.env.BRASS_AGENT_AUTO_APPROVE))return "approve";let n=Qn();return n&&n!=="auto"?n:e.approval!=="auto"?e.approval:e.output==="human"&&Zn()?"interactive":"deny"},nt=e=>{switch(et(e)){case "approve":return ka;case "deny":return la("Approval rejected because the CLI is running without interactive input. Use --yes to auto-approve.");case "interactive":return Pe()}},Ke=(e,n=2e3)=>e.length<=n?e:`${e.slice(0,n)}
|
|
14
|
+
\u2026 truncated ${e.length-n} chars`,I=(e,n=1e3)=>e.length<=n?e:`${e.slice(0,n)}\u2026 truncated ${e.length-n} chars`,B=(e,n)=>n.fullPatches?e:I(e),He=(e,n)=>({...e,...e.initialPatch?{initialPatch:B(e.initialPatch,n)}:{}}),j=(e,n={})=>{switch(e.type){case "llm.complete":return {...e,prompt:I(e.prompt)};case "patch.apply":case "patch.rollback":case "patch.propose":return {...e,patch:B(e.patch,n)};default:return e}},ne=(e,n={})=>{switch(e.type){case "fs.fileRead":return {...e,content:I(e.content)};case "llm.response":return {...e,content:I(e.content)};case "shell.result":return {...e,stdout:I(e.stdout),stderr:I(e.stderr)};case "fs.searchResult":return {...e,matches:e.matches.slice(0,30),omittedMatches:Math.max(0,e.matches.length-30)};case "patch.proposed":return {...e,patch:B(e.patch,n)};case "patch.applied":case "patch.rolledBack":return e.patch?{...e,patch:B(e.patch,n)}:e;default:return e}},ze=(e,n={})=>{switch(e.type){case "agent.run.started":case "agent.run.completed":return {...e,goal:He(e.goal,n)};case "agent.action.started":return {...e,action:j(e.action,n)};case "agent.action.completed":return {...e,action:j(e.action,n),observation:ne(e.observation,n)};case "agent.action.failed":return {...e,action:j(e.action,n)};case "agent.observation.recorded":return {...e,observation:ne(e.observation,n)};case "agent.tool.timeout":case "agent.permission.denied":case "agent.approval.requested":case "agent.approval.resolved":return {...e,action:j(e.action,n)};default:return e}},te=(e,n={})=>({...e,goal:He(e.goal,n),observations:e.observations.map(t=>ne(t,n)),errors:e.errors.map(t=>{switch(t._tag){case "PermissionDenied":case "ApprovalRejected":return {...t,action:j(t.action,n)};case "PatchError":return {...t,cause:String(t.cause),...t.patch?{patch:B(t.patch,n)}:{}};case "FsError":case "ShellError":case "LLMError":return {...t,cause:String(t.cause)};default:return t}})}),oe=e=>({protocol:"brass-agent",version:1,...e}),tt=e=>{switch(e){case "ok":return "\u2713";case "warn":return "!";case "fail":return "\u2717"}},Ve=e=>`${Math.max(0,e)}ms`,w=(e,n)=>[...e.observations].reverse().find(t=>t.type===n),ot=e=>({emit(n){switch(n.type){case "agent.run.started":console.log(`brass-agent ${n.goal.mode}`),console.log(`workspace: ${n.goal.cwd}`),e&&console.log(`config: ${e}`),console.log(`goal: ${n.goal.text}`),console.log("");break;case "agent.action.started":console.log(`\u2192 ${T$1(n.action)}`);break;case "agent.action.completed":{let t=V(n.observation);console.log(`${tt(t)} ${U(n.observation)} ${Ve(n.durationMs)}`);break}case "agent.action.failed":n.error._tag!=="ToolTimeout"&&n.error._tag!=="PermissionDenied"&&n.error._tag!=="ApprovalRejected"&&console.log(`\u2717 ${T$1(n.action)} failed with ${n.error._tag} ${Ve(n.durationMs)}`);break;case "agent.tool.timeout":console.log(`! ${T$1(n.action)} timed out after ${n.timeoutMs}ms`);break;case "agent.permission.denied":console.log(`\u2717 ${T$1(n.action)} denied: ${n.reason}`);break;case "agent.approval.requested":console.log(`? approval required for ${T$1(n.action)} (${n.risk})`);break;case "agent.approval.resolved":n.approved?console.log(`\u2713 approval granted for ${T$1(n.action)}`):console.log(`\u2717 approval rejected for ${T$1(n.action)}${n.reason?`: ${n.reason}`:""}`);break;case "agent.patch.applied":break;case "agent.patch.rolledBack":n.automatic&&console.log(`\u2713 automatic rollback completed (${n.changedFiles.join(", ")||"no files reported"})`);break;}}}),rt=(e={})=>({emit(n){console.log(JSON.stringify(ze(n,e)));}}),st=(e={})=>({emit(n){console.log(JSON.stringify(oe({type:"event",event:ze(n,e)})));}}),We=e=>e.toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"run",ee=e=>e.replace(/\\/g,"\\\\").replace(/`/g,"\\`"),at=async(e,n,t)=>{let o=await F("node:path"),r=await F("node:fs/promises"),a=o.isAbsolute(n)?n:o.resolve(e.goal.cwd,n);await r.mkdir(a,{recursive:true});let s=`${We(e.goal.id)}-${We(e.goal.text)}`,c=o.join(a,`${s}.json`),h=o.join(a,`${s}.md`),E=w(e,"agent.done"),p=w(e,"agent.error"),g=w(e,"patch.applied"),m=w(e,"patch.rolledBack");await r.writeFile(c,`${JSON.stringify(te(e,t),null,2)}
|
|
15
|
+
`,"utf8"),await r.writeFile(h,[`# Brass Agent Run ${ee(e.goal.id)}`,"",`- Goal: ${ee(e.goal.text)}`,`- Workspace: ${ee(e.goal.cwd)}`,`- Mode: ${e.goal.mode}`,`- Phase: ${e.phase}`,`- Steps: ${e.steps}`,g?`- Changed files: ${g.changedFiles.join(", ")||"none reported"}`:void 0,m?`- Rolled back files: ${m.changedFiles.join(", ")||"none reported"}`:void 0,"","## Summary","",E?.summary?.trim()||(p?`Error: ${p.error._tag}`:"No summary recorded.")].filter(Boolean).join(`
|
|
16
|
+
`),"utf8"),process.stderr?.isTTY&&console.error(`saved run artifacts: ${c} ${h}`);},it=e=>w(e,"shell.result"),lt=(e,n)=>{if(w(e,"agent.error"))return 1;let t=it(e);return t&&t.exitCode!==0?1:n.failOnPatchProposed&&w(e,"patch.proposed")&&!w(e,"patch.applied")?2:0},ct=e=>{let n=w(e,"agent.done"),t=w(e,"agent.error"),o=w(e,"patch.proposed"),r=w(e,"patch.applied"),a=w(e,"patch.rolledBack"),l=w(e,"llm.response");console.log(""),console.log(`phase: ${e.phase}`),console.log(`steps: ${e.steps}`),a?console.log(`rolled back files: ${a.changedFiles.join(", ")||"(none reported)"}`):r?console.log(`changed files: ${r.changedFiles.join(", ")||"(none reported)"}`):o&&console.log("patch: proposed only; rerun with --apply to apply it"),n?(console.log(""),console.log("summary:"),console.log(Ke(n.summary.trim()||"Agent completed."))):t?(console.log(""),console.log("error:"),console.log(JSON.stringify(t.error,null,2))):l&&(console.log(""),console.log("llm response:"),console.log(Ke(l.content.trim())));},pt=(e,n)=>e.output==="human"?ot(e.resolvedConfigPath):e.output==="events-json"?rt(n):e.output==="protocol-json"?st(n):void 0,dt=(e,n)=>{let t=ma;return {shell:t,fs:na(t),patch:oa(t),llm:zn(e.config.llm),permissions:ia(e.config.permissions),approvals:nt(e),...n?{events:n}:{},...e.config.tools?{toolPolicies:e.config.tools}:{}}},ut=e=>({index:0,cwd:e.cwd,goalText:e.goalText,mode:e.mode,patchFileMode:e.patchFileMode,...e.patchFile?{patchFile:e.patchFile}:{},...e.saveRunDir?{saveRunDir:e.saveRunDir}:{}}),gt=async(e,n,t,o)=>{let r=dt(e,o),a=new $$1({env:r}),l=n.patchFile?await Nn(n.cwd,n.patchFile):void 0,s=await a.toPromise(ba(a,{id:`agent-${Date.now()}-${n.index+1}`,cwd:n.cwd,text:n.goalText,mode:n.mode,...e.config.project?{project:e.config.project}:{},...e.config.context?{context:e.config.context}:{},...e.config.patchQuality?{patchQuality:e.config.patchQuality}:{},...e.config.rollback?{rollback:e.config.rollback}:{},...e.config.redaction?{redaction:e.config.redaction}:{},...e.language?{language:{response:e.language}}:e.config.language?{language:e.config.language}:{},...l?{initialPatch:l,initialPatchMode:n.patchFileMode}:{}}));return n.saveRunDir&&await at(s,n.saveRunDir,t),{run:n,state:s,exitCode:lt(s,{failOnPatchProposed:e.failOnPatchProposed})}},ft=e=>e.some(n=>n.exitCode===1)?1:e.some(n=>n.exitCode===2)?2:0,Qe=(e,n)=>({total:e.length,completed:n.length,failed:n.filter(t=>t.exitCode!==0).length,exitCode:ft(n),stoppedEarly:n.length<e.length}),mt=(e,n)=>{let t=Qe(e,n);console.log(""),console.log("batch summary:"),console.log(`completed: ${t.completed}/${t.total}`),console.log(`failed: ${t.failed}`),t.stoppedEarly&&console.log("stopped early: yes"),console.log(`exit code: ${t.exitCode}`);},yt=e=>{let n={cwd:e.cwd,inputCwd:e.workspaceDiscovery.inputCwd,changed:e.workspaceDiscovery.changed,disabled:!!e.workspaceDiscovery.disabled,marker:e.workspaceDiscovery.marker,markerPath:e.workspaceDiscovery.markerPath,configPath:e.resolvedConfigPath,envFiles:e.envFileLoad.paths};if(e.output==="json"||e.output==="protocol-json"){console.log(JSON.stringify(n,null,2));return}console.log("brass-agent workspace"),console.log(`input: ${n.inputCwd}`),console.log(`workspace: ${n.cwd}`),n.disabled?console.log("discovery: disabled"):n.marker?console.log(`marker: ${n.marker} (${n.markerPath})`):console.log("marker: none found; using input cwd"),n.configPath&&console.log(`config: ${n.configPath}`),n.envFiles.length>0&&console.log(`env: ${n.envFiles.join(", ")}`);},ht=async()=>{let e=await Hn(Kn(process.argv.slice(2))),n=e.batchRuns.length>0;if(e.showHelp&&(Ge(),process.exit(0)),e.where){yt(e);return}if(e.init){let s=await je({cwd:e.cwd,force:e.initForce,dryRun:e.initDryRun,profile:e.initProfile});e.output==="json"?console.log(JSON.stringify(s,null,2)):Be(s);return}if(e.doctor){let s=await _e({cwd:e.cwd,config:e.config,configPath:e.resolvedConfigPath,envFileLoad:e.envFileLoad,workspaceDiscovery:e.workspaceDiscovery});e.output==="json"?console.log(JSON.stringify(s,null,2)):xe(s),process.exitCode=s.status==="fail"?1:0;return}!e.goalText&&!n&&(Ge(),process.exit(1));let t={fullPatches:e.protocolFullPatches},o=pt(e,t),r=n?e.batchRuns:[ut(e)],a=[];for(let s of r){let c=await gt(e,s,t,o);if(a.push(c),e.output==="protocol-json"?console.log(JSON.stringify(oe({type:"final-state",state:te(c.state,t)}))):e.output==="human"&&ct(c.state),n&&e.batchStopOnFailureResolved&&c.exitCode!==0)break}if(n){let s=Qe(r,a);e.output==="json"?console.log(JSON.stringify({type:"batch",summary:s,results:a.map(c=>({index:c.run.index,goal:c.run.goalText,cwd:c.run.cwd,mode:c.run.mode,exitCode:c.exitCode,state:te(c.state,t)}))},null,2)):e.output==="protocol-json"?console.log(JSON.stringify(oe({type:"batch-summary",summary:s}))):e.output==="human"&&mt(r,a),e.ci&&(process.exitCode=s.exitCode);return}let l=a[0];if(!l)throw new Error("Agent run did not produce a result.");e.output==="json"&&console.log(JSON.stringify(l.state,null,2)),e.ci&&(process.exitCode=l.exitCode);};ht().catch(e=>{console.error(e),process.exit(1);});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'use strict';var chunk6OUI6UGZ_cjs=require('../chunk-6OUI6UGZ.cjs');require('../chunk-K47BP5A2.cjs');Object.defineProperty(exports,"AGENT_CONFIG_FILE_NAMES",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.fa}});Object.defineProperty(exports,"NodeShell",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.ma}});Object.defineProperty(exports,"PROJECT_LOCKFILE_PROBES",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.m}});Object.defineProperty(exports,"PROJECT_PROFILE_PROBES",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.i}});Object.defineProperty(exports,"autoApproveApprovals",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.ka}});Object.defineProperty(exports,"canAutoRollback",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.D}});Object.defineProperty(exports,"canRequestPatchRepair",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.x}});Object.defineProperty(exports,"commandForScript",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.r}});Object.defineProperty(exports,"decideNextAction",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.P}});Object.defineProperty(exports,"defaultPermissions",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.ja}});Object.defineProperty(exports,"deriveContextSearchQueries",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.e}});Object.defineProperty(exports,"describeCommandDiscovery",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.u}});Object.defineProperty(exports,"describeContextDiscovery",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.f}});Object.defineProperty(exports,"describeLanguagePolicy",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.N}});Object.defineProperty(exports,"describePatchQuality",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.z}});Object.defineProperty(exports,"describeProjectProfile",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.l}});Object.defineProperty(exports,"describeRollbackSafety",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.G}});Object.defineProperty(exports,"discoverNodeWorkspaceRoot",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.ta}});Object.defineProperty(exports,"discoverPackageManager",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.p}});Object.defineProperty(exports,"discoverProjectProfile",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.k}});Object.defineProperty(exports,"discoverValidationCommands",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.s}});Object.defineProperty(exports,"emitAgentEvent",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.R}});Object.defineProperty(exports,"emitAgentEvents",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.S}});Object.defineProperty(exports,"errorEventFor",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.W}});Object.defineProperty(exports,"extractLikelyFilePaths",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.d}});Object.defineProperty(exports,"extractPatchPaths",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.I}});Object.defineProperty(exports,"extractUnifiedDiff",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.H}});Object.defineProperty(exports,"goalForAgentPreset",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.ha}});Object.defineProperty(exports,"inferUserLanguage",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.L}});Object.defineProperty(exports,"initialAgentState",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.a}});Object.defineProperty(exports,"invokeAction",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.aa}});Object.defineProperty(exports,"isAgentConfigApprovalMode",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.da}});Object.defineProperty(exports,"isAgentConfigLLMProvider",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.ea}});Object.defineProperty(exports,"isAgentConfigMode",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.ca}});Object.defineProperty(exports,"isAgentPreset",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.ga}});Object.defineProperty(exports,"isRedactionEnabled",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.J}});Object.defineProperty(exports,"isTerminal",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.c}});Object.defineProperty(exports,"latestUnappliedPatch",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.C}});Object.defineProperty(exports,"loadNodeAgentConfig",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.pa}});Object.defineProperty(exports,"makeAutoDenyApprovals",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.la}});Object.defineProperty(exports,"makeConfiguredPermissions",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.ia}});Object.defineProperty(exports,"makeFakeLLM",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.sa}});Object.defineProperty(exports,"makeGoogleGenerativeAILLM",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.ra}});Object.defineProperty(exports,"makeNodeFileSystem",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.na}});Object.defineProperty(exports,"makeNodePatchService",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.oa}});Object.defineProperty(exports,"makeOpenAICompatibleLLM",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.qa}});Object.defineProperty(exports,"nextContextDiscoveryAction",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.h}});Object.defineProperty(exports,"nextProjectProbeAction",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.o}});Object.defineProperty(exports,"nextUnrunValidationCommand",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.t}});Object.defineProperty(exports,"nowMillis",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.Q}});Object.defineProperty(exports,"observationEventFor",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.X}});Object.defineProperty(exports,"observationStatus",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.V}});Object.defineProperty(exports,"parseProjectPackageJson",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.n}});Object.defineProperty(exports,"patchQualitySummary",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.w}});Object.defineProperty(exports,"patchRepairAttemptsUsed",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.v}});Object.defineProperty(exports,"patchValidationStatus",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.y}});Object.defineProperty(exports,"projectProfileProbePending",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.j}});Object.defineProperty(exports,"redactText",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.K}});Object.defineProperty(exports,"reduceAgentState",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.b}});Object.defineProperty(exports,"responseLanguageName",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.M}});Object.defineProperty(exports,"retry",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.Z}});Object.defineProperty(exports,"rollbackSafetySummary",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.A}});Object.defineProperty(exports,"runAgent",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.ba}});Object.defineProperty(exports,"runStatusFor",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.Y}});Object.defineProperty(exports,"shouldContinueRollbackStack",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.E}});Object.defineProperty(exports,"sleep",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs._}});Object.defineProperty(exports,"spanishLike",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.O}});Object.defineProperty(exports,"splitCommand",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.q}});Object.defineProperty(exports,"summarizeAgentAction",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.T}});Object.defineProperty(exports,"summarizeAgentObservation",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.U}});Object.defineProperty(exports,"summarizeContextDiscovery",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.g}});Object.defineProperty(exports,"timeout",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.$}});Object.defineProperty(exports,"unappliedPatchStack",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.B}});Object.defineProperty(exports,"workspaceValidationStatus",{enumerable:true,get:function(){return chunk6OUI6UGZ_cjs.F}});
|