@vibescope/mcp-server 0.2.0 → 0.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/dist/api-client.d.ts +64 -1
- package/dist/api-client.js +34 -3
- package/dist/handlers/bodies-of-work.js +82 -49
- package/dist/handlers/cost.js +62 -54
- package/dist/handlers/decisions.js +29 -16
- package/dist/handlers/deployment.js +112 -106
- package/dist/handlers/discovery.js +35 -5
- package/dist/handlers/fallback.js +24 -19
- package/dist/handlers/file-checkouts.d.ts +18 -0
- package/dist/handlers/file-checkouts.js +101 -0
- package/dist/handlers/findings.d.ts +6 -0
- package/dist/handlers/findings.js +85 -30
- package/dist/handlers/git-issues.js +36 -32
- package/dist/handlers/ideas.js +44 -26
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.js +6 -0
- package/dist/handlers/milestones.js +34 -27
- package/dist/handlers/organizations.js +86 -78
- package/dist/handlers/progress.js +22 -11
- package/dist/handlers/project.js +62 -22
- package/dist/handlers/requests.js +15 -11
- package/dist/handlers/roles.d.ts +18 -0
- package/dist/handlers/roles.js +130 -0
- package/dist/handlers/session.js +30 -8
- package/dist/handlers/sprints.js +76 -64
- package/dist/handlers/tasks.js +113 -73
- package/dist/handlers/validation.js +18 -14
- package/dist/tools.js +387 -0
- package/package.json +1 -1
- package/src/api-client.ts +89 -6
- package/src/handlers/__test-setup__.ts +7 -0
- package/src/handlers/bodies-of-work.ts +101 -101
- package/src/handlers/cost.test.ts +34 -44
- package/src/handlers/cost.ts +77 -92
- package/src/handlers/decisions.test.ts +3 -2
- package/src/handlers/decisions.ts +32 -27
- package/src/handlers/deployment.ts +142 -190
- package/src/handlers/discovery.test.ts +4 -5
- package/src/handlers/discovery.ts +37 -6
- package/src/handlers/fallback.ts +31 -29
- package/src/handlers/file-checkouts.test.ts +477 -0
- package/src/handlers/file-checkouts.ts +127 -0
- package/src/handlers/findings.test.ts +145 -0
- package/src/handlers/findings.ts +101 -64
- package/src/handlers/git-issues.ts +40 -80
- package/src/handlers/ideas.ts +56 -54
- package/src/handlers/index.ts +6 -0
- package/src/handlers/milestones.test.ts +1 -1
- package/src/handlers/milestones.ts +47 -45
- package/src/handlers/organizations.ts +104 -129
- package/src/handlers/progress.ts +24 -22
- package/src/handlers/project.ts +89 -57
- package/src/handlers/requests.ts +18 -14
- package/src/handlers/roles.test.ts +303 -0
- package/src/handlers/roles.ts +208 -0
- package/src/handlers/session.ts +39 -17
- package/src/handlers/sprints.ts +96 -129
- package/src/handlers/tasks.ts +144 -138
- package/src/handlers/validation.test.ts +1 -1
- package/src/handlers/validation.ts +20 -22
- package/src/tools.ts +387 -0
- package/dist/config/tool-categories.d.ts +0 -31
- package/dist/config/tool-categories.js +0 -253
- package/dist/knowledge.d.ts +0 -6
- package/dist/knowledge.js +0 -218
package/dist/api-client.d.ts
CHANGED
|
@@ -209,6 +209,7 @@ export declare class VibescopeApiClient {
|
|
|
209
209
|
estimated_minutes?: number;
|
|
210
210
|
blocking?: boolean;
|
|
211
211
|
session_id?: string;
|
|
212
|
+
task_type?: string;
|
|
212
213
|
}): Promise<ApiResponse<{
|
|
213
214
|
success: boolean;
|
|
214
215
|
task_id: string;
|
|
@@ -375,7 +376,11 @@ export declare class VibescopeApiClient {
|
|
|
375
376
|
deleteBlocker(blockerId: string): Promise<ApiResponse<{
|
|
376
377
|
success: boolean;
|
|
377
378
|
}>>;
|
|
378
|
-
getDecisions(projectId: string
|
|
379
|
+
getDecisions(projectId: string, options?: {
|
|
380
|
+
limit?: number;
|
|
381
|
+
offset?: number;
|
|
382
|
+
search_query?: string;
|
|
383
|
+
}): Promise<ApiResponse<{
|
|
379
384
|
decisions: Array<{
|
|
380
385
|
id: string;
|
|
381
386
|
title: string;
|
|
@@ -662,6 +667,8 @@ export declare class VibescopeApiClient {
|
|
|
662
667
|
getActivityFeed(projectId: string, params?: {
|
|
663
668
|
limit?: number;
|
|
664
669
|
since?: string;
|
|
670
|
+
types?: string[];
|
|
671
|
+
created_by?: string;
|
|
665
672
|
}): Promise<ApiResponse<{
|
|
666
673
|
activities: Array<{
|
|
667
674
|
type: string;
|
|
@@ -730,6 +737,7 @@ export declare class VibescopeApiClient {
|
|
|
730
737
|
success: boolean;
|
|
731
738
|
}>>;
|
|
732
739
|
queryKnowledgeBase(projectId: string, params?: {
|
|
740
|
+
scope?: 'summary' | 'detailed';
|
|
733
741
|
categories?: string[];
|
|
734
742
|
limit?: number;
|
|
735
743
|
search_query?: string;
|
|
@@ -739,22 +747,35 @@ export declare class VibescopeApiClient {
|
|
|
739
747
|
title: string;
|
|
740
748
|
category: string;
|
|
741
749
|
severity: string;
|
|
750
|
+
description?: string;
|
|
742
751
|
}>;
|
|
743
752
|
decisions?: Array<{
|
|
744
753
|
id: string;
|
|
745
754
|
title: string;
|
|
746
755
|
description: string;
|
|
756
|
+
rationale?: string;
|
|
747
757
|
}>;
|
|
748
758
|
completed_tasks?: Array<{
|
|
749
759
|
id: string;
|
|
750
760
|
title: string;
|
|
751
761
|
completed_at: string;
|
|
762
|
+
summary?: string;
|
|
752
763
|
}>;
|
|
753
764
|
resolved_blockers?: Array<{
|
|
754
765
|
id: string;
|
|
755
766
|
description: string;
|
|
756
767
|
resolution_note?: string;
|
|
757
768
|
}>;
|
|
769
|
+
progress?: Array<{
|
|
770
|
+
id: string;
|
|
771
|
+
summary: string;
|
|
772
|
+
details?: string;
|
|
773
|
+
}>;
|
|
774
|
+
qa?: Array<{
|
|
775
|
+
id: string;
|
|
776
|
+
question: string;
|
|
777
|
+
answer: string;
|
|
778
|
+
}>;
|
|
758
779
|
}>>;
|
|
759
780
|
syncSession(sessionId: string, params?: {
|
|
760
781
|
total_tokens?: number;
|
|
@@ -1163,6 +1184,48 @@ export declare class VibescopeApiClient {
|
|
|
1163
1184
|
slug: string;
|
|
1164
1185
|
title: string;
|
|
1165
1186
|
}>>>;
|
|
1187
|
+
checkoutFile(projectId: string, filePath: string, reason?: string, sessionId?: string): Promise<ApiResponse<{
|
|
1188
|
+
success: boolean;
|
|
1189
|
+
checkout_id: string;
|
|
1190
|
+
file_path: string;
|
|
1191
|
+
already_checked_out?: boolean;
|
|
1192
|
+
message: string;
|
|
1193
|
+
}>>;
|
|
1194
|
+
checkinFile(params: {
|
|
1195
|
+
checkout_id?: string;
|
|
1196
|
+
project_id?: string;
|
|
1197
|
+
file_path?: string;
|
|
1198
|
+
summary?: string;
|
|
1199
|
+
}, sessionId?: string): Promise<ApiResponse<{
|
|
1200
|
+
success: boolean;
|
|
1201
|
+
checkout_id: string;
|
|
1202
|
+
message: string;
|
|
1203
|
+
}>>;
|
|
1204
|
+
getFileCheckouts(projectId: string, options?: {
|
|
1205
|
+
status?: string;
|
|
1206
|
+
file_path?: string;
|
|
1207
|
+
limit?: number;
|
|
1208
|
+
}): Promise<ApiResponse<{
|
|
1209
|
+
checkouts: Array<{
|
|
1210
|
+
id: string;
|
|
1211
|
+
file_path: string;
|
|
1212
|
+
status: string;
|
|
1213
|
+
checked_out_at: string;
|
|
1214
|
+
checkout_reason?: string;
|
|
1215
|
+
checked_in_at?: string;
|
|
1216
|
+
checkin_summary?: string;
|
|
1217
|
+
checked_out_by?: string;
|
|
1218
|
+
}>;
|
|
1219
|
+
}>>;
|
|
1220
|
+
abandonCheckout(params: {
|
|
1221
|
+
checkout_id?: string;
|
|
1222
|
+
project_id?: string;
|
|
1223
|
+
file_path?: string;
|
|
1224
|
+
}): Promise<ApiResponse<{
|
|
1225
|
+
success: boolean;
|
|
1226
|
+
checkout_id: string;
|
|
1227
|
+
message: string;
|
|
1228
|
+
}>>;
|
|
1166
1229
|
}
|
|
1167
1230
|
export declare function getApiClient(): VibescopeApiClient;
|
|
1168
1231
|
export declare function initApiClient(config: ApiClientConfig): VibescopeApiClient;
|
package/dist/api-client.js
CHANGED
|
@@ -28,7 +28,8 @@ export class VibescopeApiClient {
|
|
|
28
28
|
return {
|
|
29
29
|
ok: false,
|
|
30
30
|
status: response.status,
|
|
31
|
-
error: data.error || `HTTP ${response.status}
|
|
31
|
+
error: data.error || `HTTP ${response.status}`,
|
|
32
|
+
data // Include full response data for additional error context
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
35
|
return {
|
|
@@ -171,8 +172,8 @@ export class VibescopeApiClient {
|
|
|
171
172
|
// ============================================================================
|
|
172
173
|
// Decision endpoints
|
|
173
174
|
// ============================================================================
|
|
174
|
-
async getDecisions(projectId) {
|
|
175
|
-
return this.proxy('get_decisions', { project_id: projectId });
|
|
175
|
+
async getDecisions(projectId, options) {
|
|
176
|
+
return this.proxy('get_decisions', { project_id: projectId, ...options });
|
|
176
177
|
}
|
|
177
178
|
async logDecision(projectId, params, sessionId) {
|
|
178
179
|
return this.proxy('log_decision', {
|
|
@@ -694,6 +695,36 @@ export class VibescopeApiClient {
|
|
|
694
695
|
async getHelpTopics() {
|
|
695
696
|
return this.proxy('get_help_topics', {});
|
|
696
697
|
}
|
|
698
|
+
// ============================================================================
|
|
699
|
+
// File Checkout endpoints (multi-agent coordination)
|
|
700
|
+
// ============================================================================
|
|
701
|
+
async checkoutFile(projectId, filePath, reason, sessionId) {
|
|
702
|
+
return this.proxy('checkout_file', {
|
|
703
|
+
project_id: projectId,
|
|
704
|
+
file_path: filePath,
|
|
705
|
+
reason
|
|
706
|
+
}, sessionId ? {
|
|
707
|
+
session_id: sessionId,
|
|
708
|
+
persona: null,
|
|
709
|
+
instance_id: ''
|
|
710
|
+
} : undefined);
|
|
711
|
+
}
|
|
712
|
+
async checkinFile(params, sessionId) {
|
|
713
|
+
return this.proxy('checkin_file', params, sessionId ? {
|
|
714
|
+
session_id: sessionId,
|
|
715
|
+
persona: null,
|
|
716
|
+
instance_id: ''
|
|
717
|
+
} : undefined);
|
|
718
|
+
}
|
|
719
|
+
async getFileCheckouts(projectId, options) {
|
|
720
|
+
return this.proxy('get_file_checkouts', {
|
|
721
|
+
project_id: projectId,
|
|
722
|
+
...options
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
async abandonCheckout(params) {
|
|
726
|
+
return this.proxy('abandon_checkout', params);
|
|
727
|
+
}
|
|
697
728
|
}
|
|
698
729
|
// Singleton instance
|
|
699
730
|
let apiClient = null;
|
|
@@ -17,13 +17,78 @@
|
|
|
17
17
|
*
|
|
18
18
|
* MIGRATED: Uses Vibescope API client instead of direct Supabase
|
|
19
19
|
*/
|
|
20
|
-
import {
|
|
20
|
+
import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
|
|
21
21
|
import { getApiClient } from '../api-client.js';
|
|
22
|
+
const BODY_OF_WORK_STATUSES = ['draft', 'active', 'completed', 'cancelled'];
|
|
23
|
+
const TASK_PHASES = ['pre', 'core', 'post'];
|
|
24
|
+
const DEPLOY_ENVIRONMENTS = ['development', 'staging', 'production'];
|
|
25
|
+
const VERSION_BUMPS = ['patch', 'minor', 'major'];
|
|
26
|
+
const DEPLOY_TRIGGERS = ['all_completed', 'all_completed_validated'];
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Argument Schemas
|
|
29
|
+
// ============================================================================
|
|
30
|
+
const createBodyOfWorkSchema = {
|
|
31
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
32
|
+
title: { type: 'string', required: true },
|
|
33
|
+
description: { type: 'string' },
|
|
34
|
+
auto_deploy_on_completion: { type: 'boolean' },
|
|
35
|
+
deploy_environment: { type: 'string', validate: createEnumValidator(DEPLOY_ENVIRONMENTS) },
|
|
36
|
+
deploy_version_bump: { type: 'string', validate: createEnumValidator(VERSION_BUMPS) },
|
|
37
|
+
deploy_trigger: { type: 'string', validate: createEnumValidator(DEPLOY_TRIGGERS) },
|
|
38
|
+
};
|
|
39
|
+
const updateBodyOfWorkSchema = {
|
|
40
|
+
body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
|
|
41
|
+
title: { type: 'string' },
|
|
42
|
+
description: { type: 'string' },
|
|
43
|
+
auto_deploy_on_completion: { type: 'boolean' },
|
|
44
|
+
deploy_environment: { type: 'string', validate: createEnumValidator(DEPLOY_ENVIRONMENTS) },
|
|
45
|
+
deploy_version_bump: { type: 'string', validate: createEnumValidator(VERSION_BUMPS) },
|
|
46
|
+
deploy_trigger: { type: 'string', validate: createEnumValidator(DEPLOY_TRIGGERS) },
|
|
47
|
+
};
|
|
48
|
+
const getBodyOfWorkSchema = {
|
|
49
|
+
body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
|
|
50
|
+
summary_only: { type: 'boolean', default: false },
|
|
51
|
+
};
|
|
52
|
+
const getBodiesOfWorkSchema = {
|
|
53
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
54
|
+
status: { type: 'string', validate: createEnumValidator(BODY_OF_WORK_STATUSES) },
|
|
55
|
+
limit: { type: 'number', default: 50 },
|
|
56
|
+
offset: { type: 'number', default: 0 },
|
|
57
|
+
search_query: { type: 'string' },
|
|
58
|
+
};
|
|
59
|
+
const deleteBodyOfWorkSchema = {
|
|
60
|
+
body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
|
|
61
|
+
};
|
|
62
|
+
const addTaskToBodyOfWorkSchema = {
|
|
63
|
+
body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
|
|
64
|
+
task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
65
|
+
phase: { type: 'string', validate: createEnumValidator(TASK_PHASES) },
|
|
66
|
+
order_index: { type: 'number' },
|
|
67
|
+
};
|
|
68
|
+
const removeTaskFromBodyOfWorkSchema = {
|
|
69
|
+
task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
70
|
+
};
|
|
71
|
+
const activateBodyOfWorkSchema = {
|
|
72
|
+
body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
|
|
73
|
+
};
|
|
74
|
+
const addTaskDependencySchema = {
|
|
75
|
+
body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
|
|
76
|
+
task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
77
|
+
depends_on_task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
78
|
+
};
|
|
79
|
+
const removeTaskDependencySchema = {
|
|
80
|
+
task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
81
|
+
depends_on_task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
82
|
+
};
|
|
83
|
+
const getTaskDependenciesSchema = {
|
|
84
|
+
body_of_work_id: { type: 'string', validate: uuidValidator },
|
|
85
|
+
task_id: { type: 'string', validate: uuidValidator },
|
|
86
|
+
};
|
|
87
|
+
const getNextBodyOfWorkTaskSchema = {
|
|
88
|
+
body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
|
|
89
|
+
};
|
|
22
90
|
export const createBodyOfWork = async (args, ctx) => {
|
|
23
|
-
const { project_id, title, description, auto_deploy_on_completion, deploy_environment, deploy_version_bump, deploy_trigger, } = args;
|
|
24
|
-
validateRequired(project_id, 'project_id');
|
|
25
|
-
validateUUID(project_id, 'project_id');
|
|
26
|
-
validateRequired(title, 'title');
|
|
91
|
+
const { project_id, title, description, auto_deploy_on_completion, deploy_environment, deploy_version_bump, deploy_trigger, } = parseArgs(args, createBodyOfWorkSchema);
|
|
27
92
|
const { session } = ctx;
|
|
28
93
|
const apiClient = getApiClient();
|
|
29
94
|
const response = await apiClient.proxy('create_body_of_work', {
|
|
@@ -53,9 +118,7 @@ export const createBodyOfWork = async (args, ctx) => {
|
|
|
53
118
|
};
|
|
54
119
|
};
|
|
55
120
|
export const updateBodyOfWork = async (args, ctx) => {
|
|
56
|
-
const { body_of_work_id, title, description, auto_deploy_on_completion, deploy_environment, deploy_version_bump, deploy_trigger, } = args;
|
|
57
|
-
validateRequired(body_of_work_id, 'body_of_work_id');
|
|
58
|
-
validateUUID(body_of_work_id, 'body_of_work_id');
|
|
121
|
+
const { body_of_work_id, title, description, auto_deploy_on_completion, deploy_environment, deploy_version_bump, deploy_trigger, } = parseArgs(args, updateBodyOfWorkSchema);
|
|
59
122
|
// Check if any updates provided
|
|
60
123
|
if (title === undefined && description === undefined && auto_deploy_on_completion === undefined &&
|
|
61
124
|
deploy_environment === undefined && deploy_version_bump === undefined && deploy_trigger === undefined) {
|
|
@@ -77,9 +140,7 @@ export const updateBodyOfWork = async (args, ctx) => {
|
|
|
77
140
|
return { result: { success: true, body_of_work_id } };
|
|
78
141
|
};
|
|
79
142
|
export const getBodyOfWork = async (args, ctx) => {
|
|
80
|
-
const { body_of_work_id, summary_only
|
|
81
|
-
validateRequired(body_of_work_id, 'body_of_work_id');
|
|
82
|
-
validateUUID(body_of_work_id, 'body_of_work_id');
|
|
143
|
+
const { body_of_work_id, summary_only } = parseArgs(args, getBodyOfWorkSchema);
|
|
83
144
|
const apiClient = getApiClient();
|
|
84
145
|
// Response type varies based on summary_only
|
|
85
146
|
const response = await apiClient.proxy('get_body_of_work', { body_of_work_id, summary_only });
|
|
@@ -89,14 +150,12 @@ export const getBodyOfWork = async (args, ctx) => {
|
|
|
89
150
|
return { result: response.data };
|
|
90
151
|
};
|
|
91
152
|
export const getBodiesOfWork = async (args, ctx) => {
|
|
92
|
-
const { project_id, status, limit
|
|
93
|
-
validateRequired(project_id, 'project_id');
|
|
94
|
-
validateUUID(project_id, 'project_id');
|
|
153
|
+
const { project_id, status, limit, offset, search_query } = parseArgs(args, getBodiesOfWorkSchema);
|
|
95
154
|
const apiClient = getApiClient();
|
|
96
155
|
const response = await apiClient.proxy('get_bodies_of_work', {
|
|
97
156
|
project_id,
|
|
98
157
|
status,
|
|
99
|
-
limit: Math.min(limit, 100),
|
|
158
|
+
limit: Math.min(limit ?? 50, 100),
|
|
100
159
|
offset,
|
|
101
160
|
search_query
|
|
102
161
|
});
|
|
@@ -106,9 +165,7 @@ export const getBodiesOfWork = async (args, ctx) => {
|
|
|
106
165
|
return { result: response.data };
|
|
107
166
|
};
|
|
108
167
|
export const deleteBodyOfWork = async (args, ctx) => {
|
|
109
|
-
const { body_of_work_id } = args;
|
|
110
|
-
validateRequired(body_of_work_id, 'body_of_work_id');
|
|
111
|
-
validateUUID(body_of_work_id, 'body_of_work_id');
|
|
168
|
+
const { body_of_work_id } = parseArgs(args, deleteBodyOfWorkSchema);
|
|
112
169
|
const apiClient = getApiClient();
|
|
113
170
|
const response = await apiClient.proxy('delete_body_of_work', {
|
|
114
171
|
body_of_work_id
|
|
@@ -119,11 +176,7 @@ export const deleteBodyOfWork = async (args, ctx) => {
|
|
|
119
176
|
return { result: { success: true, message: 'Body of work deleted. Tasks are preserved.' } };
|
|
120
177
|
};
|
|
121
178
|
export const addTaskToBodyOfWork = async (args, ctx) => {
|
|
122
|
-
const { body_of_work_id, task_id, phase, order_index } = args;
|
|
123
|
-
validateRequired(body_of_work_id, 'body_of_work_id');
|
|
124
|
-
validateUUID(body_of_work_id, 'body_of_work_id');
|
|
125
|
-
validateRequired(task_id, 'task_id');
|
|
126
|
-
validateUUID(task_id, 'task_id');
|
|
179
|
+
const { body_of_work_id, task_id, phase, order_index } = parseArgs(args, addTaskToBodyOfWorkSchema);
|
|
127
180
|
const apiClient = getApiClient();
|
|
128
181
|
const response = await apiClient.proxy('add_task_to_body_of_work', {
|
|
129
182
|
body_of_work_id,
|
|
@@ -137,9 +190,7 @@ export const addTaskToBodyOfWork = async (args, ctx) => {
|
|
|
137
190
|
return { result: response.data };
|
|
138
191
|
};
|
|
139
192
|
export const removeTaskFromBodyOfWork = async (args, ctx) => {
|
|
140
|
-
const { task_id } = args;
|
|
141
|
-
validateRequired(task_id, 'task_id');
|
|
142
|
-
validateUUID(task_id, 'task_id');
|
|
193
|
+
const { task_id } = parseArgs(args, removeTaskFromBodyOfWorkSchema);
|
|
143
194
|
const apiClient = getApiClient();
|
|
144
195
|
const response = await apiClient.proxy('remove_task_from_body_of_work', { task_id });
|
|
145
196
|
if (!response.ok) {
|
|
@@ -148,9 +199,7 @@ export const removeTaskFromBodyOfWork = async (args, ctx) => {
|
|
|
148
199
|
return { result: response.data };
|
|
149
200
|
};
|
|
150
201
|
export const activateBodyOfWork = async (args, ctx) => {
|
|
151
|
-
const { body_of_work_id } = args;
|
|
152
|
-
validateRequired(body_of_work_id, 'body_of_work_id');
|
|
153
|
-
validateUUID(body_of_work_id, 'body_of_work_id');
|
|
202
|
+
const { body_of_work_id } = parseArgs(args, activateBodyOfWorkSchema);
|
|
154
203
|
const apiClient = getApiClient();
|
|
155
204
|
const response = await apiClient.proxy('activate_body_of_work', { body_of_work_id });
|
|
156
205
|
if (!response.ok) {
|
|
@@ -159,13 +208,7 @@ export const activateBodyOfWork = async (args, ctx) => {
|
|
|
159
208
|
return { result: response.data };
|
|
160
209
|
};
|
|
161
210
|
export const addTaskDependency = async (args, ctx) => {
|
|
162
|
-
const { body_of_work_id, task_id, depends_on_task_id } = args;
|
|
163
|
-
validateRequired(body_of_work_id, 'body_of_work_id');
|
|
164
|
-
validateUUID(body_of_work_id, 'body_of_work_id');
|
|
165
|
-
validateRequired(task_id, 'task_id');
|
|
166
|
-
validateUUID(task_id, 'task_id');
|
|
167
|
-
validateRequired(depends_on_task_id, 'depends_on_task_id');
|
|
168
|
-
validateUUID(depends_on_task_id, 'depends_on_task_id');
|
|
211
|
+
const { body_of_work_id, task_id, depends_on_task_id } = parseArgs(args, addTaskDependencySchema);
|
|
169
212
|
if (task_id === depends_on_task_id) {
|
|
170
213
|
throw new Error('A task cannot depend on itself');
|
|
171
214
|
}
|
|
@@ -181,11 +224,7 @@ export const addTaskDependency = async (args, ctx) => {
|
|
|
181
224
|
return { result: response.data };
|
|
182
225
|
};
|
|
183
226
|
export const removeTaskDependency = async (args, ctx) => {
|
|
184
|
-
const { task_id, depends_on_task_id } = args;
|
|
185
|
-
validateRequired(task_id, 'task_id');
|
|
186
|
-
validateUUID(task_id, 'task_id');
|
|
187
|
-
validateRequired(depends_on_task_id, 'depends_on_task_id');
|
|
188
|
-
validateUUID(depends_on_task_id, 'depends_on_task_id');
|
|
227
|
+
const { task_id, depends_on_task_id } = parseArgs(args, removeTaskDependencySchema);
|
|
189
228
|
const apiClient = getApiClient();
|
|
190
229
|
const response = await apiClient.proxy('remove_task_dependency', {
|
|
191
230
|
task_id,
|
|
@@ -197,14 +236,10 @@ export const removeTaskDependency = async (args, ctx) => {
|
|
|
197
236
|
return { result: response.data };
|
|
198
237
|
};
|
|
199
238
|
export const getTaskDependencies = async (args, ctx) => {
|
|
200
|
-
const { body_of_work_id, task_id } = args;
|
|
239
|
+
const { body_of_work_id, task_id } = parseArgs(args, getTaskDependenciesSchema);
|
|
201
240
|
if (!body_of_work_id && !task_id) {
|
|
202
241
|
throw new Error('Either body_of_work_id or task_id is required');
|
|
203
242
|
}
|
|
204
|
-
if (body_of_work_id)
|
|
205
|
-
validateUUID(body_of_work_id, 'body_of_work_id');
|
|
206
|
-
if (task_id)
|
|
207
|
-
validateUUID(task_id, 'task_id');
|
|
208
243
|
const apiClient = getApiClient();
|
|
209
244
|
const response = await apiClient.proxy('get_task_dependencies', {
|
|
210
245
|
body_of_work_id,
|
|
@@ -216,9 +251,7 @@ export const getTaskDependencies = async (args, ctx) => {
|
|
|
216
251
|
return { result: response.data };
|
|
217
252
|
};
|
|
218
253
|
export const getNextBodyOfWorkTask = async (args, ctx) => {
|
|
219
|
-
const { body_of_work_id } = args;
|
|
220
|
-
validateRequired(body_of_work_id, 'body_of_work_id');
|
|
221
|
-
validateUUID(body_of_work_id, 'body_of_work_id');
|
|
254
|
+
const { body_of_work_id } = parseArgs(args, getNextBodyOfWorkTaskSchema);
|
|
222
255
|
const apiClient = getApiClient();
|
|
223
256
|
const response = await apiClient.proxy('get_next_body_of_work_task', { body_of_work_id });
|
|
224
257
|
if (!response.ok) {
|
package/dist/handlers/cost.js
CHANGED
|
@@ -9,20 +9,55 @@
|
|
|
9
9
|
* - delete_cost_alert
|
|
10
10
|
* - get_task_costs
|
|
11
11
|
*/
|
|
12
|
+
import { parseArgs, uuidValidator, createEnumValidator, ValidationError } from '../validators.js';
|
|
12
13
|
import { getApiClient } from '../api-client.js';
|
|
14
|
+
const VALID_PERIODS = ['daily', 'weekly', 'monthly'];
|
|
15
|
+
const VALID_ALERT_TYPES = ['warning', 'critical'];
|
|
16
|
+
// Argument schemas for type-safe parsing
|
|
17
|
+
const getCostSummarySchema = {
|
|
18
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
19
|
+
period: { type: 'string', default: 'daily', validate: createEnumValidator(VALID_PERIODS) },
|
|
20
|
+
limit: { type: 'number', default: 30 },
|
|
21
|
+
};
|
|
22
|
+
const getCostAlertsSchema = {
|
|
23
|
+
project_id: { type: 'string', validate: uuidValidator },
|
|
24
|
+
};
|
|
25
|
+
const addCostAlertSchema = {
|
|
26
|
+
project_id: { type: 'string', validate: uuidValidator },
|
|
27
|
+
threshold_amount: { type: 'number', required: true },
|
|
28
|
+
threshold_period: { type: 'string', required: true, validate: createEnumValidator(VALID_PERIODS) },
|
|
29
|
+
alert_type: { type: 'string', default: 'warning', validate: createEnumValidator(VALID_ALERT_TYPES) },
|
|
30
|
+
};
|
|
31
|
+
const updateCostAlertSchema = {
|
|
32
|
+
alert_id: { type: 'string', required: true, validate: uuidValidator },
|
|
33
|
+
threshold_amount: { type: 'number' },
|
|
34
|
+
threshold_period: { type: 'string', validate: createEnumValidator(VALID_PERIODS) },
|
|
35
|
+
alert_type: { type: 'string', validate: createEnumValidator(VALID_ALERT_TYPES) },
|
|
36
|
+
enabled: { type: 'boolean' },
|
|
37
|
+
};
|
|
38
|
+
const deleteCostAlertSchema = {
|
|
39
|
+
alert_id: { type: 'string', required: true, validate: uuidValidator },
|
|
40
|
+
};
|
|
41
|
+
const getTaskCostsSchema = {
|
|
42
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
43
|
+
limit: { type: 'number', default: 20 },
|
|
44
|
+
};
|
|
45
|
+
// Custom validator for positive numbers
|
|
46
|
+
function validatePositiveNumber(value, fieldName) {
|
|
47
|
+
if (value !== undefined && value <= 0) {
|
|
48
|
+
throw new ValidationError(`${fieldName} must be a positive number`, { field: fieldName });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
13
51
|
/**
|
|
14
52
|
* Get cost summary for a project (daily, weekly, or monthly)
|
|
15
53
|
*/
|
|
16
|
-
export const getCostSummary = async (args,
|
|
17
|
-
const { project_id, period
|
|
18
|
-
if (!project_id) {
|
|
19
|
-
return {
|
|
20
|
-
result: { error: 'project_id is required' },
|
|
21
|
-
isError: true,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
54
|
+
export const getCostSummary = async (args, _ctx) => {
|
|
55
|
+
const { project_id, period, limit } = parseArgs(args, getCostSummarySchema);
|
|
24
56
|
const apiClient = getApiClient();
|
|
25
|
-
const response = await apiClient.getCostSummary(project_id, {
|
|
57
|
+
const response = await apiClient.getCostSummary(project_id, {
|
|
58
|
+
period: period,
|
|
59
|
+
limit
|
|
60
|
+
});
|
|
26
61
|
if (!response.ok) {
|
|
27
62
|
return {
|
|
28
63
|
result: { error: response.error || 'Failed to get cost summary' },
|
|
@@ -34,8 +69,8 @@ export const getCostSummary = async (args, ctx) => {
|
|
|
34
69
|
/**
|
|
35
70
|
* Get cost alerts for the current user
|
|
36
71
|
*/
|
|
37
|
-
export const getCostAlerts = async (args,
|
|
38
|
-
const { project_id } = args;
|
|
72
|
+
export const getCostAlerts = async (args, _ctx) => {
|
|
73
|
+
const { project_id } = parseArgs(args, getCostAlertsSchema);
|
|
39
74
|
const apiClient = getApiClient();
|
|
40
75
|
const response = await apiClient.getCostAlerts();
|
|
41
76
|
if (!response.ok) {
|
|
@@ -49,26 +84,16 @@ export const getCostAlerts = async (args, ctx) => {
|
|
|
49
84
|
/**
|
|
50
85
|
* Add a cost alert
|
|
51
86
|
*/
|
|
52
|
-
export const addCostAlert = async (args,
|
|
53
|
-
const { project_id, threshold_amount, threshold_period, alert_type
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
result: { error: 'threshold_amount must be a positive number' },
|
|
57
|
-
isError: true,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
if (!threshold_period || !['daily', 'weekly', 'monthly'].includes(threshold_period)) {
|
|
61
|
-
return {
|
|
62
|
-
result: { error: 'threshold_period must be "daily", "weekly", or "monthly"' },
|
|
63
|
-
isError: true,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
87
|
+
export const addCostAlert = async (args, _ctx) => {
|
|
88
|
+
const { project_id, threshold_amount, threshold_period, alert_type } = parseArgs(args, addCostAlertSchema);
|
|
89
|
+
// Additional validation for positive amount
|
|
90
|
+
validatePositiveNumber(threshold_amount, 'threshold_amount');
|
|
66
91
|
const apiClient = getApiClient();
|
|
67
92
|
const response = await apiClient.addCostAlert({
|
|
68
93
|
project_id,
|
|
69
|
-
threshold_amount,
|
|
70
|
-
threshold_period,
|
|
71
|
-
alert_type
|
|
94
|
+
threshold_amount: threshold_amount,
|
|
95
|
+
threshold_period: threshold_period,
|
|
96
|
+
alert_type: alert_type
|
|
72
97
|
});
|
|
73
98
|
if (!response.ok) {
|
|
74
99
|
return {
|
|
@@ -81,11 +106,12 @@ export const addCostAlert = async (args, ctx) => {
|
|
|
81
106
|
/**
|
|
82
107
|
* Update a cost alert
|
|
83
108
|
*/
|
|
84
|
-
export const updateCostAlert = async (args,
|
|
85
|
-
const { alert_id, threshold_amount, threshold_period, alert_type, enabled
|
|
86
|
-
|
|
109
|
+
export const updateCostAlert = async (args, _ctx) => {
|
|
110
|
+
const { alert_id, threshold_amount, threshold_period, alert_type, enabled } = parseArgs(args, updateCostAlertSchema);
|
|
111
|
+
// Check that at least one update is provided
|
|
112
|
+
if (threshold_amount === undefined && threshold_period === undefined && alert_type === undefined && enabled === undefined) {
|
|
87
113
|
return {
|
|
88
|
-
result: { error: '
|
|
114
|
+
result: { error: 'No updates provided' },
|
|
89
115
|
isError: true,
|
|
90
116
|
};
|
|
91
117
|
}
|
|
@@ -98,12 +124,6 @@ export const updateCostAlert = async (args, ctx) => {
|
|
|
98
124
|
updates.alert_type = alert_type;
|
|
99
125
|
if (enabled !== undefined)
|
|
100
126
|
updates.enabled = enabled;
|
|
101
|
-
if (Object.keys(updates).length === 0) {
|
|
102
|
-
return {
|
|
103
|
-
result: { error: 'No updates provided' },
|
|
104
|
-
isError: true,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
127
|
const apiClient = getApiClient();
|
|
108
128
|
const response = await apiClient.updateCostAlert(alert_id, updates);
|
|
109
129
|
if (!response.ok) {
|
|
@@ -117,14 +137,8 @@ export const updateCostAlert = async (args, ctx) => {
|
|
|
117
137
|
/**
|
|
118
138
|
* Delete a cost alert
|
|
119
139
|
*/
|
|
120
|
-
export const deleteCostAlert = async (args,
|
|
121
|
-
const { alert_id } = args;
|
|
122
|
-
if (!alert_id) {
|
|
123
|
-
return {
|
|
124
|
-
result: { error: 'alert_id is required' },
|
|
125
|
-
isError: true,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
140
|
+
export const deleteCostAlert = async (args, _ctx) => {
|
|
141
|
+
const { alert_id } = parseArgs(args, deleteCostAlertSchema);
|
|
128
142
|
const apiClient = getApiClient();
|
|
129
143
|
const response = await apiClient.deleteCostAlert(alert_id);
|
|
130
144
|
if (!response.ok) {
|
|
@@ -138,14 +152,8 @@ export const deleteCostAlert = async (args, ctx) => {
|
|
|
138
152
|
/**
|
|
139
153
|
* Get task costs for a project
|
|
140
154
|
*/
|
|
141
|
-
export const getTaskCosts = async (args,
|
|
142
|
-
const { project_id, limit
|
|
143
|
-
if (!project_id) {
|
|
144
|
-
return {
|
|
145
|
-
result: { error: 'project_id is required' },
|
|
146
|
-
isError: true,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
155
|
+
export const getTaskCosts = async (args, _ctx) => {
|
|
156
|
+
const { project_id, limit } = parseArgs(args, getTaskCostsSchema);
|
|
149
157
|
const apiClient = getApiClient();
|
|
150
158
|
const response = await apiClient.getTaskCosts(project_id, limit);
|
|
151
159
|
if (!response.ok) {
|
|
@@ -8,33 +8,48 @@
|
|
|
8
8
|
*
|
|
9
9
|
* MIGRATED: Uses Vibescope API client instead of direct Supabase
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
11
|
+
import { parseArgs, uuidValidator } from '../validators.js';
|
|
12
12
|
import { getApiClient } from '../api-client.js';
|
|
13
|
+
// Argument schemas for type-safe parsing
|
|
14
|
+
const logDecisionSchema = {
|
|
15
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
16
|
+
title: { type: 'string', required: true },
|
|
17
|
+
description: { type: 'string', required: true },
|
|
18
|
+
rationale: { type: 'string' },
|
|
19
|
+
alternatives_considered: { type: 'array' },
|
|
20
|
+
};
|
|
21
|
+
const getDecisionsSchema = {
|
|
22
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
23
|
+
limit: { type: 'number', default: 50 },
|
|
24
|
+
offset: { type: 'number', default: 0 },
|
|
25
|
+
search_query: { type: 'string' },
|
|
26
|
+
};
|
|
27
|
+
const deleteDecisionSchema = {
|
|
28
|
+
decision_id: { type: 'string', required: true, validate: uuidValidator },
|
|
29
|
+
};
|
|
13
30
|
export const logDecision = async (args, ctx) => {
|
|
14
|
-
const { project_id, title, description, rationale, alternatives_considered } = args;
|
|
15
|
-
validateRequired(project_id, 'project_id');
|
|
16
|
-
validateUUID(project_id, 'project_id');
|
|
17
|
-
validateRequired(title, 'title');
|
|
18
|
-
validateRequired(description, 'description');
|
|
31
|
+
const { project_id, title, description, rationale, alternatives_considered } = parseArgs(args, logDecisionSchema);
|
|
19
32
|
const { session } = ctx;
|
|
20
33
|
const apiClient = getApiClient();
|
|
21
34
|
const response = await apiClient.logDecision(project_id, {
|
|
22
35
|
title,
|
|
23
36
|
description,
|
|
24
37
|
rationale,
|
|
25
|
-
alternatives_considered
|
|
38
|
+
alternatives_considered: alternatives_considered
|
|
26
39
|
}, session.currentSessionId || undefined);
|
|
27
40
|
if (!response.ok) {
|
|
28
41
|
throw new Error(`Failed to log decision: ${response.error}`);
|
|
29
42
|
}
|
|
30
43
|
return { result: { success: true, title, decision_id: response.data?.decision_id } };
|
|
31
44
|
};
|
|
32
|
-
export const getDecisions = async (args,
|
|
33
|
-
const { project_id } = args;
|
|
34
|
-
validateRequired(project_id, 'project_id');
|
|
35
|
-
validateUUID(project_id, 'project_id');
|
|
45
|
+
export const getDecisions = async (args, _ctx) => {
|
|
46
|
+
const { project_id, limit, offset, search_query } = parseArgs(args, getDecisionsSchema);
|
|
36
47
|
const apiClient = getApiClient();
|
|
37
|
-
const response = await apiClient.getDecisions(project_id
|
|
48
|
+
const response = await apiClient.getDecisions(project_id, {
|
|
49
|
+
limit,
|
|
50
|
+
offset,
|
|
51
|
+
search_query
|
|
52
|
+
});
|
|
38
53
|
if (!response.ok) {
|
|
39
54
|
throw new Error(`Failed to fetch decisions: ${response.error}`);
|
|
40
55
|
}
|
|
@@ -44,10 +59,8 @@ export const getDecisions = async (args, ctx) => {
|
|
|
44
59
|
},
|
|
45
60
|
};
|
|
46
61
|
};
|
|
47
|
-
export const deleteDecision = async (args,
|
|
48
|
-
const { decision_id } = args;
|
|
49
|
-
validateRequired(decision_id, 'decision_id');
|
|
50
|
-
validateUUID(decision_id, 'decision_id');
|
|
62
|
+
export const deleteDecision = async (args, _ctx) => {
|
|
63
|
+
const { decision_id } = parseArgs(args, deleteDecisionSchema);
|
|
51
64
|
const apiClient = getApiClient();
|
|
52
65
|
const response = await apiClient.deleteDecision(decision_id);
|
|
53
66
|
if (!response.ok) {
|