@cwim/kanban 1.1.20 → 1.2.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 +284 -270
- package/dashboard/dist/assets/{index-p_oYIoTL.js → index-C9-ldHtP.js} +5 -5
- package/dashboard/dist/assets/{index-WVxfVg2r.css → index-DrSkXUSP.css} +1 -1
- package/dashboard/dist/index.html +13 -13
- package/dist/cli/commands.js +79 -77
- package/dist/cli/commands.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +168 -134
- package/dist/mcp/server.js.map +1 -1
- package/dist/storage/store.d.ts +4 -4
- package/dist/storage/store.d.ts.map +1 -1
- package/dist/storage/store.js +260 -73
- package/dist/storage/store.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +14 -9
- package/dist/types.js.map +1 -1
- package/package.json +68 -68
package/dist/mcp/server.js
CHANGED
|
@@ -1,49 +1,52 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Server } from
|
|
3
|
-
import { StdioServerTransport } from
|
|
4
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from
|
|
5
|
-
import { execSync } from
|
|
6
|
-
import { listTasks, getTask, createTask, updateTask, moveTask, deleteTask, appendNote, recallTasks, listAllSessions, getCurrentSessionName, setActiveSession, } from
|
|
7
|
-
import { VALID_STATUSES } from
|
|
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 { execSync } from "child_process";
|
|
6
|
+
import { listTasks, getTask, createTask, updateTask, moveTask, deleteTask, appendNote, recallTasks, listAllSessions, getCurrentSessionName, setActiveSession, } from "../storage/store.js";
|
|
7
|
+
import { VALID_STATUSES } from "../types.js";
|
|
8
8
|
function detectAgent() {
|
|
9
9
|
if (process.env.CLAUDE_CODE)
|
|
10
|
-
return
|
|
10
|
+
return "claude";
|
|
11
11
|
if (process.env.OPENCODE)
|
|
12
|
-
return
|
|
12
|
+
return "opencode";
|
|
13
13
|
// Try to detect parent process name cross-platform
|
|
14
14
|
if (process.ppid) {
|
|
15
15
|
try {
|
|
16
|
-
let parentProcess =
|
|
17
|
-
if (process.platform ===
|
|
16
|
+
let parentProcess = "";
|
|
17
|
+
if (process.platform === "win32") {
|
|
18
18
|
// Windows: use wmic or tasklist
|
|
19
19
|
try {
|
|
20
|
-
parentProcess = execSync(`wmic process where "ProcessId=${process.ppid}" get Name /value`, { encoding:
|
|
20
|
+
parentProcess = execSync(`wmic process where "ProcessId=${process.ppid}" get Name /value`, { encoding: "utf8", timeout: 5000 });
|
|
21
21
|
}
|
|
22
22
|
catch {
|
|
23
23
|
// Fallback to tasklist
|
|
24
|
-
parentProcess = execSync(`tasklist /FI "PID eq ${process.ppid}" /FO CSV /NH`, { encoding:
|
|
24
|
+
parentProcess = execSync(`tasklist /FI "PID eq ${process.ppid}" /FO CSV /NH`, { encoding: "utf8", timeout: 5000 });
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
else {
|
|
28
28
|
// Unix-like (macOS, Linux)
|
|
29
|
-
parentProcess = execSync(`ps -p ${process.ppid} -o comm=`, {
|
|
29
|
+
parentProcess = execSync(`ps -p ${process.ppid} -o comm=`, {
|
|
30
|
+
encoding: "utf8",
|
|
31
|
+
timeout: 5000,
|
|
32
|
+
}).trim();
|
|
30
33
|
}
|
|
31
|
-
if (parentProcess.toLowerCase().includes(
|
|
32
|
-
return
|
|
33
|
-
if (parentProcess.toLowerCase().includes(
|
|
34
|
-
return
|
|
34
|
+
if (parentProcess.toLowerCase().includes("claude"))
|
|
35
|
+
return "claude";
|
|
36
|
+
if (parentProcess.toLowerCase().includes("opencode"))
|
|
37
|
+
return "opencode";
|
|
35
38
|
}
|
|
36
39
|
catch {
|
|
37
40
|
// Ignore errors from process detection
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
43
|
// Default fallback
|
|
41
|
-
return
|
|
44
|
+
return "claude";
|
|
42
45
|
}
|
|
43
46
|
const CURRENT_AGENT = detectAgent();
|
|
44
47
|
const server = new Server({
|
|
45
|
-
name:
|
|
46
|
-
version:
|
|
48
|
+
name: "cwim-kanban",
|
|
49
|
+
version: "1.2.1",
|
|
47
50
|
}, {
|
|
48
51
|
capabilities: {
|
|
49
52
|
tools: {},
|
|
@@ -53,161 +56,164 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
53
56
|
return {
|
|
54
57
|
tools: [
|
|
55
58
|
{
|
|
56
|
-
name:
|
|
57
|
-
description:
|
|
59
|
+
name: "task_create",
|
|
60
|
+
description: "Create a new task on the Kanban board",
|
|
58
61
|
inputSchema: {
|
|
59
|
-
type:
|
|
62
|
+
type: "object",
|
|
60
63
|
properties: {
|
|
61
|
-
title: { type:
|
|
64
|
+
title: { type: "string", description: "Task title (required)" },
|
|
62
65
|
description: {
|
|
63
|
-
type:
|
|
64
|
-
description:
|
|
66
|
+
type: "string",
|
|
67
|
+
description: "Optional task description",
|
|
65
68
|
},
|
|
66
69
|
status: {
|
|
67
|
-
type:
|
|
70
|
+
type: "string",
|
|
68
71
|
enum: VALID_STATUSES,
|
|
69
|
-
description:
|
|
72
|
+
description: "Initial status (default: todo)",
|
|
70
73
|
},
|
|
71
74
|
tags: {
|
|
72
|
-
type:
|
|
73
|
-
items: { type:
|
|
74
|
-
description:
|
|
75
|
+
type: "array",
|
|
76
|
+
items: { type: "string" },
|
|
77
|
+
description: "Optional tags for categorization",
|
|
75
78
|
},
|
|
76
79
|
},
|
|
77
|
-
required: [
|
|
80
|
+
required: ["title"],
|
|
78
81
|
},
|
|
79
82
|
},
|
|
80
83
|
{
|
|
81
|
-
name:
|
|
82
|
-
description:
|
|
84
|
+
name: "task_update",
|
|
85
|
+
description: "Update an existing task",
|
|
83
86
|
inputSchema: {
|
|
84
|
-
type:
|
|
87
|
+
type: "object",
|
|
85
88
|
properties: {
|
|
86
|
-
id: { type:
|
|
87
|
-
title: { type:
|
|
88
|
-
description: { type:
|
|
89
|
+
id: { type: "string", description: "Task ID (required)" },
|
|
90
|
+
title: { type: "string" },
|
|
91
|
+
description: { type: "string" },
|
|
89
92
|
status: {
|
|
90
|
-
type:
|
|
93
|
+
type: "string",
|
|
91
94
|
enum: VALID_STATUSES,
|
|
92
95
|
},
|
|
93
96
|
tags: {
|
|
94
|
-
type:
|
|
95
|
-
items: { type:
|
|
97
|
+
type: "array",
|
|
98
|
+
items: { type: "string" },
|
|
96
99
|
},
|
|
97
100
|
},
|
|
98
|
-
required: [
|
|
101
|
+
required: ["id"],
|
|
99
102
|
},
|
|
100
103
|
},
|
|
101
104
|
{
|
|
102
|
-
name:
|
|
103
|
-
description:
|
|
105
|
+
name: "task_move",
|
|
106
|
+
description: "Move a task to a different status column",
|
|
104
107
|
inputSchema: {
|
|
105
|
-
type:
|
|
108
|
+
type: "object",
|
|
106
109
|
properties: {
|
|
107
|
-
id: { type:
|
|
110
|
+
id: { type: "string", description: "Task ID (required)" },
|
|
108
111
|
status: {
|
|
109
|
-
type:
|
|
112
|
+
type: "string",
|
|
110
113
|
enum: VALID_STATUSES,
|
|
111
|
-
description:
|
|
114
|
+
description: "New status (required)",
|
|
112
115
|
},
|
|
113
116
|
},
|
|
114
|
-
required: [
|
|
117
|
+
required: ["id", "status"],
|
|
115
118
|
},
|
|
116
119
|
},
|
|
117
120
|
{
|
|
118
|
-
name:
|
|
119
|
-
description:
|
|
121
|
+
name: "task_delete",
|
|
122
|
+
description: "Delete a task from the board",
|
|
120
123
|
inputSchema: {
|
|
121
|
-
type:
|
|
124
|
+
type: "object",
|
|
122
125
|
properties: {
|
|
123
|
-
id: { type:
|
|
126
|
+
id: { type: "string", description: "Task ID (required)" },
|
|
124
127
|
},
|
|
125
|
-
required: [
|
|
128
|
+
required: ["id"],
|
|
126
129
|
},
|
|
127
130
|
},
|
|
128
131
|
{
|
|
129
|
-
name:
|
|
130
|
-
description:
|
|
132
|
+
name: "task_list",
|
|
133
|
+
description: "List all tasks in the current session, optionally filtered",
|
|
131
134
|
inputSchema: {
|
|
132
|
-
type:
|
|
135
|
+
type: "object",
|
|
133
136
|
properties: {
|
|
134
137
|
status: {
|
|
135
|
-
type:
|
|
138
|
+
type: "string",
|
|
136
139
|
enum: VALID_STATUSES,
|
|
137
|
-
description:
|
|
140
|
+
description: "Filter by status",
|
|
138
141
|
},
|
|
139
142
|
tag: {
|
|
140
|
-
type:
|
|
141
|
-
description:
|
|
143
|
+
type: "string",
|
|
144
|
+
description: "Filter by tag",
|
|
142
145
|
},
|
|
143
146
|
query: {
|
|
144
|
-
type:
|
|
145
|
-
description:
|
|
147
|
+
type: "string",
|
|
148
|
+
description: "Search query to filter tasks by title or description (case-insensitive)",
|
|
146
149
|
},
|
|
147
150
|
},
|
|
148
151
|
},
|
|
149
152
|
},
|
|
150
153
|
{
|
|
151
|
-
name:
|
|
152
|
-
description:
|
|
154
|
+
name: "task_append_note",
|
|
155
|
+
description: "Append a timestamped note to a task without overwriting existing content",
|
|
153
156
|
inputSchema: {
|
|
154
|
-
type:
|
|
157
|
+
type: "object",
|
|
155
158
|
properties: {
|
|
156
|
-
id: { type:
|
|
157
|
-
note: {
|
|
159
|
+
id: { type: "string", description: "Task ID (required)" },
|
|
160
|
+
note: {
|
|
161
|
+
type: "string",
|
|
162
|
+
description: "Note text to append (required)",
|
|
163
|
+
},
|
|
158
164
|
},
|
|
159
|
-
required: [
|
|
165
|
+
required: ["id", "note"],
|
|
160
166
|
},
|
|
161
167
|
},
|
|
162
168
|
{
|
|
163
|
-
name:
|
|
164
|
-
description:
|
|
169
|
+
name: "task_recall",
|
|
170
|
+
description: "Intelligently recall relevant task context based on current work. Call this before starting complex tasks to check for existing context and avoid duplicating work.",
|
|
165
171
|
inputSchema: {
|
|
166
|
-
type:
|
|
172
|
+
type: "object",
|
|
167
173
|
properties: {
|
|
168
174
|
context: {
|
|
169
|
-
type:
|
|
175
|
+
type: "string",
|
|
170
176
|
description: 'Brief description of what you are about to work on (e.g., "refactoring auth middleware")',
|
|
171
177
|
},
|
|
172
178
|
limit: {
|
|
173
|
-
type:
|
|
174
|
-
description:
|
|
179
|
+
type: "number",
|
|
180
|
+
description: "Max number of relevant tasks to return (default: 5)",
|
|
175
181
|
default: 5,
|
|
176
182
|
},
|
|
177
183
|
},
|
|
178
184
|
},
|
|
179
185
|
},
|
|
180
186
|
{
|
|
181
|
-
name:
|
|
182
|
-
description:
|
|
187
|
+
name: "task_get",
|
|
188
|
+
description: "Get details of a single task",
|
|
183
189
|
inputSchema: {
|
|
184
|
-
type:
|
|
190
|
+
type: "object",
|
|
185
191
|
properties: {
|
|
186
|
-
id: { type:
|
|
192
|
+
id: { type: "string", description: "Task ID (required)" },
|
|
187
193
|
},
|
|
188
|
-
required: [
|
|
194
|
+
required: ["id"],
|
|
189
195
|
},
|
|
190
196
|
},
|
|
191
197
|
{
|
|
192
|
-
name:
|
|
193
|
-
description:
|
|
198
|
+
name: "session_list",
|
|
199
|
+
description: "List all available sessions",
|
|
194
200
|
inputSchema: {
|
|
195
|
-
type:
|
|
201
|
+
type: "object",
|
|
196
202
|
properties: {},
|
|
197
203
|
},
|
|
198
204
|
},
|
|
199
205
|
{
|
|
200
|
-
name:
|
|
201
|
-
description:
|
|
206
|
+
name: "session_switch",
|
|
207
|
+
description: "Switch to a different session",
|
|
202
208
|
inputSchema: {
|
|
203
|
-
type:
|
|
209
|
+
type: "object",
|
|
204
210
|
properties: {
|
|
205
211
|
name: {
|
|
206
|
-
type:
|
|
207
|
-
description:
|
|
212
|
+
type: "string",
|
|
213
|
+
description: "Session name to switch to (required)",
|
|
208
214
|
},
|
|
209
215
|
},
|
|
210
|
-
required: [
|
|
216
|
+
required: ["name"],
|
|
211
217
|
},
|
|
212
218
|
},
|
|
213
219
|
],
|
|
@@ -217,13 +223,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
217
223
|
const { name, arguments: args = {} } = request.params;
|
|
218
224
|
try {
|
|
219
225
|
switch (name) {
|
|
220
|
-
case
|
|
226
|
+
case "task_create": {
|
|
221
227
|
const task = await createTask({
|
|
222
228
|
title: String(args.title),
|
|
223
|
-
description: args.description
|
|
229
|
+
description: args.description
|
|
230
|
+
? String(args.description)
|
|
231
|
+
: undefined,
|
|
224
232
|
status: VALID_STATUSES.includes(args.status)
|
|
225
233
|
? args.status
|
|
226
|
-
:
|
|
234
|
+
: "todo",
|
|
227
235
|
tags: Array.isArray(args.tags) ? args.tags.map(String) : undefined,
|
|
228
236
|
source: CURRENT_AGENT,
|
|
229
237
|
});
|
|
@@ -231,16 +239,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
231
239
|
return {
|
|
232
240
|
content: [
|
|
233
241
|
{
|
|
234
|
-
type:
|
|
242
|
+
type: "text",
|
|
235
243
|
text: `Created task "${task.title}" (${task.id}) in ${task.status} [Session: ${sessionName}]`,
|
|
236
244
|
},
|
|
237
245
|
],
|
|
238
246
|
};
|
|
239
247
|
}
|
|
240
|
-
case
|
|
248
|
+
case "task_update": {
|
|
241
249
|
const task = await updateTask(String(args.id), {
|
|
242
250
|
title: args.title !== undefined ? String(args.title) : undefined,
|
|
243
|
-
description: args.description !== undefined
|
|
251
|
+
description: args.description !== undefined
|
|
252
|
+
? String(args.description)
|
|
253
|
+
: undefined,
|
|
244
254
|
status: VALID_STATUSES.includes(args.status)
|
|
245
255
|
? args.status
|
|
246
256
|
: undefined,
|
|
@@ -248,48 +258,50 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
248
258
|
});
|
|
249
259
|
if (!task) {
|
|
250
260
|
return {
|
|
251
|
-
content: [{ type:
|
|
261
|
+
content: [{ type: "text", text: `Task ${args.id} not found` }],
|
|
252
262
|
isError: true,
|
|
253
263
|
};
|
|
254
264
|
}
|
|
255
265
|
return {
|
|
256
266
|
content: [
|
|
257
267
|
{
|
|
258
|
-
type:
|
|
268
|
+
type: "text",
|
|
259
269
|
text: `Updated task "${task.title}" (${task.id})`,
|
|
260
270
|
},
|
|
261
271
|
],
|
|
262
272
|
};
|
|
263
273
|
}
|
|
264
|
-
case
|
|
274
|
+
case "task_move": {
|
|
265
275
|
const task = await moveTask(String(args.id), args.status);
|
|
266
276
|
if (!task) {
|
|
267
277
|
return {
|
|
268
|
-
content: [{ type:
|
|
278
|
+
content: [{ type: "text", text: `Task ${args.id} not found` }],
|
|
269
279
|
isError: true,
|
|
270
280
|
};
|
|
271
281
|
}
|
|
272
282
|
return {
|
|
273
283
|
content: [
|
|
274
284
|
{
|
|
275
|
-
type:
|
|
285
|
+
type: "text",
|
|
276
286
|
text: `Moved "${task.title}" to ${task.status}`,
|
|
277
287
|
},
|
|
278
288
|
],
|
|
279
289
|
};
|
|
280
290
|
}
|
|
281
|
-
case
|
|
291
|
+
case "task_delete": {
|
|
282
292
|
const ok = await deleteTask(String(args.id));
|
|
283
293
|
return {
|
|
284
294
|
content: [
|
|
285
295
|
{
|
|
286
|
-
type:
|
|
287
|
-
text: ok
|
|
296
|
+
type: "text",
|
|
297
|
+
text: ok
|
|
298
|
+
? `Deleted task ${args.id}`
|
|
299
|
+
: `Task ${args.id} not found`,
|
|
288
300
|
},
|
|
289
301
|
],
|
|
290
302
|
};
|
|
291
303
|
}
|
|
292
|
-
case
|
|
304
|
+
case "task_list": {
|
|
293
305
|
const tasks = await listTasks(undefined, {
|
|
294
306
|
status: VALID_STATUSES.includes(args.status)
|
|
295
307
|
? args.status
|
|
@@ -300,19 +312,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
300
312
|
const sessionName = await getCurrentSessionName();
|
|
301
313
|
if (tasks.length === 0) {
|
|
302
314
|
return {
|
|
303
|
-
content: [
|
|
315
|
+
content: [
|
|
316
|
+
{
|
|
317
|
+
type: "text",
|
|
318
|
+
text: `No tasks found in session "${sessionName}"`,
|
|
319
|
+
},
|
|
320
|
+
],
|
|
304
321
|
};
|
|
305
322
|
}
|
|
306
|
-
const lines = tasks.map((t) => `[${t.status}] ${t.title} (${t.id}) ${t.tags.length > 0 ?
|
|
323
|
+
const lines = tasks.map((t) => `[${t.status}] ${t.title} (${t.id}) ${t.tags.length > 0 ? "#" + t.tags.join(" #") : ""}`);
|
|
307
324
|
return {
|
|
308
|
-
content: [
|
|
325
|
+
content: [
|
|
326
|
+
{
|
|
327
|
+
type: "text",
|
|
328
|
+
text: `Session: ${sessionName}\n${lines.join("\n")}`,
|
|
329
|
+
},
|
|
330
|
+
],
|
|
309
331
|
};
|
|
310
332
|
}
|
|
311
|
-
case
|
|
333
|
+
case "task_append_note": {
|
|
312
334
|
const task = await appendNote(String(args.id), String(args.note));
|
|
313
335
|
if (!task) {
|
|
314
336
|
return {
|
|
315
|
-
content: [{ type:
|
|
337
|
+
content: [{ type: "text", text: `Task ${args.id} not found` }],
|
|
316
338
|
isError: true,
|
|
317
339
|
};
|
|
318
340
|
}
|
|
@@ -320,22 +342,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
320
342
|
return {
|
|
321
343
|
content: [
|
|
322
344
|
{
|
|
323
|
-
type:
|
|
345
|
+
type: "text",
|
|
324
346
|
text: `Appended note to "${task.title}" (${task.id})\nTotal notes: ${noteCount}`,
|
|
325
347
|
},
|
|
326
348
|
],
|
|
327
349
|
};
|
|
328
350
|
}
|
|
329
|
-
case
|
|
330
|
-
const context = args.context ? String(args.context) :
|
|
331
|
-
const limit = typeof args.limit ===
|
|
351
|
+
case "task_recall": {
|
|
352
|
+
const context = args.context ? String(args.context) : "";
|
|
353
|
+
const limit = typeof args.limit === "number" ? args.limit : 5;
|
|
332
354
|
const result = await recallTasks(context, limit);
|
|
333
355
|
const sessionName = await getCurrentSessionName();
|
|
334
356
|
if (result.relevant.length === 0) {
|
|
335
357
|
return {
|
|
336
358
|
content: [
|
|
337
359
|
{
|
|
338
|
-
type:
|
|
360
|
+
type: "text",
|
|
339
361
|
text: `No relevant tasks found in session "${sessionName}".\n\nSession Summary: ${result.summary.active} active, ${result.summary.done} done, ${result.summary.blocked} blocked (total: ${result.summary.total})`,
|
|
340
362
|
},
|
|
341
363
|
],
|
|
@@ -344,83 +366,95 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
344
366
|
const lines = result.relevant.map((t) => {
|
|
345
367
|
const notesPreview = t.notes && t.notes.length > 0
|
|
346
368
|
? `\n Notes: ${t.notes.length} entries`
|
|
347
|
-
:
|
|
369
|
+
: "";
|
|
348
370
|
const reasons = result.reasons.get(t.id);
|
|
349
371
|
const reasonText = reasons && reasons.length > 0
|
|
350
|
-
? `\n Matched: ${reasons.map(r => `${r.field} "${r.matched}"`).join(
|
|
351
|
-
:
|
|
372
|
+
? `\n Matched: ${reasons.map((r) => `${r.field} "${r.matched}"`).join(", ")}`
|
|
373
|
+
: "";
|
|
352
374
|
return `• [${t.status}] ${t.title} (${t.id})${notesPreview}${reasonText}`;
|
|
353
375
|
});
|
|
354
376
|
return {
|
|
355
377
|
content: [
|
|
356
378
|
{
|
|
357
|
-
type:
|
|
358
|
-
text: `Relevant Context Found (${result.relevant.length} tasks):\n\n${lines.join(
|
|
379
|
+
type: "text",
|
|
380
|
+
text: `Relevant Context Found (${result.relevant.length} tasks):\n\n${lines.join("\n")}\n\nSession Summary: ${result.summary.active} active, ${result.summary.done} done, ${result.summary.blocked} blocked (total: ${result.summary.total})`,
|
|
359
381
|
},
|
|
360
382
|
],
|
|
361
383
|
};
|
|
362
384
|
}
|
|
363
|
-
case
|
|
385
|
+
case "task_get": {
|
|
364
386
|
const task = await getTask(String(args.id));
|
|
365
387
|
if (!task) {
|
|
366
388
|
return {
|
|
367
|
-
content: [{ type:
|
|
389
|
+
content: [{ type: "text", text: `Task ${args.id} not found` }],
|
|
368
390
|
isError: true,
|
|
369
391
|
};
|
|
370
392
|
}
|
|
371
393
|
const notesText = task.notes?.length
|
|
372
|
-
? `\nNotes (${task.notes.length}):\n${task.notes.join(
|
|
373
|
-
:
|
|
394
|
+
? `\nNotes (${task.notes.length}):\n${task.notes.join("\n")}`
|
|
395
|
+
: "";
|
|
374
396
|
return {
|
|
375
397
|
content: [
|
|
376
398
|
{
|
|
377
|
-
type:
|
|
378
|
-
text: `Task: ${task.title}\nID: ${task.id}\nStatus: ${task.status}\nDescription: ${task.description ??
|
|
399
|
+
type: "text",
|
|
400
|
+
text: `Task: ${task.title}\nID: ${task.id}\nStatus: ${task.status}\nDescription: ${task.description ?? "(none)"}${notesText}\nTags: ${task.tags.join(", ") || "(none)"}\nCreated: ${task.createdAt}\nUpdated: ${task.updatedAt}`,
|
|
379
401
|
},
|
|
380
402
|
],
|
|
381
403
|
};
|
|
382
404
|
}
|
|
383
|
-
case
|
|
405
|
+
case "session_list": {
|
|
384
406
|
const sessions = await listAllSessions();
|
|
385
407
|
const active = await getCurrentSessionName();
|
|
386
408
|
if (sessions.length === 0) {
|
|
387
409
|
return {
|
|
388
|
-
content: [{ type:
|
|
410
|
+
content: [{ type: "text", text: "No sessions found" }],
|
|
389
411
|
};
|
|
390
412
|
}
|
|
391
413
|
const lines = sessions.map((s) => {
|
|
392
|
-
const marker = s.name === active ?
|
|
414
|
+
const marker = s.name === active ? " *" : "";
|
|
393
415
|
return `- ${s.name}${marker}`;
|
|
394
416
|
});
|
|
395
417
|
return {
|
|
396
|
-
content: [
|
|
418
|
+
content: [
|
|
419
|
+
{
|
|
420
|
+
type: "text",
|
|
421
|
+
text: `Active: ${active}\n\n${lines.join("\n")}`,
|
|
422
|
+
},
|
|
423
|
+
],
|
|
397
424
|
};
|
|
398
425
|
}
|
|
399
|
-
case
|
|
426
|
+
case "session_switch": {
|
|
400
427
|
const targetName = String(args.name);
|
|
401
428
|
const sessions = await listAllSessions();
|
|
402
429
|
const exists = sessions.some((s) => s.name === targetName);
|
|
403
430
|
if (!exists) {
|
|
404
431
|
return {
|
|
405
|
-
content: [
|
|
432
|
+
content: [
|
|
433
|
+
{
|
|
434
|
+
type: "text",
|
|
435
|
+
text: `Session "${targetName}" not found. Use session_list to see available sessions.`,
|
|
436
|
+
},
|
|
437
|
+
],
|
|
406
438
|
isError: true,
|
|
407
439
|
};
|
|
408
440
|
}
|
|
409
441
|
await setActiveSession(targetName);
|
|
410
442
|
return {
|
|
411
|
-
content: [
|
|
443
|
+
content: [
|
|
444
|
+
{ type: "text", text: `Switched to session "${targetName}"` },
|
|
445
|
+
],
|
|
412
446
|
};
|
|
413
447
|
}
|
|
414
448
|
default:
|
|
415
449
|
return {
|
|
416
|
-
content: [{ type:
|
|
450
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
417
451
|
isError: true,
|
|
418
452
|
};
|
|
419
453
|
}
|
|
420
454
|
}
|
|
421
455
|
catch (err) {
|
|
422
456
|
return {
|
|
423
|
-
content: [{ type:
|
|
457
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
424
458
|
isError: true,
|
|
425
459
|
};
|
|
426
460
|
}
|