saga-mcp 1.0.1
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 +184 -0
- package/dist/db.d.ts +3 -0
- package/dist/db.js +25 -0
- package/dist/db.js.map +1 -0
- package/dist/helpers/activity-logger.d.ts +3 -0
- package/dist/helpers/activity-logger.js +15 -0
- package/dist/helpers/activity-logger.js.map +1 -0
- package/dist/helpers/sql-builder.d.ts +5 -0
- package/dist/helpers/sql-builder.js +24 -0
- package/dist/helpers/sql-builder.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -0
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +125 -0
- package/dist/schema.js.map +1 -0
- package/dist/tools/activity.d.ts +4 -0
- package/dist/tools/activity.js +125 -0
- package/dist/tools/activity.js.map +1 -0
- package/dist/tools/dashboard.d.ts +4 -0
- package/dist/tools/dashboard.js +145 -0
- package/dist/tools/dashboard.js.map +1 -0
- package/dist/tools/epics.d.ts +4 -0
- package/dist/tools/epics.js +124 -0
- package/dist/tools/epics.js.map +1 -0
- package/dist/tools/notes.d.ts +4 -0
- package/dist/tools/notes.js +169 -0
- package/dist/tools/notes.js.map +1 -0
- package/dist/tools/projects.d.ts +4 -0
- package/dist/tools/projects.js +112 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/search.d.ts +4 -0
- package/dist/tools/search.js +61 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/subtasks.d.ts +4 -0
- package/dist/tools/subtasks.js +122 -0
- package/dist/tools/subtasks.js.map +1 -0
- package/dist/tools/tasks.d.ts +4 -0
- package/dist/tools/tasks.js +190 -0
- package/dist/tools/tasks.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# saga-mcp
|
|
2
|
+
|
|
3
|
+
A Jira-like project tracker MCP server for AI agents. SQLite-backed, per-project scoped, with full hierarchy and activity logging — so LLMs never lose track.
|
|
4
|
+
|
|
5
|
+
**No more scattered markdown files.** saga-mcp gives your AI assistant a structured database to track projects, epics, tasks, subtasks, notes, and decisions across sessions.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Full hierarchy**: Projects > Epics > Tasks > Subtasks
|
|
10
|
+
- **SQLite**: Self-contained `.tracker.db` file per project — zero setup, no external database
|
|
11
|
+
- **Activity log**: Every mutation is automatically tracked with old/new values
|
|
12
|
+
- **Dashboard**: One tool call gives full project overview (stats, blocked tasks, recent changes)
|
|
13
|
+
- **Notes system**: Decisions, context, meeting notes, blockers — all searchable
|
|
14
|
+
- **Batch operations**: Create multiple subtasks or update multiple tasks in one call
|
|
15
|
+
- **22 focused tools**: Reduced from typical 38+ by combining related operations
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
### With Claude Code
|
|
20
|
+
|
|
21
|
+
Add to your project's `.mcp.json`:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"mcpServers": {
|
|
26
|
+
"saga": {
|
|
27
|
+
"command": "npx",
|
|
28
|
+
"args": ["-y", "saga-mcp"],
|
|
29
|
+
"env": {
|
|
30
|
+
"DB_PATH": "/absolute/path/to/your/project/.tracker.db"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### With Claude Desktop
|
|
38
|
+
|
|
39
|
+
Add to your Claude Desktop config (`claude_desktop_config.json`):
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"saga": {
|
|
45
|
+
"command": "npx",
|
|
46
|
+
"args": ["-y", "saga-mcp"],
|
|
47
|
+
"env": {
|
|
48
|
+
"DB_PATH": "/absolute/path/to/your/project/.tracker.db"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Manual install
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm install -g saga-mcp
|
|
59
|
+
DB_PATH=./my-project/.tracker.db saga-mcp
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Tools
|
|
63
|
+
|
|
64
|
+
### Getting Started
|
|
65
|
+
|
|
66
|
+
| Tool | Description |
|
|
67
|
+
|------|-------------|
|
|
68
|
+
| `tracker_init` | Initialize tracker and create first project |
|
|
69
|
+
| `tracker_dashboard` | Full project overview — **call this first when resuming work** |
|
|
70
|
+
|
|
71
|
+
### Projects
|
|
72
|
+
|
|
73
|
+
| Tool | Description |
|
|
74
|
+
|------|-------------|
|
|
75
|
+
| `project_create` | Create a new project |
|
|
76
|
+
| `project_list` | List projects with completion stats |
|
|
77
|
+
| `project_update` | Update project (archive to soft-delete) |
|
|
78
|
+
|
|
79
|
+
### Epics
|
|
80
|
+
|
|
81
|
+
| Tool | Description |
|
|
82
|
+
|------|-------------|
|
|
83
|
+
| `epic_create` | Create an epic within a project |
|
|
84
|
+
| `epic_list` | List epics with task counts |
|
|
85
|
+
| `epic_update` | Update an epic |
|
|
86
|
+
|
|
87
|
+
### Tasks
|
|
88
|
+
|
|
89
|
+
| Tool | Description |
|
|
90
|
+
|------|-------------|
|
|
91
|
+
| `task_create` | Create a task within an epic |
|
|
92
|
+
| `task_list` | List/filter tasks (by epic, status, priority, assignee, tag) |
|
|
93
|
+
| `task_get` | Get task with subtasks and related notes |
|
|
94
|
+
| `task_update` | Update task (status changes auto-logged) |
|
|
95
|
+
| `task_batch_update` | Update multiple tasks at once |
|
|
96
|
+
|
|
97
|
+
### Subtasks
|
|
98
|
+
|
|
99
|
+
| Tool | Description |
|
|
100
|
+
|------|-------------|
|
|
101
|
+
| `subtask_create` | Create subtask(s) — supports batch |
|
|
102
|
+
| `subtask_update` | Update subtask title/status |
|
|
103
|
+
| `subtask_delete` | Delete subtask(s) — supports batch |
|
|
104
|
+
|
|
105
|
+
### Notes
|
|
106
|
+
|
|
107
|
+
| Tool | Description |
|
|
108
|
+
|------|-------------|
|
|
109
|
+
| `note_save` | Create or update a note (upsert) |
|
|
110
|
+
| `note_list` | List notes with filters |
|
|
111
|
+
| `note_search` | Full-text search across notes |
|
|
112
|
+
| `note_delete` | Delete a note |
|
|
113
|
+
|
|
114
|
+
### Intelligence
|
|
115
|
+
|
|
116
|
+
| Tool | Description |
|
|
117
|
+
|------|-------------|
|
|
118
|
+
| `tracker_search` | Cross-entity search (projects, epics, tasks, notes) |
|
|
119
|
+
| `activity_log` | View change history with filters |
|
|
120
|
+
|
|
121
|
+
## How It Works
|
|
122
|
+
|
|
123
|
+
saga-mcp stores everything in a single SQLite file (`.tracker.db`) per project. The database is auto-created on first use with all tables and indexes — no migration step needed.
|
|
124
|
+
|
|
125
|
+
### Hierarchy
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
Project
|
|
129
|
+
└── Epic (feature/workstream)
|
|
130
|
+
└── Task (unit of work)
|
|
131
|
+
└── Subtask (checklist item)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Note Types
|
|
135
|
+
|
|
136
|
+
Notes replace scattered markdown files. Each note has a type:
|
|
137
|
+
|
|
138
|
+
| Type | Use case |
|
|
139
|
+
|------|----------|
|
|
140
|
+
| `general` | Free-form notes |
|
|
141
|
+
| `decision` | Architecture/design decisions |
|
|
142
|
+
| `context` | Conversation context for future sessions |
|
|
143
|
+
| `meeting` | Meeting notes |
|
|
144
|
+
| `technical` | Technical details, specs |
|
|
145
|
+
| `blocker` | Blockers and issues |
|
|
146
|
+
| `progress` | Progress updates |
|
|
147
|
+
| `release` | Release notes |
|
|
148
|
+
|
|
149
|
+
### Activity Log
|
|
150
|
+
|
|
151
|
+
Every create, update, and delete is automatically recorded:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"summary": "Task 'Fix CORS issue' status: blocked -> done",
|
|
156
|
+
"action": "status_changed",
|
|
157
|
+
"entity_type": "task",
|
|
158
|
+
"entity_id": 15,
|
|
159
|
+
"field_name": "status",
|
|
160
|
+
"old_value": "blocked",
|
|
161
|
+
"new_value": "done",
|
|
162
|
+
"created_at": "2026-02-21T18:30:00"
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Environment Variables
|
|
167
|
+
|
|
168
|
+
| Variable | Required | Description |
|
|
169
|
+
|----------|----------|-------------|
|
|
170
|
+
| `DB_PATH` | Yes | Absolute path to the `.tracker.db` file |
|
|
171
|
+
|
|
172
|
+
## Development
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
git clone https://github.com/spranab/saga-mcp.git
|
|
176
|
+
cd saga-mcp
|
|
177
|
+
npm install
|
|
178
|
+
npm run build
|
|
179
|
+
DB_PATH=./test.db npm start
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT
|
package/dist/db.d.ts
ADDED
package/dist/db.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { SCHEMA_SQL } from './schema.js';
|
|
3
|
+
let db = null;
|
|
4
|
+
export function getDb() {
|
|
5
|
+
if (db)
|
|
6
|
+
return db;
|
|
7
|
+
const dbPath = process.env.DB_PATH;
|
|
8
|
+
if (!dbPath) {
|
|
9
|
+
throw new Error('DB_PATH environment variable is required. Set it to the path of your .tracker.db file, e.g., DB_PATH=/path/to/project/.tracker.db');
|
|
10
|
+
}
|
|
11
|
+
db = new Database(dbPath);
|
|
12
|
+
db.pragma('journal_mode = WAL');
|
|
13
|
+
db.pragma('foreign_keys = ON');
|
|
14
|
+
db.pragma('busy_timeout = 5000');
|
|
15
|
+
db.pragma('synchronous = NORMAL');
|
|
16
|
+
db.exec(SCHEMA_SQL);
|
|
17
|
+
return db;
|
|
18
|
+
}
|
|
19
|
+
export function closeDb() {
|
|
20
|
+
if (db) {
|
|
21
|
+
db.close();
|
|
22
|
+
db = null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# 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":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,IAAI,EAAE,GAA6B,IAAI,CAAC;AAExC,MAAM,UAAU,KAAK;IACnB,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAElB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;IACnC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,mIAAmI,CACpI,CAAC;IACJ,CAAC;IAED,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE1B,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/B,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACjC,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAElC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAEpB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,IAAI,EAAE,EAAE,CAAC;QACP,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,EAAE,GAAG,IAAI,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
export declare function logActivity(db: Database.Database, entityType: string, entityId: number, action: string, fieldName: string | null, oldValue: string | null, newValue: string | null, summary: string): void;
|
|
3
|
+
export declare function logEntityUpdate(db: Database.Database, entityType: string, entityId: number, entityName: string, oldRow: Record<string, unknown>, newRow: Record<string, unknown>, trackedFields: string[]): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function logActivity(db, entityType, entityId, action, fieldName, oldValue, newValue, summary) {
|
|
2
|
+
db.prepare(`INSERT INTO activity_log (entity_type, entity_id, action, field_name, old_value, new_value, summary)
|
|
3
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(entityType, entityId, action, fieldName, oldValue, newValue, summary);
|
|
4
|
+
}
|
|
5
|
+
export function logEntityUpdate(db, entityType, entityId, entityName, oldRow, newRow, trackedFields) {
|
|
6
|
+
for (const field of trackedFields) {
|
|
7
|
+
const oldVal = String(oldRow[field] ?? '');
|
|
8
|
+
const newVal = String(newRow[field] ?? '');
|
|
9
|
+
if (oldVal !== newVal) {
|
|
10
|
+
const action = field === 'status' ? 'status_changed' : 'updated';
|
|
11
|
+
logActivity(db, entityType, entityId, action, field, oldVal, newVal, `${entityType} '${entityName}' ${field}: ${oldVal} -> ${newVal}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=activity-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-logger.js","sourceRoot":"","sources":["../../src/helpers/activity-logger.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,WAAW,CACzB,EAAqB,EACrB,UAAkB,EAClB,QAAgB,EAChB,MAAc,EACd,SAAwB,EACxB,QAAuB,EACvB,QAAuB,EACvB,OAAe;IAEf,EAAE,CAAC,OAAO,CACR;kCAC8B,CAC/B,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,EAAqB,EACrB,UAAkB,EAClB,QAAgB,EAChB,UAAkB,EAClB,MAA+B,EAC/B,MAA+B,EAC/B,aAAuB;IAEvB,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3C,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;YACjE,WAAW,CACT,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EACvD,GAAG,UAAU,KAAK,UAAU,KAAK,KAAK,KAAK,MAAM,OAAO,MAAM,EAAE,CACjE,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function buildUpdate(table: string, id: number, fields: Record<string, unknown>, allowedColumns: string[]): {
|
|
2
|
+
sql: string;
|
|
3
|
+
params: unknown[];
|
|
4
|
+
} | null;
|
|
5
|
+
export declare function addTagFilter(whereClauses: string[], params: unknown[], tag: string, table: string): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const JSON_COLUMNS = new Set(['tags', 'metadata']);
|
|
2
|
+
export function buildUpdate(table, id, fields, allowedColumns) {
|
|
3
|
+
const updates = [];
|
|
4
|
+
const params = [];
|
|
5
|
+
for (const col of allowedColumns) {
|
|
6
|
+
if (fields[col] !== undefined) {
|
|
7
|
+
updates.push(`${col} = ?`);
|
|
8
|
+
params.push(JSON_COLUMNS.has(col) ? JSON.stringify(fields[col]) : fields[col]);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
if (updates.length === 0)
|
|
12
|
+
return null;
|
|
13
|
+
updates.push("updated_at = datetime('now')");
|
|
14
|
+
params.push(id);
|
|
15
|
+
return {
|
|
16
|
+
sql: `UPDATE ${table} SET ${updates.join(', ')} WHERE id = ? RETURNING *`,
|
|
17
|
+
params,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function addTagFilter(whereClauses, params, tag, table) {
|
|
21
|
+
whereClauses.push(`EXISTS (SELECT 1 FROM json_each(${table}.tags) WHERE json_each.value = ?)`);
|
|
22
|
+
params.push(tag);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=sql-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-builder.js","sourceRoot":"","sources":["../../src/helpers/sql-builder.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AAEnD,MAAM,UAAU,WAAW,CACzB,KAAa,EACb,EAAU,EACV,MAA+B,EAC/B,cAAwB;IAExB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAc,EAAE,CAAC;IAE7B,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;QACjC,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtC,OAAO,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IAC7C,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEhB,OAAO;QACL,GAAG,EAAE,UAAU,KAAK,QAAQ,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,2BAA2B;QACzE,MAAM;KACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,YAAsB,EACtB,MAAiB,EACjB,GAAW,EACX,KAAa;IAEb,YAAY,CAAC,IAAI,CACf,mCAAmC,KAAK,mCAAmC,CAC5E,CAAC;IACF,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnB,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { definitions as projectDefs, handlers as projectHandlers } from './tools/projects.js';
|
|
6
|
+
import { definitions as epicDefs, handlers as epicHandlers } from './tools/epics.js';
|
|
7
|
+
import { definitions as taskDefs, handlers as taskHandlers } from './tools/tasks.js';
|
|
8
|
+
import { definitions as subtaskDefs, handlers as subtaskHandlers } from './tools/subtasks.js';
|
|
9
|
+
import { definitions as noteDefs, handlers as noteHandlers } from './tools/notes.js';
|
|
10
|
+
import { definitions as dashboardDefs, handlers as dashboardHandlers } from './tools/dashboard.js';
|
|
11
|
+
import { definitions as searchDefs, handlers as searchHandlers } from './tools/search.js';
|
|
12
|
+
import { definitions as activityDefs, handlers as activityHandlers } from './tools/activity.js';
|
|
13
|
+
import { closeDb } from './db.js';
|
|
14
|
+
const ALL_TOOLS = [
|
|
15
|
+
...projectDefs,
|
|
16
|
+
...epicDefs,
|
|
17
|
+
...taskDefs,
|
|
18
|
+
...subtaskDefs,
|
|
19
|
+
...noteDefs,
|
|
20
|
+
...dashboardDefs,
|
|
21
|
+
...searchDefs,
|
|
22
|
+
...activityDefs,
|
|
23
|
+
];
|
|
24
|
+
const ALL_HANDLERS = {
|
|
25
|
+
...projectHandlers,
|
|
26
|
+
...epicHandlers,
|
|
27
|
+
...taskHandlers,
|
|
28
|
+
...subtaskHandlers,
|
|
29
|
+
...noteHandlers,
|
|
30
|
+
...dashboardHandlers,
|
|
31
|
+
...searchHandlers,
|
|
32
|
+
...activityHandlers,
|
|
33
|
+
};
|
|
34
|
+
const server = new Server({ name: 'tracker', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
35
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
36
|
+
return { tools: ALL_TOOLS };
|
|
37
|
+
});
|
|
38
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
39
|
+
try {
|
|
40
|
+
const { name, arguments: args } = request.params;
|
|
41
|
+
const handler = ALL_HANDLERS[name];
|
|
42
|
+
if (!handler) {
|
|
43
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
44
|
+
}
|
|
45
|
+
const result = handler(args ?? {});
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return {
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: 'text',
|
|
55
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
isError: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
async function main() {
|
|
63
|
+
const transport = new StdioServerTransport();
|
|
64
|
+
await server.connect(transport);
|
|
65
|
+
console.error('Tracker MCP Server running on stdio');
|
|
66
|
+
}
|
|
67
|
+
process.on('SIGINT', () => {
|
|
68
|
+
closeDb();
|
|
69
|
+
process.exit(0);
|
|
70
|
+
});
|
|
71
|
+
process.on('SIGTERM', () => {
|
|
72
|
+
closeDb();
|
|
73
|
+
process.exit(0);
|
|
74
|
+
});
|
|
75
|
+
main().catch((error) => {
|
|
76
|
+
console.error('Fatal error:', error);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SCHEMA_SQL = "\n-- Core hierarchy: projects > epics > tasks > subtasks\n\nCREATE TABLE IF NOT EXISTS projects (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n description TEXT,\n status TEXT NOT NULL DEFAULT 'active'\n CHECK (status IN ('active', 'on_hold', 'completed', 'archived')),\n tags TEXT NOT NULL DEFAULT '[]',\n metadata TEXT NOT NULL DEFAULT '{}',\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE TABLE IF NOT EXISTS epics (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n description TEXT,\n status TEXT NOT NULL DEFAULT 'planned'\n CHECK (status IN ('planned', 'in_progress', 'completed', 'cancelled')),\n priority TEXT NOT NULL DEFAULT 'medium'\n CHECK (priority IN ('low', 'medium', 'high', 'critical')),\n sort_order INTEGER NOT NULL DEFAULT 0,\n tags TEXT NOT NULL DEFAULT '[]',\n metadata TEXT NOT NULL DEFAULT '{}',\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE TABLE IF NOT EXISTS tasks (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n epic_id INTEGER NOT NULL REFERENCES epics(id) ON DELETE CASCADE,\n title TEXT NOT NULL,\n description TEXT,\n status TEXT NOT NULL DEFAULT 'todo'\n CHECK (status IN ('todo', 'in_progress', 'review', 'done', 'blocked')),\n priority TEXT NOT NULL DEFAULT 'medium'\n CHECK (priority IN ('low', 'medium', 'high', 'critical')),\n sort_order INTEGER NOT NULL DEFAULT 0,\n assigned_to TEXT,\n estimated_hours REAL,\n actual_hours REAL,\n due_date TEXT,\n tags TEXT NOT NULL DEFAULT '[]',\n metadata TEXT NOT NULL DEFAULT '{}',\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE TABLE IF NOT EXISTS subtasks (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,\n title TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'todo'\n CHECK (status IN ('todo', 'in_progress', 'done')),\n sort_order INTEGER NOT NULL DEFAULT 0,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Unified notes (replaces summaries + status_updates + context)\n\nCREATE TABLE IF NOT EXISTS notes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n title TEXT NOT NULL,\n content TEXT NOT NULL,\n note_type TEXT NOT NULL DEFAULT 'general'\n CHECK (note_type IN (\n 'general', 'decision', 'context', 'meeting',\n 'technical', 'blocker', 'progress', 'release'\n )),\n related_entity_type TEXT CHECK (related_entity_type IN ('project', 'epic', 'task') OR related_entity_type IS NULL),\n related_entity_id INTEGER,\n tags TEXT NOT NULL DEFAULT '[]',\n metadata TEXT NOT NULL DEFAULT '{}',\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Automatic activity log\n\nCREATE TABLE IF NOT EXISTS activity_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n entity_type TEXT NOT NULL,\n entity_id INTEGER NOT NULL,\n action TEXT NOT NULL,\n field_name TEXT,\n old_value TEXT,\n new_value TEXT,\n summary TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Indexes\n\nCREATE INDEX IF NOT EXISTS idx_epics_project_id ON epics(project_id);\nCREATE INDEX IF NOT EXISTS idx_tasks_epic_id ON tasks(epic_id);\nCREATE INDEX IF NOT EXISTS idx_subtasks_task_id ON subtasks(task_id);\n\nCREATE INDEX IF NOT EXISTS idx_projects_status ON projects(status);\nCREATE INDEX IF NOT EXISTS idx_epics_status ON epics(status);\nCREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);\nCREATE INDEX IF NOT EXISTS idx_subtasks_status ON subtasks(status);\n\nCREATE INDEX IF NOT EXISTS idx_epics_priority ON epics(priority);\nCREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks(priority);\n\nCREATE INDEX IF NOT EXISTS idx_epics_sort ON epics(project_id, sort_order);\nCREATE INDEX IF NOT EXISTS idx_tasks_sort ON tasks(epic_id, sort_order);\nCREATE INDEX IF NOT EXISTS idx_subtasks_sort ON subtasks(task_id, sort_order);\n\nCREATE INDEX IF NOT EXISTS idx_notes_type ON notes(note_type);\nCREATE INDEX IF NOT EXISTS idx_notes_entity ON notes(related_entity_type, related_entity_id);\nCREATE INDEX IF NOT EXISTS idx_notes_created ON notes(created_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_activity_entity ON activity_log(entity_type, entity_id);\nCREATE INDEX IF NOT EXISTS idx_activity_created ON activity_log(created_at DESC);\nCREATE INDEX IF NOT EXISTS idx_activity_action ON activity_log(action);\n\nCREATE INDEX IF NOT EXISTS idx_tasks_assigned ON tasks(assigned_to);\nCREATE INDEX IF NOT EXISTS idx_tasks_due ON tasks(due_date);\n";
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export const SCHEMA_SQL = `
|
|
2
|
+
-- Core hierarchy: projects > epics > tasks > subtasks
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
5
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6
|
+
name TEXT NOT NULL,
|
|
7
|
+
description TEXT,
|
|
8
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
9
|
+
CHECK (status IN ('active', 'on_hold', 'completed', 'archived')),
|
|
10
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
11
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
12
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
13
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
CREATE TABLE IF NOT EXISTS epics (
|
|
17
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
18
|
+
project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
19
|
+
name TEXT NOT NULL,
|
|
20
|
+
description TEXT,
|
|
21
|
+
status TEXT NOT NULL DEFAULT 'planned'
|
|
22
|
+
CHECK (status IN ('planned', 'in_progress', 'completed', 'cancelled')),
|
|
23
|
+
priority TEXT NOT NULL DEFAULT 'medium'
|
|
24
|
+
CHECK (priority IN ('low', 'medium', 'high', 'critical')),
|
|
25
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
26
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
27
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
28
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
29
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
33
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
34
|
+
epic_id INTEGER NOT NULL REFERENCES epics(id) ON DELETE CASCADE,
|
|
35
|
+
title TEXT NOT NULL,
|
|
36
|
+
description TEXT,
|
|
37
|
+
status TEXT NOT NULL DEFAULT 'todo'
|
|
38
|
+
CHECK (status IN ('todo', 'in_progress', 'review', 'done', 'blocked')),
|
|
39
|
+
priority TEXT NOT NULL DEFAULT 'medium'
|
|
40
|
+
CHECK (priority IN ('low', 'medium', 'high', 'critical')),
|
|
41
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
42
|
+
assigned_to TEXT,
|
|
43
|
+
estimated_hours REAL,
|
|
44
|
+
actual_hours REAL,
|
|
45
|
+
due_date TEXT,
|
|
46
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
47
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
48
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
49
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
CREATE TABLE IF NOT EXISTS subtasks (
|
|
53
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
54
|
+
task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
55
|
+
title TEXT NOT NULL,
|
|
56
|
+
status TEXT NOT NULL DEFAULT 'todo'
|
|
57
|
+
CHECK (status IN ('todo', 'in_progress', 'done')),
|
|
58
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
59
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
60
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
-- Unified notes (replaces summaries + status_updates + context)
|
|
64
|
+
|
|
65
|
+
CREATE TABLE IF NOT EXISTS notes (
|
|
66
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
67
|
+
title TEXT NOT NULL,
|
|
68
|
+
content TEXT NOT NULL,
|
|
69
|
+
note_type TEXT NOT NULL DEFAULT 'general'
|
|
70
|
+
CHECK (note_type IN (
|
|
71
|
+
'general', 'decision', 'context', 'meeting',
|
|
72
|
+
'technical', 'blocker', 'progress', 'release'
|
|
73
|
+
)),
|
|
74
|
+
related_entity_type TEXT CHECK (related_entity_type IN ('project', 'epic', 'task') OR related_entity_type IS NULL),
|
|
75
|
+
related_entity_id INTEGER,
|
|
76
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
77
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
78
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
79
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
-- Automatic activity log
|
|
83
|
+
|
|
84
|
+
CREATE TABLE IF NOT EXISTS activity_log (
|
|
85
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
86
|
+
entity_type TEXT NOT NULL,
|
|
87
|
+
entity_id INTEGER NOT NULL,
|
|
88
|
+
action TEXT NOT NULL,
|
|
89
|
+
field_name TEXT,
|
|
90
|
+
old_value TEXT,
|
|
91
|
+
new_value TEXT,
|
|
92
|
+
summary TEXT,
|
|
93
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
-- Indexes
|
|
97
|
+
|
|
98
|
+
CREATE INDEX IF NOT EXISTS idx_epics_project_id ON epics(project_id);
|
|
99
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_epic_id ON tasks(epic_id);
|
|
100
|
+
CREATE INDEX IF NOT EXISTS idx_subtasks_task_id ON subtasks(task_id);
|
|
101
|
+
|
|
102
|
+
CREATE INDEX IF NOT EXISTS idx_projects_status ON projects(status);
|
|
103
|
+
CREATE INDEX IF NOT EXISTS idx_epics_status ON epics(status);
|
|
104
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
|
|
105
|
+
CREATE INDEX IF NOT EXISTS idx_subtasks_status ON subtasks(status);
|
|
106
|
+
|
|
107
|
+
CREATE INDEX IF NOT EXISTS idx_epics_priority ON epics(priority);
|
|
108
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks(priority);
|
|
109
|
+
|
|
110
|
+
CREATE INDEX IF NOT EXISTS idx_epics_sort ON epics(project_id, sort_order);
|
|
111
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_sort ON tasks(epic_id, sort_order);
|
|
112
|
+
CREATE INDEX IF NOT EXISTS idx_subtasks_sort ON subtasks(task_id, sort_order);
|
|
113
|
+
|
|
114
|
+
CREATE INDEX IF NOT EXISTS idx_notes_type ON notes(note_type);
|
|
115
|
+
CREATE INDEX IF NOT EXISTS idx_notes_entity ON notes(related_entity_type, related_entity_id);
|
|
116
|
+
CREATE INDEX IF NOT EXISTS idx_notes_created ON notes(created_at DESC);
|
|
117
|
+
|
|
118
|
+
CREATE INDEX IF NOT EXISTS idx_activity_entity ON activity_log(entity_type, entity_id);
|
|
119
|
+
CREATE INDEX IF NOT EXISTS idx_activity_created ON activity_log(created_at DESC);
|
|
120
|
+
CREATE INDEX IF NOT EXISTS idx_activity_action ON activity_log(action);
|
|
121
|
+
|
|
122
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_assigned ON tasks(assigned_to);
|
|
123
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_due ON tasks(due_date);
|
|
124
|
+
`;
|
|
125
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2HzB,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { getDb } from '../db.js';
|
|
2
|
+
import { logActivity } from '../helpers/activity-logger.js';
|
|
3
|
+
export const definitions = [
|
|
4
|
+
{
|
|
5
|
+
name: 'activity_log',
|
|
6
|
+
description: 'View the activity log showing what changed and when. Useful for understanding recent progress or reviewing what happened since the last session.',
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
entity_type: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
enum: ['project', 'epic', 'task', 'subtask', 'note'],
|
|
13
|
+
description: 'Filter by entity type',
|
|
14
|
+
},
|
|
15
|
+
entity_id: { type: 'integer', description: 'Filter by specific entity' },
|
|
16
|
+
action: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
enum: ['created', 'updated', 'deleted', 'status_changed'],
|
|
19
|
+
description: 'Filter by action type',
|
|
20
|
+
},
|
|
21
|
+
since: { type: 'string', description: 'ISO 8601 datetime - show only activity after this time' },
|
|
22
|
+
limit: { type: 'integer', default: 50 },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'task_batch_update',
|
|
28
|
+
description: 'Update multiple tasks at once. Useful for changing status of several tasks (e.g., mark 3 tasks as done) or reassigning tasks.',
|
|
29
|
+
inputSchema: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
ids: {
|
|
33
|
+
type: 'array',
|
|
34
|
+
items: { type: 'integer' },
|
|
35
|
+
description: 'Task IDs to update',
|
|
36
|
+
},
|
|
37
|
+
status: { type: 'string', enum: ['todo', 'in_progress', 'review', 'done', 'blocked'] },
|
|
38
|
+
priority: { type: 'string', enum: ['low', 'medium', 'high', 'critical'] },
|
|
39
|
+
assigned_to: { type: 'string' },
|
|
40
|
+
},
|
|
41
|
+
required: ['ids'],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
function handleActivityLog(args) {
|
|
46
|
+
const db = getDb();
|
|
47
|
+
const entityType = args.entity_type;
|
|
48
|
+
const entityId = args.entity_id;
|
|
49
|
+
const action = args.action;
|
|
50
|
+
const since = args.since;
|
|
51
|
+
const limit = args.limit ?? 50;
|
|
52
|
+
const whereClauses = [];
|
|
53
|
+
const params = [];
|
|
54
|
+
if (entityType) {
|
|
55
|
+
whereClauses.push('entity_type = ?');
|
|
56
|
+
params.push(entityType);
|
|
57
|
+
}
|
|
58
|
+
if (entityId !== undefined) {
|
|
59
|
+
whereClauses.push('entity_id = ?');
|
|
60
|
+
params.push(entityId);
|
|
61
|
+
}
|
|
62
|
+
if (action) {
|
|
63
|
+
whereClauses.push('action = ?');
|
|
64
|
+
params.push(action);
|
|
65
|
+
}
|
|
66
|
+
if (since) {
|
|
67
|
+
whereClauses.push('created_at > ?');
|
|
68
|
+
params.push(since);
|
|
69
|
+
}
|
|
70
|
+
const whereStr = whereClauses.length > 0 ? `WHERE ${whereClauses.join(' AND ')}` : '';
|
|
71
|
+
const sql = `SELECT * FROM activity_log ${whereStr} ORDER BY created_at DESC LIMIT ?`;
|
|
72
|
+
params.push(limit);
|
|
73
|
+
return db.prepare(sql).all(...params);
|
|
74
|
+
}
|
|
75
|
+
function handleTaskBatchUpdate(args) {
|
|
76
|
+
const db = getDb();
|
|
77
|
+
const ids = args.ids;
|
|
78
|
+
const status = args.status;
|
|
79
|
+
const priority = args.priority;
|
|
80
|
+
const assignedTo = args.assigned_to;
|
|
81
|
+
if (!status && !priority && assignedTo === undefined) {
|
|
82
|
+
throw new Error('Provide at least one field to update: status, priority, or assigned_to');
|
|
83
|
+
}
|
|
84
|
+
const getStmt = db.prepare('SELECT * FROM tasks WHERE id = ?');
|
|
85
|
+
const results = db.transaction(() => {
|
|
86
|
+
return ids.map((id) => {
|
|
87
|
+
const oldRow = getStmt.get(id);
|
|
88
|
+
if (!oldRow)
|
|
89
|
+
throw new Error(`Task ${id} not found`);
|
|
90
|
+
const updates = [];
|
|
91
|
+
const params = [];
|
|
92
|
+
if (status) {
|
|
93
|
+
updates.push('status = ?');
|
|
94
|
+
params.push(status);
|
|
95
|
+
}
|
|
96
|
+
if (priority) {
|
|
97
|
+
updates.push('priority = ?');
|
|
98
|
+
params.push(priority);
|
|
99
|
+
}
|
|
100
|
+
if (assignedTo !== undefined) {
|
|
101
|
+
updates.push('assigned_to = ?');
|
|
102
|
+
params.push(assignedTo);
|
|
103
|
+
}
|
|
104
|
+
updates.push("updated_at = datetime('now')");
|
|
105
|
+
params.push(id);
|
|
106
|
+
const newRow = db
|
|
107
|
+
.prepare(`UPDATE tasks SET ${updates.join(', ')} WHERE id = ? RETURNING *`)
|
|
108
|
+
.get(...params);
|
|
109
|
+
// Log status changes
|
|
110
|
+
if (status && oldRow.status !== status) {
|
|
111
|
+
logActivity(db, 'task', id, 'status_changed', 'status', oldRow.status, status, `Task '${newRow.title}' status: ${oldRow.status} -> ${status}`);
|
|
112
|
+
}
|
|
113
|
+
if (priority && oldRow.priority !== priority) {
|
|
114
|
+
logActivity(db, 'task', id, 'updated', 'priority', oldRow.priority, priority, `Task '${newRow.title}' priority: ${oldRow.priority} -> ${priority}`);
|
|
115
|
+
}
|
|
116
|
+
return newRow;
|
|
117
|
+
});
|
|
118
|
+
})();
|
|
119
|
+
return { updated: results.length, tasks: results };
|
|
120
|
+
}
|
|
121
|
+
export const handlers = {
|
|
122
|
+
activity_log: handleActivityLog,
|
|
123
|
+
task_batch_update: handleTaskBatchUpdate,
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.js","sourceRoot":"","sources":["../../src/tools/activity.ts"],"names":[],"mappings":"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"}
|