agent-tasks 1.6.4 → 1.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +11 -11
  3. package/dist/context.d.ts +21 -0
  4. package/dist/context.d.ts.map +1 -0
  5. package/dist/context.js +48 -0
  6. package/dist/context.js.map +1 -0
  7. package/dist/db.d.ts +10 -0
  8. package/dist/db.d.ts.map +1 -0
  9. package/dist/db.js +112 -0
  10. package/dist/db.js.map +1 -0
  11. package/dist/domain/agent-bridge.d.ts +13 -0
  12. package/dist/domain/agent-bridge.d.ts.map +1 -0
  13. package/dist/domain/agent-bridge.js +99 -0
  14. package/dist/domain/agent-bridge.js.map +1 -0
  15. package/dist/domain/approvals.d.ts +19 -0
  16. package/dist/domain/approvals.d.ts.map +1 -0
  17. package/dist/domain/approvals.js +99 -0
  18. package/dist/domain/approvals.js.map +1 -0
  19. package/dist/domain/cleanup.d.ts +28 -0
  20. package/dist/domain/cleanup.d.ts.map +1 -0
  21. package/dist/domain/cleanup.js +68 -0
  22. package/dist/domain/cleanup.js.map +1 -0
  23. package/dist/domain/collaborators.d.ts +15 -0
  24. package/dist/domain/collaborators.d.ts.map +1 -0
  25. package/dist/domain/collaborators.js +72 -0
  26. package/dist/domain/collaborators.js.map +1 -0
  27. package/dist/domain/comments.d.ts +14 -0
  28. package/dist/domain/comments.d.ts.map +1 -0
  29. package/dist/domain/comments.js +66 -0
  30. package/dist/domain/comments.js.map +1 -0
  31. package/dist/domain/events.d.ts +10 -0
  32. package/dist/domain/events.d.ts.map +1 -0
  33. package/dist/domain/events.js +61 -0
  34. package/dist/domain/events.js.map +1 -0
  35. package/dist/domain/rules.d.ts +2 -0
  36. package/dist/domain/rules.d.ts.map +1 -0
  37. package/dist/domain/rules.js +67 -0
  38. package/dist/domain/rules.js.map +1 -0
  39. package/dist/domain/tasks.d.ts +66 -0
  40. package/dist/domain/tasks.d.ts.map +1 -0
  41. package/dist/domain/tasks.js +655 -0
  42. package/dist/domain/tasks.js.map +1 -0
  43. package/dist/domain/validate.d.ts +16 -0
  44. package/dist/domain/validate.d.ts.map +1 -0
  45. package/dist/domain/validate.js +32 -0
  46. package/dist/domain/validate.js.map +1 -0
  47. package/dist/event-bus.d.ts +10 -0
  48. package/dist/event-bus.d.ts.map +1 -0
  49. package/dist/event-bus.js +38 -0
  50. package/dist/event-bus.js.map +1 -0
  51. package/dist/index.d.ts +3 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +132 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/server.d.ts +10 -0
  56. package/dist/server.d.ts.map +1 -0
  57. package/dist/server.js +95 -0
  58. package/dist/server.js.map +1 -0
  59. package/dist/session.d.ts +7 -0
  60. package/dist/session.d.ts.map +1 -0
  61. package/dist/session.js +11 -0
  62. package/dist/session.js.map +1 -0
  63. package/dist/storage/database.d.ts +15 -0
  64. package/dist/storage/database.d.ts.map +1 -0
  65. package/dist/storage/database.js +224 -0
  66. package/dist/storage/database.js.map +1 -0
  67. package/dist/tasks.d.ts +32 -0
  68. package/dist/tasks.d.ts.map +1 -0
  69. package/dist/tasks.js +410 -0
  70. package/dist/tasks.js.map +1 -0
  71. package/dist/transport/mcp.d.ts +6 -0
  72. package/dist/transport/mcp.d.ts.map +1 -0
  73. package/dist/transport/mcp.js +731 -0
  74. package/dist/transport/mcp.js.map +1 -0
  75. package/dist/transport/rest.d.ts +4 -0
  76. package/dist/transport/rest.d.ts.map +1 -0
  77. package/dist/transport/rest.js +553 -0
  78. package/dist/transport/rest.js.map +1 -0
  79. package/dist/transport/ws.d.ts +10 -0
  80. package/dist/transport/ws.d.ts.map +1 -0
  81. package/dist/transport/ws.js +191 -0
  82. package/dist/transport/ws.js.map +1 -0
  83. package/dist/types.d.ts +147 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/types.js +35 -0
  86. package/dist/types.js.map +1 -0
  87. package/dist/ui/app.js +1960 -0
  88. package/dist/ui/index.html +165 -0
  89. package/dist/ui/morphdom.min.js +1 -0
  90. package/dist/ui/styles.css +2512 -0
  91. package/package.json +1 -1
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 keshrath
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.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 keshrath
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 CHANGED
@@ -3,8 +3,8 @@
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
4
  [![Node.js](https://img.shields.io/badge/node-%3E%3D20.11-brightgreen)](https://nodejs.org/)
5
5
  [![Tests](https://img.shields.io/badge/tests-337%20passing-brightgreen)]()
6
- [![MCP Tools](https://img.shields.io/badge/MCP%20tools-33-purple)]()
7
- [![REST Endpoints](https://img.shields.io/badge/REST-18%20endpoints-orange)]()
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
8
 
9
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
10
 
@@ -37,6 +37,7 @@ When you run multiple AI agents on the same codebase, they need a shared task pi
37
37
  - **Real-time kanban dashboard** — drag-and-drop, side panel, inline creation, dark/light theme
38
38
  - **3 transport layers** — MCP (stdio), REST API (HTTP), WebSocket (real-time events)
39
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
40
41
  - **Agent bridge** — notifies connected agents on task events
41
42
 
42
43
  ---
@@ -98,7 +99,7 @@ Add agent-tasks as an MCP server in `~/.claude/settings.json`:
98
99
  }
99
100
  ```
100
101
 
101
- Once configured, Claude Code can use all 33 MCP tools directly — creating tasks, advancing stages, adding artifacts, commenting, and more. See the [Setup Guide](docs/SETUP.md) for detailed integration steps.
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.
102
103
 
103
104
  ---
104
105
 
@@ -114,11 +115,11 @@ Once configured, Claude Code can use all 33 MCP tools directly — creating task
114
115
  | **Approvals** (5) | `task_request_approval`, `task_approve`, `task_reject`, `task_pending_approvals`, `task_review_cycle` |
115
116
  | **Config & utils** (4) | `task_pipeline_config`, `task_set_session`, `task_cleanup`, `task_generate_rules` |
116
117
 
117
- See [full API reference](docs/api.md) for detailed descriptions of every tool and endpoint.
118
+ See [full API reference](docs/API.md) for detailed descriptions of every tool and endpoint.
118
119
 
119
- ## REST API (18 endpoints)
120
+ ## REST API (19 endpoints)
120
121
 
121
- All endpoints return JSON. CORS enabled. See [full API reference](docs/api.md#rest-api-18-endpoints) for details.
122
+ All endpoints return JSON. CORS enabled. See [full API reference](docs/API.md#rest-api-18-endpoints) for details.
122
123
 
123
124
  ```
124
125
  GET /health Health check with version + uptime
@@ -167,11 +168,10 @@ npm run check # Full CI: typecheck + lint + format + test
167
168
 
168
169
  ## Documentation
169
170
 
170
- - [API Reference](docs/api.md) — all 33 MCP tools, 18 REST endpoints, WebSocket protocol
171
- - [Architecture](docs/architecture.md) — source structure, design principles, database schema
172
- - [Dashboard](docs/dashboard.md) — kanban board features, keyboard shortcuts, screenshots
173
- - [Hooks](docs/hooks.md) — TodoWrite bridge hook for Claude Code
174
- - [Setup Guide](docs/SETUP.md) — detailed installation, integration, and configuration
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
175
  - [Changelog](CHANGELOG.md)
176
176
 
177
177
  ---
@@ -0,0 +1,21 @@
1
+ import { type Db, type DbOptions } from './storage/database.js';
2
+ import { EventBus } from './domain/events.js';
3
+ import { TaskService } from './domain/tasks.js';
4
+ import { CommentService } from './domain/comments.js';
5
+ import { CollaboratorService } from './domain/collaborators.js';
6
+ import { ApprovalService } from './domain/approvals.js';
7
+ import { AgentBridge } from './domain/agent-bridge.js';
8
+ import { CleanupService } from './domain/cleanup.js';
9
+ export interface AppContext {
10
+ readonly db: Db;
11
+ readonly events: EventBus;
12
+ readonly tasks: TaskService;
13
+ readonly comments: CommentService;
14
+ readonly collaborators: CollaboratorService;
15
+ readonly approvals: ApprovalService;
16
+ readonly agentBridge: AgentBridge;
17
+ readonly cleanup: CleanupService;
18
+ close(): void;
19
+ }
20
+ export declare function createContext(dbOptions?: DbOptions): AppContext;
21
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAOA,OAAO,EAAY,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,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;AAErD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,mBAAmB,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,UAAU,CAkC/D"}
@@ -0,0 +1,48 @@
1
+ // =============================================================================
2
+ // agent-tasks — Application context
3
+ //
4
+ // Dependency injection root. Creates and wires together all services.
5
+ // Every layer receives its dependencies explicitly — no global state.
6
+ // =============================================================================
7
+ import { createDb } from './storage/database.js';
8
+ import { EventBus } from './domain/events.js';
9
+ import { TaskService } from './domain/tasks.js';
10
+ import { CommentService } from './domain/comments.js';
11
+ import { CollaboratorService } from './domain/collaborators.js';
12
+ import { ApprovalService } from './domain/approvals.js';
13
+ import { AgentBridge } from './domain/agent-bridge.js';
14
+ import { CleanupService } from './domain/cleanup.js';
15
+ export function createContext(dbOptions) {
16
+ const db = createDb(dbOptions);
17
+ const events = new EventBus();
18
+ let closed = false;
19
+ const tasks = new TaskService(db, events);
20
+ const comments = new CommentService(db, events);
21
+ const collaborators = new CollaboratorService(db, events);
22
+ const approvals = new ApprovalService(db, events);
23
+ const agentBridge = new AgentBridge(events);
24
+ const retentionDays = parseInt(process.env.AGENT_TASKS_RETENTION_DAYS ?? '30', 10);
25
+ const cleanup = new CleanupService(db, retentionDays);
26
+ agentBridge.start();
27
+ cleanup.start();
28
+ return {
29
+ db,
30
+ events,
31
+ tasks,
32
+ comments,
33
+ collaborators,
34
+ approvals,
35
+ agentBridge,
36
+ cleanup,
37
+ close() {
38
+ if (closed)
39
+ return;
40
+ closed = true;
41
+ cleanup.stop();
42
+ agentBridge.stop();
43
+ events.removeAll();
44
+ db.close();
45
+ },
46
+ };
47
+ }
48
+ //# sourceMappingURL=context.js.map
@@ -0,0 +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"}
package/dist/db.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import Database from 'better-sqlite3';
2
+ export declare function log(level: string, msg: string): void;
3
+ export declare function initDb(): Promise<Database.Database>;
4
+ export declare function getDb(): Database.Database;
5
+ export declare function transaction<T>(fn: () => T): T;
6
+ export declare function queryAll<T>(sql: string, params?: unknown[]): T[];
7
+ export declare function queryOne<T>(sql: string, params?: unknown[]): T | null;
8
+ export declare function run(sql: string, params?: unknown[]): Database.RunResult;
9
+ export declare function closeDb(): void;
10
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAOtC,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED,wBAAsB,MAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAqBzD;AAED,wBAAgB,KAAK,IAAI,QAAQ,CAAC,QAAQ,CAGzC;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAG7C;AAuDD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,CAIhE;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,IAAI,CAKrE;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,SAAS,CAIvE;AAED,wBAAgB,OAAO,IAAI,IAAI,CAS9B"}
package/dist/db.js ADDED
@@ -0,0 +1,112 @@
1
+ import Database from 'better-sqlite3';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ import { mkdirSync } from 'fs';
5
+ let db = null;
6
+ export function log(level, msg) {
7
+ process.stderr.write(`[agent-tasks:${level}] ${new Date().toISOString()} ${msg}\n`);
8
+ }
9
+ export async function initDb() {
10
+ if (db)
11
+ return db;
12
+ const inMemory = !!process.env.AGENT_TASKS_TEST;
13
+ if (inMemory) {
14
+ db = new Database(':memory:');
15
+ }
16
+ else {
17
+ const dir = join(homedir(), '.claude');
18
+ mkdirSync(dir, { recursive: true });
19
+ const dbPath = process.env.AGENT_TASKS_DB || join(dir, 'agent-tasks.db');
20
+ db = new Database(dbPath);
21
+ }
22
+ db.pragma('journal_mode = WAL');
23
+ db.pragma('busy_timeout = 5000');
24
+ db.pragma('synchronous = NORMAL');
25
+ db.pragma('foreign_keys = ON');
26
+ initSchema(db);
27
+ return db;
28
+ }
29
+ export function getDb() {
30
+ if (!db)
31
+ throw new Error('Database not initialized. Call initDb() first.');
32
+ return db;
33
+ }
34
+ export function transaction(fn) {
35
+ const database = getDb();
36
+ return database.transaction(fn)();
37
+ }
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
+ )
55
+ `);
56
+ database.exec(`CREATE INDEX IF NOT EXISTS idx_tasks_assigned ON tasks(assigned_to, status)`);
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
+ )
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
+ )
75
+ `);
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
+ )
83
+ `);
84
+ }
85
+ export function queryAll(sql, params) {
86
+ const database = getDb();
87
+ const stmt = database.prepare(sql);
88
+ return (params?.length ? stmt.all(...params) : stmt.all());
89
+ }
90
+ export function queryOne(sql, params) {
91
+ const database = getDb();
92
+ const stmt = database.prepare(sql);
93
+ const row = params?.length ? stmt.get(...params) : stmt.get();
94
+ return row ?? null;
95
+ }
96
+ export function run(sql, params) {
97
+ const database = getDb();
98
+ const stmt = database.prepare(sql);
99
+ return params?.length ? stmt.run(...params) : stmt.run();
100
+ }
101
+ export function closeDb() {
102
+ if (db) {
103
+ try {
104
+ db.close();
105
+ }
106
+ catch (e) {
107
+ log('warn', `Error closing db: ${e}`);
108
+ }
109
+ db = null;
110
+ }
111
+ }
112
+ //# sourceMappingURL=db.js.map
package/dist/db.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { EventBus } from './events.js';
2
+ export declare class AgentBridge {
3
+ private readonly events;
4
+ private unsubs;
5
+ constructor(events: EventBus);
6
+ start(): void;
7
+ stop(): void;
8
+ fetchAgents(): Promise<unknown[]>;
9
+ private notify;
10
+ private notifyChannel;
11
+ private postMessage;
12
+ }
13
+ //# sourceMappingURL=agent-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/domain/agent-bridge.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAI5C,qBAAa,WAAW;IAGV,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,MAAM,CAAsB;gBAEP,MAAM,EAAE,QAAQ;IAE7C,KAAK,IAAI,IAAI;IAiDb,IAAI,IAAI,IAAI;IAKZ,WAAW,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAuBjC,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,WAAW;CA6BpB"}
@@ -0,0 +1,99 @@
1
+ // =============================================================================
2
+ // agent-tasks — Agent-comm bridge
3
+ //
4
+ // Listens to task events and forwards notifications to agents via
5
+ // agent-comm's REST API. Lightweight runtime integration — no npm dependency.
6
+ // =============================================================================
7
+ import http from 'http';
8
+ const AGENT_COMM_URL = process.env.AGENT_COMM_URL || 'http://localhost:3421';
9
+ export class AgentBridge {
10
+ events;
11
+ unsubs = [];
12
+ constructor(events) {
13
+ this.events = events;
14
+ }
15
+ start() {
16
+ this.unsubs.push(this.events.on('task:claimed', (e) => {
17
+ const task = e.data.task;
18
+ if (task.assigned_to) {
19
+ this.notify(`Task #${task.id} "${task.title}" has been assigned to you.`, task.assigned_to);
20
+ }
21
+ }));
22
+ this.unsubs.push(this.events.on('task:advanced', (e) => {
23
+ const task = e.data.task;
24
+ const toStage = e.data.to_stage;
25
+ if (task.assigned_to) {
26
+ this.notify(`Task #${task.id} "${task.title}" advanced to ${toStage}.`, task.assigned_to);
27
+ }
28
+ }));
29
+ this.unsubs.push(this.events.on('comment:created', (e) => {
30
+ const comment = e.data.comment;
31
+ this.notifyChannel(`Comment on task #${comment.task_id} by ${comment.agent_id}: ${comment.content.slice(0, 100)}`);
32
+ }));
33
+ this.unsubs.push(this.events.on('approval:requested', (e) => {
34
+ const approval = e.data.approval;
35
+ if (approval.reviewer) {
36
+ this.notify(`Approval requested for task #${approval.task_id} at stage ${approval.stage}.`, approval.reviewer);
37
+ }
38
+ }));
39
+ }
40
+ stop() {
41
+ for (const unsub of this.unsubs)
42
+ unsub();
43
+ this.unsubs = [];
44
+ }
45
+ fetchAgents() {
46
+ return new Promise((resolve) => {
47
+ const url = new URL('/api/agents', AGENT_COMM_URL);
48
+ http
49
+ .get(url, { timeout: 3000 }, (res) => {
50
+ let body = '';
51
+ res.on('data', (c) => (body += c));
52
+ res.on('end', () => {
53
+ try {
54
+ resolve(JSON.parse(body));
55
+ }
56
+ catch {
57
+ resolve([]);
58
+ }
59
+ });
60
+ })
61
+ .on('error', () => resolve([]))
62
+ .on('timeout', function () {
63
+ this.destroy();
64
+ resolve([]);
65
+ });
66
+ });
67
+ }
68
+ notify(content, agentName) {
69
+ this.postMessage({ to: agentName, content }).catch(() => { });
70
+ }
71
+ notifyChannel(content) {
72
+ this.postMessage({ channel: 'general', content }).catch(() => { });
73
+ }
74
+ postMessage(payload) {
75
+ return new Promise((resolve) => {
76
+ const data = JSON.stringify({ ...payload, from: 'agent-tasks' });
77
+ const url = new URL('/api/messages', AGENT_COMM_URL);
78
+ const req = http.request({
79
+ hostname: url.hostname,
80
+ port: url.port,
81
+ path: url.pathname,
82
+ method: 'POST',
83
+ headers: {
84
+ 'Content-Type': 'application/json',
85
+ 'Content-Length': Buffer.byteLength(data),
86
+ },
87
+ timeout: 3000,
88
+ }, () => resolve());
89
+ req.on('error', () => resolve());
90
+ req.on('timeout', () => {
91
+ req.destroy();
92
+ resolve();
93
+ });
94
+ req.write(data);
95
+ req.end();
96
+ });
97
+ }
98
+ }
99
+ //# sourceMappingURL=agent-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-bridge.js","sourceRoot":"","sources":["../../src/domain/agent-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { Db } from '../storage/database.js';
2
+ import type { EventBus } from './events.js';
3
+ import type { TaskApproval } from '../types.js';
4
+ export declare class ApprovalService {
5
+ private readonly db;
6
+ private readonly events;
7
+ constructor(db: Db, events: EventBus);
8
+ request(taskId: number, stage: string, reviewer?: string): TaskApproval;
9
+ approve(approvalId: number, reviewer: string, comment?: string): TaskApproval;
10
+ reject(approvalId: number, reviewer: string, comment?: string): TaskApproval;
11
+ getPending(reviewer?: string): TaskApproval[];
12
+ getForTask(taskId: number): TaskApproval[];
13
+ isApprovalRequired(stage: string, project?: string): boolean;
14
+ hasPendingApproval(taskId: number, stage: string): boolean;
15
+ isApproved(taskId: number, stage: string): boolean;
16
+ private validateReviewer;
17
+ private resolve;
18
+ }
19
+ //# sourceMappingURL=approvals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../../src/domain/approvals.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,aAAa,CAAC;AAIhE,qBAAa,eAAe;IAExB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,QAAQ;IAGnC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY;IAiCvE,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY;IAK7E,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY;IAM5E,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE;IAY7C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE;IAO1C,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO;IAkB5D,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAQ1D,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAQlD,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,OAAO;CA4BhB"}
@@ -0,0 +1,99 @@
1
+ // =============================================================================
2
+ // agent-tasks — Approval domain service
3
+ //
4
+ // Stage-gated approvals for pipeline advancement.
5
+ // =============================================================================
6
+ import { NotFoundError, ConflictError, ValidationError } from '../types.js';
7
+ import { rejectNullBytes, rejectControlChars, MAX_AGENT_ID_LENGTH } from './validate.js';
8
+ export class ApprovalService {
9
+ db;
10
+ events;
11
+ constructor(db, events) {
12
+ this.db = db;
13
+ this.events = events;
14
+ }
15
+ request(taskId, stage, reviewer) {
16
+ const task = this.db.queryOne('SELECT id, project FROM tasks WHERE id = ?', [taskId]);
17
+ if (!task)
18
+ throw new NotFoundError('Task', taskId);
19
+ if (reviewer) {
20
+ this.validateReviewer(reviewer);
21
+ }
22
+ const existing = this.db.queryOne(`SELECT * FROM task_approvals WHERE task_id = ? AND stage = ? AND status = 'pending'`, [taskId, stage]);
23
+ if (existing)
24
+ throw new ConflictError(`Pending approval already exists for task ${taskId} at stage ${stage}.`);
25
+ const result = this.db.run(`INSERT INTO task_approvals (task_id, stage, reviewer) VALUES (?, ?, ?)`, [taskId, stage, reviewer ?? null]);
26
+ const approval = this.db.queryOne('SELECT * FROM task_approvals WHERE id = ?', [
27
+ Number(result.lastInsertRowid),
28
+ ]);
29
+ this.events.emit('approval:requested', { approval });
30
+ return approval;
31
+ }
32
+ approve(approvalId, reviewer, comment) {
33
+ this.validateReviewer(reviewer);
34
+ return this.resolve(approvalId, 'approved', reviewer, comment);
35
+ }
36
+ reject(approvalId, reviewer, comment) {
37
+ this.validateReviewer(reviewer);
38
+ if (!comment?.trim())
39
+ throw new ValidationError('Rejection requires a comment.');
40
+ return this.resolve(approvalId, 'rejected', reviewer, comment);
41
+ }
42
+ getPending(reviewer) {
43
+ if (reviewer) {
44
+ return this.db.queryAll(`SELECT * FROM task_approvals WHERE status = 'pending' AND (reviewer = ? OR reviewer IS NULL) ORDER BY requested_at ASC`, [reviewer]);
45
+ }
46
+ return this.db.queryAll(`SELECT * FROM task_approvals WHERE status = 'pending' ORDER BY requested_at ASC`);
47
+ }
48
+ getForTask(taskId) {
49
+ return this.db.queryAll('SELECT * FROM task_approvals WHERE task_id = ? ORDER BY requested_at DESC', [taskId]);
50
+ }
51
+ isApprovalRequired(stage, project) {
52
+ if (!project)
53
+ return false;
54
+ const config = this.db.queryOne('SELECT * FROM pipeline_config WHERE project = ?', [project]);
55
+ if (!config?.approval_config)
56
+ return false;
57
+ try {
58
+ const approvalConfig = JSON.parse(config.approval_config);
59
+ return !!approvalConfig[stage]?.required;
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
65
+ hasPendingApproval(taskId, stage) {
66
+ const pending = this.db.queryOne(`SELECT id FROM task_approvals WHERE task_id = ? AND stage = ? AND status = 'pending'`, [taskId, stage]);
67
+ return !!pending;
68
+ }
69
+ isApproved(taskId, stage) {
70
+ const approved = this.db.queryOne(`SELECT id FROM task_approvals WHERE task_id = ? AND stage = ? AND status = 'approved'`, [taskId, stage]);
71
+ return !!approved;
72
+ }
73
+ validateReviewer(reviewer) {
74
+ rejectNullBytes(reviewer, 'reviewer');
75
+ rejectControlChars(reviewer, 'reviewer');
76
+ if (reviewer.length > MAX_AGENT_ID_LENGTH) {
77
+ throw new ValidationError(`Reviewer name too long (max ${MAX_AGENT_ID_LENGTH} chars).`);
78
+ }
79
+ }
80
+ resolve(approvalId, status, reviewer, comment) {
81
+ const approval = this.db.queryOne('SELECT * FROM task_approvals WHERE id = ?', [
82
+ approvalId,
83
+ ]);
84
+ if (!approval)
85
+ throw new NotFoundError('Approval', approvalId);
86
+ if (approval.status !== 'pending') {
87
+ throw new ConflictError(`Approval ${approvalId} is already ${approval.status}.`);
88
+ }
89
+ this.db.run(`UPDATE task_approvals SET status = ?, reviewer = ?, resolved_at = datetime('now'), comment = ? WHERE id = ?`, [status, reviewer, comment ?? null, approvalId]);
90
+ const resolved = this.db.queryOne('SELECT * FROM task_approvals WHERE id = ?', [
91
+ approvalId,
92
+ ]);
93
+ this.events.emit(status === 'approved' ? 'approval:approved' : 'approval:rejected', {
94
+ approval: resolved,
95
+ });
96
+ return resolved;
97
+ }
98
+ }
99
+ //# sourceMappingURL=approvals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvals.js","sourceRoot":"","sources":["../../src/domain/approvals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { Db } from '../storage/database.js';
2
+ export declare class CleanupService {
3
+ private readonly db;
4
+ private readonly retentionDays;
5
+ private timer;
6
+ constructor(db: Db, retentionDays?: number);
7
+ start(): void;
8
+ stop(): void;
9
+ run(): {
10
+ purgedTasks: number;
11
+ purgedComments: number;
12
+ purgedArtifacts: number;
13
+ purgedApprovals: number;
14
+ };
15
+ purgeAll(): {
16
+ purgedTasks: number;
17
+ purgedComments: number;
18
+ purgedArtifacts: number;
19
+ purgedApprovals: number;
20
+ };
21
+ purgeEverything(): {
22
+ purgedTasks: number;
23
+ purgedComments: number;
24
+ purgedArtifacts: number;
25
+ purgedApprovals: number;
26
+ };
27
+ }
28
+ //# sourceMappingURL=cleanup.d.ts.map
@@ -0,0 +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"}