@vibescope/mcp-server 0.3.13 → 0.3.15
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/dist/api-client/fallback.d.ts +33 -0
- package/dist/api-client/fallback.js +21 -0
- package/dist/api-client/findings.d.ts +34 -62
- package/dist/api-client/findings.js +20 -39
- package/dist/api-client/index.d.ts +47 -54
- package/dist/api-client/index.js +26 -10
- package/dist/api-client/milestones.d.ts +36 -37
- package/dist/api-client/milestones.js +17 -30
- package/dist/api-client/validation.d.ts +35 -0
- package/dist/api-client/validation.js +23 -0
- package/dist/api-client.js +8 -1
- package/dist/handlers/cloud-agents.js +4 -12
- package/dist/handlers/discovery.js +1 -0
- package/dist/handlers/findings.js +1 -1
- package/dist/handlers/ideas.js +1 -1
- package/dist/handlers/session.js +1 -0
- package/dist/handlers/tool-docs.js +344 -0
- package/docs/TOOLS.md +30 -34
- package/package.json +1 -1
- package/src/api-client/fallback.ts +52 -0
- package/src/api-client/findings.ts +56 -110
- package/src/api-client/index.ts +34 -14
- package/src/api-client/milestones.ts +47 -67
- package/src/api-client/validation.ts +60 -0
- package/src/api-client.ts +10 -1
- package/src/handlers/cloud-agents.test.ts +438 -0
- package/src/handlers/cloud-agents.ts +7 -17
- package/src/handlers/discovery.ts +1 -0
- package/src/handlers/findings.ts +1 -1
- package/src/handlers/ideas.ts +1 -1
- package/src/handlers/session.ts +1 -0
- package/src/handlers/tool-docs.test.ts +511 -0
- package/src/handlers/tool-docs.ts +382 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
cleanupStaleCloudAgents,
|
|
4
|
+
listCloudAgents,
|
|
5
|
+
} from './cloud-agents.js';
|
|
6
|
+
import { ValidationError } from '../validators.js';
|
|
7
|
+
import { createMockContext, testUUID } from './__test-utils__.js';
|
|
8
|
+
import { mockApiClient } from './__test-setup__.js';
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// cleanupStaleCloudAgents Tests
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
describe('cleanupStaleCloudAgents', () => {
|
|
15
|
+
beforeEach(() => vi.clearAllMocks());
|
|
16
|
+
|
|
17
|
+
it('should throw error for missing project_id', async () => {
|
|
18
|
+
const ctx = createMockContext();
|
|
19
|
+
|
|
20
|
+
await expect(
|
|
21
|
+
cleanupStaleCloudAgents({}, ctx)
|
|
22
|
+
).rejects.toThrow(ValidationError);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should throw error for invalid project_id UUID', async () => {
|
|
26
|
+
const ctx = createMockContext();
|
|
27
|
+
|
|
28
|
+
await expect(
|
|
29
|
+
cleanupStaleCloudAgents({ project_id: 'invalid' }, ctx)
|
|
30
|
+
).rejects.toThrow(ValidationError);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should cleanup stale agents successfully', async () => {
|
|
34
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
35
|
+
ok: true,
|
|
36
|
+
data: {
|
|
37
|
+
cleaned: 2,
|
|
38
|
+
failed: 0,
|
|
39
|
+
agents: [
|
|
40
|
+
{ id: 'agent-1', name: 'Agent 1', success: true },
|
|
41
|
+
{ id: 'agent-2', name: 'Agent 2', success: true },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
const ctx = createMockContext();
|
|
46
|
+
|
|
47
|
+
const result = await cleanupStaleCloudAgents(
|
|
48
|
+
{ project_id: testUUID() },
|
|
49
|
+
ctx
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
expect(result.result).toMatchObject({
|
|
53
|
+
cleaned: 2,
|
|
54
|
+
failed: 0,
|
|
55
|
+
message: 'Cleaned up 2 stale agents',
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should call API with correct parameters', async () => {
|
|
60
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
61
|
+
ok: true,
|
|
62
|
+
data: { cleaned: 0, agents: [] },
|
|
63
|
+
});
|
|
64
|
+
const ctx = createMockContext();
|
|
65
|
+
|
|
66
|
+
await cleanupStaleCloudAgents(
|
|
67
|
+
{
|
|
68
|
+
project_id: testUUID(),
|
|
69
|
+
stale_minutes: 10,
|
|
70
|
+
include_running: true,
|
|
71
|
+
dry_run: false,
|
|
72
|
+
},
|
|
73
|
+
ctx
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
77
|
+
'cleanup_stale_cloud_agents',
|
|
78
|
+
{
|
|
79
|
+
project_id: testUUID(),
|
|
80
|
+
staleMinutes: 10,
|
|
81
|
+
includeRunning: true,
|
|
82
|
+
dryRun: false,
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should use default values for optional parameters', async () => {
|
|
88
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
89
|
+
ok: true,
|
|
90
|
+
data: { cleaned: 0, agents: [] },
|
|
91
|
+
});
|
|
92
|
+
const ctx = createMockContext();
|
|
93
|
+
|
|
94
|
+
await cleanupStaleCloudAgents(
|
|
95
|
+
{ project_id: testUUID() },
|
|
96
|
+
ctx
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
100
|
+
'cleanup_stale_cloud_agents',
|
|
101
|
+
{
|
|
102
|
+
project_id: testUUID(),
|
|
103
|
+
staleMinutes: 5, // default
|
|
104
|
+
includeRunning: false, // default
|
|
105
|
+
dryRun: false, // default
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should return dry run preview when dry_run is true', async () => {
|
|
111
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
112
|
+
ok: true,
|
|
113
|
+
data: {
|
|
114
|
+
wouldClean: 3,
|
|
115
|
+
dryRun: true,
|
|
116
|
+
agents: [
|
|
117
|
+
{ id: 'agent-1', status: 'failed', createdAt: '2025-01-14T10:00:00Z' },
|
|
118
|
+
{ id: 'agent-2', status: 'starting', createdAt: '2025-01-14T10:05:00Z' },
|
|
119
|
+
{ id: 'agent-3', status: 'stopped', createdAt: '2025-01-14T10:10:00Z' },
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
const ctx = createMockContext();
|
|
124
|
+
|
|
125
|
+
const result = await cleanupStaleCloudAgents(
|
|
126
|
+
{ project_id: testUUID(), dry_run: true },
|
|
127
|
+
ctx
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
expect(result.result).toMatchObject({
|
|
131
|
+
dryRun: true,
|
|
132
|
+
wouldClean: 3,
|
|
133
|
+
message: 'Found 3 stale agents that would be cleaned up',
|
|
134
|
+
});
|
|
135
|
+
expect((result.result as { agents: unknown[] }).agents).toHaveLength(3);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should include failed count in response', async () => {
|
|
139
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
140
|
+
ok: true,
|
|
141
|
+
data: {
|
|
142
|
+
cleaned: 2,
|
|
143
|
+
failed: 1,
|
|
144
|
+
agents: [
|
|
145
|
+
{ id: 'agent-1', success: true },
|
|
146
|
+
{ id: 'agent-2', success: true },
|
|
147
|
+
{ id: 'agent-3', success: false, error: 'Agent still running' },
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
const ctx = createMockContext();
|
|
152
|
+
|
|
153
|
+
const result = await cleanupStaleCloudAgents(
|
|
154
|
+
{ project_id: testUUID() },
|
|
155
|
+
ctx
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
expect(result.result).toMatchObject({
|
|
159
|
+
cleaned: 2,
|
|
160
|
+
failed: 1,
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should return error when API call fails', async () => {
|
|
165
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
166
|
+
ok: false,
|
|
167
|
+
error: 'Failed to cleanup agents',
|
|
168
|
+
});
|
|
169
|
+
const ctx = createMockContext();
|
|
170
|
+
|
|
171
|
+
const result = await cleanupStaleCloudAgents(
|
|
172
|
+
{ project_id: testUUID() },
|
|
173
|
+
ctx
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(result.isError).toBe(true);
|
|
177
|
+
expect(result.result).toMatchObject({
|
|
178
|
+
error: 'Failed to cleanup agents',
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should return error when API returns undefined data', async () => {
|
|
183
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
184
|
+
ok: true,
|
|
185
|
+
data: undefined,
|
|
186
|
+
});
|
|
187
|
+
const ctx = createMockContext();
|
|
188
|
+
|
|
189
|
+
const result = await cleanupStaleCloudAgents(
|
|
190
|
+
{ project_id: testUUID() },
|
|
191
|
+
ctx
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
expect(result.isError).toBe(true);
|
|
195
|
+
expect(result.result).toMatchObject({
|
|
196
|
+
error: 'Failed to cleanup stale agents',
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// ============================================================================
|
|
202
|
+
// listCloudAgents Tests
|
|
203
|
+
// ============================================================================
|
|
204
|
+
|
|
205
|
+
describe('listCloudAgents', () => {
|
|
206
|
+
beforeEach(() => vi.clearAllMocks());
|
|
207
|
+
|
|
208
|
+
it('should throw error for missing project_id', async () => {
|
|
209
|
+
const ctx = createMockContext();
|
|
210
|
+
|
|
211
|
+
await expect(
|
|
212
|
+
listCloudAgents({}, ctx)
|
|
213
|
+
).rejects.toThrow(ValidationError);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should throw error for invalid project_id UUID', async () => {
|
|
217
|
+
const ctx = createMockContext();
|
|
218
|
+
|
|
219
|
+
await expect(
|
|
220
|
+
listCloudAgents({ project_id: 'invalid' }, ctx)
|
|
221
|
+
).rejects.toThrow(ValidationError);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should throw error for invalid status value', async () => {
|
|
225
|
+
const ctx = createMockContext();
|
|
226
|
+
|
|
227
|
+
await expect(
|
|
228
|
+
listCloudAgents({ project_id: testUUID(), status: 'invalid_status' }, ctx)
|
|
229
|
+
).rejects.toThrow(ValidationError);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should return empty list when no agents', async () => {
|
|
233
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
234
|
+
ok: true,
|
|
235
|
+
data: { agents: [] },
|
|
236
|
+
});
|
|
237
|
+
const ctx = createMockContext();
|
|
238
|
+
|
|
239
|
+
const result = await listCloudAgents(
|
|
240
|
+
{ project_id: testUUID() },
|
|
241
|
+
ctx
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
expect(result.result).toMatchObject({
|
|
245
|
+
agents: [],
|
|
246
|
+
count: 0,
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should return agents list with count', async () => {
|
|
251
|
+
const mockAgents = [
|
|
252
|
+
{
|
|
253
|
+
id: 'agent-1',
|
|
254
|
+
name: 'Test Agent 1',
|
|
255
|
+
status: 'running',
|
|
256
|
+
created_at: '2025-01-14T10:00:00Z',
|
|
257
|
+
last_heartbeat: '2025-01-14T12:00:00Z',
|
|
258
|
+
public_ip: '10.0.0.1',
|
|
259
|
+
ecs_task_id: 'task-abc123',
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: 'agent-2',
|
|
263
|
+
name: 'Test Agent 2',
|
|
264
|
+
status: 'stopped',
|
|
265
|
+
created_at: '2025-01-14T09:00:00Z',
|
|
266
|
+
},
|
|
267
|
+
];
|
|
268
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
269
|
+
ok: true,
|
|
270
|
+
data: { agents: mockAgents },
|
|
271
|
+
});
|
|
272
|
+
const ctx = createMockContext();
|
|
273
|
+
|
|
274
|
+
const result = await listCloudAgents(
|
|
275
|
+
{ project_id: testUUID() },
|
|
276
|
+
ctx
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
expect(result.result).toMatchObject({
|
|
280
|
+
agents: mockAgents,
|
|
281
|
+
count: 2,
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('should call API with status filter', async () => {
|
|
286
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
287
|
+
ok: true,
|
|
288
|
+
data: { agents: [] },
|
|
289
|
+
});
|
|
290
|
+
const ctx = createMockContext();
|
|
291
|
+
|
|
292
|
+
await listCloudAgents(
|
|
293
|
+
{ project_id: testUUID(), status: 'running' },
|
|
294
|
+
ctx
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
298
|
+
'list_cloud_agents',
|
|
299
|
+
{
|
|
300
|
+
project_id: testUUID(),
|
|
301
|
+
status: 'running',
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('should not pass status when "all" is specified', async () => {
|
|
307
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
308
|
+
ok: true,
|
|
309
|
+
data: { agents: [] },
|
|
310
|
+
});
|
|
311
|
+
const ctx = createMockContext();
|
|
312
|
+
|
|
313
|
+
await listCloudAgents(
|
|
314
|
+
{ project_id: testUUID(), status: 'all' },
|
|
315
|
+
ctx
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
319
|
+
'list_cloud_agents',
|
|
320
|
+
{
|
|
321
|
+
project_id: testUUID(),
|
|
322
|
+
status: undefined,
|
|
323
|
+
}
|
|
324
|
+
);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should use default status of "all"', async () => {
|
|
328
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
329
|
+
ok: true,
|
|
330
|
+
data: { agents: [] },
|
|
331
|
+
});
|
|
332
|
+
const ctx = createMockContext();
|
|
333
|
+
|
|
334
|
+
await listCloudAgents(
|
|
335
|
+
{ project_id: testUUID() },
|
|
336
|
+
ctx
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
340
|
+
'list_cloud_agents',
|
|
341
|
+
{
|
|
342
|
+
project_id: testUUID(),
|
|
343
|
+
status: undefined, // 'all' becomes undefined
|
|
344
|
+
}
|
|
345
|
+
);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should filter by starting status', async () => {
|
|
349
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
350
|
+
ok: true,
|
|
351
|
+
data: { agents: [{ id: 'a1', status: 'starting', created_at: '2025-01-14T10:00:00Z' }] },
|
|
352
|
+
});
|
|
353
|
+
const ctx = createMockContext();
|
|
354
|
+
|
|
355
|
+
const result = await listCloudAgents(
|
|
356
|
+
{ project_id: testUUID(), status: 'starting' },
|
|
357
|
+
ctx
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
361
|
+
'list_cloud_agents',
|
|
362
|
+
{ project_id: testUUID(), status: 'starting' }
|
|
363
|
+
);
|
|
364
|
+
expect((result.result as { count: number }).count).toBe(1);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('should filter by failed status', async () => {
|
|
368
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
369
|
+
ok: true,
|
|
370
|
+
data: { agents: [] },
|
|
371
|
+
});
|
|
372
|
+
const ctx = createMockContext();
|
|
373
|
+
|
|
374
|
+
await listCloudAgents(
|
|
375
|
+
{ project_id: testUUID(), status: 'failed' },
|
|
376
|
+
ctx
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
380
|
+
'list_cloud_agents',
|
|
381
|
+
{ project_id: testUUID(), status: 'failed' }
|
|
382
|
+
);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should filter by stopped status', async () => {
|
|
386
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
387
|
+
ok: true,
|
|
388
|
+
data: { agents: [] },
|
|
389
|
+
});
|
|
390
|
+
const ctx = createMockContext();
|
|
391
|
+
|
|
392
|
+
await listCloudAgents(
|
|
393
|
+
{ project_id: testUUID(), status: 'stopped' },
|
|
394
|
+
ctx
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
expect(mockApiClient.proxy).toHaveBeenCalledWith(
|
|
398
|
+
'list_cloud_agents',
|
|
399
|
+
{ project_id: testUUID(), status: 'stopped' }
|
|
400
|
+
);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('should return error when API call fails', async () => {
|
|
404
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
405
|
+
ok: false,
|
|
406
|
+
error: 'Database error',
|
|
407
|
+
});
|
|
408
|
+
const ctx = createMockContext();
|
|
409
|
+
|
|
410
|
+
const result = await listCloudAgents(
|
|
411
|
+
{ project_id: testUUID() },
|
|
412
|
+
ctx
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
expect(result.isError).toBe(true);
|
|
416
|
+
expect(result.result).toMatchObject({
|
|
417
|
+
error: 'Database error',
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('should return error when API returns undefined data', async () => {
|
|
422
|
+
mockApiClient.proxy.mockResolvedValue({
|
|
423
|
+
ok: true,
|
|
424
|
+
data: undefined,
|
|
425
|
+
});
|
|
426
|
+
const ctx = createMockContext();
|
|
427
|
+
|
|
428
|
+
const result = await listCloudAgents(
|
|
429
|
+
{ project_id: testUUID() },
|
|
430
|
+
ctx
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
expect(result.isError).toBe(true);
|
|
434
|
+
expect(result.result).toMatchObject({
|
|
435
|
+
error: 'Failed to list cloud agents',
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
});
|
|
@@ -60,16 +60,11 @@ interface ListAgentsResponse {
|
|
|
60
60
|
* Clean up stale cloud agents that failed to start or lost connection.
|
|
61
61
|
* Only operates on agents in the specified project (security scoped).
|
|
62
62
|
*/
|
|
63
|
-
export const cleanupStaleCloudAgents: Handler = async (args,
|
|
63
|
+
export const cleanupStaleCloudAgents: Handler = async (args, _ctx) => {
|
|
64
64
|
const { project_id, stale_minutes, include_running, dry_run } = parseArgs(args, cleanupStaleAgentsSchema);
|
|
65
65
|
|
|
66
|
-
// Ensure user has an active session with this project (security check)
|
|
67
|
-
if (ctx.session.currentProjectId && ctx.session.currentProjectId !== project_id) {
|
|
68
|
-
return error('Cannot cleanup agents for a different project than your current session');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
66
|
const apiClient = getApiClient();
|
|
72
|
-
|
|
67
|
+
|
|
73
68
|
// Call the cleanup endpoint via fetch (since it's a new endpoint not in the client)
|
|
74
69
|
const response = await apiClient.proxy<CleanupResponse>('cleanup_stale_cloud_agents', {
|
|
75
70
|
project_id,
|
|
@@ -78,12 +73,12 @@ export const cleanupStaleCloudAgents: Handler = async (args, ctx) => {
|
|
|
78
73
|
dryRun: dry_run,
|
|
79
74
|
});
|
|
80
75
|
|
|
81
|
-
if (!response.ok) {
|
|
76
|
+
if (!response.ok || !response.data) {
|
|
82
77
|
return error(response.error || 'Failed to cleanup stale agents');
|
|
83
78
|
}
|
|
84
79
|
|
|
85
80
|
const data = response.data!;
|
|
86
|
-
|
|
81
|
+
|
|
87
82
|
if (dry_run) {
|
|
88
83
|
return success({
|
|
89
84
|
dryRun: true,
|
|
@@ -104,22 +99,17 @@ export const cleanupStaleCloudAgents: Handler = async (args, ctx) => {
|
|
|
104
99
|
/**
|
|
105
100
|
* List cloud agents for a project with optional status filter.
|
|
106
101
|
*/
|
|
107
|
-
export const listCloudAgents: Handler = async (args,
|
|
102
|
+
export const listCloudAgents: Handler = async (args, _ctx) => {
|
|
108
103
|
const { project_id, status } = parseArgs(args, listCloudAgentsSchema);
|
|
109
104
|
|
|
110
|
-
// Ensure user has an active session with this project (security check)
|
|
111
|
-
if (ctx.session.currentProjectId && ctx.session.currentProjectId !== project_id) {
|
|
112
|
-
return error('Cannot list agents for a different project than your current session');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
105
|
const apiClient = getApiClient();
|
|
116
|
-
|
|
106
|
+
|
|
117
107
|
const response = await apiClient.proxy<ListAgentsResponse>('list_cloud_agents', {
|
|
118
108
|
project_id,
|
|
119
109
|
status: status === 'all' ? undefined : status,
|
|
120
110
|
});
|
|
121
111
|
|
|
122
|
-
if (!response.ok) {
|
|
112
|
+
if (!response.ok || !response.data) {
|
|
123
113
|
return error(response.error || 'Failed to list cloud agents');
|
|
124
114
|
}
|
|
125
115
|
|
|
@@ -327,6 +327,7 @@ export const TOOL_CATEGORIES: Record<string, { description: string; tools: Array
|
|
|
327
327
|
cloud_agents: {
|
|
328
328
|
description: 'Cloud agent management and cleanup',
|
|
329
329
|
tools: [
|
|
330
|
+
{ name: 'update_agent_status', brief: 'Update agent dashboard status message' },
|
|
330
331
|
{ name: 'cleanup_stale_cloud_agents', brief: 'Clean up stale cloud agents' },
|
|
331
332
|
{ name: 'list_cloud_agents', brief: 'List cloud agents for project' },
|
|
332
333
|
],
|
package/src/handlers/findings.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { getApiClient } from '../api-client.js';
|
|
|
16
16
|
|
|
17
17
|
const VALID_FINDING_CATEGORIES = ['performance', 'security', 'code_quality', 'accessibility', 'documentation', 'architecture', 'testing', 'other'] as const;
|
|
18
18
|
const VALID_FINDING_SEVERITIES = ['info', 'low', 'medium', 'high', 'critical'] as const;
|
|
19
|
-
const VALID_FINDING_STATUSES = ['open', 'addressed', 'dismissed', 'wontfix'] as const;
|
|
19
|
+
const VALID_FINDING_STATUSES = ['open', 'in_development', 'implemented', 'addressed', 'dismissed', 'wontfix'] as const;
|
|
20
20
|
|
|
21
21
|
type FindingCategory = typeof VALID_FINDING_CATEGORIES[number];
|
|
22
22
|
type FindingSeverity = typeof VALID_FINDING_SEVERITIES[number];
|
package/src/handlers/ideas.ts
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from '../validators.js';
|
|
22
22
|
import { getApiClient } from '../api-client.js';
|
|
23
23
|
|
|
24
|
-
const VALID_IDEA_STATUSES = ['raw', 'exploring', 'planned', 'in_development', 'shipped'] as const;
|
|
24
|
+
const VALID_IDEA_STATUSES = ['raw', 'exploring', 'planned', 'in_development', 'implemented', 'shipped'] as const;
|
|
25
25
|
type IdeaStatus = typeof VALID_IDEA_STATUSES[number];
|
|
26
26
|
|
|
27
27
|
// Argument schemas for type-safe parsing
|
package/src/handlers/session.ts
CHANGED
|
@@ -337,6 +337,7 @@ export const startWorkSession: Handler = async (args, ctx) => {
|
|
|
337
337
|
}
|
|
338
338
|
agentRules.push('COMPLETE TASKS: Always call complete_task() after creating a PR. This is mandatory.');
|
|
339
339
|
agentRules.push('REVIEW REQUIRED: All tasks must be reviewed by another agent before merging.');
|
|
340
|
+
agentRules.push('STATUS UPDATES: Call update_agent_status(status_message: "Working on: TASK_TITLE") whenever you start a new task, and update_task(task_id, status: "in_progress") to claim it.');
|
|
340
341
|
|
|
341
342
|
result.AGENT_RULES = agentRules;
|
|
342
343
|
|