@ob1-sg/horizon 0.1.10
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/LICENSE +21 -0
- package/README.md +344 -0
- package/package.json +52 -0
- package/prompts/agent1-linear-reader.md +248 -0
- package/prompts/agent2-worker-implement.md +193 -0
- package/prompts/agent2-worker-oneshot.md +165 -0
- package/prompts/agent2-worker-plan.md +265 -0
- package/prompts/agent2-worker-research.md +329 -0
- package/prompts/agent2-worker-specification.md +279 -0
- package/prompts/agent2-worker-validate.md +199 -0
- package/prompts/agent2-worker.md +44 -0
- package/prompts/agent3-linear-writer.md +275 -0
- package/prompts/fragments/merge-auto.md +300 -0
- package/prompts/fragments/merge-direct.md +112 -0
- package/prompts/fragments/merge-pr.md +156 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Brandon Ong
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
# Horizon - Autonomous Product Development Agent
|
|
2
|
+
|
|
3
|
+
Horizon is an AI-powered autonomous development system that works on Linear tickets without human intervention. It orchestrates multiple AI agents to research, plan, implement, and validate code changes, all while keeping Linear updated with progress.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Global Installation (Recommended)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @ob1-sg/horizon
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Local Project Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install --save-dev @ob1-sg/horizon
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
1. **Install Horizon** (see above)
|
|
22
|
+
|
|
23
|
+
2. **Run Horizon**:
|
|
24
|
+
```bash
|
|
25
|
+
cd your-project
|
|
26
|
+
horizon # Global install
|
|
27
|
+
npx horizon # Local install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
3. **First-run setup** - On first run, Horizon will:
|
|
31
|
+
- Prompt for your Linear API key
|
|
32
|
+
- Auto-detect your Linear team
|
|
33
|
+
- Create necessary directories and configuration
|
|
34
|
+
- Start the development loop
|
|
35
|
+
|
|
36
|
+
4. **Create tickets** in Linear and Horizon will work on them autonomously
|
|
37
|
+
|
|
38
|
+
For advanced configuration, run `horizon config` to change settings like provider, model, and iteration limits.
|
|
39
|
+
|
|
40
|
+
## How It Works
|
|
41
|
+
|
|
42
|
+
Horizon uses three core concepts: **Pods**, **Loops**, and **Agents**.
|
|
43
|
+
|
|
44
|
+
### Pods, Loops, and Agents
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
48
|
+
│ Pod (swift-wyvern) │
|
|
49
|
+
├─────────────────────────────────────────────────────────────────────────┤
|
|
50
|
+
│ │
|
|
51
|
+
│ Loop 1: Ticket RSK-42 │
|
|
52
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
53
|
+
│ │ Agent 1 │ │ Agent 2 │ │ Agent 3 │ │
|
|
54
|
+
│ │ Linear │────▶│ Worker │────▶│ Linear │ │
|
|
55
|
+
│ │ Reader │ │ │ │ Writer │ │
|
|
56
|
+
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
|
57
|
+
│ │
|
|
58
|
+
│ Loop 2: Ticket RSK-43 │
|
|
59
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
60
|
+
│ │ Agent 1 │────▶│ Agent 2 │────▶│ Agent 3 │ │
|
|
61
|
+
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
|
62
|
+
│ │
|
|
63
|
+
│ Loop 3: ... │
|
|
64
|
+
│ │
|
|
65
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- **Pod**: A running instance of Horizon. Each pod gets a unique name (e.g., "swift-wyvern") and continuously processes tickets until stopped. You can run multiple pods in parallel.
|
|
69
|
+
|
|
70
|
+
- **Loop**: One complete cycle of work. Each loop claims a ticket, works on it, and updates Linear. A pod runs loops continuously until there's no more work.
|
|
71
|
+
|
|
72
|
+
- **Agent**: An AI worker that handles one part of the loop. Three agents work in sequence to complete each loop.
|
|
73
|
+
|
|
74
|
+
### The Agent Pipeline
|
|
75
|
+
|
|
76
|
+
1. **Agent 1 (Linear Reader)**: Scans Linear for available tickets, prioritizes by urgency, **claims** the highest-priority ticket, and gathers context.
|
|
77
|
+
|
|
78
|
+
2. **Agent 2 (Worker)**: The developer. Reads code, writes code, runs tests, and commits changes.
|
|
79
|
+
|
|
80
|
+
3. **Agent 3 (Linear Writer)**: Updates Linear with results - posts comments, updates status, and links commits.
|
|
81
|
+
|
|
82
|
+
### Parallel Execution
|
|
83
|
+
|
|
84
|
+
Multiple pods can work on the same codebase simultaneously. Horizon prevents conflicts through **ticket claiming**:
|
|
85
|
+
|
|
86
|
+
1. When Agent 1 finds a ticket to work on, it immediately changes the status to "In Progress"
|
|
87
|
+
2. Other pods see this status and skip the ticket
|
|
88
|
+
3. Each pod works on different tickets, avoiding collisions
|
|
89
|
+
|
|
90
|
+
This lets you scale development by running multiple Horizon pods in parallel - on different machines, in CI, or as background processes.
|
|
91
|
+
|
|
92
|
+
### Linear as State Machine
|
|
93
|
+
|
|
94
|
+
Horizon uses Linear as its state machine. You don't need to configure Horizon or tell it what to work on - just add tickets to Linear and Horizon handles the rest.
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
98
|
+
│ ∞ Needs │────▶│ ∞ Needs │────▶│ ∞ Needs │────▶│ ∞ Needs │────▶│ ∞ Needs │
|
|
99
|
+
│ Research │ │ Spec* │ │ Plan │ │ Implement │ │ Validate │
|
|
100
|
+
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
|
101
|
+
│ │ │ │ │
|
|
102
|
+
▼ ▼ ▼ ▼ ▼
|
|
103
|
+
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
104
|
+
│ ∞ Research │ │ ∞ Spec In │ │ ∞ Plan In │ │∞ Implement │ │ ∞ Validate │
|
|
105
|
+
│ In Progress │ │ Progress* │ │ Progress │ │ In Progress │ │ In Progress │
|
|
106
|
+
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
|
107
|
+
│
|
|
108
|
+
* Specification is optional - only when ▼
|
|
109
|
+
research determines UX decisions are needed ┌─────────────┐
|
|
110
|
+
│ ∞ Done │
|
|
111
|
+
└─────────────┘
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**How it works:**
|
|
115
|
+
|
|
116
|
+
1. **You create a ticket** in Linear and set its status to `∞ Needs Research` (or any "Needs" status)
|
|
117
|
+
2. **Horizon picks it up** - Agent 1 scans for tickets in "Needs" statuses
|
|
118
|
+
3. **Horizon claims it** - Changes status to "In Progress" so other pods skip it
|
|
119
|
+
4. **Horizon works on it** - Agent 2 does the actual development work
|
|
120
|
+
5. **Horizon advances it** - Agent 3 moves the ticket to the next status
|
|
121
|
+
6. **Repeat** until the ticket reaches `∞ Done`
|
|
122
|
+
|
|
123
|
+
This means you can queue up work by creating tickets, and Horizon will process them in priority order. You can also intervene at any point - move a ticket back to a "Needs" status and Horizon will re-do that step.
|
|
124
|
+
|
|
125
|
+
## Directory Structure
|
|
126
|
+
|
|
127
|
+
After running Horizon, your project will have:
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
your-project/
|
|
131
|
+
├── .horizon/ # Runtime data (gitignored)
|
|
132
|
+
│ ├── env # Configuration and credentials
|
|
133
|
+
│ ├── mcp.json # MCP configuration for Claude Code
|
|
134
|
+
│ ├── output/ # Runtime logs
|
|
135
|
+
│ └── attachments/ # Downloaded issue attachments
|
|
136
|
+
├── horizon-docs/ # Work artifacts (committed)
|
|
137
|
+
│ ├── research/ # Research documents
|
|
138
|
+
│ ├── plans/ # Implementation plans
|
|
139
|
+
│ └── validation/ # Validation reports
|
|
140
|
+
└── CLAUDE.md # Your project's Claude instructions
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Configuration
|
|
144
|
+
|
|
145
|
+
### Environment Variables
|
|
146
|
+
|
|
147
|
+
Set these in `.horizon/env` or export them:
|
|
148
|
+
|
|
149
|
+
| Variable | Description | Default |
|
|
150
|
+
|----------|-------------|---------|
|
|
151
|
+
| `LINEAR_API_KEY` | Your Linear API key | (required) |
|
|
152
|
+
| `LINEAR_TEAM_KEY` | Linear team identifier (e.g., "RSK") | (required) |
|
|
153
|
+
| `HORIZON_PROVIDER` | AI provider: "claude" or "codex" | "claude" |
|
|
154
|
+
| `HORIZON_CLAUDE_MODEL` | Claude model: "opus", "sonnet", "haiku" | "opus" |
|
|
155
|
+
| `HORIZON_MAX_ITERATIONS` | Stop after N iterations (0 = unlimited) | 0 |
|
|
156
|
+
| `HORIZON_MERGE_MODE` | Merge mode: "merge" or "pr" | "merge" |
|
|
157
|
+
|
|
158
|
+
### Merge Modes
|
|
159
|
+
|
|
160
|
+
Horizon supports two modes for completing work:
|
|
161
|
+
|
|
162
|
+
**Direct Merge (default)**
|
|
163
|
+
```bash
|
|
164
|
+
export HORIZON_MERGE_MODE=merge
|
|
165
|
+
```
|
|
166
|
+
Horizon merges completed work directly to main. Best for trusted autonomous operation.
|
|
167
|
+
|
|
168
|
+
**Pull Request Mode**
|
|
169
|
+
```bash
|
|
170
|
+
export HORIZON_MERGE_MODE=pr
|
|
171
|
+
```
|
|
172
|
+
Horizon creates a pull request instead of merging. The ticket moves to `∞ Awaiting Merge` status until a human reviews and merges the PR. Best for teams that want human oversight.
|
|
173
|
+
|
|
174
|
+
### Using Codex CLI as Provider
|
|
175
|
+
|
|
176
|
+
When using Codex (`HORIZON_PROVIDER=codex`), configure Linear MCP in Codex:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
codex mcp add linear --url https://mcp.linear.app/mcp
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## CLI Commands
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
horizon # Run the main development loop
|
|
186
|
+
horizon config # Configure Horizon settings
|
|
187
|
+
horizon uninstall # Remove Horizon from current project
|
|
188
|
+
horizon --help # Show help
|
|
189
|
+
horizon --version # Show version
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Updates
|
|
193
|
+
|
|
194
|
+
Horizon automatically checks for updates once per day. When a new version is available, you'll see a notification on startup:
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
Update available: 0.1.3 → 0.1.4
|
|
198
|
+
Run: npm install -g @ob1-sg/horizon@latest
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Update with:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npm install -g @ob1-sg/horizon@latest
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Releasing (Maintainers)
|
|
208
|
+
|
|
209
|
+
Releases are performed via GitHub Actions.
|
|
210
|
+
|
|
211
|
+
### Preflight
|
|
212
|
+
|
|
213
|
+
1. Confirm GitHub repo secrets:
|
|
214
|
+
- `NPM_TOKEN` (required): npm publish token with access to publish `@ob1-sg/horizon`.
|
|
215
|
+
- `RELEASE_TOKEN` (optional): GitHub token/PAT with `contents: write` in case `github.token` can’t push to `main` due to branch protections (also used to create the GitHub Release).
|
|
216
|
+
2. Confirm no other release run is in progress (the workflow uses `concurrency: release`).
|
|
217
|
+
|
|
218
|
+
### Optional: dry run (CI gates only)
|
|
219
|
+
|
|
220
|
+
1. GitHub → Actions → `Release` → Run workflow (branch: `main`)
|
|
221
|
+
2. Inputs:
|
|
222
|
+
- `release_type`: `patch`
|
|
223
|
+
- `npm_tag`: `latest`
|
|
224
|
+
- `dry_run`: `true`
|
|
225
|
+
3. Expectation: build/typecheck/tests run and pass; **no** version bump commit, tag, GitHub Release, or npm publish is created.
|
|
226
|
+
|
|
227
|
+
### Patch release
|
|
228
|
+
|
|
229
|
+
1. GitHub → Actions → `Release` → Run workflow (branch: `main`)
|
|
230
|
+
2. Inputs:
|
|
231
|
+
- `release_type`: `patch` (or `minor` / `major`)
|
|
232
|
+
- `npm_tag`: `latest` (or another intended dist-tag)
|
|
233
|
+
- `dry_run`: `false`
|
|
234
|
+
3. Expected outputs:
|
|
235
|
+
- A commit on `main` with message like `chore(release): vX.Y.Z`
|
|
236
|
+
- A git tag `vX.Y.Z` pushed to the repo
|
|
237
|
+
- A GitHub Release created for `vX.Y.Z` (auto-generated notes)
|
|
238
|
+
- `@ob1-sg/horizon@X.Y.Z` published to npm under the chosen dist-tag
|
|
239
|
+
|
|
240
|
+
### Recovery (partial failures)
|
|
241
|
+
|
|
242
|
+
- **Tag exists but npm publish failed**: GitHub → Actions → `Publish existing ref to npm`
|
|
243
|
+
- `ref`: the tag (e.g. `vX.Y.Z`)
|
|
244
|
+
- `npm_tag`: the intended dist-tag (default `latest`)
|
|
245
|
+
- **npm publish succeeded but GitHub Release creation failed**: create a GitHub Release from the existing tag (UI or `gh release create vX.Y.Z --generate-notes`).
|
|
246
|
+
|
|
247
|
+
### Rollback (only if necessary)
|
|
248
|
+
|
|
249
|
+
If a tag was created but should not exist (and npm publish did not occur):
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
git tag -d vX.Y.Z && git push origin :refs/tags/vX.Y.Z
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Delete the GitHub Release for that tag in the UI if one was created.
|
|
256
|
+
|
|
257
|
+
### Local parity checks (optional)
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
npm ci
|
|
261
|
+
npm run build
|
|
262
|
+
npm run typecheck
|
|
263
|
+
npm test
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Linear Workflow Statuses
|
|
267
|
+
|
|
268
|
+
Horizon creates these statuses in Linear:
|
|
269
|
+
|
|
270
|
+
**Ready statuses** (waiting for Horizon):
|
|
271
|
+
- `∞ Needs Research`
|
|
272
|
+
- `∞ Needs Specification` (optional - when UX decisions are needed)
|
|
273
|
+
- `∞ Needs Plan`
|
|
274
|
+
- `∞ Needs Implement`
|
|
275
|
+
- `∞ Needs Validate`
|
|
276
|
+
|
|
277
|
+
**In Progress statuses** (Horizon is working):
|
|
278
|
+
- `∞ Research In Progress`
|
|
279
|
+
- `∞ Specification In Progress`
|
|
280
|
+
- `∞ Plan In Progress`
|
|
281
|
+
- `∞ Implement In Progress`
|
|
282
|
+
- `∞ Validate In Progress`
|
|
283
|
+
|
|
284
|
+
**Intervention statuses** (requires human action):
|
|
285
|
+
- `∞ Blocked` - Agent needs clarification or decision before proceeding
|
|
286
|
+
- `∞ Awaiting Merge` - Work complete, PR awaiting human review/merge (PR mode only)
|
|
287
|
+
|
|
288
|
+
**Terminal statuses**:
|
|
289
|
+
- `∞ Done`
|
|
290
|
+
- `∞ Canceled`
|
|
291
|
+
|
|
292
|
+
## Writing Good Tickets
|
|
293
|
+
|
|
294
|
+
Horizon works best with clear, specific tickets:
|
|
295
|
+
|
|
296
|
+
**Good ticket**:
|
|
297
|
+
> Add a dark mode toggle to the settings page. Should save preference to localStorage and apply a .dark-theme class to the body.
|
|
298
|
+
|
|
299
|
+
**Tips**:
|
|
300
|
+
- Include acceptance criteria when possible
|
|
301
|
+
- Reference existing code patterns to follow
|
|
302
|
+
- Specify any constraints or requirements
|
|
303
|
+
- Link related issues if dependencies exist
|
|
304
|
+
|
|
305
|
+
## Developing Horizon
|
|
306
|
+
|
|
307
|
+
If you want to contribute or modify Horizon:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Clone the repo
|
|
311
|
+
git clone https://github.com/ob1-sg/horizon
|
|
312
|
+
cd horizon
|
|
313
|
+
|
|
314
|
+
# Install dependencies
|
|
315
|
+
npm install
|
|
316
|
+
|
|
317
|
+
# Build
|
|
318
|
+
npm run build
|
|
319
|
+
|
|
320
|
+
# Run from source
|
|
321
|
+
npm start
|
|
322
|
+
|
|
323
|
+
# Type check
|
|
324
|
+
npm run typecheck
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Prerequisites
|
|
328
|
+
|
|
329
|
+
- Node.js 18+
|
|
330
|
+
- [Claude Code](https://claude.ai/claude-code) or Codex CLI installed
|
|
331
|
+
- A Linear account with API access
|
|
332
|
+
- Git repository initialized
|
|
333
|
+
|
|
334
|
+
## Acknowledgments
|
|
335
|
+
|
|
336
|
+
Horizon builds on ideas and techniques from the AI engineering community:
|
|
337
|
+
|
|
338
|
+
- **[Dex Horthy](https://github.com/humanlayer/advanced-context-engineering-for-coding-agents/blob/main/ace-fca.md)** - For the research/plan/implement pattern that structures how Horizon approaches work
|
|
339
|
+
- **[Geoff Huntley](https://ghuntley.com/ralph/)** - For the Ralph Wiggum technique that inspires Horizon's autonomous agent approach
|
|
340
|
+
- **[Vaibhav @ BoundaryML](https://boundaryml.com/podcast)** - For BAML and the "AI That Works" podcast series on building reliable AI systems
|
|
341
|
+
|
|
342
|
+
## License
|
|
343
|
+
|
|
344
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ob1-sg/horizon",
|
|
3
|
+
"version": "0.1.10",
|
|
4
|
+
"description": "Linear-orchestrated autonomous agent system",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"horizon": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/**/*",
|
|
12
|
+
"prompts/**/*"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"start": "node dist/cli.js",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:watch": "vitest"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18.0.0"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/ob1-sg/horizon.git"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"ai",
|
|
34
|
+
"autonomous",
|
|
35
|
+
"linear",
|
|
36
|
+
"developer",
|
|
37
|
+
"agent",
|
|
38
|
+
"claude",
|
|
39
|
+
"horizon"
|
|
40
|
+
],
|
|
41
|
+
"author": "OB1",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"homepage": "https://github.com/ob1-sg/horizon",
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@linear/sdk": "^31.0.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^20.10.0",
|
|
49
|
+
"typescript": "^5.3.0",
|
|
50
|
+
"vitest": "^4.0.18"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Agent 1: Linear Reader
|
|
2
|
+
|
|
3
|
+
**EXECUTE NOW.** Query Linear and select work for the Worker agent.
|
|
4
|
+
|
|
5
|
+
Your job:
|
|
6
|
+
1. Get all available issue statuses for the team
|
|
7
|
+
2. Get all non-completed issues from Linear
|
|
8
|
+
3. Check for stale `∞ ... In Progress` issues and reset them
|
|
9
|
+
4. Select the highest-priority issue ready for work
|
|
10
|
+
5. Gather full context including related issues
|
|
11
|
+
6. Claim it and output the details for Agent 2
|
|
12
|
+
|
|
13
|
+
## Important: Parallel Execution Environment
|
|
14
|
+
|
|
15
|
+
Multiple agents may be running simultaneously and looking at issues together. This means:
|
|
16
|
+
|
|
17
|
+
1. **Statuses can change at any time** - Another agent may claim an issue between when you fetch and when you try to claim
|
|
18
|
+
2. **Always use fresh data** - Before claiming, re-check the current status to minimize conflicts
|
|
19
|
+
3. **Handle claim failures gracefully** - If claiming fails (issue already claimed), simply move on to the next best issue
|
|
20
|
+
|
|
21
|
+
### Best Practices for Parallel Execution:
|
|
22
|
+
- Prefer issues that have been in their current status longer (less likely to be targeted by other agents)
|
|
23
|
+
- If you see an issue transition to an `∞ ... In Progress` status after your initial fetch, skip it
|
|
24
|
+
- When claiming, verify the status hasn't changed before updating
|
|
25
|
+
- **Pod Continuity**: If an `∞ ... In Progress` issue was claimed by a different loop instance (pod) within the last hour, prefer other available work—this lets the same pod complete all stages of a feature; only consider taking over if no other work is available or the claim is older than 1 hour
|
|
26
|
+
|
|
27
|
+
## Execute These Steps
|
|
28
|
+
|
|
29
|
+
### Step 1: Get Available Statuses
|
|
30
|
+
|
|
31
|
+
Use `mcp__linear__list_issue_statuses` with the team parameter to get all available workflow statuses.
|
|
32
|
+
|
|
33
|
+
**IMPORTANT**: Horizon uses `∞` prefixed statuses for its workflow stages. However, Horizon can pick up work from ANY backlog or todo-like status (not just `∞ Backlog`).
|
|
34
|
+
|
|
35
|
+
This gives you the full list of status names and their types. Look for:
|
|
36
|
+
|
|
37
|
+
**Entry Points (any of these can be picked up for work)**:
|
|
38
|
+
- Any status with type "backlog" (e.g., `Backlog`, `∞ Backlog`, `Triage`, etc.)
|
|
39
|
+
- Any status with type "unstarted" (e.g., `Todo`, `Ready`, etc.) - these are "ready to work" statuses
|
|
40
|
+
- Any `∞ Needs ...` status (issues already in Horizon's workflow)
|
|
41
|
+
|
|
42
|
+
**Note on Status Type Hierarchy**:
|
|
43
|
+
1. First prioritize Horizon's `∞` prefixed statuses (already in workflow)
|
|
44
|
+
2. Then backlog-type statuses (explicitly waiting for work)
|
|
45
|
+
3. Then unstarted-type statuses (like "Todo" - ready but not started)
|
|
46
|
+
|
|
47
|
+
**Horizon Workflow Statuses (use these exact names for stage transitions)**:
|
|
48
|
+
- **Ready statuses** (unstarted):
|
|
49
|
+
- `∞ Needs Research`
|
|
50
|
+
- `∞ Needs Specification`
|
|
51
|
+
- `∞ Needs Plan`
|
|
52
|
+
- `∞ Needs Implement`
|
|
53
|
+
- `∞ Needs Validate`
|
|
54
|
+
- **In Progress statuses** (started):
|
|
55
|
+
- `∞ Research In Progress`
|
|
56
|
+
- `∞ Specification In Progress`
|
|
57
|
+
- `∞ Plan In Progress`
|
|
58
|
+
- `∞ Implement In Progress`
|
|
59
|
+
- `∞ Validate In Progress`
|
|
60
|
+
- `∞ Oneshot In Progress`
|
|
61
|
+
- **Intervention status** (requires human action):
|
|
62
|
+
- `∞ Blocked` - Agent needs clarification or decision before proceeding
|
|
63
|
+
- `∞ Awaiting Merge` - Work complete, PR awaiting human review/merge
|
|
64
|
+
- **Done**: `∞ Done`
|
|
65
|
+
- **Canceled**: `∞ Canceled`
|
|
66
|
+
|
|
67
|
+
If you don't see these `∞` statuses, output NO_WORK with reason "Horizon statuses not initialized".
|
|
68
|
+
|
|
69
|
+
### Step 2: Get Issues (Excluding Completed/Canceled)
|
|
70
|
+
|
|
71
|
+
To avoid cluttering context with completed work, make **separate queries** for the statuses Horizon can work on. Use `mcp__linear__list_issues` with these parameters:
|
|
72
|
+
|
|
73
|
+
**Query 1**: Get backlog and todo issues (entry points for new work)
|
|
74
|
+
|
|
75
|
+
Query ALL backlog-type AND unstarted-type statuses identified in Step 1. This includes not just `∞ Backlog` but any status that represents "ready for work". Make separate calls for each:
|
|
76
|
+
- `state: "∞ Backlog"`, `includeArchived: false`
|
|
77
|
+
- `state: "Backlog"`, `includeArchived: false` (if this status exists)
|
|
78
|
+
- `state: "Todo"`, `includeArchived: false` (if this status exists)
|
|
79
|
+
- Any other backlog-type or unstarted-type statuses found in Step 1
|
|
80
|
+
|
|
81
|
+
**Important**: Always query all entry point statuses. If `∞ Backlog` is empty, there may still be work available in other statuses like `Backlog`, `Todo`, or `Triage`. The goal is to find any work that is ready to be picked up.
|
|
82
|
+
|
|
83
|
+
**Exclusions**: Do NOT pick up issues from standard started-type statuses like `In Progress` or `In Review` - these are being actively worked on by humans. Only pick up from backlog-type and unstarted-type statuses (which represent "waiting for work" states).
|
|
84
|
+
|
|
85
|
+
**Query 2**: Get issues in Horizon workflow ready for work
|
|
86
|
+
Make separate calls for each `∞ Needs *` status:
|
|
87
|
+
- `state: "∞ Needs Research"`, `includeArchived: false`
|
|
88
|
+
- `state: "∞ Needs Specification"`, `includeArchived: false`
|
|
89
|
+
- `state: "∞ Needs Plan"`, `includeArchived: false`
|
|
90
|
+
- `state: "∞ Needs Implement"`, `includeArchived: false`
|
|
91
|
+
- `state: "∞ Needs Validate"`, `includeArchived: false`
|
|
92
|
+
|
|
93
|
+
**Query 3**: Get in-progress issues (to check for stale claims)
|
|
94
|
+
Make separate calls for each `∞ ... In Progress` status:
|
|
95
|
+
- `state: "∞ Research In Progress"`, `includeArchived: false`
|
|
96
|
+
- `state: "∞ Specification In Progress"`, `includeArchived: false`
|
|
97
|
+
- `state: "∞ Plan In Progress"`, `includeArchived: false`
|
|
98
|
+
- `state: "∞ Implement In Progress"`, `includeArchived: false`
|
|
99
|
+
- `state: "∞ Validate In Progress"`, `includeArchived: false`
|
|
100
|
+
- `state: "∞ Oneshot In Progress"`, `includeArchived: false`
|
|
101
|
+
|
|
102
|
+
**Query 4**: Get blocked issues (for awareness, cannot be picked up)
|
|
103
|
+
- `state: "∞ Blocked"`, `includeArchived: false`
|
|
104
|
+
|
|
105
|
+
**Important**: You can make multiple tool calls in parallel within a single message to speed this up. Only use the `∞` prefixed statuses that were confirmed to exist in Step 1.
|
|
106
|
+
|
|
107
|
+
**Do NOT query for**:
|
|
108
|
+
- `∞ Done`, `Done`, `[RL] Done` (completed)
|
|
109
|
+
- `∞ Canceled`, `Canceled`, `[RL] Canceled`, `Duplicate` (canceled)
|
|
110
|
+
- `∞ Awaiting Merge` (waiting for human to merge PR)
|
|
111
|
+
|
|
112
|
+
This approach fetches only actionable issues and avoids wasting context on completed work.
|
|
113
|
+
|
|
114
|
+
### Step 3: Check for Stale "∞ ... In Progress" Issues
|
|
115
|
+
|
|
116
|
+
**Note**: In a multi-agent environment, another agent may be actively working on or may have just completed an issue in progress. Be cautious when resetting.
|
|
117
|
+
|
|
118
|
+
For any issue with an `∞ ... In Progress` status:
|
|
119
|
+
1. Use `mcp__linear__list_comments` to find the most recent "Agent Claimed" comment
|
|
120
|
+
2. Also check for any "Stage Complete" or "Stage Failed" comments that are more recent than the claim
|
|
121
|
+
3. If the claim timestamp is more than 4 hours ago AND there are no recent completion comments:
|
|
122
|
+
- **Re-fetch the issue status** before resetting to ensure it hasn't changed
|
|
123
|
+
- If status is still an `∞ ... In Progress` status: Post a timeout reset comment and update status
|
|
124
|
+
- If status has changed: Another agent completed the work, skip resetting this issue
|
|
125
|
+
|
|
126
|
+
### Step 4: Select the Best Issue
|
|
127
|
+
|
|
128
|
+
**IMPORTANT**: Do NOT list or output all issues. Analyze the issue titles internally and select the single most important issue to work on.
|
|
129
|
+
|
|
130
|
+
#### Hard Filters (must skip these):
|
|
131
|
+
|
|
132
|
+
1. **Blocked by incomplete dependency**: If an issue has a "blocked by" relationship to another issue that is not yet completed, skip it. The blocker must be finished first.
|
|
133
|
+
|
|
134
|
+
2. **Claimed by another agent within the last hour**: Check comments for "Agent Claimed" - if another pod claimed it less than 1 hour ago, skip it.
|
|
135
|
+
|
|
136
|
+
3. **Completed or canceled**: Status type "completed" or "canceled". (Note: These should not appear if Step 2 was followed correctly, but verify as a safety check.)
|
|
137
|
+
|
|
138
|
+
4. **Blocked status**: Issues in `∞ Blocked` status require human intervention and must not be picked up.
|
|
139
|
+
|
|
140
|
+
#### Soft Preferences (use judgment):
|
|
141
|
+
|
|
142
|
+
After filtering, read the **titles** of remaining issues and use your judgment to pick the most important one:
|
|
143
|
+
|
|
144
|
+
- Consider business impact, urgency, and what would be most valuable to complete
|
|
145
|
+
- Prefer issues that are **blocking other issues** - completing them unblocks more work
|
|
146
|
+
- Prefer issues closer to completion (e.g., `∞ Needs Validate` over `∞ Needs Research`)
|
|
147
|
+
- Prefer to avoid issues currently in an `∞ ... In Progress` status by another pod (even if >1 hour old), but this is not a hard blocker if nothing else is available
|
|
148
|
+
|
|
149
|
+
**Do NOT rely on priority labels** - they are often not populated. Use semantic understanding of the issue titles to determine actual importance.
|
|
150
|
+
|
|
151
|
+
#### If nothing passes hard filters:
|
|
152
|
+
|
|
153
|
+
If all issues are either blocked, recently claimed, or completed, output NO_WORK.
|
|
154
|
+
|
|
155
|
+
### Step 5: Gather Full Context
|
|
156
|
+
|
|
157
|
+
Use `mcp__linear__get_issue` with `includeRelations: true`.
|
|
158
|
+
|
|
159
|
+
Also gather:
|
|
160
|
+
- **Parent Issue**: Read parent to understand broader goal
|
|
161
|
+
- **Sub-Issues**: List children to understand scope. Note: Some sub-issues may have been created during the planning stage and already have plans. These will typically be in `∞ Needs Implement` status.
|
|
162
|
+
- **Project**: Note project context
|
|
163
|
+
- **Blocking/Blocked**: Check dependency relationships
|
|
164
|
+
- **Comments**: Read all comments for previous work and clarifications
|
|
165
|
+
|
|
166
|
+
### Step 6: Decide Stage
|
|
167
|
+
|
|
168
|
+
Map the issue's current status to the appropriate stage:
|
|
169
|
+
- Any backlog-type status (e.g., `Backlog`, `∞ Backlog`, `Triage`) → research
|
|
170
|
+
- Any unstarted-type status (e.g., `Todo`, `Ready`) → research
|
|
171
|
+
- `∞ Needs Research` → research
|
|
172
|
+
- `∞ Needs Specification` → specification
|
|
173
|
+
- `∞ Needs Plan` → plan
|
|
174
|
+
- `∞ Needs Implement` → implement
|
|
175
|
+
- `∞ Needs Validate` → validate
|
|
176
|
+
- `∞ Oneshot In Progress` → oneshot (for issues already classified by Agent 2)
|
|
177
|
+
|
|
178
|
+
Use the actual status names from Step 1 to determine the appropriate stage.
|
|
179
|
+
|
|
180
|
+
**Note**: Agent 1 no longer decides whether a ticket is oneshot or staged. Agent 2 makes this determination during the research stage based on actual complexity assessment.
|
|
181
|
+
|
|
182
|
+
### Step 7: Claim the Issue
|
|
183
|
+
|
|
184
|
+
**Important**: Before claiming, re-fetch the issue to confirm it's still available.
|
|
185
|
+
|
|
186
|
+
1. **Re-check status**: Use `mcp__linear__get_issue` to get the current status
|
|
187
|
+
- If the status has changed from what you saw in Step 4, the issue may have been claimed by another agent
|
|
188
|
+
- If now an `∞ ... In Progress` status: Skip this issue and return to Step 4 to select the next best option
|
|
189
|
+
- If still available: Proceed with claiming
|
|
190
|
+
|
|
191
|
+
2. **Claim the issue**:
|
|
192
|
+
- Update the status to the appropriate `∞ ... In Progress` status:
|
|
193
|
+
- `∞ Research In Progress`
|
|
194
|
+
- `∞ Specification In Progress`
|
|
195
|
+
- `∞ Plan In Progress`
|
|
196
|
+
- `∞ Implement In Progress`
|
|
197
|
+
- `∞ Validate In Progress`
|
|
198
|
+
- `∞ Oneshot In Progress`
|
|
199
|
+
- Post a comment (include your loop instance name from the Agent Instance section at the top of your prompt):
|
|
200
|
+
```
|
|
201
|
+
Agent Claimed | {loop instance name} | {TIMESTAMP}
|
|
202
|
+
|
|
203
|
+
**Loop Instance**: {loop instance name}
|
|
204
|
+
**Stage**: {stage}
|
|
205
|
+
**Timeout**: 4 hours
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
3. **Handle claim conflicts**: If the status update fails or you detect another agent's recent claim comment:
|
|
209
|
+
- Do NOT retry claiming this issue
|
|
210
|
+
- Return to Step 4 and select the next best available issue
|
|
211
|
+
- If no other issues are available, output NO_WORK
|
|
212
|
+
|
|
213
|
+
### Step 8: Output for Agent 2
|
|
214
|
+
|
|
215
|
+
Write out all the information Agent 2 needs to do the work:
|
|
216
|
+
|
|
217
|
+
- Issue ID and identifier (e.g., RSK-6)
|
|
218
|
+
- Issue title
|
|
219
|
+
- Full description
|
|
220
|
+
- Stage to execute (research/specification/plan/implement/validate, or oneshot if status is "Oneshot In Progress")
|
|
221
|
+
- Priority
|
|
222
|
+
- Labels
|
|
223
|
+
- Parent issue details (if any)
|
|
224
|
+
- Sub-issues (if any)
|
|
225
|
+
- Blocking/blocked relationships (if any)
|
|
226
|
+
- Project context (if any)
|
|
227
|
+
- Previous comments and any artifact paths mentioned
|
|
228
|
+
- Any other relevant context
|
|
229
|
+
|
|
230
|
+
Just write this naturally - Agent 2 will read your output directly.
|
|
231
|
+
|
|
232
|
+
## If No Work Available
|
|
233
|
+
|
|
234
|
+
If there are no issues to work on, output:
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
NO_WORK
|
|
238
|
+
|
|
239
|
+
Reason: {explain why - all done, all in progress, etc.}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Reminders
|
|
243
|
+
|
|
244
|
+
- Only use Linear MCP tools
|
|
245
|
+
- Don't read filesystem or write code
|
|
246
|
+
- Output everything Agent 2 needs - they cannot access Linear
|
|
247
|
+
- **Parallel execution**: Multiple agents may be running simultaneously. Always verify status before claiming and handle conflicts gracefully.
|
|
248
|
+
- **Fresh data**: When in doubt, re-fetch issue status before making updates
|