@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.
Files changed (167) hide show
  1. package/.agents/skills/bug-fix/SKILL.md +91 -0
  2. package/.flue/agents/bug-fix.ts +107 -0
  3. package/.flue/app.ts +16 -0
  4. package/Dockerfile +8 -0
  5. package/LICENSE +21 -0
  6. package/README.md +251 -0
  7. package/dist-node/agent/bug-fix-skill.d.ts +2 -0
  8. package/dist-node/agent/bug-fix-skill.d.ts.map +1 -0
  9. package/dist-node/agent/bug-fix-skill.js +64 -0
  10. package/dist-node/agent/bug-fix-skill.js.map +1 -0
  11. package/dist-node/agent/client.d.ts +14 -0
  12. package/dist-node/agent/client.d.ts.map +1 -0
  13. package/dist-node/agent/client.js +110 -0
  14. package/dist-node/agent/client.js.map +1 -0
  15. package/dist-node/cli/commands/deploy.d.ts +3 -0
  16. package/dist-node/cli/commands/deploy.d.ts.map +1 -0
  17. package/dist-node/cli/commands/deploy.js +94 -0
  18. package/dist-node/cli/commands/deploy.js.map +1 -0
  19. package/dist-node/cli/commands/init.d.ts +3 -0
  20. package/dist-node/cli/commands/init.d.ts.map +1 -0
  21. package/dist-node/cli/commands/init.js +115 -0
  22. package/dist-node/cli/commands/init.js.map +1 -0
  23. package/dist-node/cli/commands/pickup.d.ts +11 -0
  24. package/dist-node/cli/commands/pickup.d.ts.map +1 -0
  25. package/dist-node/cli/commands/pickup.js +43 -0
  26. package/dist-node/cli/commands/pickup.js.map +1 -0
  27. package/dist-node/cli/commands/setup.d.ts +3 -0
  28. package/dist-node/cli/commands/setup.d.ts.map +1 -0
  29. package/dist-node/cli/commands/setup.js +163 -0
  30. package/dist-node/cli/commands/setup.js.map +1 -0
  31. package/dist-node/cli/commands/status.d.ts +3 -0
  32. package/dist-node/cli/commands/status.d.ts.map +1 -0
  33. package/dist-node/cli/commands/status.js +26 -0
  34. package/dist-node/cli/commands/status.js.map +1 -0
  35. package/dist-node/cli/index.d.ts +3 -0
  36. package/dist-node/cli/index.d.ts.map +1 -0
  37. package/dist-node/cli/index.js +36 -0
  38. package/dist-node/cli/index.js.map +1 -0
  39. package/dist-node/cli/paths.d.ts +7 -0
  40. package/dist-node/cli/paths.d.ts.map +1 -0
  41. package/dist-node/cli/paths.js +29 -0
  42. package/dist-node/cli/paths.js.map +1 -0
  43. package/dist-node/cli/state.d.ts +16 -0
  44. package/dist-node/cli/state.d.ts.map +1 -0
  45. package/dist-node/cli/state.js +214 -0
  46. package/dist-node/cli/state.js.map +1 -0
  47. package/dist-node/core/pickup.d.ts +14 -0
  48. package/dist-node/core/pickup.d.ts.map +1 -0
  49. package/dist-node/core/pickup.js +42 -0
  50. package/dist-node/core/pickup.js.map +1 -0
  51. package/dist-node/github/index.d.ts +3 -0
  52. package/dist-node/github/index.d.ts.map +1 -0
  53. package/dist-node/github/index.js +2 -0
  54. package/dist-node/github/index.js.map +1 -0
  55. package/dist-node/github/manifest.d.ts +34 -0
  56. package/dist-node/github/manifest.d.ts.map +1 -0
  57. package/dist-node/github/manifest.js +71 -0
  58. package/dist-node/github/manifest.js.map +1 -0
  59. package/dist-node/integrations/github.d.ts +29 -0
  60. package/dist-node/integrations/github.d.ts.map +1 -0
  61. package/dist-node/integrations/github.js +199 -0
  62. package/dist-node/integrations/github.js.map +1 -0
  63. package/dist-node/integrations/linear.d.ts +15 -0
  64. package/dist-node/integrations/linear.d.ts.map +1 -0
  65. package/dist-node/integrations/linear.js +146 -0
  66. package/dist-node/integrations/linear.js.map +1 -0
  67. package/dist-node/integrations/telegram.d.ts +24 -0
  68. package/dist-node/integrations/telegram.d.ts.map +1 -0
  69. package/dist-node/integrations/telegram.js +39 -0
  70. package/dist-node/integrations/telegram.js.map +1 -0
  71. package/dist-node/integrations/webhooks.d.ts +3 -0
  72. package/dist-node/integrations/webhooks.d.ts.map +1 -0
  73. package/dist-node/integrations/webhooks.js +37 -0
  74. package/dist-node/integrations/webhooks.js.map +1 -0
  75. package/dist-node/sandbox/github-token.d.ts +7 -0
  76. package/dist-node/sandbox/github-token.d.ts.map +1 -0
  77. package/dist-node/sandbox/github-token.js +66 -0
  78. package/dist-node/sandbox/github-token.js.map +1 -0
  79. package/dist-node/sandbox/index.d.ts +2 -0
  80. package/dist-node/sandbox/index.d.ts.map +1 -0
  81. package/dist-node/sandbox/index.js +2 -0
  82. package/dist-node/sandbox/index.js.map +1 -0
  83. package/dist-node/server/app.d.ts +9 -0
  84. package/dist-node/server/app.d.ts.map +1 -0
  85. package/dist-node/server/app.js +216 -0
  86. package/dist-node/server/app.js.map +1 -0
  87. package/dist-node/shared/config.d.ts +5 -0
  88. package/dist-node/shared/config.d.ts.map +1 -0
  89. package/dist-node/shared/config.js +135 -0
  90. package/dist-node/shared/config.js.map +1 -0
  91. package/dist-node/shared/constants.d.ts +16 -0
  92. package/dist-node/shared/constants.d.ts.map +1 -0
  93. package/dist-node/shared/constants.js +29 -0
  94. package/dist-node/shared/constants.js.map +1 -0
  95. package/dist-node/shared/format.d.ts +12 -0
  96. package/dist-node/shared/format.d.ts.map +1 -0
  97. package/dist-node/shared/format.js +71 -0
  98. package/dist-node/shared/format.js.map +1 -0
  99. package/dist-node/shared/index.d.ts +7 -0
  100. package/dist-node/shared/index.d.ts.map +1 -0
  101. package/dist-node/shared/index.js +7 -0
  102. package/dist-node/shared/index.js.map +1 -0
  103. package/dist-node/shared/readiness.d.ts +3 -0
  104. package/dist-node/shared/readiness.d.ts.map +1 -0
  105. package/dist-node/shared/readiness.js +91 -0
  106. package/dist-node/shared/readiness.js.map +1 -0
  107. package/dist-node/shared/run-state.d.ts +5 -0
  108. package/dist-node/shared/run-state.d.ts.map +1 -0
  109. package/dist-node/shared/run-state.js +26 -0
  110. package/dist-node/shared/run-state.js.map +1 -0
  111. package/dist-node/shared/types.d.ts +230 -0
  112. package/dist-node/shared/types.d.ts.map +1 -0
  113. package/dist-node/shared/types.js +5 -0
  114. package/dist-node/shared/types.js.map +1 -0
  115. package/dist-node/sources/github.d.ts +15 -0
  116. package/dist-node/sources/github.d.ts.map +1 -0
  117. package/dist-node/sources/github.js +44 -0
  118. package/dist-node/sources/github.js.map +1 -0
  119. package/dist-node/sources/index.d.ts +6 -0
  120. package/dist-node/sources/index.d.ts.map +1 -0
  121. package/dist-node/sources/index.js +16 -0
  122. package/dist-node/sources/index.js.map +1 -0
  123. package/dist-node/sources/linear.d.ts +15 -0
  124. package/dist-node/sources/linear.d.ts.map +1 -0
  125. package/dist-node/sources/linear.js +32 -0
  126. package/dist-node/sources/linear.js.map +1 -0
  127. package/dist-node/sources/types.d.ts +15 -0
  128. package/dist-node/sources/types.d.ts.map +1 -0
  129. package/dist-node/sources/types.js +2 -0
  130. package/dist-node/sources/types.js.map +1 -0
  131. package/docs/RELEASING.md +52 -0
  132. package/docs/SETUP.md +439 -0
  133. package/package.json +64 -0
  134. package/src/agent/bug-fix-skill.ts +63 -0
  135. package/src/agent/client.ts +156 -0
  136. package/src/cli/commands/deploy.ts +106 -0
  137. package/src/cli/commands/init.ts +119 -0
  138. package/src/cli/commands/pickup.ts +44 -0
  139. package/src/cli/commands/setup.ts +217 -0
  140. package/src/cli/commands/status.ts +24 -0
  141. package/src/cli/index.ts +38 -0
  142. package/src/cli/paths.ts +29 -0
  143. package/src/cli/state.ts +228 -0
  144. package/src/core/pickup.ts +66 -0
  145. package/src/github/index.ts +2 -0
  146. package/src/github/manifest.ts +97 -0
  147. package/src/integrations/github.ts +241 -0
  148. package/src/integrations/linear.ts +195 -0
  149. package/src/integrations/telegram.ts +48 -0
  150. package/src/integrations/webhooks.ts +53 -0
  151. package/src/sandbox/github-token.ts +92 -0
  152. package/src/sandbox/index.ts +1 -0
  153. package/src/server/app.ts +292 -0
  154. package/src/shared/config.ts +154 -0
  155. package/src/shared/constants.ts +30 -0
  156. package/src/shared/format.ts +84 -0
  157. package/src/shared/index.ts +6 -0
  158. package/src/shared/readiness.ts +116 -0
  159. package/src/shared/run-state.ts +32 -0
  160. package/src/shared/types.ts +257 -0
  161. package/src/sources/github.ts +57 -0
  162. package/src/sources/index.ts +20 -0
  163. package/src/sources/linear.ts +44 -0
  164. package/src/sources/types.ts +16 -0
  165. package/tsconfig.json +25 -0
  166. package/tsconfig.node.json +16 -0
  167. 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
+ }