loreli 1.0.0 → 2.0.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/README.md +66 -26
- package/package.json +17 -14
- package/packages/action/prompts/action.md +172 -0
- package/packages/action/src/index.js +33 -5
- package/packages/agent/README.md +107 -18
- package/packages/agent/src/backends/claude.js +111 -11
- package/packages/agent/src/backends/codex.js +78 -5
- package/packages/agent/src/backends/cursor.js +104 -27
- package/packages/agent/src/backends/index.js +162 -5
- package/packages/agent/src/cli.js +80 -3
- package/packages/agent/src/discover.js +396 -0
- package/packages/agent/src/factory.js +39 -34
- package/packages/agent/src/models.js +24 -6
- package/packages/classify/README.md +136 -0
- package/packages/classify/prompts/blocker.md +12 -0
- package/packages/classify/prompts/feedback.md +14 -0
- package/packages/classify/prompts/pane-state.md +20 -0
- package/packages/classify/src/index.js +81 -0
- package/packages/config/README.md +156 -91
- package/packages/config/src/defaults.js +32 -21
- package/packages/config/src/index.js +33 -2
- package/packages/config/src/schema.js +57 -39
- package/packages/hub/src/github.js +59 -20
- package/packages/identity/README.md +1 -1
- package/packages/identity/src/index.js +2 -2
- package/packages/knowledge/README.md +86 -106
- package/packages/knowledge/src/index.js +56 -225
- package/packages/mcp/README.md +51 -7
- package/packages/mcp/instructions.md +6 -1
- package/packages/mcp/scaffolding/loreli.yml +115 -77
- package/packages/mcp/scaffolding/mcp-configs/.codex/config.toml +1 -0
- package/packages/mcp/scaffolding/mcp-configs/.cursor/mcp.json +4 -1
- package/packages/mcp/scaffolding/mcp-configs/.mcp.json +4 -1
- package/packages/mcp/src/index.js +45 -16
- package/packages/mcp/src/tools/agent-context.js +44 -0
- package/packages/mcp/src/tools/agents.js +34 -13
- package/packages/mcp/src/tools/context.js +3 -2
- package/packages/mcp/src/tools/github.js +11 -47
- package/packages/mcp/src/tools/hitl.js +19 -6
- package/packages/mcp/src/tools/index.js +2 -1
- package/packages/mcp/src/tools/refactor.js +227 -0
- package/packages/mcp/src/tools/repo.js +44 -0
- package/packages/mcp/src/tools/start.js +159 -90
- package/packages/mcp/src/tools/status.js +5 -2
- package/packages/mcp/src/tools/work.js +18 -8
- package/packages/orchestrator/src/index.js +345 -79
- package/packages/planner/README.md +84 -1
- package/packages/planner/prompts/plan-reviewer.md +109 -0
- package/packages/planner/prompts/planner.md +191 -0
- package/packages/planner/prompts/tiebreaker-reviewer.md +71 -0
- package/packages/planner/src/index.js +326 -111
- package/packages/review/README.md +2 -2
- package/packages/review/prompts/reviewer.md +158 -0
- package/packages/review/src/index.js +196 -76
- package/packages/risk/README.md +81 -22
- package/packages/risk/prompts/risk.md +272 -0
- package/packages/risk/src/index.js +44 -33
- package/packages/tmux/src/index.js +61 -12
- package/packages/workflow/README.md +18 -14
- package/packages/workflow/prompts/preamble.md +14 -0
- package/packages/workflow/src/index.js +191 -12
- package/packages/workspace/README.md +2 -2
- package/packages/workspace/src/index.js +69 -18
package/README.md
CHANGED
|
@@ -52,6 +52,7 @@ export GITHUB_TOKEN=ghp_your_token_here
|
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
To persist it, add the export to your shell profile (`~/.zshrc`, `~/.bashrc`) or create a `.env` file in the project root. Never commit tokens to version control.
|
|
55
|
+
> 🔐 **Secret handling** — Personal access tokens carry the same blast radius as your GitHub account. Keep them in environment variables or a secret manager, scope them to the minimum permissions, and rotate them regularly. See [GitHub’s “Keeping your API credentials secure” guidance](https://docs.github.com/en/rest/authentication/keeping-your-api-credentials-secure) for the official checklist.
|
|
55
56
|
|
|
56
57
|
<details>
|
|
57
58
|
<summary>Using a fine-grained token instead</summary>
|
|
@@ -81,6 +82,12 @@ Hold off on running `loreli` commands until you've completed Step 4, because the
|
|
|
81
82
|
|
|
82
83
|
Loreli runs as an MCP server over stdio. Your IDE or MCP client needs a config entry that tells it how to start Loreli. Add this to your **global** (user-level) MCP settings so the token stays out of project files.
|
|
83
84
|
|
|
85
|
+
Before editing any config file:
|
|
86
|
+
|
|
87
|
+
1. Export `GITHUB_TOKEN` in the shell or source it from a local `.env` so the value never lands in Git history.
|
|
88
|
+
2. Use your client’s environment forwarding rather than pasting literals. Cursor and VS Code expand `${env:NAME}` placeholders inside `mcpServers` blocks, while Claude’s `.mcp.json` honors `${NAME}` tokens per the [Cursor MCP docs](https://docs.cursor.com/ko/context/mcp), [VS Code variable reference](https://code.visualstudio.com/docs/editor/variables-reference), and [Claude Code MCP guide](https://docs.claude.ai/claude-code/mcp).
|
|
89
|
+
3. Keep repo-level `loreli.yml` and workspace configs secret-free; only user-level config or wrapper scripts should reference tokens.
|
|
90
|
+
|
|
84
91
|
**Cursor / VS Code** — open **Settings** > search **MCP** > **Edit in settings.json**, or add directly to your user-level `~/.cursor/mcp.json`:
|
|
85
92
|
|
|
86
93
|
```json
|
|
@@ -90,13 +97,15 @@ Loreli runs as an MCP server over stdio. Your IDE or MCP client needs a config e
|
|
|
90
97
|
"command": "npx",
|
|
91
98
|
"args": ["loreli", "mcp"],
|
|
92
99
|
"env": {
|
|
93
|
-
"GITHUB_TOKEN": "
|
|
100
|
+
"GITHUB_TOKEN": "${env:GITHUB_TOKEN}"
|
|
94
101
|
}
|
|
95
102
|
}
|
|
96
103
|
}
|
|
97
104
|
}
|
|
98
105
|
```
|
|
99
106
|
|
|
107
|
+
Cursor and VS Code both expand `${env:NAME}` placeholders, so this entry reads the token you already exported (`export GITHUB_TOKEN=...`) without storing it in the config. Cursor additionally supports an `envFile` attribute if you prefer pointing at a dedicated `.env` file that lives outside your repos (see the [Cursor MCP docs](https://docs.cursor.com/ko/context/mcp) for the full schema).
|
|
108
|
+
|
|
100
109
|
**Claude Code (CLI)** — add to your user-level config at `~/.claude/.mcp.json`:
|
|
101
110
|
|
|
102
111
|
```json
|
|
@@ -106,14 +115,14 @@ Loreli runs as an MCP server over stdio. Your IDE or MCP client needs a config e
|
|
|
106
115
|
"command": "npx",
|
|
107
116
|
"args": ["loreli", "mcp"],
|
|
108
117
|
"env": {
|
|
109
|
-
"GITHUB_TOKEN": "
|
|
118
|
+
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
|
|
110
119
|
}
|
|
111
120
|
}
|
|
112
121
|
}
|
|
113
122
|
}
|
|
114
123
|
```
|
|
115
124
|
|
|
116
|
-
The `env` block keeps the token scoped to Loreli's process. Alternatively, if `GITHUB_TOKEN` is already exported in your shell profile, you can omit the `env` block entirely — Loreli reads `process.env.GITHUB_TOKEN` at startup.
|
|
125
|
+
The `env` block keeps the token scoped to Loreli's process, and Claude expands `${VAR}` placeholders at load time per the [Claude Code MCP guide](https://docs.claude.ai/claude-code/mcp#environment-variable-expansion), so you only need to manage the token in your shell. Alternatively, if `GITHUB_TOKEN` is already exported in your shell profile, you can omit the `env` block entirely — Loreli reads `process.env.GITHUB_TOKEN` at startup.
|
|
117
126
|
|
|
118
127
|
<details>
|
|
119
128
|
<summary>Claude Desktop</summary>
|
|
@@ -129,12 +138,14 @@ Add to your Claude Desktop config file:
|
|
|
129
138
|
"command": "npx",
|
|
130
139
|
"args": ["loreli", "mcp"],
|
|
131
140
|
"env": {
|
|
132
|
-
"GITHUB_TOKEN": "
|
|
141
|
+
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
|
|
133
142
|
}
|
|
134
143
|
}
|
|
135
144
|
}
|
|
136
145
|
}
|
|
137
146
|
```
|
|
147
|
+
|
|
148
|
+
Claude Desktop shares the same `.mcp.json` schema as the CLI, so `${GITHUB_TOKEN}` resolves against the environment variable available when the desktop app launches. Keep the variable in your login shell or macOS Keychain-backed launch script instead of copying the literal token into this file.
|
|
138
149
|
</details>
|
|
139
150
|
|
|
140
151
|
<details>
|
|
@@ -146,9 +157,10 @@ Add to `~/.codex/config.toml` (user-level):
|
|
|
146
157
|
[mcp_servers.loreli]
|
|
147
158
|
command = "npx"
|
|
148
159
|
args = ["loreli", "mcp"]
|
|
160
|
+
env_vars = ["GITHUB_TOKEN"]
|
|
149
161
|
```
|
|
150
162
|
|
|
151
|
-
Codex
|
|
163
|
+
Codex only forwards a short built-in whitelist of environment variables to STDIO MCP servers; anything sensitive like `GITHUB_TOKEN` must be explicitly listed in `env_vars` so the CLI copies it from your shell into the Loreli process without writing the value to disk. Once you add the line above and export `GITHUB_TOKEN` in your shell, Codex will pass it through on launch per the [Codex config reference](https://developers.openai.com/codex/config-reference) and [docs/example-config](https://fossies.org/linux/codex-rust/docs/example-config.md).
|
|
152
164
|
</details>
|
|
153
165
|
|
|
154
166
|
To confirm your MCP configuration is discoverable by the CLI, run the commands below in a shell where `GITHUB_TOKEN` is set. This matters because the CLI resolves tool calls through the MCP server entry, and without it even `loreli --version` and `loreli tools list` will fail. You should see the version string and a list of available tools.
|
|
@@ -299,13 +311,15 @@ Death snapshots are cleaned up by `Storage.prune()` alongside other session data
|
|
|
299
311
|
|
|
300
312
|
## Agent Backends
|
|
301
313
|
|
|
302
|
-
Loreli supports multiple agent backends. The `BackendRegistry` auto-discovers which are available at startup.
|
|
314
|
+
Loreli supports multiple agent backends. The `BackendRegistry` auto-discovers which are available at startup, including available models for backends that support runtime discovery.
|
|
315
|
+
|
|
316
|
+
| Backend | CLI Binary | Provider | Model Discovery |
|
|
317
|
+
|---------|-----------|----------|----------------|
|
|
318
|
+
| Claude | `claude` | Anthropic | Proxy listing (`/v1/models` or `/models`) when `ANTHROPIC_BASE_URL` is configured; otherwise static defaults |
|
|
319
|
+
| Cursor | `cursor-agent` | Multi-provider | `--list-models` with auto tier classification |
|
|
320
|
+
| Codex | `codex` | OpenAI | Proxy listing (`/v1/models` or `/models`) when `OPENAI_BASE_URL` is configured; otherwise static defaults |
|
|
303
321
|
|
|
304
|
-
|
|
305
|
-
|---------|-----------|----------|-------------|
|
|
306
|
-
| Claude | `claude` | Anthropic | Stays running in tmux, receives messages |
|
|
307
|
-
| Cursor | `cursor-agent` | Multi-provider | Multi-provider via Cursor's model routing |
|
|
308
|
-
| Codex | `codex` | OpenAI | Interactive CLI agent (--full-auto, --no-alt-screen, file-based prompts) |
|
|
322
|
+
Model aliases (`fast`, `balanced`, `powerful`) resolve through: config override > runtime discovery > static defaults > pass-through. See [packages/agent/README.md](packages/agent/README.md) for the full resolution chain and LiteLLM/proxy override docs.
|
|
309
323
|
|
|
310
324
|
Loreli derives review strategy from detected side capability at runtime:
|
|
311
325
|
|
|
@@ -333,7 +347,7 @@ pr:
|
|
|
333
347
|
command: npm test # default pre-PR command; blocks pr/create on failure
|
|
334
348
|
selfReview:
|
|
335
349
|
enabled: true # default: pr/create requires preview + confirm=true
|
|
336
|
-
model: balanced # fast | balanced | powerful
|
|
350
|
+
model: balanced # fast | balanced | powerful (global fallback)
|
|
337
351
|
labels:
|
|
338
352
|
track: true # enable provider/model label tracking
|
|
339
353
|
extra: [] # additional labels applied to all loreli items
|
|
@@ -341,6 +355,9 @@ timeouts:
|
|
|
341
355
|
stall: "10m" # human-readable before agent is considered stalled
|
|
342
356
|
shutdown: "1m" # graceful shutdown before kill
|
|
343
357
|
poll: "2s" # orchestrator poll interval
|
|
358
|
+
rapidDeath: "15s" # startup crash detection window
|
|
359
|
+
proxyDiscovery: "5s" # HTTP timeout for proxy model discovery
|
|
360
|
+
nudge: true # enable tier-1 stall nudge messages
|
|
344
361
|
watch:
|
|
345
362
|
interval: "60s" # reactor tick interval
|
|
346
363
|
maxRounds: 7 # max review rounds before escalation
|
|
@@ -352,11 +369,30 @@ agents:
|
|
|
352
369
|
disallowedTools: # CLI tools denied in agent workspaces
|
|
353
370
|
- gh
|
|
354
371
|
- curl
|
|
355
|
-
|
|
356
|
-
action:
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
372
|
+
workflows: # per-role model, scaling, trace, prompt
|
|
373
|
+
action:
|
|
374
|
+
model: balanced
|
|
375
|
+
maxAgents: 3
|
|
376
|
+
# prompt: .loreli/action.md # optional custom prompt per role
|
|
377
|
+
reviewer:
|
|
378
|
+
model: balanced
|
|
379
|
+
maxAgents: 2
|
|
380
|
+
trace:
|
|
381
|
+
enabled: true
|
|
382
|
+
maxOutputChars: 4000
|
|
383
|
+
risk:
|
|
384
|
+
model: fast
|
|
385
|
+
maxAgents: 3
|
|
386
|
+
skip: false
|
|
387
|
+
trace:
|
|
388
|
+
enabled: true
|
|
389
|
+
maxOutputChars: 2000
|
|
390
|
+
planner:
|
|
391
|
+
model: powerful
|
|
392
|
+
maxAgents: 1
|
|
393
|
+
trace:
|
|
394
|
+
enabled: true
|
|
395
|
+
maxOutputChars: 4000
|
|
360
396
|
```
|
|
361
397
|
|
|
362
398
|
`pr.validation.command` and `pr.selfReview.enabled` harden PR creation quality gates and are enabled by default:
|
|
@@ -403,7 +439,7 @@ See [packages/config/README.md](packages/config/README.md#configurable-base-bran
|
|
|
403
439
|
|
|
404
440
|
## Custom Prompt Extensions
|
|
405
441
|
|
|
406
|
-
Projects can inject custom instructions into agent prompts by setting `
|
|
442
|
+
Projects can inject custom instructions into agent prompts by setting `workflows.{role}.prompt` in `loreli.yml`. Each role (`action`, `reviewer`, `planner`, `risk`) can have a `prompt` key whose value is a file path relative to the repository root. Loreli reads the file from the target repo (via the GitHub API) once per session and prepends its content to the rendered prompt — after the built-in autonomous preamble, before the role-specific template.
|
|
407
443
|
|
|
408
444
|
This mechanism addresses a common need: different repositories have different coding standards, architectural constraints, or domain knowledge that agents should follow. Per-role prompts let you tailor instructions to each role — action agents might need coding standards and API constraints, while reviewers need quality gates and review checklists.
|
|
409
445
|
|
|
@@ -434,24 +470,28 @@ Create prompt files in your repository — for example under `.loreli/`:
|
|
|
434
470
|
</review-standards>
|
|
435
471
|
```
|
|
436
472
|
|
|
437
|
-
Then reference them in `loreli.yml`:
|
|
473
|
+
Then reference them in `loreli.yml` under `workflows`:
|
|
438
474
|
|
|
439
475
|
```yaml
|
|
440
|
-
|
|
441
|
-
action:
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
476
|
+
workflows:
|
|
477
|
+
action:
|
|
478
|
+
prompt: .loreli/action.md
|
|
479
|
+
reviewer:
|
|
480
|
+
prompt: .loreli/review.md
|
|
481
|
+
planner:
|
|
482
|
+
prompt: .loreli/planner.md
|
|
483
|
+
risk:
|
|
484
|
+
prompt: .loreli/risk.md # optional — omit roles that don't need custom prompts
|
|
445
485
|
```
|
|
446
486
|
|
|
447
487
|
After the next `start()`, each agent role receives only its own custom instructions at the top of its prompt, before the role-specific template content. Roles without a configured prompt file are unaffected.
|
|
448
488
|
|
|
449
489
|
### How It Works
|
|
450
490
|
|
|
451
|
-
The custom prompt flows through the same rendering pipeline as the autonomous preamble — `Workflow.render()` and `Workflow.renderFrom()` resolve `
|
|
491
|
+
The custom prompt flows through the same rendering pipeline as the autonomous preamble — `Workflow.render()` and `Workflow.renderFrom()` resolve `workflows.{role}.prompt` from config and prepend the content automatically. The injection order is:
|
|
452
492
|
|
|
453
493
|
1. **Autonomous preamble** — built-in headless operation directives (always present)
|
|
454
|
-
2. **Custom prompt** — role-specific project instructions from `
|
|
494
|
+
2. **Custom prompt** — role-specific project instructions from `workflows.{role}.prompt` (when configured)
|
|
455
495
|
3. **Role template** — the action/planner/reviewer prompt
|
|
456
496
|
|
|
457
497
|
The file is read once from GitHub when the first prompt for that role is rendered and cached for the remainder of the session. If the file is missing or unreadable, rendering continues normally without it — agents are never blocked by a missing custom prompt.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loreli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agentic team orchestration via GitHub — an MCP server that coordinates AI agents using issues, PRs, and reviews as the communication layer.",
|
|
6
6
|
"exports": {
|
|
@@ -21,23 +21,13 @@
|
|
|
21
21
|
"./marker": "./packages/marker/src/index.js",
|
|
22
22
|
"./knowledge": "./packages/knowledge/src/index.js",
|
|
23
23
|
"./context": "./packages/context/src/index.js",
|
|
24
|
+
"./classify": "./packages/classify/src/index.js",
|
|
24
25
|
"./test-utils": "./packages/test-utils/src/index.js",
|
|
25
26
|
"./mcp": "./packages/mcp/src/index.js"
|
|
26
27
|
},
|
|
27
28
|
"bin": {
|
|
28
29
|
"loreli": "./bin/loreli.js"
|
|
29
30
|
},
|
|
30
|
-
"scripts": {
|
|
31
|
-
"pretest": "node --env-file-if-exists=.env scripts/clean-test-repo.js",
|
|
32
|
-
"test": "node --env-file-if-exists=.env --test --test-concurrency=1 --experimental-test-coverage --test-coverage-branches=90 --test-coverage-functions=90 --test-coverage-lines=90 packages/*/test/*.test.js",
|
|
33
|
-
"test:ci": "LORELI_TEST_MODE=unit node --env-file-if-exists=.env --test --test-concurrency=1 packages/*/test/*.test.js",
|
|
34
|
-
"e2e": "node --env-file-if-exists=.env scripts/clean-test-repo.js && node --env-file-if-exists=.env --test --test-concurrency=1 e2e/single/*.test.js e2e/multi/*.test.js",
|
|
35
|
-
"e2e:single": "node --env-file-if-exists=.env scripts/clean-test-repo.js && node --env-file-if-exists=.env --test --test-concurrency=1 e2e/single/*.test.js",
|
|
36
|
-
"e2e:multi": "node --env-file-if-exists=.env scripts/clean-test-repo.js && node --env-file-if-exists=.env --test --test-concurrency=1 e2e/multi/*.test.js",
|
|
37
|
-
"lint:secrets": "npx secretlint \"**/*\"",
|
|
38
|
-
"prepublishOnly": "pnpm lint:secrets",
|
|
39
|
-
"prepare": "node node_modules/pre-commit/install.js && node node_modules/pre-push/install.js"
|
|
40
|
-
},
|
|
41
31
|
"pre-commit": [
|
|
42
32
|
"lint:secrets"
|
|
43
33
|
],
|
|
@@ -45,7 +35,7 @@
|
|
|
45
35
|
"lint:secrets"
|
|
46
36
|
],
|
|
47
37
|
"dependencies": {
|
|
48
|
-
"@mcp-layer/cli": "^1.
|
|
38
|
+
"@mcp-layer/cli": "^1.3.0",
|
|
49
39
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
50
40
|
"@octokit/rest": "^21.1.1",
|
|
51
41
|
"@secretlint/secretlint-rule-preset-recommend": "^11.3.1",
|
|
@@ -77,7 +67,20 @@
|
|
|
77
67
|
],
|
|
78
68
|
"license": "MIT",
|
|
79
69
|
"devDependencies": {
|
|
70
|
+
"@changesets/cli": "^2.29.2",
|
|
80
71
|
"pre-commit": "^1.2.2",
|
|
81
72
|
"pre-push": "^0.1.4"
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"pretest": "node --env-file-if-exists=.env scripts/clean-test-repo.js",
|
|
76
|
+
"test": "node --env-file-if-exists=.env --test --test-concurrency=1 --experimental-test-coverage --test-coverage-branches=90 --test-coverage-functions=90 --test-coverage-lines=90 packages/*/test/*.test.js",
|
|
77
|
+
"test:ci": "LORELI_TEST_MODE=unit node --env-file-if-exists=.env --test --test-concurrency=1 packages/*/test/*.test.js",
|
|
78
|
+
"e2e": "node --env-file-if-exists=.env scripts/clean-test-repo.js && node --env-file-if-exists=.env --test --test-concurrency=1 e2e/single/*.test.js e2e/multi/*.test.js",
|
|
79
|
+
"e2e:single": "node --env-file-if-exists=.env scripts/clean-test-repo.js && node --env-file-if-exists=.env --test --test-concurrency=1 e2e/single/*.test.js",
|
|
80
|
+
"e2e:multi": "node --env-file-if-exists=.env scripts/clean-test-repo.js && node --env-file-if-exists=.env --test --test-concurrency=1 e2e/multi/*.test.js",
|
|
81
|
+
"lint:secrets": "npx secretlint \"**/*\"",
|
|
82
|
+
"changeset": "changeset",
|
|
83
|
+
"changeset:version": "changeset version",
|
|
84
|
+
"changeset:publish": "changeset publish"
|
|
82
85
|
}
|
|
83
|
-
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
You are **{{name}}**, an action agent for the repository **{{{repo}}}**.
|
|
2
|
+
|
|
3
|
+
<instructions>
|
|
4
|
+
|
|
5
|
+
## Objective
|
|
6
|
+
|
|
7
|
+
Claim open issues, implement the work, and create pull requests for review.
|
|
8
|
+
|
|
9
|
+
### Claiming
|
|
10
|
+
|
|
11
|
+
1. Find an unclaimed issue (no claim comment yet).
|
|
12
|
+
2. Use the **comment** tool with `claim: true` to claim it.
|
|
13
|
+
3. First-comment-wins — if someone claimed before you, move to the next issue.
|
|
14
|
+
|
|
15
|
+
### Tools
|
|
16
|
+
|
|
17
|
+
Use these Loreli MCP tools for all GitHub operations:
|
|
18
|
+
|
|
19
|
+
- **pr** (action: `create`) — Open a pull request. Provide `title`, `body`, `head` (branch name), and `reasoning`. Your claimed issue is auto-referenced with "Closes #N". Agent stamp and labels are applied automatically.
|
|
20
|
+
- **read** — Read any issue, PR, or discussion by number. Use this to look up acceptance criteria, understand linked issues, or review discussion context.
|
|
21
|
+
- **comment** — Post a comment on your current work item. Use for progress updates or responding to reviewer feedback. Set `claim: true` to claim an issue.
|
|
22
|
+
- **comment** (abandon: `true`) — Abandon the current issue as fundamentally impossible. Requires `reason`. Posts an abandon comment, labels the issue, closes it, and notifies human reviewers. Use ONLY when the issue describes work that cannot be done (e.g., "remove a directory that doesn't exist", "implement an API that conflicts with existing architecture"). Do NOT use for "this is hard" or "I need more time."
|
|
23
|
+
- **refactor** — Flag a bug, code smell, or tech debt you encounter during implementation. Call **immediately on discovery** — do not wait until your PR is complete. Provide `kind`, `title`, `description` with stable identifiers, and optionally `files` (with `#R<start>-R<end>` range hints) and `impact`. The tool deduplicates and creates a planning discussion automatically.
|
|
24
|
+
- **plan** (action: `escalate`) — Flag a process, architecture, or scope coordination concern as a new discussion. Provide `title` and `body`. Use this for concerns about the overall approach, not for code-level issues (use **refactor** for those).
|
|
25
|
+
|
|
26
|
+
### Working
|
|
27
|
+
|
|
28
|
+
Your workspace is already a checkout of **{{{repo}}}** with git fully configured.
|
|
29
|
+
|
|
30
|
+
**Trust the workspace git setup:**
|
|
31
|
+
- Do NOT clone the repository again, run `git init`, create alternate `.git` directories, or modify git internals.
|
|
32
|
+
- If `ls -la` shows `@` flags on files, that is a macOS extended attribute (harmless). Verify with `git status`, not `ls`.
|
|
33
|
+
- Git credentials and push URLs are pre-configured.
|
|
34
|
+
|
|
35
|
+
You are on branch `{{{branch}}}`. Use this branch for all your work — do not create other branches.
|
|
36
|
+
|
|
37
|
+
### Policy Preflight
|
|
38
|
+
|
|
39
|
+
Before implementation, check whether `AGENTS.md` exists at the repository root. If present, read it and treat it as the authoritative repository policy for coding style, testing, docs, and validation expectations.
|
|
40
|
+
|
|
41
|
+
### Implementation
|
|
42
|
+
|
|
43
|
+
1. Read the issue's acceptance criteria and testing strategy.
|
|
44
|
+
2. Write tests first, following the TDD approach described in the issue.
|
|
45
|
+
3. Implement the code to make the tests pass.
|
|
46
|
+
4. Verify your work (see Verification below).
|
|
47
|
+
5. Use the **pr** tool (action: `create`) with `head` as `{{{branch}}}` only after verification is complete. The tool automatically stages, commits, and pushes your changes before creating the PR.
|
|
48
|
+
|
|
49
|
+
### Verification
|
|
50
|
+
|
|
51
|
+
Before creating a PR, verify your work passes quality checks. Unverified PRs waste the reviewer's session and delay the entire pipeline:
|
|
52
|
+
|
|
53
|
+
1. Run the test suite and confirm all tests pass.
|
|
54
|
+
2. If the repo has a linter or type checker, run it and fix any errors you introduced.
|
|
55
|
+
3. Review your own diff — check for debug statements, commented-out code, or unintended changes.
|
|
56
|
+
|
|
57
|
+
Only create the PR after verification succeeds.
|
|
58
|
+
Your PR `reasoning` must explicitly list the verification commands you ran and confirm they passed.
|
|
59
|
+
|
|
60
|
+
### Completion Gate
|
|
61
|
+
|
|
62
|
+
You are **not done until** exactly one terminal outcome is reached:
|
|
63
|
+
|
|
64
|
+
1. **PR path**: the **pr** tool with `action: create` succeeds for `head: {{{branch}}}` and returns a PR number.
|
|
65
|
+
2. **Abandon path**: the **comment** tool with `abandon: true` is posted with a concrete reason.
|
|
66
|
+
|
|
67
|
+
Do **not** end your turn with only a local summary, passing tests, or `git status`.
|
|
68
|
+
Do **not stop** at the Codex idle prompt before one terminal outcome above is complete.
|
|
69
|
+
If PR creation fails, diagnose and retry with corrected arguments instead of stopping.
|
|
70
|
+
|
|
71
|
+
### Documentation
|
|
72
|
+
|
|
73
|
+
When your changes touch these areas, documentation is mandatory:
|
|
74
|
+
|
|
75
|
+
- **Architectural changes** (new packages, new modules, changed data flow): create or update architecture diagrams in `docs/` and explain the decision rationale.
|
|
76
|
+
- **Error handling** (new error classes, try/catch blocks, error responses): document each error with resolution steps that anyone can follow regardless of skill level.
|
|
77
|
+
- **New APIs** (exported functions, REST endpoints, tool definitions): document parameters, return shapes, and error behavior in JSDoc and the package README.
|
|
78
|
+
|
|
79
|
+
### PR Reasoning
|
|
80
|
+
|
|
81
|
+
When creating a PR, include a `reasoning` parameter summarizing your approach. This is included in the PR body as a collapsible trace for human review.
|
|
82
|
+
|
|
83
|
+
### Rules
|
|
84
|
+
|
|
85
|
+
- Stay within the scope defined in the issue's acceptance criteria. Scope creep in a multi-agent system cascades — your unplanned changes can conflict with parallel agents working on adjacent issues.
|
|
86
|
+
- Do not add features or fixes beyond the issue scope. When you encounter bugs, code smells, or tech debt during your work, use **refactor** immediately to flag them — then continue your assigned task. For process, architecture, or scope coordination concerns, use **plan** (action: `escalate`) instead.
|
|
87
|
+
- Commit with descriptive messages following the repo's conventions.
|
|
88
|
+
- When review comments arrive, address them promptly and use **comment** to request re-review.
|
|
89
|
+
- If approaching context limits, commit and push your progress, then post a comment documenting remaining work before stopping.
|
|
90
|
+
|
|
91
|
+
</instructions>
|
|
92
|
+
|
|
93
|
+
<output_format>
|
|
94
|
+
|
|
95
|
+
Structure your PR `reasoning` parameter with:
|
|
96
|
+
|
|
97
|
+
- **Approach**: What strategy you chose and why.
|
|
98
|
+
- **Key decisions**: Any trade-offs or alternatives you considered.
|
|
99
|
+
- **Verification**: What you tested and how you confirmed correctness.
|
|
100
|
+
|
|
101
|
+
</output_format>
|
|
102
|
+
|
|
103
|
+
<examples>
|
|
104
|
+
|
|
105
|
+
<example title="Well-formed PR reasoning">
|
|
106
|
+
**Approach**: Implemented exponential backoff using a recursive retry wrapper around the existing `fetch` call. Chose composition over modifying `fetch` directly to keep the retry logic testable in isolation.
|
|
107
|
+
|
|
108
|
+
**Key decisions**: Used jitter via `Math.random() * baseDelay * 0.5` to avoid thundering herd. Capped at 3 retries based on the acceptance criteria. Classified 429 and 5xx as retryable; all other 4xx propagate immediately.
|
|
109
|
+
|
|
110
|
+
**Verification**: All 5 test scenarios from the issue pass. Ran `pnpm test` — 47/47 passing. Ran `pnpm lint` — no new warnings. Reviewed diff for debug artifacts — clean.
|
|
111
|
+
</example>
|
|
112
|
+
|
|
113
|
+
</examples>
|
|
114
|
+
|
|
115
|
+
{{#issue}}
|
|
116
|
+
<context>
|
|
117
|
+
|
|
118
|
+
## Assigned Issue
|
|
119
|
+
|
|
120
|
+
You have been assigned the following issue. It is already claimed on your behalf — proceed directly to implementation.
|
|
121
|
+
|
|
122
|
+
{{{issue}}}
|
|
123
|
+
|
|
124
|
+
</context>
|
|
125
|
+
{{/issue}}
|
|
126
|
+
|
|
127
|
+
{{#reviewFeedback}}
|
|
128
|
+
<context>
|
|
129
|
+
|
|
130
|
+
## Review Feedback
|
|
131
|
+
|
|
132
|
+
The reviewer **{{reviewer}}** ({{reviewerProvider}}) has requested changes on **PR #{{number}}**:
|
|
133
|
+
|
|
134
|
+
{{#comments}}
|
|
135
|
+
- {{body}}
|
|
136
|
+
{{/comments}}
|
|
137
|
+
|
|
138
|
+
Address each item:
|
|
139
|
+
|
|
140
|
+
1. Make the requested changes.
|
|
141
|
+
2. Run verification again to confirm nothing is broken.
|
|
142
|
+
3. Push your commits.
|
|
143
|
+
4. Use the **comment** tool to post: "@{{reviewer}} Changes addressed, ready for re-review."
|
|
144
|
+
|
|
145
|
+
</context>
|
|
146
|
+
{{/reviewFeedback}}
|
|
147
|
+
|
|
148
|
+
{{#hitl}}
|
|
149
|
+
<context>
|
|
150
|
+
|
|
151
|
+
## Human In The Loop Feedback
|
|
152
|
+
|
|
153
|
+
A human reviewer has provided feedback on your PR. Their comments are below:
|
|
154
|
+
|
|
155
|
+
{{#feedback}}
|
|
156
|
+
- **@{{author}}**: {{body}}
|
|
157
|
+
{{/feedback}}
|
|
158
|
+
|
|
159
|
+
Address each comment carefully:
|
|
160
|
+
|
|
161
|
+
1. Make the requested changes.
|
|
162
|
+
2. Run verification again to confirm nothing is broken.
|
|
163
|
+
3. Push your commits.
|
|
164
|
+
4. Use the **comment** tool to post: "@{{reviewer}} Changes addressed, ready for re-review."
|
|
165
|
+
5. Do **not** merge — the human reviewer will merge when satisfied.
|
|
166
|
+
|
|
167
|
+
</context>
|
|
168
|
+
{{/hitl}}
|
|
169
|
+
|
|
170
|
+
<agent_metadata>
|
|
171
|
+
Faction: {{faction}} | Provider: {{provider}}
|
|
172
|
+
</agent_metadata>
|
|
@@ -313,7 +313,8 @@ export class ActionWorkflow extends Workflow {
|
|
|
313
313
|
try {
|
|
314
314
|
await reset(cwd, agent.identity.name, issue.number, base, options);
|
|
315
315
|
} catch (err) {
|
|
316
|
-
log.warn(`dispatch: workspace reset failed for ${agent.identity.name}: ${err.message}`);
|
|
316
|
+
log.warn(`dispatch: workspace reset failed for ${agent.identity.name}: ${err.message} — skipping issue`);
|
|
317
|
+
continue;
|
|
317
318
|
}
|
|
318
319
|
|
|
319
320
|
// Dormant agents need respawning; spawned agents are already alive.
|
|
@@ -391,7 +392,8 @@ export class ActionWorkflow extends Workflow {
|
|
|
391
392
|
let agent = null;
|
|
392
393
|
for (const c of allComments) {
|
|
393
394
|
if (has(c.body, 'claim')) {
|
|
394
|
-
|
|
395
|
+
const parsed = parse(c.body, 'claim')?.agent;
|
|
396
|
+
if (parsed) agent = parsed;
|
|
395
397
|
}
|
|
396
398
|
if (has(c.body, 'release') || has(c.body, 'restart')) {
|
|
397
399
|
agent = null;
|
|
@@ -476,6 +478,23 @@ export class ActionWorkflow extends Workflow {
|
|
|
476
478
|
* @throws {Error} When no action agents are available.
|
|
477
479
|
*/
|
|
478
480
|
async work(repo) {
|
|
481
|
+
if (this._dispatching) return [];
|
|
482
|
+
this._dispatching = true;
|
|
483
|
+
|
|
484
|
+
try {
|
|
485
|
+
return await this._work(repo);
|
|
486
|
+
} finally {
|
|
487
|
+
this._dispatching = false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Internal work implementation, guarded by _dispatching in work().
|
|
493
|
+
*
|
|
494
|
+
* @param {string} repo - Repository in "owner/name" format.
|
|
495
|
+
* @returns {Promise<Array<{issue: number, agent: string, reviewer: string|null}>>}
|
|
496
|
+
*/
|
|
497
|
+
async _work(repo) {
|
|
479
498
|
log.info(`work cycle: ${repo}`);
|
|
480
499
|
const actions = this.agents().filter(function active(a) {
|
|
481
500
|
return a.state !== 'dormant' && this._stale(a.identity?.name) !== true;
|
|
@@ -544,7 +563,8 @@ export class ActionWorkflow extends Workflow {
|
|
|
544
563
|
try {
|
|
545
564
|
await reset(cwd, agent.identity.name, issue.number, base, options);
|
|
546
565
|
} catch (err) {
|
|
547
|
-
log.warn(`work: branch reset failed for ${agent.identity.name}: ${err.message}`);
|
|
566
|
+
log.warn(`work: branch reset failed for ${agent.identity.name}: ${err.message} — skipping issue`);
|
|
567
|
+
continue;
|
|
548
568
|
}
|
|
549
569
|
|
|
550
570
|
const issueBranch = `${agent.identity.name}/issue-${issue.number}`;
|
|
@@ -621,15 +641,23 @@ export class ActionWorkflow extends Workflow {
|
|
|
621
641
|
|
|
622
642
|
agent = await this.enlist(provider, 'action', {
|
|
623
643
|
theme: pick(cfg?.get?.('theme')),
|
|
624
|
-
model: cfg?.get?.('model'),
|
|
625
644
|
config: cfg
|
|
626
645
|
});
|
|
627
646
|
}
|
|
628
647
|
|
|
629
|
-
//
|
|
648
|
+
// Fetch PR details so the rework prompt includes branch context.
|
|
649
|
+
// Without this, the agent cannot identify which branch to work on.
|
|
650
|
+
let prDetails;
|
|
651
|
+
try {
|
|
652
|
+
prDetails = await this.hub.pull(repo, pr);
|
|
653
|
+
} catch (err) {
|
|
654
|
+
log.warn(`rework: failed to fetch PR #${pr}: ${err.message}`);
|
|
655
|
+
}
|
|
656
|
+
|
|
630
657
|
await this.dispatch(agent, {
|
|
631
658
|
name: agent.identity.name,
|
|
632
659
|
repo,
|
|
660
|
+
branch: prDetails?.head ?? `${agent.identity.name}/work`,
|
|
633
661
|
faction: agent.identity.faction,
|
|
634
662
|
provider: agent.identity.provider,
|
|
635
663
|
model: agent.identity.model,
|