session-collab-mcp 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +254 -0
- package/migrations/0006_composite_indexes.sql +14 -0
- package/package.json +10 -1
- package/src/cli.ts +1 -0
- package/src/db/__tests__/queries.test.ts +799 -0
- package/src/db/__tests__/test-helper.ts +216 -0
- package/src/db/queries.ts +27 -9
- package/src/db/sqlite-adapter.ts +6 -6
- package/src/mcp/schemas.ts +200 -0
- package/src/mcp/tools/claim.ts +38 -59
- package/src/mcp/tools/decision.ts +26 -13
- package/src/mcp/tools/lsp.ts +36 -55
- package/src/mcp/tools/message.ts +28 -14
- package/src/mcp/tools/session.ts +82 -42
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import type { DatabaseAdapter } from '../../db/sqlite-adapter.js';
|
|
4
4
|
import type { McpTool, McpToolResult } from '../protocol';
|
|
5
5
|
import { createToolResult } from '../protocol';
|
|
6
|
-
import type { DecisionCategory } from '../../db/types';
|
|
7
6
|
import { addDecision, listDecisions, getSession } from '../../db/queries';
|
|
7
|
+
import { validateInput, decisionAddSchema, decisionListSchema } from '../schemas';
|
|
8
8
|
|
|
9
9
|
export const decisionTools: McpTool[] = [
|
|
10
10
|
{
|
|
@@ -61,13 +61,17 @@ export async function handleDecisionTool(
|
|
|
61
61
|
): Promise<McpToolResult> {
|
|
62
62
|
switch (name) {
|
|
63
63
|
case 'collab_decision_add': {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
const validation = validateInput(decisionAddSchema, args);
|
|
65
|
+
if (!validation.success) {
|
|
66
|
+
return createToolResult(
|
|
67
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
68
|
+
true
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const input = validation.data;
|
|
68
72
|
|
|
69
73
|
// Verify session
|
|
70
|
-
const session = await getSession(db,
|
|
74
|
+
const session = await getSession(db, input.session_id);
|
|
71
75
|
if (!session || session.status !== 'active') {
|
|
72
76
|
return createToolResult(
|
|
73
77
|
JSON.stringify({
|
|
@@ -79,10 +83,10 @@ export async function handleDecisionTool(
|
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
const decision = await addDecision(db, {
|
|
82
|
-
session_id:
|
|
83
|
-
category,
|
|
84
|
-
title,
|
|
85
|
-
description,
|
|
86
|
+
session_id: input.session_id,
|
|
87
|
+
category: input.category,
|
|
88
|
+
title: input.title,
|
|
89
|
+
description: input.description,
|
|
86
90
|
});
|
|
87
91
|
|
|
88
92
|
return createToolResult(
|
|
@@ -95,10 +99,19 @@ export async function handleDecisionTool(
|
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
case 'collab_decision_list': {
|
|
98
|
-
const
|
|
99
|
-
|
|
102
|
+
const validation = validateInput(decisionListSchema, args);
|
|
103
|
+
if (!validation.success) {
|
|
104
|
+
return createToolResult(
|
|
105
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
106
|
+
true
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
const input = validation.data;
|
|
100
110
|
|
|
101
|
-
const decisions = await listDecisions(db, {
|
|
111
|
+
const decisions = await listDecisions(db, {
|
|
112
|
+
category: input.category,
|
|
113
|
+
limit: input.limit ?? 20,
|
|
114
|
+
});
|
|
102
115
|
|
|
103
116
|
return createToolResult(
|
|
104
117
|
JSON.stringify(
|
package/src/mcp/tools/lsp.ts
CHANGED
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
import type { DatabaseAdapter } from '../../db/sqlite-adapter.js';
|
|
4
4
|
import type { McpTool, McpToolResult } from '../protocol';
|
|
5
5
|
import { createToolResult } from '../protocol';
|
|
6
|
-
import type { SymbolType, ConflictInfo
|
|
6
|
+
import type { SymbolType, ConflictInfo } from '../../db/types';
|
|
7
7
|
import { storeReferences, analyzeClaimImpact, clearSessionReferences } from '../../db/queries';
|
|
8
|
+
import {
|
|
9
|
+
validateInput,
|
|
10
|
+
analyzeSymbolsSchema,
|
|
11
|
+
validateSymbolsSchema,
|
|
12
|
+
storeReferencesSchema,
|
|
13
|
+
impactAnalysisSchema,
|
|
14
|
+
} from '../schemas';
|
|
8
15
|
|
|
9
16
|
// LSP Symbol Kind mapping (from LSP spec)
|
|
10
17
|
const LSP_SYMBOL_KIND_MAP: Record<number, SymbolType> = {
|
|
@@ -305,24 +312,17 @@ export async function handleLspTool(
|
|
|
305
312
|
): Promise<McpToolResult> {
|
|
306
313
|
switch (name) {
|
|
307
314
|
case 'collab_analyze_symbols': {
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
const references = args.references as SymbolReference[] | undefined;
|
|
311
|
-
const checkSymbols = args.check_symbols as string[] | undefined;
|
|
312
|
-
|
|
313
|
-
if (!sessionId) {
|
|
314
|
-
return createToolResult(
|
|
315
|
-
JSON.stringify({ error: 'INVALID_INPUT', message: 'session_id is required' }),
|
|
316
|
-
true
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (!files || !Array.isArray(files) || files.length === 0) {
|
|
315
|
+
const validation = validateInput(analyzeSymbolsSchema, args);
|
|
316
|
+
if (!validation.success) {
|
|
321
317
|
return createToolResult(
|
|
322
|
-
JSON.stringify({ error: 'INVALID_INPUT', message:
|
|
318
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
323
319
|
true
|
|
324
320
|
);
|
|
325
321
|
}
|
|
322
|
+
const input = validation.data;
|
|
323
|
+
const files = input.files as FileSymbolInput[];
|
|
324
|
+
const references = input.references as SymbolReference[] | undefined;
|
|
325
|
+
const checkSymbols = input.check_symbols;
|
|
326
326
|
|
|
327
327
|
// Build reference lookup map if provided
|
|
328
328
|
const referenceMap = new Map<string, SymbolReference>();
|
|
@@ -352,10 +352,10 @@ export async function handleLspTool(
|
|
|
352
352
|
const symbolNames = [...new Set(symbolsToAnalyze.map((s) => s.name))];
|
|
353
353
|
|
|
354
354
|
// Check for symbol-level claims
|
|
355
|
-
const symbolConflicts = await querySymbolConflicts(db, fileList, symbolNames,
|
|
355
|
+
const symbolConflicts = await querySymbolConflicts(db, fileList, symbolNames, input.session_id);
|
|
356
356
|
|
|
357
357
|
// Check for file-level claims
|
|
358
|
-
const fileConflicts = await queryFileConflicts(db, fileList,
|
|
358
|
+
const fileConflicts = await queryFileConflicts(db, fileList, input.session_id);
|
|
359
359
|
|
|
360
360
|
// Build result with conflict status for each symbol
|
|
361
361
|
const analyzedSymbols: AnalyzedSymbol[] = [];
|
|
@@ -455,23 +455,18 @@ export async function handleLspTool(
|
|
|
455
455
|
}
|
|
456
456
|
|
|
457
457
|
case 'collab_validate_symbols': {
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
const lspSymbols = args.lsp_symbols as Array<{ name: string; kind: number }>;
|
|
461
|
-
|
|
462
|
-
if (!file || !symbols || !lspSymbols) {
|
|
458
|
+
const validation = validateInput(validateSymbolsSchema, args);
|
|
459
|
+
if (!validation.success) {
|
|
463
460
|
return createToolResult(
|
|
464
|
-
JSON.stringify({
|
|
465
|
-
error: 'INVALID_INPUT',
|
|
466
|
-
message: 'file, symbols, and lsp_symbols are required',
|
|
467
|
-
}),
|
|
461
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
468
462
|
true
|
|
469
463
|
);
|
|
470
464
|
}
|
|
465
|
+
const input = validation.data;
|
|
471
466
|
|
|
472
467
|
// Build set of available symbol names from LSP data
|
|
473
468
|
const availableSymbols = new Set<string>();
|
|
474
|
-
for (const lspSym of
|
|
469
|
+
for (const lspSym of input.lsp_symbols) {
|
|
475
470
|
availableSymbols.add(lspSym.name);
|
|
476
471
|
}
|
|
477
472
|
|
|
@@ -480,7 +475,7 @@ export async function handleLspTool(
|
|
|
480
475
|
const invalid: string[] = [];
|
|
481
476
|
const suggestions: Record<string, string[]> = {};
|
|
482
477
|
|
|
483
|
-
for (const sym of symbols) {
|
|
478
|
+
for (const sym of input.symbols) {
|
|
484
479
|
if (availableSymbols.has(sym)) {
|
|
485
480
|
valid.push(sym);
|
|
486
481
|
} else {
|
|
@@ -502,7 +497,7 @@ export async function handleLspTool(
|
|
|
502
497
|
return createToolResult(
|
|
503
498
|
JSON.stringify({
|
|
504
499
|
valid: allValid,
|
|
505
|
-
file,
|
|
500
|
+
file: input.file,
|
|
506
501
|
valid_symbols: valid,
|
|
507
502
|
invalid_symbols: invalid,
|
|
508
503
|
suggestions: Object.keys(suggestions).length > 0 ? suggestions : undefined,
|
|
@@ -515,32 +510,23 @@ export async function handleLspTool(
|
|
|
515
510
|
}
|
|
516
511
|
|
|
517
512
|
case 'collab_store_references': {
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
const clearExisting = args.clear_existing as boolean | undefined;
|
|
521
|
-
|
|
522
|
-
if (!sessionId) {
|
|
523
|
-
return createToolResult(
|
|
524
|
-
JSON.stringify({ error: 'INVALID_INPUT', message: 'session_id is required' }),
|
|
525
|
-
true
|
|
526
|
-
);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
if (!references || !Array.isArray(references)) {
|
|
513
|
+
const validation = validateInput(storeReferencesSchema, args);
|
|
514
|
+
if (!validation.success) {
|
|
530
515
|
return createToolResult(
|
|
531
|
-
JSON.stringify({ error: 'INVALID_INPUT', message:
|
|
516
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
532
517
|
true
|
|
533
518
|
);
|
|
534
519
|
}
|
|
520
|
+
const input = validation.data;
|
|
535
521
|
|
|
536
522
|
// Clear existing references if requested
|
|
537
523
|
let cleared = 0;
|
|
538
|
-
if (
|
|
539
|
-
cleared = await clearSessionReferences(db,
|
|
524
|
+
if (input.clear_existing) {
|
|
525
|
+
cleared = await clearSessionReferences(db, input.session_id);
|
|
540
526
|
}
|
|
541
527
|
|
|
542
528
|
// Store new references
|
|
543
|
-
const result = await storeReferences(db,
|
|
529
|
+
const result = await storeReferences(db, input.session_id, input.references);
|
|
544
530
|
|
|
545
531
|
return createToolResult(
|
|
546
532
|
JSON.stringify({
|
|
@@ -554,21 +540,16 @@ export async function handleLspTool(
|
|
|
554
540
|
}
|
|
555
541
|
|
|
556
542
|
case 'collab_impact_analysis': {
|
|
557
|
-
const
|
|
558
|
-
|
|
559
|
-
const symbol = args.symbol as string;
|
|
560
|
-
|
|
561
|
-
if (!sessionId || !file || !symbol) {
|
|
543
|
+
const validation = validateInput(impactAnalysisSchema, args);
|
|
544
|
+
if (!validation.success) {
|
|
562
545
|
return createToolResult(
|
|
563
|
-
JSON.stringify({
|
|
564
|
-
error: 'INVALID_INPUT',
|
|
565
|
-
message: 'session_id, file, and symbol are required',
|
|
566
|
-
}),
|
|
546
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
567
547
|
true
|
|
568
548
|
);
|
|
569
549
|
}
|
|
550
|
+
const input = validation.data;
|
|
570
551
|
|
|
571
|
-
const impact = await analyzeClaimImpact(db, file, symbol,
|
|
552
|
+
const impact = await analyzeClaimImpact(db, input.file, input.symbol, input.session_id);
|
|
572
553
|
|
|
573
554
|
// Determine risk level
|
|
574
555
|
let riskLevel: 'low' | 'medium' | 'high';
|
package/src/mcp/tools/message.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { DatabaseAdapter } from '../../db/sqlite-adapter.js';
|
|
|
4
4
|
import type { McpTool, McpToolResult } from '../protocol';
|
|
5
5
|
import { createToolResult } from '../protocol';
|
|
6
6
|
import { sendMessage, listMessages, getSession } from '../../db/queries';
|
|
7
|
+
import { validateInput, messageSendSchema, messageListSchema } from '../schemas';
|
|
7
8
|
|
|
8
9
|
export const messageTools: McpTool[] = [
|
|
9
10
|
{
|
|
@@ -59,12 +60,17 @@ export async function handleMessageTool(
|
|
|
59
60
|
): Promise<McpToolResult> {
|
|
60
61
|
switch (name) {
|
|
61
62
|
case 'collab_message_send': {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
const validation = validateInput(messageSendSchema, args);
|
|
64
|
+
if (!validation.success) {
|
|
65
|
+
return createToolResult(
|
|
66
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
67
|
+
true
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const input = validation.data;
|
|
65
71
|
|
|
66
72
|
// Verify sender session
|
|
67
|
-
const fromSession = await getSession(db,
|
|
73
|
+
const fromSession = await getSession(db, input.from_session_id);
|
|
68
74
|
if (!fromSession || fromSession.status !== 'active') {
|
|
69
75
|
return createToolResult(
|
|
70
76
|
JSON.stringify({
|
|
@@ -76,8 +82,8 @@ export async function handleMessageTool(
|
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
// Verify target session if specified
|
|
79
|
-
if (
|
|
80
|
-
const toSession = await getSession(db,
|
|
85
|
+
if (input.to_session_id) {
|
|
86
|
+
const toSession = await getSession(db, input.to_session_id);
|
|
81
87
|
if (!toSession || toSession.status !== 'active') {
|
|
82
88
|
return createToolResult(
|
|
83
89
|
JSON.stringify({
|
|
@@ -90,28 +96,36 @@ export async function handleMessageTool(
|
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
const message = await sendMessage(db, {
|
|
93
|
-
from_session_id:
|
|
94
|
-
to_session_id:
|
|
95
|
-
content,
|
|
99
|
+
from_session_id: input.from_session_id,
|
|
100
|
+
to_session_id: input.to_session_id,
|
|
101
|
+
content: input.content,
|
|
96
102
|
});
|
|
97
103
|
|
|
98
104
|
return createToolResult(
|
|
99
105
|
JSON.stringify({
|
|
100
106
|
success: true,
|
|
101
107
|
message_id: message.id,
|
|
102
|
-
sent_to:
|
|
108
|
+
sent_to: input.to_session_id ?? 'all sessions (broadcast)',
|
|
103
109
|
message: 'Message sent successfully.',
|
|
104
110
|
})
|
|
105
111
|
);
|
|
106
112
|
}
|
|
107
113
|
|
|
108
114
|
case 'collab_message_list': {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
const validation = validateInput(messageListSchema, args);
|
|
116
|
+
if (!validation.success) {
|
|
117
|
+
return createToolResult(
|
|
118
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
119
|
+
true
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
const input = validation.data;
|
|
123
|
+
|
|
124
|
+
const unreadOnly = input.unread_only ?? true;
|
|
125
|
+
const markAsRead = input.mark_as_read ?? true;
|
|
112
126
|
|
|
113
127
|
const messages = await listMessages(db, {
|
|
114
|
-
session_id:
|
|
128
|
+
session_id: input.session_id,
|
|
115
129
|
unread_only: unreadOnly,
|
|
116
130
|
mark_as_read: markAsRead,
|
|
117
131
|
});
|
package/src/mcp/tools/session.ts
CHANGED
|
@@ -14,8 +14,17 @@ import {
|
|
|
14
14
|
cleanupStaleSessions,
|
|
15
15
|
listClaims,
|
|
16
16
|
} from '../../db/queries';
|
|
17
|
-
import type { TodoItem, SessionConfig
|
|
17
|
+
import type { TodoItem, SessionConfig } from '../../db/types';
|
|
18
18
|
import { DEFAULT_SESSION_CONFIG } from '../../db/types';
|
|
19
|
+
import {
|
|
20
|
+
validateInput,
|
|
21
|
+
sessionStartSchema,
|
|
22
|
+
sessionEndSchema,
|
|
23
|
+
sessionListSchema,
|
|
24
|
+
sessionHeartbeatSchema,
|
|
25
|
+
statusUpdateSchema,
|
|
26
|
+
configSchema,
|
|
27
|
+
} from '../schemas';
|
|
19
28
|
|
|
20
29
|
export const sessionTools: McpTool[] = [
|
|
21
30
|
{
|
|
@@ -177,17 +186,26 @@ export async function handleSessionTool(
|
|
|
177
186
|
): Promise<McpToolResult> {
|
|
178
187
|
switch (name) {
|
|
179
188
|
case 'collab_session_start': {
|
|
189
|
+
const validation = validateInput(sessionStartSchema, args);
|
|
190
|
+
if (!validation.success) {
|
|
191
|
+
return createToolResult(
|
|
192
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
193
|
+
true
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
const input = validation.data;
|
|
197
|
+
|
|
180
198
|
// Cleanup stale sessions first
|
|
181
199
|
await cleanupStaleSessions(db, 30);
|
|
182
200
|
|
|
183
201
|
const session = await createSession(db, {
|
|
184
|
-
name:
|
|
185
|
-
project_root:
|
|
186
|
-
machine_id:
|
|
202
|
+
name: input.name,
|
|
203
|
+
project_root: input.project_root,
|
|
204
|
+
machine_id: input.machine_id,
|
|
187
205
|
user_id: userId,
|
|
188
206
|
});
|
|
189
207
|
|
|
190
|
-
const activeSessions = await listSessions(db, { project_root:
|
|
208
|
+
const activeSessions = await listSessions(db, { project_root: input.project_root, user_id: userId });
|
|
191
209
|
|
|
192
210
|
return createToolResult(
|
|
193
211
|
JSON.stringify(
|
|
@@ -208,10 +226,16 @@ export async function handleSessionTool(
|
|
|
208
226
|
}
|
|
209
227
|
|
|
210
228
|
case 'collab_session_end': {
|
|
211
|
-
const
|
|
212
|
-
|
|
229
|
+
const validation = validateInput(sessionEndSchema, args);
|
|
230
|
+
if (!validation.success) {
|
|
231
|
+
return createToolResult(
|
|
232
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
233
|
+
true
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
const input = validation.data;
|
|
213
237
|
|
|
214
|
-
const session = await getSession(db,
|
|
238
|
+
const session = await getSession(db, input.session_id);
|
|
215
239
|
if (!session) {
|
|
216
240
|
return createToolResult(
|
|
217
241
|
JSON.stringify({ error: 'SESSION_NOT_FOUND', message: 'Session not found' }),
|
|
@@ -219,21 +243,30 @@ export async function handleSessionTool(
|
|
|
219
243
|
);
|
|
220
244
|
}
|
|
221
245
|
|
|
222
|
-
await endSession(db,
|
|
246
|
+
await endSession(db, input.session_id, input.release_claims);
|
|
223
247
|
|
|
224
248
|
return createToolResult(
|
|
225
249
|
JSON.stringify({
|
|
226
250
|
success: true,
|
|
227
|
-
message: `Session ended. All claims marked as ${
|
|
251
|
+
message: `Session ended. All claims marked as ${input.release_claims === 'complete' ? 'completed' : 'abandoned'}.`,
|
|
228
252
|
})
|
|
229
253
|
);
|
|
230
254
|
}
|
|
231
255
|
|
|
232
256
|
case 'collab_session_list': {
|
|
257
|
+
const validation = validateInput(sessionListSchema, args);
|
|
258
|
+
if (!validation.success) {
|
|
259
|
+
return createToolResult(
|
|
260
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
261
|
+
true
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
const input = validation.data;
|
|
265
|
+
|
|
233
266
|
// Do not filter by user_id - collaboration tool should show all sessions
|
|
234
267
|
const sessions = await listSessions(db, {
|
|
235
|
-
include_inactive:
|
|
236
|
-
project_root:
|
|
268
|
+
include_inactive: input.include_inactive,
|
|
269
|
+
project_root: input.project_root,
|
|
237
270
|
});
|
|
238
271
|
|
|
239
272
|
// Get active claims count for each session and include status info
|
|
@@ -278,13 +311,18 @@ export async function handleSessionTool(
|
|
|
278
311
|
}
|
|
279
312
|
|
|
280
313
|
case 'collab_session_heartbeat': {
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
314
|
+
const validation = validateInput(sessionHeartbeatSchema, args);
|
|
315
|
+
if (!validation.success) {
|
|
316
|
+
return createToolResult(
|
|
317
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
318
|
+
true
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
const input = validation.data;
|
|
284
322
|
|
|
285
|
-
const updated = await updateSessionHeartbeat(db,
|
|
286
|
-
current_task:
|
|
287
|
-
todos,
|
|
323
|
+
const updated = await updateSessionHeartbeat(db, input.session_id, {
|
|
324
|
+
current_task: input.current_task,
|
|
325
|
+
todos: input.todos as TodoItem[] | undefined,
|
|
288
326
|
});
|
|
289
327
|
|
|
290
328
|
if (!updated) {
|
|
@@ -301,18 +339,24 @@ export async function handleSessionTool(
|
|
|
301
339
|
JSON.stringify({
|
|
302
340
|
success: true,
|
|
303
341
|
message: 'Heartbeat updated',
|
|
304
|
-
status_synced: !!(
|
|
342
|
+
status_synced: !!(input.current_task || input.todos),
|
|
305
343
|
})
|
|
306
344
|
);
|
|
307
345
|
}
|
|
308
346
|
|
|
309
347
|
case 'collab_status_update': {
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
348
|
+
const validation = validateInput(statusUpdateSchema, args);
|
|
349
|
+
if (!validation.success) {
|
|
350
|
+
return createToolResult(
|
|
351
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
352
|
+
true
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
const input = validation.data;
|
|
356
|
+
const todos = input.todos as TodoItem[] | undefined;
|
|
313
357
|
|
|
314
358
|
// Validate session exists
|
|
315
|
-
const session = await getSession(db,
|
|
359
|
+
const session = await getSession(db, input.session_id);
|
|
316
360
|
if (!session || session.status !== 'active') {
|
|
317
361
|
return createToolResult(
|
|
318
362
|
JSON.stringify({
|
|
@@ -323,8 +367,8 @@ export async function handleSessionTool(
|
|
|
323
367
|
);
|
|
324
368
|
}
|
|
325
369
|
|
|
326
|
-
await updateSessionStatus(db,
|
|
327
|
-
current_task:
|
|
370
|
+
await updateSessionStatus(db, input.session_id, {
|
|
371
|
+
current_task: input.current_task,
|
|
328
372
|
todos,
|
|
329
373
|
});
|
|
330
374
|
|
|
@@ -343,28 +387,24 @@ export async function handleSessionTool(
|
|
|
343
387
|
JSON.stringify({
|
|
344
388
|
success: true,
|
|
345
389
|
message: 'Status updated successfully.',
|
|
346
|
-
current_task:
|
|
390
|
+
current_task: input.current_task ?? null,
|
|
347
391
|
progress,
|
|
348
392
|
})
|
|
349
393
|
);
|
|
350
394
|
}
|
|
351
395
|
|
|
352
396
|
case 'collab_config': {
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
// Validate session_id input
|
|
356
|
-
if (!sessionId || typeof sessionId !== 'string') {
|
|
397
|
+
const validation = validateInput(configSchema, args);
|
|
398
|
+
if (!validation.success) {
|
|
357
399
|
return createToolResult(
|
|
358
|
-
JSON.stringify({
|
|
359
|
-
error: 'INVALID_INPUT',
|
|
360
|
-
message: 'session_id is required',
|
|
361
|
-
}),
|
|
400
|
+
JSON.stringify({ error: 'INVALID_INPUT', message: validation.error }),
|
|
362
401
|
true
|
|
363
402
|
);
|
|
364
403
|
}
|
|
404
|
+
const input = validation.data;
|
|
365
405
|
|
|
366
406
|
// Validate session exists
|
|
367
|
-
const session = await getSession(db,
|
|
407
|
+
const session = await getSession(db, input.session_id);
|
|
368
408
|
if (!session || session.status !== 'active') {
|
|
369
409
|
return createToolResult(
|
|
370
410
|
JSON.stringify({
|
|
@@ -387,17 +427,17 @@ export async function handleSessionTool(
|
|
|
387
427
|
|
|
388
428
|
// Update with new values
|
|
389
429
|
const newConfig: SessionConfig = {
|
|
390
|
-
mode:
|
|
391
|
-
allow_release_others:
|
|
392
|
-
?
|
|
430
|
+
mode: input.mode ?? currentConfig.mode,
|
|
431
|
+
allow_release_others: input.allow_release_others !== undefined
|
|
432
|
+
? input.allow_release_others
|
|
393
433
|
: currentConfig.allow_release_others,
|
|
394
|
-
auto_release_stale:
|
|
395
|
-
?
|
|
434
|
+
auto_release_stale: input.auto_release_stale !== undefined
|
|
435
|
+
? input.auto_release_stale
|
|
396
436
|
: currentConfig.auto_release_stale,
|
|
397
|
-
stale_threshold_hours:
|
|
437
|
+
stale_threshold_hours: input.stale_threshold_hours ?? currentConfig.stale_threshold_hours,
|
|
398
438
|
};
|
|
399
439
|
|
|
400
|
-
await updateSessionConfig(db,
|
|
440
|
+
await updateSessionConfig(db, input.session_id, newConfig);
|
|
401
441
|
|
|
402
442
|
return createToolResult(
|
|
403
443
|
JSON.stringify({
|