@nairon-ai/aegis 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/bug-fix/SKILL.md +91 -0
- package/.flue/agents/bug-fix.ts +107 -0
- package/.flue/app.ts +16 -0
- package/Dockerfile +8 -0
- package/LICENSE +21 -0
- package/README.md +251 -0
- package/dist-node/agent/bug-fix-skill.d.ts +2 -0
- package/dist-node/agent/bug-fix-skill.d.ts.map +1 -0
- package/dist-node/agent/bug-fix-skill.js +64 -0
- package/dist-node/agent/bug-fix-skill.js.map +1 -0
- package/dist-node/agent/client.d.ts +14 -0
- package/dist-node/agent/client.d.ts.map +1 -0
- package/dist-node/agent/client.js +110 -0
- package/dist-node/agent/client.js.map +1 -0
- package/dist-node/cli/commands/deploy.d.ts +3 -0
- package/dist-node/cli/commands/deploy.d.ts.map +1 -0
- package/dist-node/cli/commands/deploy.js +94 -0
- package/dist-node/cli/commands/deploy.js.map +1 -0
- package/dist-node/cli/commands/init.d.ts +3 -0
- package/dist-node/cli/commands/init.d.ts.map +1 -0
- package/dist-node/cli/commands/init.js +115 -0
- package/dist-node/cli/commands/init.js.map +1 -0
- package/dist-node/cli/commands/pickup.d.ts +11 -0
- package/dist-node/cli/commands/pickup.d.ts.map +1 -0
- package/dist-node/cli/commands/pickup.js +43 -0
- package/dist-node/cli/commands/pickup.js.map +1 -0
- package/dist-node/cli/commands/setup.d.ts +3 -0
- package/dist-node/cli/commands/setup.d.ts.map +1 -0
- package/dist-node/cli/commands/setup.js +163 -0
- package/dist-node/cli/commands/setup.js.map +1 -0
- package/dist-node/cli/commands/status.d.ts +3 -0
- package/dist-node/cli/commands/status.d.ts.map +1 -0
- package/dist-node/cli/commands/status.js +26 -0
- package/dist-node/cli/commands/status.js.map +1 -0
- package/dist-node/cli/index.d.ts +3 -0
- package/dist-node/cli/index.d.ts.map +1 -0
- package/dist-node/cli/index.js +36 -0
- package/dist-node/cli/index.js.map +1 -0
- package/dist-node/cli/paths.d.ts +7 -0
- package/dist-node/cli/paths.d.ts.map +1 -0
- package/dist-node/cli/paths.js +29 -0
- package/dist-node/cli/paths.js.map +1 -0
- package/dist-node/cli/state.d.ts +16 -0
- package/dist-node/cli/state.d.ts.map +1 -0
- package/dist-node/cli/state.js +214 -0
- package/dist-node/cli/state.js.map +1 -0
- package/dist-node/core/pickup.d.ts +14 -0
- package/dist-node/core/pickup.d.ts.map +1 -0
- package/dist-node/core/pickup.js +42 -0
- package/dist-node/core/pickup.js.map +1 -0
- package/dist-node/github/index.d.ts +3 -0
- package/dist-node/github/index.d.ts.map +1 -0
- package/dist-node/github/index.js +2 -0
- package/dist-node/github/index.js.map +1 -0
- package/dist-node/github/manifest.d.ts +34 -0
- package/dist-node/github/manifest.d.ts.map +1 -0
- package/dist-node/github/manifest.js +71 -0
- package/dist-node/github/manifest.js.map +1 -0
- package/dist-node/integrations/github.d.ts +29 -0
- package/dist-node/integrations/github.d.ts.map +1 -0
- package/dist-node/integrations/github.js +199 -0
- package/dist-node/integrations/github.js.map +1 -0
- package/dist-node/integrations/linear.d.ts +15 -0
- package/dist-node/integrations/linear.d.ts.map +1 -0
- package/dist-node/integrations/linear.js +146 -0
- package/dist-node/integrations/linear.js.map +1 -0
- package/dist-node/integrations/telegram.d.ts +24 -0
- package/dist-node/integrations/telegram.d.ts.map +1 -0
- package/dist-node/integrations/telegram.js +39 -0
- package/dist-node/integrations/telegram.js.map +1 -0
- package/dist-node/integrations/webhooks.d.ts +3 -0
- package/dist-node/integrations/webhooks.d.ts.map +1 -0
- package/dist-node/integrations/webhooks.js +37 -0
- package/dist-node/integrations/webhooks.js.map +1 -0
- package/dist-node/sandbox/github-token.d.ts +7 -0
- package/dist-node/sandbox/github-token.d.ts.map +1 -0
- package/dist-node/sandbox/github-token.js +66 -0
- package/dist-node/sandbox/github-token.js.map +1 -0
- package/dist-node/sandbox/index.d.ts +2 -0
- package/dist-node/sandbox/index.d.ts.map +1 -0
- package/dist-node/sandbox/index.js +2 -0
- package/dist-node/sandbox/index.js.map +1 -0
- package/dist-node/server/app.d.ts +9 -0
- package/dist-node/server/app.d.ts.map +1 -0
- package/dist-node/server/app.js +216 -0
- package/dist-node/server/app.js.map +1 -0
- package/dist-node/shared/config.d.ts +5 -0
- package/dist-node/shared/config.d.ts.map +1 -0
- package/dist-node/shared/config.js +135 -0
- package/dist-node/shared/config.js.map +1 -0
- package/dist-node/shared/constants.d.ts +16 -0
- package/dist-node/shared/constants.d.ts.map +1 -0
- package/dist-node/shared/constants.js +29 -0
- package/dist-node/shared/constants.js.map +1 -0
- package/dist-node/shared/format.d.ts +12 -0
- package/dist-node/shared/format.d.ts.map +1 -0
- package/dist-node/shared/format.js +71 -0
- package/dist-node/shared/format.js.map +1 -0
- package/dist-node/shared/index.d.ts +7 -0
- package/dist-node/shared/index.d.ts.map +1 -0
- package/dist-node/shared/index.js +7 -0
- package/dist-node/shared/index.js.map +1 -0
- package/dist-node/shared/readiness.d.ts +3 -0
- package/dist-node/shared/readiness.d.ts.map +1 -0
- package/dist-node/shared/readiness.js +91 -0
- package/dist-node/shared/readiness.js.map +1 -0
- package/dist-node/shared/run-state.d.ts +5 -0
- package/dist-node/shared/run-state.d.ts.map +1 -0
- package/dist-node/shared/run-state.js +26 -0
- package/dist-node/shared/run-state.js.map +1 -0
- package/dist-node/shared/types.d.ts +230 -0
- package/dist-node/shared/types.d.ts.map +1 -0
- package/dist-node/shared/types.js +5 -0
- package/dist-node/shared/types.js.map +1 -0
- package/dist-node/sources/github.d.ts +15 -0
- package/dist-node/sources/github.d.ts.map +1 -0
- package/dist-node/sources/github.js +44 -0
- package/dist-node/sources/github.js.map +1 -0
- package/dist-node/sources/index.d.ts +6 -0
- package/dist-node/sources/index.d.ts.map +1 -0
- package/dist-node/sources/index.js +16 -0
- package/dist-node/sources/index.js.map +1 -0
- package/dist-node/sources/linear.d.ts +15 -0
- package/dist-node/sources/linear.d.ts.map +1 -0
- package/dist-node/sources/linear.js +32 -0
- package/dist-node/sources/linear.js.map +1 -0
- package/dist-node/sources/types.d.ts +15 -0
- package/dist-node/sources/types.d.ts.map +1 -0
- package/dist-node/sources/types.js +2 -0
- package/dist-node/sources/types.js.map +1 -0
- package/docs/RELEASING.md +52 -0
- package/docs/SETUP.md +439 -0
- package/package.json +64 -0
- package/src/agent/bug-fix-skill.ts +63 -0
- package/src/agent/client.ts +156 -0
- package/src/cli/commands/deploy.ts +106 -0
- package/src/cli/commands/init.ts +119 -0
- package/src/cli/commands/pickup.ts +44 -0
- package/src/cli/commands/setup.ts +217 -0
- package/src/cli/commands/status.ts +24 -0
- package/src/cli/index.ts +38 -0
- package/src/cli/paths.ts +29 -0
- package/src/cli/state.ts +228 -0
- package/src/core/pickup.ts +66 -0
- package/src/github/index.ts +2 -0
- package/src/github/manifest.ts +97 -0
- package/src/integrations/github.ts +241 -0
- package/src/integrations/linear.ts +195 -0
- package/src/integrations/telegram.ts +48 -0
- package/src/integrations/webhooks.ts +53 -0
- package/src/sandbox/github-token.ts +92 -0
- package/src/sandbox/index.ts +1 -0
- package/src/server/app.ts +292 -0
- package/src/shared/config.ts +154 -0
- package/src/shared/constants.ts +30 -0
- package/src/shared/format.ts +84 -0
- package/src/shared/index.ts +6 -0
- package/src/shared/readiness.ts +116 -0
- package/src/shared/run-state.ts +32 -0
- package/src/shared/types.ts +257 -0
- package/src/sources/github.ts +57 -0
- package/src/sources/index.ts +20 -0
- package/src/sources/linear.ts +44 -0
- package/src/sources/types.ts +16 -0
- package/tsconfig.json +25 -0
- package/tsconfig.node.json +16 -0
- package/wrangler.jsonc +43 -0
package/docs/SETUP.md
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
# Connect Aegis To A Project
|
|
2
|
+
|
|
3
|
+
This guide is the exact path for using this repo to monitor bugs in another GitHub repo.
|
|
4
|
+
|
|
5
|
+
## Mental Model
|
|
6
|
+
|
|
7
|
+
You have two repos:
|
|
8
|
+
|
|
9
|
+
- **Aegis repo:** the agent/control repo. You fork it and deploy it.
|
|
10
|
+
- **Monitored repo:** the app repo Aegis fixes, like `acme/web-app`.
|
|
11
|
+
|
|
12
|
+
Aegis stores state in the tracker you already use:
|
|
13
|
+
|
|
14
|
+
- GitHub labels, issue comments, PR branches
|
|
15
|
+
- Linear statuses/comments when Linear is connected
|
|
16
|
+
|
|
17
|
+
No database is required for Aegis itself.
|
|
18
|
+
|
|
19
|
+
## What You Need
|
|
20
|
+
|
|
21
|
+
Required for local dry-run:
|
|
22
|
+
|
|
23
|
+
- Bun
|
|
24
|
+
- GitHub access to the monitored repo
|
|
25
|
+
|
|
26
|
+
Required for cloud/AFK mode:
|
|
27
|
+
|
|
28
|
+
- Cloudflare account
|
|
29
|
+
- Workers Paid plan, because Cloudflare Sandbox/Containers run the coding sandboxes
|
|
30
|
+
- Docker running locally during deploy
|
|
31
|
+
- `wrangler` login
|
|
32
|
+
- OpenAI API key or another Flue-supported model key
|
|
33
|
+
|
|
34
|
+
Optional:
|
|
35
|
+
|
|
36
|
+
- Linear API key if you use Linear as the bug tracker
|
|
37
|
+
- Telegram bot if you want phone approvals
|
|
38
|
+
- Read-only production database URL
|
|
39
|
+
- Vercel token/project ID for logs
|
|
40
|
+
|
|
41
|
+
## Step 1: Install Aegis In Your Product Repo
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
cd /path/to/your-product-repo
|
|
45
|
+
npx @nairon-ai/aegis init
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This creates:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
.aegis/.env.example
|
|
52
|
+
.aegis/.gitignore
|
|
53
|
+
.aegis/README.md
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For local development before Aegis is published to npm, use the GitHub package:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx github:Nairon-AI/aegis init
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
After npm publish, the intended install flow is:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install --save-dev @nairon-ai/aegis
|
|
66
|
+
npx aegis init
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Step 2: Set Up Cloudflare
|
|
70
|
+
|
|
71
|
+
Skip this step if you only want local dry-run.
|
|
72
|
+
|
|
73
|
+
1. Create a Cloudflare account.
|
|
74
|
+
2. Enable Workers Paid.
|
|
75
|
+
3. Install/start Docker Desktop.
|
|
76
|
+
4. Log in:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npx wrangler login
|
|
80
|
+
npx wrangler whoami
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
If deploy fails with a container/Docker error, Docker is usually not running. If deploy fails with a plan/capability error, check the Cloudflare Workers plan.
|
|
84
|
+
|
|
85
|
+
## Step 3: Give Aegis GitHub Access
|
|
86
|
+
|
|
87
|
+
Pick one path.
|
|
88
|
+
|
|
89
|
+
### Option A: GitHub Token
|
|
90
|
+
|
|
91
|
+
Fastest first setup. Good for proving the repo works.
|
|
92
|
+
|
|
93
|
+
Create a fine-grained personal access token:
|
|
94
|
+
|
|
95
|
+
- Repository access: only the monitored repo
|
|
96
|
+
- Contents: read/write
|
|
97
|
+
- Issues: read/write
|
|
98
|
+
- Pull requests: read/write
|
|
99
|
+
- Metadata: read-only
|
|
100
|
+
|
|
101
|
+
The token lets Aegis scan issues, create branches, push fixes, and open PRs.
|
|
102
|
+
|
|
103
|
+
### Option B: GitHub App
|
|
104
|
+
|
|
105
|
+
Better for teams and production use.
|
|
106
|
+
|
|
107
|
+
Create a GitHub App with:
|
|
108
|
+
|
|
109
|
+
- Contents: read/write
|
|
110
|
+
- Issues: read/write
|
|
111
|
+
- Pull requests: read/write
|
|
112
|
+
- Metadata: read-only
|
|
113
|
+
- Events: Issues, Issue comments
|
|
114
|
+
|
|
115
|
+
Then:
|
|
116
|
+
|
|
117
|
+
1. Generate a private key.
|
|
118
|
+
2. Install the app on the monitored repo.
|
|
119
|
+
3. Save the App ID.
|
|
120
|
+
4. Save the Installation ID from the installation URL.
|
|
121
|
+
5. Keep the private key `.pem` available for setup.
|
|
122
|
+
|
|
123
|
+
If you do not know which one to pick, use a GitHub token first, then move to a GitHub App once the flow works.
|
|
124
|
+
|
|
125
|
+
## Step 4: Run The Setup Wizard
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npx @nairon-ai/aegis setup
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Important answers:
|
|
132
|
+
|
|
133
|
+
- `GitHub repo to monitor`: the app repo, for example `acme/web-app`
|
|
134
|
+
- `Base branch`: usually `main`
|
|
135
|
+
- GitHub auth: token or app credentials from Step 3
|
|
136
|
+
- `GitHub webhook secret`: generate one and keep it in `.aegis/.env`
|
|
137
|
+
- `Automation mode`: start with `plan-first`
|
|
138
|
+
- `Context profile`: start with `minimal`
|
|
139
|
+
- `OpenAI API key`: required for the default `openai/...` model
|
|
140
|
+
|
|
141
|
+
This writes `.aegis/.env`. Do not commit it.
|
|
142
|
+
|
|
143
|
+
Minimum useful `.aegis/.env`:
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
MONITORED_REPO=acme/web-app
|
|
147
|
+
BASE_BRANCH=main
|
|
148
|
+
READY_LABEL=ready to implement
|
|
149
|
+
BUG_LABEL=bug
|
|
150
|
+
AUTOMATION_MODE=plan-first
|
|
151
|
+
CONTEXT_PROFILE=minimal
|
|
152
|
+
GITHUB_TOKEN=github_pat_...
|
|
153
|
+
GITHUB_WEBHOOK_SECRET=...
|
|
154
|
+
AGENT_MODEL=openai/gpt-5.1
|
|
155
|
+
OPENAI_API_KEY=sk-...
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Step 5: Prepare The Monitored Repo
|
|
159
|
+
|
|
160
|
+
In the monitored repo, create these labels:
|
|
161
|
+
|
|
162
|
+
- `bug`
|
|
163
|
+
- `ready to implement`
|
|
164
|
+
|
|
165
|
+
Aegis creates its own labels when needed:
|
|
166
|
+
|
|
167
|
+
- `aegis:in-progress`
|
|
168
|
+
- `aegis:needs-info`
|
|
169
|
+
- `aegis:blocked`
|
|
170
|
+
- `aegis:pr-opened`
|
|
171
|
+
|
|
172
|
+
If you use different label names, enter those names in setup.
|
|
173
|
+
|
|
174
|
+
## Step 6: Create A Test Bug
|
|
175
|
+
|
|
176
|
+
Create a GitHub issue in the monitored repo:
|
|
177
|
+
|
|
178
|
+
```text
|
|
179
|
+
Title: Bug: Checkout fails after applying a discount
|
|
180
|
+
|
|
181
|
+
What happened:
|
|
182
|
+
A customer applied a discount code and clicked Checkout. The page showed an error.
|
|
183
|
+
|
|
184
|
+
What should have happened:
|
|
185
|
+
Checkout should continue or show a clear validation message.
|
|
186
|
+
|
|
187
|
+
Where:
|
|
188
|
+
/checkout
|
|
189
|
+
|
|
190
|
+
When:
|
|
191
|
+
Around 2026-05-14 10:15 UTC
|
|
192
|
+
|
|
193
|
+
Error/log:
|
|
194
|
+
Paste the relevant log, provider link, stack trace, screenshot, or Linear context.
|
|
195
|
+
|
|
196
|
+
Steps to reproduce:
|
|
197
|
+
1. Add item to cart
|
|
198
|
+
2. Apply discount code TEST10
|
|
199
|
+
3. Click Checkout
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Add both labels:
|
|
203
|
+
|
|
204
|
+
- `bug`
|
|
205
|
+
- `ready to implement`
|
|
206
|
+
|
|
207
|
+
The issue must include enough context for a developer to start. If it does not, Aegis should ask one concrete follow-up question instead of starting a sandbox run.
|
|
208
|
+
|
|
209
|
+
## Step 7: Dry-Run Pickup Locally
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
npx @nairon-ai/aegis pickup --dry-run
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Good result:
|
|
216
|
+
|
|
217
|
+
```text
|
|
218
|
+
github:#123 would run - ready bug with enough context
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Also acceptable:
|
|
222
|
+
|
|
223
|
+
```text
|
|
224
|
+
github:#123 needs info - missing reproduction steps
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Bad result:
|
|
228
|
+
|
|
229
|
+
```text
|
|
230
|
+
No ready bugs found.
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Usually means:
|
|
234
|
+
|
|
235
|
+
- wrong `MONITORED_REPO`
|
|
236
|
+
- labels do not match exactly
|
|
237
|
+
- issue is closed
|
|
238
|
+
- token/app is not installed on that repo
|
|
239
|
+
- Linear/GitHub setup is pointing at the wrong team/project
|
|
240
|
+
|
|
241
|
+
## Step 8: Deploy Aegis
|
|
242
|
+
|
|
243
|
+
Make sure Docker is running, then:
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
npx @nairon-ai/aegis deploy
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Deploy does four things:
|
|
250
|
+
|
|
251
|
+
1. Pushes `.aegis/.env` values into Cloudflare secrets.
|
|
252
|
+
2. Builds the Flue Worker.
|
|
253
|
+
3. Deploys with Wrangler.
|
|
254
|
+
4. Saves and pushes `AEGIS_WORKER_URL`.
|
|
255
|
+
|
|
256
|
+
Expected result:
|
|
257
|
+
|
|
258
|
+
```text
|
|
259
|
+
Saved Worker URL: https://aegis-worker.YOUR_SUBDOMAIN.workers.dev
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Health check:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
curl https://aegis-worker.YOUR_SUBDOMAIN.workers.dev/
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Manual pickup:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
curl -X POST https://aegis-worker.YOUR_SUBDOMAIN.workers.dev/api/pickup
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Step 9: Add Webhooks
|
|
275
|
+
|
|
276
|
+
Webhooks make Aegis react quickly. Cron still scans every 10 minutes.
|
|
277
|
+
|
|
278
|
+
### GitHub Webhook
|
|
279
|
+
|
|
280
|
+
If using a GitHub App, configure the app webhook.
|
|
281
|
+
|
|
282
|
+
If using a token, create a repo webhook in the monitored repo:
|
|
283
|
+
|
|
284
|
+
```text
|
|
285
|
+
Payload URL: https://YOUR_WORKER.workers.dev/webhook/github
|
|
286
|
+
Content type: application/json
|
|
287
|
+
Secret: value from GITHUB_WEBHOOK_SECRET
|
|
288
|
+
Events: Issues, Issue comments
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
What each event does:
|
|
292
|
+
|
|
293
|
+
- `Issues`: triggers pickup when a bug is opened/labeled/edited/reopened.
|
|
294
|
+
- `Issue comments`: handles `/aegis proceed`, `/aegis ask`, and `/aegis stop`.
|
|
295
|
+
|
|
296
|
+
### Linear Webhook
|
|
297
|
+
|
|
298
|
+
Only needed if Linear is connected.
|
|
299
|
+
|
|
300
|
+
```text
|
|
301
|
+
URL: https://YOUR_WORKER.workers.dev/webhook/linear
|
|
302
|
+
Secret: value from LINEAR_WEBHOOK_SECRET
|
|
303
|
+
Resource types: Issue, Comment
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Configure Linear to match the setup wizard:
|
|
307
|
+
|
|
308
|
+
- Team ID
|
|
309
|
+
- Optional project ID
|
|
310
|
+
- Ready status, default `Ready to Implement`
|
|
311
|
+
- Bug label, default `bug`
|
|
312
|
+
|
|
313
|
+
### Telegram Webhook
|
|
314
|
+
|
|
315
|
+
Only needed if Telegram approvals are enabled.
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
|
|
319
|
+
-d "url=https://YOUR_WORKER.workers.dev/webhook/telegram" \
|
|
320
|
+
-d "secret_token=$TELEGRAM_WEBHOOK_SECRET"
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Telegram commands:
|
|
324
|
+
|
|
325
|
+
```text
|
|
326
|
+
/aegis proceed <run-id>
|
|
327
|
+
/aegis ask <run-id> <question>
|
|
328
|
+
/aegis stop <run-id>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Step 10: Expected First Real Run
|
|
332
|
+
|
|
333
|
+
1. You label an issue `bug` + `ready to implement`.
|
|
334
|
+
2. Aegis adds `aegis:in-progress`.
|
|
335
|
+
3. Aegis comments with the run ID and branch name.
|
|
336
|
+
4. Aegis starts a sandbox planning run.
|
|
337
|
+
5. Aegis posts an RCA checkpoint in the issue.
|
|
338
|
+
6. Telegram receives a short human summary if configured.
|
|
339
|
+
7. You approve with `/aegis proceed <run-id>`.
|
|
340
|
+
8. Aegis patches the branch, runs checks, pushes it, opens a PR, and comments the PR link.
|
|
341
|
+
|
|
342
|
+
## Approval Rules
|
|
343
|
+
|
|
344
|
+
Default mode is `plan-first`.
|
|
345
|
+
|
|
346
|
+
Aegis does not patch until a human approves:
|
|
347
|
+
|
|
348
|
+
```text
|
|
349
|
+
/aegis proceed <run-id>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Ask for clarification:
|
|
353
|
+
|
|
354
|
+
```text
|
|
355
|
+
/aegis ask <run-id> Can you verify this only affects checkout?
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Stop the run:
|
|
359
|
+
|
|
360
|
+
```text
|
|
361
|
+
/aegis stop <run-id>
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Production Context
|
|
365
|
+
|
|
366
|
+
Start with `CONTEXT_PROFILE=minimal`.
|
|
367
|
+
|
|
368
|
+
Use `production` only after the basic flow works:
|
|
369
|
+
|
|
370
|
+
```text
|
|
371
|
+
CONTEXT_PROFILE=production
|
|
372
|
+
DATABASE_URL=postgres://readonly...
|
|
373
|
+
VERCEL_TOKEN=...
|
|
374
|
+
VERCEL_PROJECT_ID=...
|
|
375
|
+
VERCEL_TEAM_ID=...
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Rules:
|
|
379
|
+
|
|
380
|
+
- DB user should be read-only.
|
|
381
|
+
- Prefer a read replica.
|
|
382
|
+
- Do not provide write credentials.
|
|
383
|
+
- Paste provider links/errors into the issue whenever possible.
|
|
384
|
+
|
|
385
|
+
## Troubleshooting
|
|
386
|
+
|
|
387
|
+
`No ready bugs found.`
|
|
388
|
+
|
|
389
|
+
- Check exact labels.
|
|
390
|
+
- Check `MONITORED_REPO`.
|
|
391
|
+
- Check the issue is open.
|
|
392
|
+
- Check GitHub token/app repo access.
|
|
393
|
+
- Check Linear team/project/status/label names.
|
|
394
|
+
|
|
395
|
+
`GitHub 401/403.`
|
|
396
|
+
|
|
397
|
+
- Token expired or missing permissions.
|
|
398
|
+
- GitHub App not installed on monitored repo.
|
|
399
|
+
- Installation ID is wrong.
|
|
400
|
+
- Private key was not copied correctly.
|
|
401
|
+
|
|
402
|
+
`Aegis claims but no plan appears.`
|
|
403
|
+
|
|
404
|
+
- Check `AEGIS_WORKER_URL`.
|
|
405
|
+
- Check `OPENAI_API_KEY`.
|
|
406
|
+
- Check Cloudflare Worker logs.
|
|
407
|
+
- Trigger `POST /api/pickup` once and inspect the response/logs.
|
|
408
|
+
|
|
409
|
+
`Deploy fails before Worker URL prints.`
|
|
410
|
+
|
|
411
|
+
- Run `bunx wrangler login`.
|
|
412
|
+
- Run `bunx wrangler whoami`.
|
|
413
|
+
- Start Docker.
|
|
414
|
+
- Confirm Workers Paid is enabled.
|
|
415
|
+
|
|
416
|
+
`GitHub comments do not approve runs.`
|
|
417
|
+
|
|
418
|
+
- Confirm webhook URL is `/webhook/github`.
|
|
419
|
+
- Confirm content type is `application/json`.
|
|
420
|
+
- Confirm `GITHUB_WEBHOOK_SECRET` matches the webhook secret.
|
|
421
|
+
- Confirm Issue comments event is enabled.
|
|
422
|
+
|
|
423
|
+
`Telegram commands do nothing.`
|
|
424
|
+
|
|
425
|
+
- Confirm `TELEGRAM_WEBHOOK_SECRET` exists in `.aegis/.env`.
|
|
426
|
+
- Re-run the `setWebhook` command.
|
|
427
|
+
- Confirm the chat ID is the same chat that receives Aegis messages.
|
|
428
|
+
|
|
429
|
+
## Safe Defaults
|
|
430
|
+
|
|
431
|
+
Recommended first config:
|
|
432
|
+
|
|
433
|
+
```text
|
|
434
|
+
AUTOMATION_MODE=plan-first
|
|
435
|
+
CONTEXT_PROFILE=minimal
|
|
436
|
+
MAX_CONCURRENT_RUNS=1
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Increase concurrency only after the first few PRs are boring in the best way.
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nairon-ai/aegis",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Self-hosted AFK bug-fixing agent for GitHub Issues and Linear",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Nairon-AI/aegis.git"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"aegis": "dist-node/cli/index.js"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
".agents/skills/bug-fix",
|
|
18
|
+
".flue",
|
|
19
|
+
"dist-node",
|
|
20
|
+
"src",
|
|
21
|
+
"docs",
|
|
22
|
+
"Dockerfile",
|
|
23
|
+
"README.md",
|
|
24
|
+
"tsconfig.json",
|
|
25
|
+
"tsconfig.node.json",
|
|
26
|
+
"wrangler.jsonc"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"aegis": "bun ./src/cli/index.ts",
|
|
30
|
+
"dev": "flue dev --target cloudflare --env .env.local",
|
|
31
|
+
"build": "flue build --target cloudflare",
|
|
32
|
+
"build:node": "tsc -p tsconfig.node.json && chmod +x dist-node/cli/index.js",
|
|
33
|
+
"deploy": "bun run build && wrangler deploy --config dist/wrangler.jsonc",
|
|
34
|
+
"prepack": "npm run build:node",
|
|
35
|
+
"release": "semantic-release",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"check": "biome check .",
|
|
38
|
+
"check:fix": "biome check --write .",
|
|
39
|
+
"format": "biome format --write ."
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@cloudflare/sandbox": "0.10.1",
|
|
43
|
+
"@flue/cli": "0.5.3",
|
|
44
|
+
"@flue/sdk": "0.5.3",
|
|
45
|
+
"agents": "0.12.4",
|
|
46
|
+
"citty": "0.2.2",
|
|
47
|
+
"consola": "3.4.0",
|
|
48
|
+
"hono": "4.7.0",
|
|
49
|
+
"valibot": "1.4.0",
|
|
50
|
+
"wrangler": "4.90.1"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@biomejs/biome": "1.9.0",
|
|
54
|
+
"@cloudflare/workers-types": "4.20260511.1",
|
|
55
|
+
"@semantic-release/commit-analyzer": "13.0.1",
|
|
56
|
+
"@semantic-release/github": "12.0.8",
|
|
57
|
+
"@semantic-release/npm": "13.1.5",
|
|
58
|
+
"@semantic-release/release-notes-generator": "14.1.1",
|
|
59
|
+
"bun-types": "1.3.13",
|
|
60
|
+
"semantic-release": "25.0.3",
|
|
61
|
+
"typescript": "5.9.3"
|
|
62
|
+
},
|
|
63
|
+
"license": "MIT"
|
|
64
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export const BUG_FIX_SKILL_TEXT = `---
|
|
2
|
+
name: bug-fix
|
|
3
|
+
description: Investigate a ready bug ticket, produce a non-technical approval summary, and after approval implement a minimal tested fix.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Bug Fix
|
|
7
|
+
|
|
8
|
+
You are Aegis, an AFK bug-fixing SRE. You only work on bug tickets. You do not implement features.
|
|
9
|
+
|
|
10
|
+
Read the JSON context file passed in contextPath. It contains the command, source ticket, repo, branch, and optional production context.
|
|
11
|
+
|
|
12
|
+
## Hard Rules
|
|
13
|
+
|
|
14
|
+
- Do not push to main.
|
|
15
|
+
- Do not open PRs yourself. Return PR title/body only.
|
|
16
|
+
- Do not use git add -A or git add .
|
|
17
|
+
- Do not include secrets in output.
|
|
18
|
+
- If confidence is low, return blocked or needs-info.
|
|
19
|
+
- Keep the fix minimal.
|
|
20
|
+
- Run repo-native type checks for changed surfaces when possible.
|
|
21
|
+
- Run the narrowest relevant test suite when possible.
|
|
22
|
+
|
|
23
|
+
## Human Summary Style
|
|
24
|
+
|
|
25
|
+
humanSummary is for Telegram. Keep it short, plain English, and skimmable on a phone.
|
|
26
|
+
|
|
27
|
+
Prefer: what user did, what broke, why it likely broke at a high level, what Aegis wants to change, risk.
|
|
28
|
+
|
|
29
|
+
Avoid: file paths, function names, column names, stack internals, and jargon unless unavoidable.
|
|
30
|
+
|
|
31
|
+
## Plan Command
|
|
32
|
+
|
|
33
|
+
When command is plan:
|
|
34
|
+
|
|
35
|
+
1. Read the issue carefully.
|
|
36
|
+
2. Inspect the codebase and nearest docs.
|
|
37
|
+
3. If production profile is enabled, use only read-only checks.
|
|
38
|
+
4. Determine whether there is enough context.
|
|
39
|
+
5. Return awaiting-approval, needs-info, or blocked.
|
|
40
|
+
|
|
41
|
+
Do not edit files during plan.
|
|
42
|
+
|
|
43
|
+
## Implement Command
|
|
44
|
+
|
|
45
|
+
When command is implement:
|
|
46
|
+
|
|
47
|
+
1. Re-read the context and issue.
|
|
48
|
+
2. Implement the smallest fix that addresses the root cause.
|
|
49
|
+
3. Add or update regression coverage when practical.
|
|
50
|
+
4. Run type checks and relevant tests.
|
|
51
|
+
5. Stage only files you changed with explicit file paths.
|
|
52
|
+
6. Commit to the current branch.
|
|
53
|
+
7. Push the current branch to origin.
|
|
54
|
+
8. Return PR title/body and evidence.
|
|
55
|
+
|
|
56
|
+
If you cannot make a confident fix, return blocked or needs-info without committing.
|
|
57
|
+
|
|
58
|
+
## PR Body
|
|
59
|
+
|
|
60
|
+
Include summary, source ticket link, what changed, tests run, risk, and note that human review is required.
|
|
61
|
+
|
|
62
|
+
Use Refs, not Closes, unless the context explicitly says otherwise.
|
|
63
|
+
`;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { GitHubClient } from "../integrations/github.js";
|
|
2
|
+
import { sendTelegramMessage } from "../integrations/telegram.js";
|
|
3
|
+
import { formatIssuePlanComment, formatTelegramPlanMessage } from "../shared/format.js";
|
|
4
|
+
import type {
|
|
5
|
+
AegisConfig,
|
|
6
|
+
AgentImplementationResult,
|
|
7
|
+
AgentPlanResult,
|
|
8
|
+
AgentRunContext,
|
|
9
|
+
ReadinessDecision,
|
|
10
|
+
WorkItem,
|
|
11
|
+
WorkRun,
|
|
12
|
+
} from "../shared/types.js";
|
|
13
|
+
import { sourceFor } from "../sources/index.js";
|
|
14
|
+
|
|
15
|
+
export async function startPlanRun(params: {
|
|
16
|
+
config: AegisConfig;
|
|
17
|
+
item: WorkItem;
|
|
18
|
+
run: WorkRun;
|
|
19
|
+
decision: ReadinessDecision;
|
|
20
|
+
}): Promise<void> {
|
|
21
|
+
const result = await callBugFixAgent<AgentPlanResult>(
|
|
22
|
+
params.config,
|
|
23
|
+
await buildAgentContext({
|
|
24
|
+
config: params.config,
|
|
25
|
+
item: params.item,
|
|
26
|
+
run: params.run,
|
|
27
|
+
command: "plan",
|
|
28
|
+
}),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const source = sourceFor(params.config, params.item.source);
|
|
32
|
+
if (result.outcome === "needs-info") {
|
|
33
|
+
await source.postNeedsInfo(params.item, result.question ?? result.humanSummary);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (result.outcome === "blocked") {
|
|
37
|
+
await source.markBlocked(params.item, result.blocker ?? result.technicalSummary);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await source.postPlan(params.item, formatIssuePlanComment(params.run.id, result));
|
|
42
|
+
await sendTelegramMessage(
|
|
43
|
+
params.config,
|
|
44
|
+
formatTelegramPlanMessage(params.run.id, params.item, result),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function startImplementationRun(params: {
|
|
49
|
+
config: AegisConfig;
|
|
50
|
+
item: WorkItem;
|
|
51
|
+
run: WorkRun;
|
|
52
|
+
approvedBy: string;
|
|
53
|
+
}): Promise<void> {
|
|
54
|
+
const context = await buildAgentContext({
|
|
55
|
+
config: params.config,
|
|
56
|
+
item: params.item,
|
|
57
|
+
run: params.run,
|
|
58
|
+
command: "implement",
|
|
59
|
+
approval: { approvedBy: params.approvedBy, approvedAt: new Date().toISOString() },
|
|
60
|
+
});
|
|
61
|
+
const result = await callBugFixAgent<AgentImplementationResult>(params.config, context);
|
|
62
|
+
const source = sourceFor(params.config, params.item.source);
|
|
63
|
+
|
|
64
|
+
if (result.outcome !== "implemented" || result.confidence === "low") {
|
|
65
|
+
await source.markBlocked(
|
|
66
|
+
params.item,
|
|
67
|
+
result.blocker ??
|
|
68
|
+
result.question ??
|
|
69
|
+
"Aegis found the bug but was not confident enough to open a PR.",
|
|
70
|
+
);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const github = new GitHubClient(params.config);
|
|
75
|
+
const pr = await github.createPullRequest({
|
|
76
|
+
repo: params.item.repo,
|
|
77
|
+
title: result.prTitle ?? `fix: ${params.item.title}`,
|
|
78
|
+
body: result.prBody ?? buildFallbackPrBody(params.item, result),
|
|
79
|
+
head: params.run.branchName,
|
|
80
|
+
base: params.config.baseBranch,
|
|
81
|
+
});
|
|
82
|
+
await source.linkPullRequest(params.item, pr.url);
|
|
83
|
+
await sendTelegramMessage(
|
|
84
|
+
params.config,
|
|
85
|
+
`Aegis opened a PR for ${params.item.identifier}:\n${pr.url}`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function buildAgentContext(params: {
|
|
90
|
+
config: AegisConfig;
|
|
91
|
+
item: WorkItem;
|
|
92
|
+
run: WorkRun;
|
|
93
|
+
command: AgentRunContext["command"];
|
|
94
|
+
approval?: AgentRunContext["approval"];
|
|
95
|
+
}): Promise<AgentRunContext> {
|
|
96
|
+
const github = new GitHubClient(params.config);
|
|
97
|
+
return {
|
|
98
|
+
runId: params.run.id,
|
|
99
|
+
command: params.command,
|
|
100
|
+
mode: params.run.mode,
|
|
101
|
+
contextProfile: params.config.contextProfile,
|
|
102
|
+
item: params.item,
|
|
103
|
+
branchName: params.run.branchName,
|
|
104
|
+
baseBranch: params.config.baseBranch,
|
|
105
|
+
repoCloneUrl: await github.getCloneUrl(params.item.repo),
|
|
106
|
+
production:
|
|
107
|
+
params.config.contextProfile === "production"
|
|
108
|
+
? {
|
|
109
|
+
databaseUrl: params.config.production?.databaseUrl,
|
|
110
|
+
vercelToken: params.config.production?.vercelToken,
|
|
111
|
+
vercelProjectId: params.config.production?.vercelProjectId,
|
|
112
|
+
vercelTeamId: params.config.production?.vercelTeamId,
|
|
113
|
+
logLookbackMinutes: params.config.production?.logLookbackMinutes ?? 45,
|
|
114
|
+
}
|
|
115
|
+
: undefined,
|
|
116
|
+
approval: params.approval,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function callBugFixAgent<T>(config: AegisConfig, context: AgentRunContext): Promise<T> {
|
|
121
|
+
if (!config.workerUrl) {
|
|
122
|
+
throw new Error("AEGIS_WORKER_URL is required so the Worker can invoke its Flue agent");
|
|
123
|
+
}
|
|
124
|
+
const response = await fetch(`${config.workerUrl}/agents/bug-fix/${context.runId}`, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers: { "Content-Type": "application/json" },
|
|
127
|
+
body: JSON.stringify(context),
|
|
128
|
+
});
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
const text = await response.text();
|
|
131
|
+
throw new Error(`Bug-fix agent failed: ${response.status} ${text}`);
|
|
132
|
+
}
|
|
133
|
+
return (await response.json()) as T;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function buildFallbackPrBody(item: WorkItem, result: AgentImplementationResult): string {
|
|
137
|
+
return `## Summary
|
|
138
|
+
${result.technicalSummary}
|
|
139
|
+
|
|
140
|
+
## Human summary
|
|
141
|
+
${result.humanSummary}
|
|
142
|
+
|
|
143
|
+
## Source ticket
|
|
144
|
+
Refs ${item.url}
|
|
145
|
+
|
|
146
|
+
## Files changed
|
|
147
|
+
${result.filesChanged.map((file) => `- \`${file}\``).join("\n") || "- See diff"}
|
|
148
|
+
|
|
149
|
+
## Tests
|
|
150
|
+
${result.testsRun.map((test) => `- ${test}`).join("\n") || "- Not reported"}
|
|
151
|
+
|
|
152
|
+
## Risk
|
|
153
|
+
${result.risk}
|
|
154
|
+
|
|
155
|
+
Generated by Aegis. Human review required.`;
|
|
156
|
+
}
|