@vibescope/mcp-server 0.0.1 → 0.1.0
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 +113 -98
- package/dist/api-client.d.ts +1114 -0
- package/dist/api-client.js +698 -0
- package/dist/cli.d.ts +1 -6
- package/dist/cli.js +39 -240
- package/dist/config/tool-categories.d.ts +31 -0
- package/dist/config/tool-categories.js +253 -0
- package/dist/handlers/blockers.js +57 -58
- package/dist/handlers/bodies-of-work.d.ts +2 -0
- package/dist/handlers/bodies-of-work.js +106 -476
- package/dist/handlers/cost.d.ts +1 -0
- package/dist/handlers/cost.js +35 -113
- package/dist/handlers/decisions.d.ts +2 -0
- package/dist/handlers/decisions.js +28 -27
- package/dist/handlers/deployment.js +112 -828
- package/dist/handlers/discovery.js +31 -0
- package/dist/handlers/fallback.d.ts +2 -0
- package/dist/handlers/fallback.js +39 -134
- package/dist/handlers/findings.js +43 -67
- package/dist/handlers/git-issues.d.ts +9 -13
- package/dist/handlers/git-issues.js +80 -225
- package/dist/handlers/ideas.d.ts +3 -0
- package/dist/handlers/ideas.js +53 -134
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.js +6 -0
- package/dist/handlers/milestones.d.ts +2 -0
- package/dist/handlers/milestones.js +51 -98
- package/dist/handlers/organizations.js +79 -275
- package/dist/handlers/progress.d.ts +2 -0
- package/dist/handlers/progress.js +25 -123
- package/dist/handlers/project.js +42 -221
- package/dist/handlers/requests.d.ts +2 -0
- package/dist/handlers/requests.js +23 -83
- package/dist/handlers/session.js +99 -585
- package/dist/handlers/sprints.d.ts +32 -0
- package/dist/handlers/sprints.js +274 -0
- package/dist/handlers/tasks.d.ts +7 -10
- package/dist/handlers/tasks.js +230 -900
- package/dist/handlers/tool-docs.d.ts +8 -0
- package/dist/handlers/tool-docs.js +657 -0
- package/dist/handlers/types.d.ts +11 -3
- package/dist/handlers/validation.d.ts +1 -1
- package/dist/handlers/validation.js +26 -153
- package/dist/index.js +473 -160
- package/dist/knowledge.js +106 -9
- package/dist/tools.js +4 -0
- package/dist/validators.d.ts +21 -0
- package/dist/validators.js +91 -0
- package/package.json +2 -3
- package/src/api-client.ts +1752 -0
- package/src/cli.test.ts +128 -302
- package/src/cli.ts +41 -285
- package/src/handlers/__test-setup__.ts +210 -0
- package/src/handlers/__test-utils__.ts +4 -134
- package/src/handlers/blockers.test.ts +114 -124
- package/src/handlers/blockers.ts +68 -70
- package/src/handlers/bodies-of-work.test.ts +236 -831
- package/src/handlers/bodies-of-work.ts +194 -525
- package/src/handlers/cost.test.ts +149 -113
- package/src/handlers/cost.ts +44 -132
- package/src/handlers/decisions.test.ts +111 -209
- package/src/handlers/decisions.ts +35 -27
- package/src/handlers/deployment.test.ts +193 -239
- package/src/handlers/deployment.ts +140 -895
- package/src/handlers/discovery.test.ts +20 -67
- package/src/handlers/discovery.ts +32 -0
- package/src/handlers/fallback.test.ts +128 -361
- package/src/handlers/fallback.ts +62 -148
- package/src/handlers/findings.test.ts +127 -345
- package/src/handlers/findings.ts +49 -66
- package/src/handlers/git-issues.test.ts +623 -0
- package/src/handlers/git-issues.ts +174 -0
- package/src/handlers/ideas.test.ts +229 -343
- package/src/handlers/ideas.ts +69 -143
- package/src/handlers/index.ts +6 -0
- package/src/handlers/milestones.test.ts +167 -281
- package/src/handlers/milestones.ts +54 -93
- package/src/handlers/organizations.test.ts +275 -467
- package/src/handlers/organizations.ts +84 -294
- package/src/handlers/progress.test.ts +112 -218
- package/src/handlers/progress.ts +29 -142
- package/src/handlers/project.test.ts +203 -226
- package/src/handlers/project.ts +48 -238
- package/src/handlers/requests.test.ts +74 -342
- package/src/handlers/requests.ts +25 -83
- package/src/handlers/session.test.ts +241 -206
- package/src/handlers/session.ts +110 -657
- package/src/handlers/sprints.test.ts +711 -0
- package/src/handlers/sprints.ts +497 -0
- package/src/handlers/tasks.test.ts +608 -353
- package/src/handlers/tasks.ts +248 -1025
- package/src/handlers/types.ts +12 -4
- package/src/handlers/validation.test.ts +189 -572
- package/src/handlers/validation.ts +29 -166
- package/src/index.ts +473 -184
- package/src/knowledge.ts +107 -9
- package/src/tools.ts +2506 -0
- package/src/validators.test.ts +223 -223
- package/src/validators.ts +127 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +14 -13
- package/dist/cli.test.d.ts +0 -1
- package/dist/cli.test.js +0 -367
- package/dist/handlers/__test-utils__.d.ts +0 -72
- package/dist/handlers/__test-utils__.js +0 -176
- package/dist/handlers/checkouts.d.ts +0 -37
- package/dist/handlers/checkouts.js +0 -377
- package/dist/handlers/knowledge-query.d.ts +0 -22
- package/dist/handlers/knowledge-query.js +0 -253
- package/dist/handlers/knowledge.d.ts +0 -12
- package/dist/handlers/knowledge.js +0 -108
- package/dist/handlers/roles.d.ts +0 -30
- package/dist/handlers/roles.js +0 -281
- package/dist/handlers/tasks.test.d.ts +0 -1
- package/dist/handlers/tasks.test.js +0 -431
- package/dist/utils.test.d.ts +0 -1
- package/dist/utils.test.js +0 -532
- package/dist/validators.test.d.ts +0 -1
- package/dist/validators.test.js +0 -176
- package/src/tmpclaude-0078-cwd +0 -1
- package/src/tmpclaude-0ee1-cwd +0 -1
- package/src/tmpclaude-2dd5-cwd +0 -1
- package/src/tmpclaude-344c-cwd +0 -1
- package/src/tmpclaude-3860-cwd +0 -1
- package/src/tmpclaude-4b63-cwd +0 -1
- package/src/tmpclaude-5c73-cwd +0 -1
- package/src/tmpclaude-5ee3-cwd +0 -1
- package/src/tmpclaude-6795-cwd +0 -1
- package/src/tmpclaude-709e-cwd +0 -1
- package/src/tmpclaude-9839-cwd +0 -1
- package/src/tmpclaude-d829-cwd +0 -1
- package/src/tmpclaude-e072-cwd +0 -1
- package/src/tmpclaude-f6ee-cwd +0 -1
- package/tmpclaude-0439-cwd +0 -1
- package/tmpclaude-132f-cwd +0 -1
- package/tmpclaude-15bb-cwd +0 -1
- package/tmpclaude-165a-cwd +0 -1
- package/tmpclaude-1ba9-cwd +0 -1
- package/tmpclaude-21a3-cwd +0 -1
- package/tmpclaude-2a38-cwd +0 -1
- package/tmpclaude-2adf-cwd +0 -1
- package/tmpclaude-2f56-cwd +0 -1
- package/tmpclaude-3626-cwd +0 -1
- package/tmpclaude-3727-cwd +0 -1
- package/tmpclaude-40bc-cwd +0 -1
- package/tmpclaude-436f-cwd +0 -1
- package/tmpclaude-4783-cwd +0 -1
- package/tmpclaude-4b6d-cwd +0 -1
- package/tmpclaude-4ba4-cwd +0 -1
- package/tmpclaude-51e6-cwd +0 -1
- package/tmpclaude-5ecf-cwd +0 -1
- package/tmpclaude-6f97-cwd +0 -1
- package/tmpclaude-7fb2-cwd +0 -1
- package/tmpclaude-825c-cwd +0 -1
- package/tmpclaude-8baf-cwd +0 -1
- package/tmpclaude-8d9f-cwd +0 -1
- package/tmpclaude-975c-cwd +0 -1
- package/tmpclaude-9983-cwd +0 -1
- package/tmpclaude-a045-cwd +0 -1
- package/tmpclaude-ac4a-cwd +0 -1
- package/tmpclaude-b593-cwd +0 -1
- package/tmpclaude-b891-cwd +0 -1
- package/tmpclaude-c032-cwd +0 -1
- package/tmpclaude-cf43-cwd +0 -1
- package/tmpclaude-d040-cwd +0 -1
- package/tmpclaude-dcdd-cwd +0 -1
- package/tmpclaude-dcee-cwd +0 -1
- package/tmpclaude-e16b-cwd +0 -1
- package/tmpclaude-ecd2-cwd +0 -1
- package/tmpclaude-f48d-cwd +0 -1
|
@@ -1,95 +1,8 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
3
|
-
import type { HandlerContext, TokenUsage } from './types.js';
|
|
4
2
|
import { logDecision, getDecisions, deleteDecision } from './decisions.js';
|
|
5
3
|
import { ValidationError } from '../validators.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// Test Utilities
|
|
9
|
-
// ============================================================================
|
|
10
|
-
|
|
11
|
-
function createMockSupabase(overrides: {
|
|
12
|
-
selectResult?: { data: unknown; error: unknown };
|
|
13
|
-
insertResult?: { data: unknown; error: unknown };
|
|
14
|
-
deleteResult?: { data: unknown; error: unknown };
|
|
15
|
-
} = {}) {
|
|
16
|
-
const defaultResult = { data: null, error: null };
|
|
17
|
-
let currentOperation = 'select';
|
|
18
|
-
let insertThenSelect = false;
|
|
19
|
-
|
|
20
|
-
const mock = {
|
|
21
|
-
from: vi.fn().mockReturnThis(),
|
|
22
|
-
select: vi.fn(() => {
|
|
23
|
-
if (currentOperation === 'insert') {
|
|
24
|
-
insertThenSelect = true;
|
|
25
|
-
} else {
|
|
26
|
-
currentOperation = 'select';
|
|
27
|
-
insertThenSelect = false;
|
|
28
|
-
}
|
|
29
|
-
return mock;
|
|
30
|
-
}),
|
|
31
|
-
insert: vi.fn(() => {
|
|
32
|
-
currentOperation = 'insert';
|
|
33
|
-
insertThenSelect = false;
|
|
34
|
-
return mock;
|
|
35
|
-
}),
|
|
36
|
-
delete: vi.fn(() => {
|
|
37
|
-
currentOperation = 'delete';
|
|
38
|
-
insertThenSelect = false;
|
|
39
|
-
return mock;
|
|
40
|
-
}),
|
|
41
|
-
eq: vi.fn().mockReturnThis(),
|
|
42
|
-
order: vi.fn().mockReturnThis(),
|
|
43
|
-
single: vi.fn(() => {
|
|
44
|
-
if (currentOperation === 'insert' || insertThenSelect) {
|
|
45
|
-
return Promise.resolve(overrides.insertResult ?? defaultResult);
|
|
46
|
-
}
|
|
47
|
-
if (currentOperation === 'select') {
|
|
48
|
-
return Promise.resolve(overrides.selectResult ?? defaultResult);
|
|
49
|
-
}
|
|
50
|
-
return Promise.resolve(defaultResult);
|
|
51
|
-
}),
|
|
52
|
-
then: vi.fn((resolve: (value: unknown) => void) => {
|
|
53
|
-
if (currentOperation === 'insert' || insertThenSelect) {
|
|
54
|
-
return Promise.resolve(overrides.insertResult ?? defaultResult).then(resolve);
|
|
55
|
-
}
|
|
56
|
-
if (currentOperation === 'select') {
|
|
57
|
-
return Promise.resolve(overrides.selectResult ?? defaultResult).then(resolve);
|
|
58
|
-
}
|
|
59
|
-
if (currentOperation === 'delete') {
|
|
60
|
-
return Promise.resolve(overrides.deleteResult ?? defaultResult).then(resolve);
|
|
61
|
-
}
|
|
62
|
-
return Promise.resolve(defaultResult).then(resolve);
|
|
63
|
-
}),
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
return mock as unknown as SupabaseClient;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function createMockContext(
|
|
70
|
-
supabase: SupabaseClient,
|
|
71
|
-
options: { sessionId?: string | null } = {}
|
|
72
|
-
): HandlerContext {
|
|
73
|
-
const defaultTokenUsage: TokenUsage = {
|
|
74
|
-
callCount: 5,
|
|
75
|
-
totalTokens: 2500,
|
|
76
|
-
byTool: {},
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const sessionId = 'sessionId' in options ? options.sessionId : 'session-123';
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
supabase,
|
|
83
|
-
auth: { userId: 'user-123', apiKeyId: 'api-key-123' },
|
|
84
|
-
session: {
|
|
85
|
-
instanceId: 'instance-abc',
|
|
86
|
-
currentSessionId: sessionId,
|
|
87
|
-
currentPersona: 'Wave',
|
|
88
|
-
tokenUsage: defaultTokenUsage,
|
|
89
|
-
},
|
|
90
|
-
updateSession: vi.fn(),
|
|
91
|
-
};
|
|
92
|
-
}
|
|
4
|
+
import { createMockContext } from './__test-utils__.js';
|
|
5
|
+
import { mockApiClient } from './__test-setup__.js';
|
|
93
6
|
|
|
94
7
|
// ============================================================================
|
|
95
8
|
// logDecision Tests
|
|
@@ -99,126 +12,129 @@ describe('logDecision', () => {
|
|
|
99
12
|
beforeEach(() => vi.clearAllMocks());
|
|
100
13
|
|
|
101
14
|
it('should throw error for missing project_id', async () => {
|
|
102
|
-
const
|
|
103
|
-
const ctx = createMockContext(supabase);
|
|
15
|
+
const ctx = createMockContext();
|
|
104
16
|
|
|
105
17
|
await expect(
|
|
106
|
-
logDecision({ title: '
|
|
18
|
+
logDecision({ title: 'Test', description: 'Desc' }, ctx)
|
|
107
19
|
).rejects.toThrow(ValidationError);
|
|
108
20
|
});
|
|
109
21
|
|
|
110
22
|
it('should throw error for invalid project_id UUID', async () => {
|
|
111
|
-
const
|
|
112
|
-
const ctx = createMockContext(supabase);
|
|
23
|
+
const ctx = createMockContext();
|
|
113
24
|
|
|
114
25
|
await expect(
|
|
115
|
-
logDecision({ project_id: 'invalid', title: '
|
|
26
|
+
logDecision({ project_id: 'invalid', title: 'Test', description: 'Desc' }, ctx)
|
|
116
27
|
).rejects.toThrow(ValidationError);
|
|
117
28
|
});
|
|
118
29
|
|
|
119
30
|
it('should throw error for missing title', async () => {
|
|
120
|
-
const
|
|
121
|
-
const ctx = createMockContext(supabase);
|
|
31
|
+
const ctx = createMockContext();
|
|
122
32
|
|
|
123
33
|
await expect(
|
|
124
|
-
logDecision({ project_id: '123e4567-e89b-12d3-a456-426614174000', description: '
|
|
34
|
+
logDecision({ project_id: '123e4567-e89b-12d3-a456-426614174000', description: 'Desc' }, ctx)
|
|
125
35
|
).rejects.toThrow(ValidationError);
|
|
126
36
|
});
|
|
127
37
|
|
|
128
38
|
it('should throw error for missing description', async () => {
|
|
129
|
-
const
|
|
130
|
-
const ctx = createMockContext(supabase);
|
|
39
|
+
const ctx = createMockContext();
|
|
131
40
|
|
|
132
41
|
await expect(
|
|
133
|
-
logDecision({ project_id: '123e4567-e89b-12d3-a456-426614174000', title: '
|
|
42
|
+
logDecision({ project_id: '123e4567-e89b-12d3-a456-426614174000', title: 'Test' }, ctx)
|
|
134
43
|
).rejects.toThrow(ValidationError);
|
|
135
44
|
});
|
|
136
45
|
|
|
137
46
|
it('should log decision successfully', async () => {
|
|
138
|
-
|
|
139
|
-
|
|
47
|
+
mockApiClient.logDecision.mockResolvedValue({
|
|
48
|
+
ok: true,
|
|
49
|
+
data: { decision_id: 'dec-123' },
|
|
140
50
|
});
|
|
141
|
-
const ctx = createMockContext(
|
|
51
|
+
const ctx = createMockContext();
|
|
142
52
|
|
|
143
53
|
const result = await logDecision(
|
|
144
54
|
{
|
|
145
55
|
project_id: '123e4567-e89b-12d3-a456-426614174000',
|
|
146
|
-
title: 'Use
|
|
147
|
-
description: '
|
|
56
|
+
title: 'Use TypeScript',
|
|
57
|
+
description: 'We will use TypeScript for type safety',
|
|
148
58
|
},
|
|
149
59
|
ctx
|
|
150
60
|
);
|
|
151
61
|
|
|
152
62
|
expect(result.result).toMatchObject({
|
|
153
63
|
success: true,
|
|
154
|
-
title: 'Use
|
|
64
|
+
title: 'Use TypeScript',
|
|
65
|
+
decision_id: 'dec-123',
|
|
155
66
|
});
|
|
156
67
|
});
|
|
157
68
|
|
|
158
69
|
it('should log decision with rationale and alternatives', async () => {
|
|
159
|
-
|
|
160
|
-
|
|
70
|
+
mockApiClient.logDecision.mockResolvedValue({
|
|
71
|
+
ok: true,
|
|
72
|
+
data: { decision_id: 'dec-456' },
|
|
161
73
|
});
|
|
162
|
-
const ctx = createMockContext(
|
|
74
|
+
const ctx = createMockContext();
|
|
163
75
|
|
|
164
76
|
await logDecision(
|
|
165
77
|
{
|
|
166
78
|
project_id: '123e4567-e89b-12d3-a456-426614174000',
|
|
167
79
|
title: 'Use PostgreSQL',
|
|
168
|
-
description: '
|
|
169
|
-
rationale: 'Better JSON support and
|
|
80
|
+
description: 'We will use PostgreSQL as our database',
|
|
81
|
+
rationale: 'Better JSON support and performance',
|
|
170
82
|
alternatives_considered: ['MySQL', 'MongoDB'],
|
|
171
83
|
},
|
|
172
84
|
ctx
|
|
173
85
|
);
|
|
174
86
|
|
|
175
|
-
expect(
|
|
176
|
-
|
|
87
|
+
expect(mockApiClient.logDecision).toHaveBeenCalledWith(
|
|
88
|
+
'123e4567-e89b-12d3-a456-426614174000',
|
|
89
|
+
{
|
|
177
90
|
title: 'Use PostgreSQL',
|
|
178
|
-
description: '
|
|
179
|
-
rationale: 'Better JSON support and
|
|
91
|
+
description: 'We will use PostgreSQL as our database',
|
|
92
|
+
rationale: 'Better JSON support and performance',
|
|
180
93
|
alternatives_considered: ['MySQL', 'MongoDB'],
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
})
|
|
94
|
+
},
|
|
95
|
+
'session-123'
|
|
184
96
|
);
|
|
185
97
|
});
|
|
186
98
|
|
|
187
|
-
it('should include session_id in
|
|
188
|
-
|
|
189
|
-
|
|
99
|
+
it('should include session_id in API call', async () => {
|
|
100
|
+
mockApiClient.logDecision.mockResolvedValue({
|
|
101
|
+
ok: true,
|
|
102
|
+
data: { decision_id: 'dec-789' },
|
|
190
103
|
});
|
|
191
|
-
const ctx = createMockContext(
|
|
104
|
+
const ctx = createMockContext({ sessionId: 'my-session' });
|
|
192
105
|
|
|
193
106
|
await logDecision(
|
|
194
107
|
{
|
|
195
108
|
project_id: '123e4567-e89b-12d3-a456-426614174000',
|
|
196
|
-
title: 'Decision',
|
|
197
|
-
description: '
|
|
109
|
+
title: 'Test Decision',
|
|
110
|
+
description: 'Test description',
|
|
198
111
|
},
|
|
199
112
|
ctx
|
|
200
113
|
);
|
|
201
114
|
|
|
202
|
-
expect(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
})
|
|
115
|
+
expect(mockApiClient.logDecision).toHaveBeenCalledWith(
|
|
116
|
+
'123e4567-e89b-12d3-a456-426614174000',
|
|
117
|
+
expect.any(Object),
|
|
118
|
+
'my-session'
|
|
207
119
|
);
|
|
208
120
|
});
|
|
209
121
|
|
|
210
|
-
it('should throw error when
|
|
211
|
-
|
|
212
|
-
|
|
122
|
+
it('should throw error when API call fails', async () => {
|
|
123
|
+
mockApiClient.logDecision.mockResolvedValue({
|
|
124
|
+
ok: false,
|
|
125
|
+
error: 'Insert failed',
|
|
213
126
|
});
|
|
214
|
-
const ctx = createMockContext(
|
|
127
|
+
const ctx = createMockContext();
|
|
215
128
|
|
|
216
129
|
await expect(
|
|
217
|
-
logDecision(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
130
|
+
logDecision(
|
|
131
|
+
{
|
|
132
|
+
project_id: '123e4567-e89b-12d3-a456-426614174000',
|
|
133
|
+
title: 'Test',
|
|
134
|
+
description: 'Test',
|
|
135
|
+
},
|
|
136
|
+
ctx
|
|
137
|
+
)
|
|
222
138
|
).rejects.toThrow('Failed to log decision: Insert failed');
|
|
223
139
|
});
|
|
224
140
|
});
|
|
@@ -231,15 +147,13 @@ describe('getDecisions', () => {
|
|
|
231
147
|
beforeEach(() => vi.clearAllMocks());
|
|
232
148
|
|
|
233
149
|
it('should throw error for missing project_id', async () => {
|
|
234
|
-
const
|
|
235
|
-
const ctx = createMockContext(supabase);
|
|
150
|
+
const ctx = createMockContext();
|
|
236
151
|
|
|
237
152
|
await expect(getDecisions({}, ctx)).rejects.toThrow(ValidationError);
|
|
238
153
|
});
|
|
239
154
|
|
|
240
155
|
it('should throw error for invalid project_id UUID', async () => {
|
|
241
|
-
const
|
|
242
|
-
const ctx = createMockContext(supabase);
|
|
156
|
+
const ctx = createMockContext();
|
|
243
157
|
|
|
244
158
|
await expect(
|
|
245
159
|
getDecisions({ project_id: 'invalid' }, ctx)
|
|
@@ -247,10 +161,11 @@ describe('getDecisions', () => {
|
|
|
247
161
|
});
|
|
248
162
|
|
|
249
163
|
it('should return empty list when no decisions', async () => {
|
|
250
|
-
|
|
251
|
-
|
|
164
|
+
mockApiClient.getDecisions.mockResolvedValue({
|
|
165
|
+
ok: true,
|
|
166
|
+
data: { decisions: [] },
|
|
252
167
|
});
|
|
253
|
-
const ctx = createMockContext(
|
|
168
|
+
const ctx = createMockContext();
|
|
254
169
|
|
|
255
170
|
const result = await getDecisions(
|
|
256
171
|
{ project_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
@@ -264,65 +179,55 @@ describe('getDecisions', () => {
|
|
|
264
179
|
|
|
265
180
|
it('should return decisions list', async () => {
|
|
266
181
|
const mockDecisions = [
|
|
267
|
-
{ id: 'd1', title: 'Decision 1', description: 'Desc 1'
|
|
268
|
-
{ id: 'd2', title: 'Decision 2', description: 'Desc 2'
|
|
182
|
+
{ id: 'd1', title: 'Decision 1', description: 'Desc 1' },
|
|
183
|
+
{ id: 'd2', title: 'Decision 2', description: 'Desc 2' },
|
|
269
184
|
];
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
185
|
+
mockApiClient.getDecisions.mockResolvedValue({
|
|
186
|
+
ok: true,
|
|
187
|
+
data: { decisions: mockDecisions },
|
|
273
188
|
});
|
|
274
|
-
const ctx = createMockContext(
|
|
189
|
+
const ctx = createMockContext();
|
|
275
190
|
|
|
276
191
|
const result = await getDecisions(
|
|
277
192
|
{ project_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
278
193
|
ctx
|
|
279
194
|
);
|
|
280
195
|
|
|
281
|
-
expect(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
it('should query decisions table', async () => {
|
|
285
|
-
const supabase = createMockSupabase({
|
|
286
|
-
selectResult: { data: [], error: null },
|
|
196
|
+
expect(result.result).toMatchObject({
|
|
197
|
+
decisions: mockDecisions,
|
|
287
198
|
});
|
|
288
|
-
const ctx = createMockContext(supabase);
|
|
289
|
-
|
|
290
|
-
await getDecisions(
|
|
291
|
-
{ project_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
292
|
-
ctx
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
expect(supabase.from).toHaveBeenCalledWith('decisions');
|
|
296
199
|
});
|
|
297
200
|
|
|
298
|
-
it('should
|
|
299
|
-
|
|
300
|
-
|
|
201
|
+
it('should call API client getDecisions', async () => {
|
|
202
|
+
mockApiClient.getDecisions.mockResolvedValue({
|
|
203
|
+
ok: true,
|
|
204
|
+
data: { decisions: [] },
|
|
301
205
|
});
|
|
302
|
-
const ctx = createMockContext(
|
|
206
|
+
const ctx = createMockContext();
|
|
303
207
|
|
|
304
208
|
await getDecisions(
|
|
305
209
|
{ project_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
306
210
|
ctx
|
|
307
211
|
);
|
|
308
212
|
|
|
309
|
-
expect(
|
|
213
|
+
expect(mockApiClient.getDecisions).toHaveBeenCalledWith(
|
|
214
|
+
'123e4567-e89b-12d3-a456-426614174000'
|
|
215
|
+
);
|
|
310
216
|
});
|
|
311
217
|
|
|
312
218
|
it('should throw error when query fails', async () => {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
...supabase,
|
|
319
|
-
then: (resolve: (val: unknown) => void) =>
|
|
320
|
-
Promise.resolve({ data: null, error: { message: 'Query failed' } }).then(resolve),
|
|
321
|
-
} as unknown as ReturnType<SupabaseClient['from']>);
|
|
219
|
+
mockApiClient.getDecisions.mockResolvedValue({
|
|
220
|
+
ok: false,
|
|
221
|
+
error: 'Query failed',
|
|
222
|
+
});
|
|
223
|
+
const ctx = createMockContext();
|
|
322
224
|
|
|
323
225
|
await expect(
|
|
324
|
-
getDecisions(
|
|
325
|
-
|
|
226
|
+
getDecisions(
|
|
227
|
+
{ project_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
228
|
+
ctx
|
|
229
|
+
)
|
|
230
|
+
).rejects.toThrow('Failed to fetch decisions: Query failed');
|
|
326
231
|
});
|
|
327
232
|
});
|
|
328
233
|
|
|
@@ -334,15 +239,13 @@ describe('deleteDecision', () => {
|
|
|
334
239
|
beforeEach(() => vi.clearAllMocks());
|
|
335
240
|
|
|
336
241
|
it('should throw error for missing decision_id', async () => {
|
|
337
|
-
const
|
|
338
|
-
const ctx = createMockContext(supabase);
|
|
242
|
+
const ctx = createMockContext();
|
|
339
243
|
|
|
340
244
|
await expect(deleteDecision({}, ctx)).rejects.toThrow(ValidationError);
|
|
341
245
|
});
|
|
342
246
|
|
|
343
247
|
it('should throw error for invalid decision_id UUID', async () => {
|
|
344
|
-
const
|
|
345
|
-
const ctx = createMockContext(supabase);
|
|
248
|
+
const ctx = createMockContext();
|
|
346
249
|
|
|
347
250
|
await expect(
|
|
348
251
|
deleteDecision({ decision_id: 'invalid' }, ctx)
|
|
@@ -350,10 +253,11 @@ describe('deleteDecision', () => {
|
|
|
350
253
|
});
|
|
351
254
|
|
|
352
255
|
it('should delete decision successfully', async () => {
|
|
353
|
-
|
|
354
|
-
|
|
256
|
+
mockApiClient.deleteDecision.mockResolvedValue({
|
|
257
|
+
ok: true,
|
|
258
|
+
data: { success: true },
|
|
355
259
|
});
|
|
356
|
-
const ctx = createMockContext(
|
|
260
|
+
const ctx = createMockContext();
|
|
357
261
|
|
|
358
262
|
const result = await deleteDecision(
|
|
359
263
|
{ decision_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
@@ -365,37 +269,35 @@ describe('deleteDecision', () => {
|
|
|
365
269
|
});
|
|
366
270
|
});
|
|
367
271
|
|
|
368
|
-
it('should call
|
|
369
|
-
|
|
370
|
-
|
|
272
|
+
it('should call API client deleteDecision', async () => {
|
|
273
|
+
mockApiClient.deleteDecision.mockResolvedValue({
|
|
274
|
+
ok: true,
|
|
275
|
+
data: { success: true },
|
|
371
276
|
});
|
|
372
|
-
const ctx = createMockContext(
|
|
277
|
+
const ctx = createMockContext();
|
|
373
278
|
|
|
374
279
|
await deleteDecision(
|
|
375
280
|
{ decision_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
376
281
|
ctx
|
|
377
282
|
);
|
|
378
283
|
|
|
379
|
-
expect(
|
|
380
|
-
|
|
381
|
-
|
|
284
|
+
expect(mockApiClient.deleteDecision).toHaveBeenCalledWith(
|
|
285
|
+
'123e4567-e89b-12d3-a456-426614174000'
|
|
286
|
+
);
|
|
382
287
|
});
|
|
383
288
|
|
|
384
289
|
it('should throw error when delete fails', async () => {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
...supabase,
|
|
391
|
-
eq: vi.fn().mockReturnValue({
|
|
392
|
-
then: (resolve: (val: unknown) => void) =>
|
|
393
|
-
Promise.resolve({ data: null, error: { message: 'Delete failed' } }).then(resolve),
|
|
394
|
-
}),
|
|
395
|
-
} as unknown as ReturnType<SupabaseClient['from']>);
|
|
290
|
+
mockApiClient.deleteDecision.mockResolvedValue({
|
|
291
|
+
ok: false,
|
|
292
|
+
error: 'Delete failed',
|
|
293
|
+
});
|
|
294
|
+
const ctx = createMockContext();
|
|
396
295
|
|
|
397
296
|
await expect(
|
|
398
|
-
deleteDecision(
|
|
399
|
-
|
|
297
|
+
deleteDecision(
|
|
298
|
+
{ decision_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
299
|
+
ctx
|
|
300
|
+
)
|
|
301
|
+
).rejects.toThrow('Failed to delete decision: Delete failed');
|
|
400
302
|
});
|
|
401
303
|
});
|
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
* - log_decision
|
|
6
6
|
* - get_decisions
|
|
7
7
|
* - delete_decision
|
|
8
|
+
*
|
|
9
|
+
* MIGRATED: Uses Vibescope API client instead of direct Supabase
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
import type { Handler, HandlerRegistry } from './types.js';
|
|
11
13
|
import { validateRequired, validateUUID } from '../validators.js';
|
|
14
|
+
import { getApiClient } from '../api-client.js';
|
|
12
15
|
|
|
13
16
|
export const logDecision: Handler = async (args, ctx) => {
|
|
14
17
|
const { project_id, title, description, rationale, alternatives_considered } = args as {
|
|
@@ -24,40 +27,44 @@ export const logDecision: Handler = async (args, ctx) => {
|
|
|
24
27
|
validateRequired(title, 'title');
|
|
25
28
|
validateRequired(description, 'description');
|
|
26
29
|
|
|
27
|
-
const {
|
|
30
|
+
const { session } = ctx;
|
|
31
|
+
const apiClient = getApiClient();
|
|
28
32
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
rationale: rationale || null,
|
|
36
|
-
alternatives_considered: alternatives_considered || null,
|
|
37
|
-
created_by: 'agent',
|
|
38
|
-
created_by_session_id: session.currentSessionId,
|
|
39
|
-
});
|
|
33
|
+
const response = await apiClient.logDecision(project_id, {
|
|
34
|
+
title,
|
|
35
|
+
description,
|
|
36
|
+
rationale,
|
|
37
|
+
alternatives_considered
|
|
38
|
+
}, session.currentSessionId || undefined);
|
|
40
39
|
|
|
41
|
-
if (
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(`Failed to log decision: ${response.error}`);
|
|
42
|
+
}
|
|
42
43
|
|
|
43
|
-
return { result: { success: true, title } };
|
|
44
|
+
return { result: { success: true, title, decision_id: response.data?.decision_id } };
|
|
44
45
|
};
|
|
45
46
|
|
|
46
47
|
export const getDecisions: Handler = async (args, ctx) => {
|
|
47
|
-
const { project_id } = args as {
|
|
48
|
+
const { project_id } = args as {
|
|
49
|
+
project_id: string;
|
|
50
|
+
};
|
|
48
51
|
|
|
49
52
|
validateRequired(project_id, 'project_id');
|
|
50
53
|
validateUUID(project_id, 'project_id');
|
|
51
54
|
|
|
52
|
-
const
|
|
53
|
-
.from('decisions')
|
|
54
|
-
.select('id, title, description, rationale, created_at')
|
|
55
|
-
.eq('project_id', project_id)
|
|
56
|
-
.order('created_at', { ascending: false });
|
|
55
|
+
const apiClient = getApiClient();
|
|
57
56
|
|
|
58
|
-
|
|
57
|
+
const response = await apiClient.getDecisions(project_id);
|
|
59
58
|
|
|
60
|
-
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
throw new Error(`Failed to fetch decisions: ${response.error}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
result: {
|
|
65
|
+
decisions: response.data?.decisions || [],
|
|
66
|
+
},
|
|
67
|
+
};
|
|
61
68
|
};
|
|
62
69
|
|
|
63
70
|
export const deleteDecision: Handler = async (args, ctx) => {
|
|
@@ -66,12 +73,13 @@ export const deleteDecision: Handler = async (args, ctx) => {
|
|
|
66
73
|
validateRequired(decision_id, 'decision_id');
|
|
67
74
|
validateUUID(decision_id, 'decision_id');
|
|
68
75
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
.eq('id', decision_id);
|
|
76
|
+
const apiClient = getApiClient();
|
|
77
|
+
|
|
78
|
+
const response = await apiClient.deleteDecision(decision_id);
|
|
73
79
|
|
|
74
|
-
if (
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
throw new Error(`Failed to delete decision: ${response.error}`);
|
|
82
|
+
}
|
|
75
83
|
|
|
76
84
|
return { result: { success: true } };
|
|
77
85
|
};
|