agent-tasks 1.6.6 → 1.6.8

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 CHANGED
@@ -1,181 +1,191 @@
1
- # agent-tasks
2
-
3
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
- [![Node.js](https://img.shields.io/badge/node-%3E%3D20.11-brightgreen)](https://nodejs.org/)
5
- [![Tests](https://img.shields.io/badge/tests-337%20passing-brightgreen)]()
6
- [![MCP Tools](https://img.shields.io/badge/MCP%20tools-31-purple)]()
7
- [![REST Endpoints](https://img.shields.io/badge/REST-19%20endpoints-orange)]()
8
-
9
- **Pipeline-driven task management for AI coding agents.** An [MCP](https://modelcontextprotocol.io/) server with stage-gated pipelines, multi-agent collaboration, and a real-time kanban dashboard. Tasks flow through configurable stages — `backlog`, `spec`, `plan`, `implement`, `test`, `review`, `done` — with dependency tracking, approval workflows, artifact versioning, and threaded comments.
10
-
11
- Built for AI coding agents (Claude Code, Codex CLI, Gemini CLI, Aider) but works equally well with any MCP client, REST consumer, or WebSocket listener.
12
-
13
- ---
14
-
15
- | Light Theme | Dark Theme |
16
- | -------------------------------------------------------- | ------------------------------------------------------ |
17
- | ![Light mode dashboard](docs/assets/dashboard-light.png) | ![Dark mode dashboard](docs/assets/dashboard-dark.png) |
18
-
19
- ---
20
-
21
- ## Why agent-tasks?
22
-
23
- When you run multiple AI agents on the same codebase, they need a shared task pipeline — not just a flat todo list. They need stages, dependencies, approvals, and visibility.
24
-
25
- ---
26
-
27
- ## Features
28
-
29
- - **Pipeline stages** — configurable per project: `backlog` > `spec` > `plan` > `implement` > `test` > `review` > `done`
30
- - **Task dependencies** — DAG with automatic cycle detection; blocks advancement until resolved
31
- - **Approval workflows** — stage-gated approve/reject with auto-regress on rejection
32
- - **Multi-agent collaboration** — roles (collaborator, reviewer, watcher), claiming, assignment
33
- - **Subtask hierarchies** — parent/child task trees with progress tracking
34
- - **Threaded comments** — async discussions between agents on any task
35
- - **Artifact versioning** — per-stage document attachments with automatic versioning and diff viewer
36
- - **Full-text search** — FTS5 search across task titles and descriptions
37
- - **Real-time kanban dashboard** — drag-and-drop, side panel, inline creation, dark/light theme
38
- - **3 transport layers** — MCP (stdio), REST API (HTTP), WebSocket (real-time events)
39
- - **TodoWrite bridge** — intercepts Claude Code's built-in TodoWrite and syncs to the pipeline
40
- - **Task cleanup hooks** — auto-fails orphaned tasks on session stop and cleans up stale tasks on session start
41
- - **Agent bridge** — notifies connected agents on task events
42
-
43
- ---
44
-
45
- ## Quick Start
46
-
47
- ### Install from npm
48
-
49
- ```bash
50
- npm install -g agent-tasks
51
- ```
52
-
53
- ### Or clone from source
54
-
55
- ```bash
56
- git clone https://github.com/keshrath/agent-tasks.git
57
- cd agent-tasks
58
- npm install
59
- npm run build
60
- ```
61
-
62
- ### Option 1: MCP server (for AI agents)
63
-
64
- Add to your MCP client config (Claude Code, Cline, etc.):
65
-
66
- ```json
67
- {
68
- "mcpServers": {
69
- "agent-tasks": {
70
- "command": "npx",
71
- "args": ["agent-tasks"]
72
- }
73
- }
74
- }
75
- ```
76
-
77
- The dashboard auto-starts at http://localhost:3422 on the first MCP connection.
78
-
79
- ### Option 2: Standalone server (for REST/WebSocket clients)
80
-
81
- ```bash
82
- node dist/server.js --port 3422
83
- ```
84
-
85
- ---
86
-
87
- ## Claude Code Integration
88
-
89
- Add agent-tasks as an MCP server in `~/.claude/settings.json`:
90
-
91
- ```json
92
- {
93
- "mcpServers": {
94
- "agent-tasks": {
95
- "command": "node",
96
- "args": ["/path/to/agent-tasks/dist/index.js"]
97
- }
98
- }
99
- }
100
- ```
101
-
102
- Once configured, Claude Code can use all 31 MCP tools directly — creating tasks, advancing stages, adding artifacts, commenting, and more. See the [Setup Guide](docs/SETUP.md) for detailed integration steps.
103
-
104
- ---
105
-
106
- ## MCP Tools (33)
107
-
108
- | Category | Tools |
109
- | ----------------------- | ----------------------------------------------------------------------------------------------------------- |
110
- | **Task lifecycle** (12) | `task_create`, `task_list`, `task_next`, `task_claim`, `task_advance`, `task_regress`, `task_complete`, ... |
111
- | **Subtasks & deps** (4) | `task_expand`, `task_get_subtasks`, `task_add_dependency`, `task_remove_dependency` |
112
- | **Artifacts** (2) | `task_add_artifact`, `task_get_artifacts` |
113
- | **Comments** (2) | `task_comment`, `task_get_comments` |
114
- | **Collaboration** (2) | `task_add_collaborator`, `task_remove_collaborator` |
115
- | **Approvals** (5) | `task_request_approval`, `task_approve`, `task_reject`, `task_pending_approvals`, `task_review_cycle` |
116
- | **Config & utils** (4) | `task_pipeline_config`, `task_set_session`, `task_cleanup`, `task_generate_rules` |
117
-
118
- See [full API reference](docs/API.md) for detailed descriptions of every tool and endpoint.
119
-
120
- ## REST API (19 endpoints)
121
-
122
- All endpoints return JSON. CORS enabled. See [full API reference](docs/API.md#rest-api-18-endpoints) for details.
123
-
124
- ```
125
- GET /health Health check with version + uptime
126
- GET /api/tasks List tasks (status, stage, project, assignee filters)
127
- GET /api/tasks/:id Get a single task
128
- GET /api/tasks/:id/subtasks Subtasks of a parent
129
- GET /api/tasks/:id/artifacts Artifacts (filter by stage)
130
- GET /api/tasks/:id/comments Comments on a task
131
- GET /api/tasks/:id/dependencies Dependencies for a task
132
- GET /api/dependencies All dependencies across all tasks
133
- GET /api/pipeline Pipeline stage configuration
134
- GET /api/overview Full state dump
135
- GET /api/agents Online agents
136
- GET /api/search?q= Full-text search
137
-
138
- POST /api/tasks Create a new task
139
- PUT /api/tasks/:id Update task fields
140
- PUT /api/tasks/:id/stage Change stage (advance or regress)
141
- POST /api/tasks/:id/comments Add a comment
142
- POST /api/cleanup Trigger manual cleanup
143
- ```
144
-
145
- ---
146
-
147
- ## Testing
148
-
149
- ```bash
150
- npm test # 337 tests across 12 suites
151
- npm run test:watch # Watch mode
152
- npm run test:coverage # Coverage report
153
- npm run check # Full CI: typecheck + lint + format + test
154
- ```
155
-
156
- ---
157
-
158
- ## Environment variables
159
-
160
- | Variable | Default | Description |
161
- | -------------------------- | ------------------------------- | ---------------------------------------------------- |
162
- | `AGENT_TASKS_DB` | `~/.agent-tasks/agent-tasks.db` | SQLite database file path |
163
- | `AGENT_TASKS_PORT` | `3422` | Dashboard HTTP/WebSocket port |
164
- | `AGENT_TASKS_INSTRUCTIONS` | enabled | Set to `0` to disable response-embedded instructions |
165
- | `AGENT_COMM_URL` | `http://localhost:3421` | Agent-comm REST URL for bridge notifications |
166
-
167
- ---
168
-
169
- ## Documentation
170
-
171
- - [API Reference](docs/API.md) — all 31 MCP tools, 19 REST endpoints, WebSocket protocol
172
- - [Architecture](docs/ARCHITECTURE.md) — source structure, design principles, database schema
173
- - [Dashboard](docs/DASHBOARD.md) kanban board features, keyboard shortcuts, screenshots
174
- - [Setup Guide](docs/SETUP.md) — installation, client setup (Claude Code, OpenCode, Cursor, Windsurf), hooks
175
- - [Changelog](CHANGELOG.md)
176
-
177
- ---
178
-
179
- ## License
180
-
181
- MIT see [LICENSE](LICENSE)
1
+ # agent-tasks
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D20.11-brightgreen)](https://nodejs.org/)
5
+ [![Tests](https://img.shields.io/badge/tests-337%20passing-brightgreen)]()
6
+ [![MCP Tools](https://img.shields.io/badge/MCP%20tools-31-purple)]()
7
+ [![REST Endpoints](https://img.shields.io/badge/REST-19%20endpoints-orange)]()
8
+
9
+ **Pipeline-driven task management for AI coding agents.** An [MCP](https://modelcontextprotocol.io/) server with stage-gated pipelines, multi-agent collaboration, and a real-time kanban dashboard. Tasks flow through configurable stages — `backlog`, `spec`, `plan`, `implement`, `test`, `review`, `done` — with dependency tracking, approval workflows, artifact versioning, and threaded comments.
10
+
11
+ Built for AI coding agents (Claude Code, Codex CLI, Gemini CLI, Aider) but works equally well with any MCP client, REST consumer, or WebSocket listener.
12
+
13
+ ---
14
+
15
+ | Light Theme | Dark Theme |
16
+ | -------------------------------------------------------- | ------------------------------------------------------ |
17
+ | ![Light mode dashboard](docs/assets/dashboard-light.png) | ![Dark mode dashboard](docs/assets/dashboard-dark.png) |
18
+
19
+ ---
20
+
21
+ ## Why agent-tasks?
22
+
23
+ When you run multiple AI agents on the same codebase, they need a shared task pipeline — not just a flat todo list. They need stages, dependencies, approvals, and visibility.
24
+
25
+ ---
26
+
27
+ ## Features
28
+
29
+ - **Pipeline stages** — configurable per project: `backlog` > `spec` > `plan` > `implement` > `test` > `review` > `done`
30
+ - **Task dependencies** — DAG with automatic cycle detection; blocks advancement until resolved
31
+ - **Approval workflows** — stage-gated approve/reject with auto-regress on rejection
32
+ - **Multi-agent collaboration** — roles (collaborator, reviewer, watcher), claiming, assignment
33
+ - **Subtask hierarchies** — parent/child task trees with progress tracking
34
+ - **Threaded comments** — async discussions between agents on any task
35
+ - **Artifact versioning** — per-stage document attachments with automatic versioning and diff viewer
36
+ - **Full-text search** — FTS5 search across task titles and descriptions
37
+ - **Real-time kanban dashboard** — drag-and-drop, side panel, inline creation, dark/light theme
38
+ - **3 transport layers** — MCP (stdio), REST API (HTTP), WebSocket (real-time events)
39
+ - **TodoWrite bridge** — intercepts Claude Code's built-in TodoWrite and syncs to the pipeline
40
+ - **Stage gates** — configurable per-project gates that require comments or artifacts before advancing
41
+ - **Heartbeat-based cleanup** — auto-fails tasks from dead agents using agent-comm heartbeat data
42
+ - **Task cleanup hooks** — auto-fails orphaned tasks on session stop and cleans up stale tasks on session start
43
+ - **Agent bridge** — notifies connected agents on task events
44
+
45
+ ---
46
+
47
+ ## Quick Start
48
+
49
+ ### Install from npm
50
+
51
+ ```bash
52
+ npm install -g agent-tasks
53
+ ```
54
+
55
+ ### Or clone from source
56
+
57
+ ```bash
58
+ git clone https://github.com/keshrath/agent-tasks.git
59
+ cd agent-tasks
60
+ npm install
61
+ npm run build
62
+ ```
63
+
64
+ ### Option 1: MCP server (for AI agents)
65
+
66
+ Add to your MCP client config (Claude Code, Cline, etc.):
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "agent-tasks": {
72
+ "command": "npx",
73
+ "args": ["agent-tasks"]
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ The dashboard auto-starts at http://localhost:3422 on the first MCP connection.
80
+
81
+ ### Option 2: Standalone server (for REST/WebSocket clients)
82
+
83
+ ```bash
84
+ node dist/server.js --port 3422
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Claude Code Integration
90
+
91
+ Add agent-tasks as an MCP server in `~/.claude/settings.json`:
92
+
93
+ ```json
94
+ {
95
+ "mcpServers": {
96
+ "agent-tasks": {
97
+ "command": "node",
98
+ "args": ["/path/to/agent-tasks/dist/index.js"]
99
+ }
100
+ }
101
+ }
102
+ ```
103
+
104
+ Once configured, Claude Code can use all 31 MCP tools directly — creating tasks, advancing stages, adding artifacts, commenting, and more. See the [Setup Guide](docs/SETUP.md) for detailed integration steps.
105
+
106
+ ---
107
+
108
+ ## MCP Tools (33)
109
+
110
+ | Category | Tools |
111
+ | ----------------------- | ----------------------------------------------------------------------------------------------------------- |
112
+ | **Task lifecycle** (12) | `task_create`, `task_list`, `task_next`, `task_claim`, `task_advance`, `task_regress`, `task_complete`, ... |
113
+ | **Subtasks & deps** (4) | `task_expand`, `task_get_subtasks`, `task_add_dependency`, `task_remove_dependency` |
114
+ | **Artifacts** (2) | `task_add_artifact`, `task_get_artifacts` |
115
+ | **Comments** (2) | `task_comment`, `task_get_comments` |
116
+ | **Collaboration** (2) | `task_add_collaborator`, `task_remove_collaborator` |
117
+ | **Approvals** (5) | `task_request_approval`, `task_approve`, `task_reject`, `task_pending_approvals`, `task_review_cycle` |
118
+ | **Config & utils** (4) | `task_pipeline_config`, `task_set_session`, `task_cleanup`, `task_generate_rules` |
119
+
120
+ See [full API reference](docs/API.md) for detailed descriptions of every tool and endpoint.
121
+
122
+ ## REST API (19 endpoints)
123
+
124
+ All endpoints return JSON. CORS enabled. See [full API reference](docs/API.md#rest-api-18-endpoints) for details.
125
+
126
+ ```
127
+ GET /health Health check with version + uptime
128
+ GET /api/tasks List tasks (status, stage, project, assignee filters)
129
+ GET /api/tasks/:id Get a single task
130
+ GET /api/tasks/:id/subtasks Subtasks of a parent
131
+ GET /api/tasks/:id/artifacts Artifacts (filter by stage)
132
+ GET /api/tasks/:id/comments Comments on a task
133
+ GET /api/tasks/:id/dependencies Dependencies for a task
134
+ GET /api/dependencies All dependencies across all tasks
135
+ GET /api/pipeline Pipeline stage configuration
136
+ GET /api/overview Full state dump
137
+ GET /api/agents Online agents
138
+ GET /api/search?q= Full-text search
139
+
140
+ POST /api/tasks Create a new task
141
+ PUT /api/tasks/:id Update task fields
142
+ PUT /api/tasks/:id/stage Change stage (advance or regress)
143
+ POST /api/tasks/:id/comments Add a comment
144
+ POST /api/cleanup Trigger manual cleanup
145
+ ```
146
+
147
+ ---
148
+
149
+ ## Testing
150
+
151
+ ```bash
152
+ npm test # 337 tests across 12 suites
153
+ npm run test:watch # Watch mode
154
+ npm run test:coverage # Coverage report
155
+ npm run check # Full CI: typecheck + lint + format + test
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Environment variables
161
+
162
+ | Variable | Default | Description |
163
+ | -------------------------- | ------------------------------- | ---------------------------------------------------- |
164
+ | `AGENT_TASKS_DB` | `~/.agent-tasks/agent-tasks.db` | SQLite database file path |
165
+ | `AGENT_TASKS_PORT` | `3422` | Dashboard HTTP/WebSocket port |
166
+ | `AGENT_TASKS_INSTRUCTIONS` | enabled | Set to `0` to disable response-embedded instructions |
167
+ | `AGENT_COMM_URL` | `http://localhost:3421` | Agent-comm REST URL for bridge notifications |
168
+
169
+ ---
170
+
171
+ ## Dependencies
172
+
173
+ **Required**: Node.js >= 20.11, better-sqlite3 (bundled)
174
+
175
+ **Optional**: [agent-comm](https://github.com/keshrath/agent-comm) — Heartbeat-based task cleanup requires agent-comm running at `AGENT_COMM_URL` (default: `http://localhost:3421`). Without agent-comm, stale agent detection is skipped gracefully. Flow: agent-comm tracks heartbeats → agent-tasks checks heartbeats → auto-fails tasks from dead agents.
176
+
177
+ ---
178
+
179
+ ## Documentation
180
+
181
+ - [API Reference](docs/API.md) — all 31 MCP tools, 19 REST endpoints, WebSocket protocol
182
+ - [Architecture](docs/ARCHITECTURE.md) — source structure, design principles, database schema
183
+ - [Dashboard](docs/DASHBOARD.md) — kanban board features, keyboard shortcuts, screenshots
184
+ - [Setup Guide](docs/SETUP.md) — installation, client setup (Claude Code, OpenCode, Cursor, Windsurf), hooks
185
+ - [Changelog](CHANGELOG.md)
186
+
187
+ ---
188
+
189
+ ## License
190
+
191
+ MIT — see [LICENSE](LICENSE)
package/dist/context.js CHANGED
@@ -22,7 +22,7 @@ export function createContext(dbOptions) {
22
22
  const approvals = new ApprovalService(db, events);
23
23
  const agentBridge = new AgentBridge(events);
24
24
  const retentionDays = parseInt(process.env.AGENT_TASKS_RETENTION_DAYS ?? '30', 10);
25
- const cleanup = new CleanupService(db, retentionDays);
25
+ const cleanup = new CleanupService(db, retentionDays, agentBridge);
26
26
  agentBridge.start();
27
27
  cleanup.start();
28
28
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,oCAAoC;AACpC,EAAE;AACF,sEAAsE;AACtE,sEAAsE;AACtE,gFAAgF;AAEhF,OAAO,EAAE,QAAQ,EAA2B,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAcrD,MAAM,UAAU,aAAa,CAAC,SAAqB;IACjD,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;IAC9B,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,IAAI,mBAAmB,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC1D,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;IACnF,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;IAEtD,WAAW,CAAC,KAAK,EAAE,CAAC;IACpB,OAAO,CAAC,KAAK,EAAE,CAAC;IAEhB,OAAO;QACL,EAAE;QACF,MAAM;QACN,KAAK;QACL,QAAQ;QACR,aAAa;QACb,SAAS;QACT,WAAW;QACX,OAAO;QACP,KAAK;YACH,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,GAAG,IAAI,CAAC;YACd,OAAO,CAAC,IAAI,EAAE,CAAC;YACf,WAAW,CAAC,IAAI,EAAE,CAAC;YACnB,MAAM,CAAC,SAAS,EAAE,CAAC;YACnB,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,oCAAoC;AACpC,EAAE;AACF,sEAAsE;AACtE,sEAAsE;AACtE,gFAAgF;AAEhF,OAAO,EAAE,QAAQ,EAA2B,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAcrD,MAAM,UAAU,aAAa,CAAC,SAAqB;IACjD,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;IAC9B,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,IAAI,mBAAmB,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC1D,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;IACnF,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,EAAE,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;IAEnE,WAAW,CAAC,KAAK,EAAE,CAAC;IACpB,OAAO,CAAC,KAAK,EAAE,CAAC;IAEhB,OAAO;QACL,EAAE;QACF,MAAM;QACN,KAAK;QACL,QAAQ;QACR,aAAa;QACb,SAAS;QACT,WAAW;QACX,OAAO;QACP,KAAK;YACH,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,GAAG,IAAI,CAAC;YACd,OAAO,CAAC,IAAI,EAAE,CAAC;YACf,WAAW,CAAC,IAAI,EAAE,CAAC;YACnB,MAAM,CAAC,SAAS,EAAE,CAAC;YACnB,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC"}
package/dist/db.js CHANGED
@@ -36,50 +36,50 @@ export function transaction(fn) {
36
36
  return database.transaction(fn)();
37
37
  }
38
38
  function initSchema(database) {
39
- database.exec(`
40
- CREATE TABLE IF NOT EXISTS tasks (
41
- id INTEGER PRIMARY KEY AUTOINCREMENT,
42
- title TEXT NOT NULL,
43
- description TEXT,
44
- created_by TEXT NOT NULL,
45
- assigned_to TEXT,
46
- status TEXT NOT NULL DEFAULT 'pending',
47
- stage TEXT NOT NULL DEFAULT 'backlog',
48
- priority INTEGER NOT NULL DEFAULT 0,
49
- project TEXT,
50
- tags TEXT,
51
- result TEXT,
52
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
53
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
54
- )
39
+ database.exec(`
40
+ CREATE TABLE IF NOT EXISTS tasks (
41
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
42
+ title TEXT NOT NULL,
43
+ description TEXT,
44
+ created_by TEXT NOT NULL,
45
+ assigned_to TEXT,
46
+ status TEXT NOT NULL DEFAULT 'pending',
47
+ stage TEXT NOT NULL DEFAULT 'backlog',
48
+ priority INTEGER NOT NULL DEFAULT 0,
49
+ project TEXT,
50
+ tags TEXT,
51
+ result TEXT,
52
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
53
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
54
+ )
55
55
  `);
56
56
  database.exec(`CREATE INDEX IF NOT EXISTS idx_tasks_assigned ON tasks(assigned_to, status)`);
57
57
  database.exec(`CREATE INDEX IF NOT EXISTS idx_tasks_stage ON tasks(stage, priority)`);
58
- database.exec(`
59
- CREATE TABLE IF NOT EXISTS task_dependencies (
60
- task_id INTEGER NOT NULL,
61
- depends_on INTEGER NOT NULL,
62
- PRIMARY KEY (task_id, depends_on)
63
- )
58
+ database.exec(`
59
+ CREATE TABLE IF NOT EXISTS task_dependencies (
60
+ task_id INTEGER NOT NULL,
61
+ depends_on INTEGER NOT NULL,
62
+ PRIMARY KEY (task_id, depends_on)
63
+ )
64
64
  `);
65
- database.exec(`
66
- CREATE TABLE IF NOT EXISTS task_artifacts (
67
- id INTEGER PRIMARY KEY AUTOINCREMENT,
68
- task_id INTEGER NOT NULL,
69
- stage TEXT NOT NULL,
70
- name TEXT NOT NULL,
71
- content TEXT NOT NULL,
72
- created_by TEXT NOT NULL,
73
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
74
- )
65
+ database.exec(`
66
+ CREATE TABLE IF NOT EXISTS task_artifacts (
67
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
68
+ task_id INTEGER NOT NULL,
69
+ stage TEXT NOT NULL,
70
+ name TEXT NOT NULL,
71
+ content TEXT NOT NULL,
72
+ created_by TEXT NOT NULL,
73
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
74
+ )
75
75
  `);
76
76
  database.exec(`CREATE INDEX IF NOT EXISTS idx_task_artifacts_task ON task_artifacts(task_id, stage)`);
77
- database.exec(`
78
- CREATE TABLE IF NOT EXISTS pipeline_config (
79
- project TEXT PRIMARY KEY,
80
- stages TEXT NOT NULL DEFAULT '["backlog","spec","plan","implement","test","review","done","cancelled"]',
81
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
82
- )
77
+ database.exec(`
78
+ CREATE TABLE IF NOT EXISTS pipeline_config (
79
+ project TEXT PRIMARY KEY,
80
+ stages TEXT NOT NULL DEFAULT '["backlog","spec","plan","implement","test","review","done","cancelled"]',
81
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
82
+ )
83
83
  `);
84
84
  }
85
85
  export function queryAll(sql, params) {
@@ -1,9 +1,12 @@
1
1
  import type { Db } from '../storage/database.js';
2
+ import type { AgentBridge } from './agent-bridge.js';
3
+ import type { Task } from '../types.js';
2
4
  export declare class CleanupService {
3
5
  private readonly db;
4
6
  private readonly retentionDays;
7
+ private readonly agentBridge?;
5
8
  private timer;
6
- constructor(db: Db, retentionDays?: number);
9
+ constructor(db: Db, retentionDays?: number, agentBridge?: AgentBridge | undefined);
7
10
  start(): void;
8
11
  stop(): void;
9
12
  run(): {
@@ -18,6 +21,10 @@ export declare class CleanupService {
18
21
  purgedArtifacts: number;
19
22
  purgedApprovals: number;
20
23
  };
24
+ failStaleAgentTasks(timeoutMinutes?: number): Promise<{
25
+ failed: Task[];
26
+ checked: number;
27
+ }>;
21
28
  purgeEverything(): {
22
29
  purgedTasks: number;
23
30
  purgedComments: number;
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/domain/cleanup.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAC;AAEjD,qBAAa,cAAc;IAIvB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAJhC,OAAO,CAAC,KAAK,CAA+C;gBAGzC,EAAE,EAAE,EAAE,EACN,aAAa,GAAE,MAAW;IAG7C,KAAK,IAAI,IAAI;IAQb,IAAI,IAAI,IAAI;IAOZ,GAAG,IAAI;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;KACzB;IAmCD,QAAQ,IAAI;QACV,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;KACzB;IAoBD,eAAe,IAAI;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;KACzB;CAaF"}
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/domain/cleanup.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC,qBAAa,cAAc;IAIvB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAL/B,OAAO,CAAC,KAAK,CAA+C;gBAGzC,EAAE,EAAE,EAAE,EACN,aAAa,GAAE,MAAW,EAC1B,WAAW,CAAC,EAAE,WAAW,YAAA;IAG5C,KAAK,IAAI,IAAI;IAWb,IAAI,IAAI,IAAI;IAOZ,GAAG,IAAI;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;KACzB;IAmCD,QAAQ,IAAI;QACV,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;KACzB;IAoBK,mBAAmB,CACvB,cAAc,GAAE,MAAW,GAC1B,OAAO,CAAC;QAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IA6E/C,eAAe,IAAI;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;KACzB;CAaF"}
@@ -1,17 +1,22 @@
1
1
  export class CleanupService {
2
2
  db;
3
3
  retentionDays;
4
+ agentBridge;
4
5
  timer = null;
5
- constructor(db, retentionDays = 30) {
6
+ constructor(db, retentionDays = 30, agentBridge) {
6
7
  this.db = db;
7
8
  this.retentionDays = retentionDays;
9
+ this.agentBridge = agentBridge;
8
10
  }
9
11
  start() {
10
12
  // Run cleanup every hour
11
13
  this.timer = setInterval(() => this.run(), 60 * 60 * 1000);
12
14
  this.timer.unref();
13
15
  // Run once on start (delayed 10s)
14
- setTimeout(() => this.run(), 10_000).unref();
16
+ setTimeout(() => {
17
+ this.run();
18
+ this.failStaleAgentTasks().catch(() => { });
19
+ }, 10_000).unref();
15
20
  }
16
21
  stop() {
17
22
  if (this.timer) {
@@ -51,6 +56,69 @@ export class CleanupService {
51
56
  purgedApprovals: approvals.changes,
52
57
  };
53
58
  }
59
+ async failStaleAgentTasks(timeoutMinutes = 30) {
60
+ if (!this.agentBridge) {
61
+ return { failed: [], checked: 0 };
62
+ }
63
+ const inProgressTasks = this.db.queryAll(`SELECT * FROM tasks WHERE status = 'in_progress' AND assigned_to IS NOT NULL`);
64
+ if (inProgressTasks.length === 0) {
65
+ return { failed: [], checked: 0 };
66
+ }
67
+ const agentNames = [...new Set(inProgressTasks.map((t) => t.assigned_to))];
68
+ let agents;
69
+ try {
70
+ agents = (await this.agentBridge.fetchAgents());
71
+ }
72
+ catch {
73
+ return { failed: [], checked: agentNames.length };
74
+ }
75
+ if (!Array.isArray(agents) || agents.length === 0) {
76
+ return { failed: [], checked: agentNames.length };
77
+ }
78
+ const agentMap = new Map();
79
+ for (const a of agents) {
80
+ if (a.name)
81
+ agentMap.set(a.name, a);
82
+ }
83
+ const cutoff = Date.now() - timeoutMinutes * 60 * 1000;
84
+ const staleAgents = new Set();
85
+ for (const name of agentNames) {
86
+ const agent = agentMap.get(name);
87
+ if (!agent) {
88
+ staleAgents.add(name);
89
+ continue;
90
+ }
91
+ if (agent.status === 'offline') {
92
+ staleAgents.add(name);
93
+ continue;
94
+ }
95
+ if (agent.last_heartbeat) {
96
+ const hbTime = new Date(agent.last_heartbeat).getTime();
97
+ if (hbTime < cutoff) {
98
+ staleAgents.add(name);
99
+ }
100
+ }
101
+ }
102
+ const failed = [];
103
+ for (const task of inProgressTasks) {
104
+ if (!staleAgents.has(task.assigned_to))
105
+ continue;
106
+ const agent = agentMap.get(task.assigned_to);
107
+ const reason = agent
108
+ ? `Auto-failed: agent "${task.assigned_to}" has not heartbeated for ${timeoutMinutes} minutes`
109
+ : `Auto-failed: agent "${task.assigned_to}" is no longer registered`;
110
+ try {
111
+ this.db.run(`UPDATE tasks SET status = 'failed', result = ?, updated_at = datetime('now') WHERE id = ?`, [reason, task.id]);
112
+ const updated = this.db.queryOne('SELECT * FROM tasks WHERE id = ?', [task.id]);
113
+ if (updated)
114
+ failed.push(updated);
115
+ }
116
+ catch {
117
+ /* skip tasks that can't be failed */
118
+ }
119
+ }
120
+ return { failed, checked: agentNames.length };
121
+ }
54
122
  purgeEverything() {
55
123
  // Nuclear option — delete ALL tasks regardless of status/stage
56
124
  const tasks = this.db.run(`DELETE FROM tasks`);
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/domain/cleanup.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,cAAc;IAIN;IACA;IAJX,KAAK,GAA0C,IAAI,CAAC;IAE5D,YACmB,EAAM,EACN,gBAAwB,EAAE;QAD1B,OAAE,GAAF,EAAE,CAAI;QACN,kBAAa,GAAb,aAAa,CAAa;IAC1C,CAAC;IAEJ,KAAK;QACH,yBAAyB;QACzB,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,kCAAkC;QAClC,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;IAC/C,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;IAED,GAAG;QAMD,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;aAC3E,WAAW,EAAE;aACb,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC;aACjB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAEhB,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CACvB,iFAAiF,EACjF,CAAC,MAAM,CAAC,CACT,CAAC;QAEF,oFAAoF;QACpF,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAC1B,uEAAuE,CACxE,CAAC;QAEF,8DAA8D;QAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAC3B,wEAAwE,CACzE,CAAC;QAEF,0CAA0C;QAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAC3B,0EAA0E,EAC1E,CAAC,MAAM,CAAC,CACT,CAAC;QAEF,OAAO;YACL,WAAW,EAAE,KAAK,CAAC,OAAO;YAC1B,cAAc,EAAE,QAAQ,CAAC,OAAO;YAChC,eAAe,EAAE,SAAS,CAAC,OAAO;YAClC,eAAe,EAAE,SAAS,CAAC,OAAO;SACnC,CAAC;IACJ,CAAC;IAED,QAAQ;QAMN,uEAAuE;QACvE,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CACvB,gGAAgG,CACjG,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAC1B,uEAAuE,CACxE,CAAC;QACF,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAC3B,wEAAwE,CACzE,CAAC;QACF,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;QACtF,OAAO;YACL,WAAW,EAAE,KAAK,CAAC,OAAO;YAC1B,cAAc,EAAE,QAAQ,CAAC,OAAO;YAChC,eAAe,EAAE,SAAS,CAAC,OAAO;YAClC,eAAe,EAAE,SAAS,CAAC,OAAO;SACnC,CAAC;IACJ,CAAC;IAED,eAAe;QAMb,+DAA+D;QAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC5D,OAAO;YACL,WAAW,EAAE,KAAK,CAAC,OAAO;YAC1B,cAAc,EAAE,QAAQ,CAAC,OAAO;YAChC,eAAe,EAAE,SAAS,CAAC,OAAO;YAClC,eAAe,EAAE,SAAS,CAAC,OAAO;SACnC,CAAC;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/domain/cleanup.ts"],"names":[],"mappings":"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"}