@vibescope/mcp-server 0.5.0 → 0.5.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.
Files changed (161) hide show
  1. package/CHANGELOG.md +84 -84
  2. package/README.md +194 -194
  3. package/dist/api-client/tasks.d.ts +1 -0
  4. package/dist/cli-init.js +21 -21
  5. package/dist/cli.js +26 -26
  6. package/dist/handlers/tasks.js +7 -1
  7. package/dist/handlers/tool-docs.js +1216 -1216
  8. package/dist/index.js +73 -73
  9. package/dist/templates/agent-guidelines.d.ts +1 -1
  10. package/dist/templates/agent-guidelines.js +205 -205
  11. package/dist/templates/help-content.js +1621 -1621
  12. package/dist/tools/bodies-of-work.js +6 -6
  13. package/dist/tools/cloud-agents.js +22 -22
  14. package/dist/tools/milestones.js +2 -2
  15. package/dist/tools/requests.js +1 -1
  16. package/dist/tools/session.js +11 -11
  17. package/dist/tools/sprints.js +9 -9
  18. package/dist/tools/tasks.js +43 -35
  19. package/dist/tools/worktrees.js +14 -14
  20. package/dist/utils.js +11 -11
  21. package/docs/TOOLS.md +2687 -2685
  22. package/package.json +53 -53
  23. package/scripts/generate-docs.ts +212 -212
  24. package/scripts/version-bump.ts +203 -203
  25. package/src/api-client/blockers.ts +86 -86
  26. package/src/api-client/bodies-of-work.ts +194 -194
  27. package/src/api-client/chat.ts +50 -50
  28. package/src/api-client/connectors.ts +152 -152
  29. package/src/api-client/cost.ts +185 -185
  30. package/src/api-client/decisions.ts +87 -87
  31. package/src/api-client/deployment.ts +313 -313
  32. package/src/api-client/discovery.ts +81 -81
  33. package/src/api-client/fallback.ts +52 -52
  34. package/src/api-client/file-checkouts.ts +115 -115
  35. package/src/api-client/findings.ts +100 -100
  36. package/src/api-client/git-issues.ts +88 -88
  37. package/src/api-client/ideas.ts +112 -112
  38. package/src/api-client/index.ts +592 -592
  39. package/src/api-client/milestones.ts +83 -83
  40. package/src/api-client/organizations.ts +185 -185
  41. package/src/api-client/progress.ts +94 -94
  42. package/src/api-client/project.ts +181 -181
  43. package/src/api-client/requests.ts +54 -54
  44. package/src/api-client/session.ts +220 -220
  45. package/src/api-client/sprints.ts +227 -227
  46. package/src/api-client/subtasks.ts +57 -57
  47. package/src/api-client/tasks.ts +451 -450
  48. package/src/api-client/types.ts +32 -32
  49. package/src/api-client/validation.ts +60 -60
  50. package/src/api-client/worktrees.ts +53 -53
  51. package/src/api-client.test.ts +847 -847
  52. package/src/api-client.ts +2728 -2728
  53. package/src/cli-init.ts +558 -558
  54. package/src/cli.test.ts +284 -284
  55. package/src/cli.ts +204 -204
  56. package/src/handlers/__test-setup__.ts +240 -240
  57. package/src/handlers/__test-utils__.ts +89 -89
  58. package/src/handlers/blockers.test.ts +468 -468
  59. package/src/handlers/blockers.ts +172 -172
  60. package/src/handlers/bodies-of-work.test.ts +704 -704
  61. package/src/handlers/bodies-of-work.ts +526 -526
  62. package/src/handlers/chat.test.ts +185 -185
  63. package/src/handlers/chat.ts +101 -101
  64. package/src/handlers/cloud-agents.test.ts +438 -438
  65. package/src/handlers/cloud-agents.ts +156 -156
  66. package/src/handlers/connectors.test.ts +834 -834
  67. package/src/handlers/connectors.ts +229 -229
  68. package/src/handlers/cost.test.ts +462 -462
  69. package/src/handlers/cost.ts +285 -285
  70. package/src/handlers/decisions.test.ts +382 -382
  71. package/src/handlers/decisions.ts +153 -153
  72. package/src/handlers/deployment.test.ts +551 -551
  73. package/src/handlers/deployment.ts +570 -570
  74. package/src/handlers/discovery.test.ts +206 -206
  75. package/src/handlers/discovery.ts +433 -433
  76. package/src/handlers/fallback.test.ts +537 -537
  77. package/src/handlers/fallback.ts +194 -194
  78. package/src/handlers/file-checkouts.test.ts +750 -750
  79. package/src/handlers/file-checkouts.ts +185 -185
  80. package/src/handlers/findings.test.ts +633 -633
  81. package/src/handlers/findings.ts +239 -239
  82. package/src/handlers/git-issues.test.ts +631 -631
  83. package/src/handlers/git-issues.ts +136 -136
  84. package/src/handlers/ideas.test.ts +644 -644
  85. package/src/handlers/ideas.ts +207 -207
  86. package/src/handlers/index.ts +93 -93
  87. package/src/handlers/milestones.test.ts +475 -475
  88. package/src/handlers/milestones.ts +180 -180
  89. package/src/handlers/organizations.test.ts +826 -826
  90. package/src/handlers/organizations.ts +315 -315
  91. package/src/handlers/progress.test.ts +269 -269
  92. package/src/handlers/progress.ts +77 -77
  93. package/src/handlers/project.test.ts +546 -546
  94. package/src/handlers/project.ts +245 -245
  95. package/src/handlers/requests.test.ts +303 -303
  96. package/src/handlers/requests.ts +99 -99
  97. package/src/handlers/roles.test.ts +305 -305
  98. package/src/handlers/roles.ts +219 -219
  99. package/src/handlers/session.test.ts +998 -998
  100. package/src/handlers/session.ts +1105 -1105
  101. package/src/handlers/sprints.test.ts +732 -732
  102. package/src/handlers/sprints.ts +537 -537
  103. package/src/handlers/tasks.test.ts +931 -931
  104. package/src/handlers/tasks.ts +1144 -1137
  105. package/src/handlers/tool-categories.test.ts +66 -66
  106. package/src/handlers/tool-docs.test.ts +511 -511
  107. package/src/handlers/tool-docs.ts +1595 -1595
  108. package/src/handlers/types.test.ts +259 -259
  109. package/src/handlers/types.ts +176 -176
  110. package/src/handlers/validation.test.ts +582 -582
  111. package/src/handlers/validation.ts +164 -164
  112. package/src/handlers/version.ts +63 -63
  113. package/src/index.test.ts +674 -674
  114. package/src/index.ts +884 -884
  115. package/src/setup.test.ts +243 -243
  116. package/src/setup.ts +410 -410
  117. package/src/templates/agent-guidelines.ts +233 -233
  118. package/src/templates/help-content.ts +1751 -1751
  119. package/src/token-tracking.test.ts +463 -463
  120. package/src/token-tracking.ts +167 -167
  121. package/src/tools/blockers.ts +122 -122
  122. package/src/tools/bodies-of-work.ts +283 -283
  123. package/src/tools/chat.ts +72 -72
  124. package/src/tools/cloud-agents.ts +101 -101
  125. package/src/tools/connectors.ts +191 -191
  126. package/src/tools/cost.ts +111 -111
  127. package/src/tools/decisions.ts +111 -111
  128. package/src/tools/deployment.ts +455 -455
  129. package/src/tools/discovery.ts +76 -76
  130. package/src/tools/fallback.ts +111 -111
  131. package/src/tools/features.ts +154 -154
  132. package/src/tools/file-checkouts.ts +145 -145
  133. package/src/tools/findings.ts +101 -101
  134. package/src/tools/git-issues.ts +130 -130
  135. package/src/tools/ideas.ts +162 -162
  136. package/src/tools/index.ts +145 -145
  137. package/src/tools/milestones.ts +118 -118
  138. package/src/tools/organizations.ts +224 -224
  139. package/src/tools/persona-templates.ts +25 -25
  140. package/src/tools/progress.ts +73 -73
  141. package/src/tools/project.ts +210 -210
  142. package/src/tools/requests.ts +68 -68
  143. package/src/tools/roles.ts +112 -112
  144. package/src/tools/session.ts +181 -181
  145. package/src/tools/sprints.ts +298 -298
  146. package/src/tools/tasks.ts +583 -575
  147. package/src/tools/tools.test.ts +222 -222
  148. package/src/tools/types.ts +9 -9
  149. package/src/tools/validation.ts +75 -75
  150. package/src/tools/version.ts +34 -34
  151. package/src/tools/worktrees.ts +66 -66
  152. package/src/tools.test.ts +416 -416
  153. package/src/utils.test.ts +1014 -1014
  154. package/src/utils.ts +586 -586
  155. package/src/validators.test.ts +223 -223
  156. package/src/validators.ts +249 -249
  157. package/src/version.ts +162 -162
  158. package/tsconfig.json +16 -16
  159. package/vitest.config.ts +14 -14
  160. package/dist/tools.d.ts +0 -2
  161. package/dist/tools.js +0 -3602
package/src/api-client.ts CHANGED
@@ -1,2728 +1,2728 @@
1
- /**
2
- * Vibescope API Client
3
- *
4
- * HTTP client for communicating with the Vibescope API.
5
- * All database operations are handled server-side through these endpoints.
6
- */
7
-
8
- import crypto from 'crypto';
9
-
10
- const DEFAULT_API_URL = 'https://vibescope.dev';
11
-
12
- // Stable instance ID for this process — persists across start_work_session calls
13
- // so the API can recognise reconnections after context clears
14
- const PROCESS_INSTANCE_ID = crypto.randomUUID();
15
-
16
- // Retry configuration defaults
17
- const DEFAULT_RETRY_STATUS_CODES = [429, 503, 504];
18
- const DEFAULT_MAX_RETRIES = 3;
19
- const DEFAULT_BASE_DELAY_MS = 1000;
20
- const DEFAULT_MAX_DELAY_MS = 30000;
21
-
22
- interface RetryConfig {
23
- maxRetries?: number;
24
- baseDelayMs?: number;
25
- maxDelayMs?: number;
26
- retryStatusCodes?: number[];
27
- }
28
-
29
- interface ApiClientConfig {
30
- apiKey: string;
31
- baseUrl?: string;
32
- retry?: RetryConfig;
33
- }
34
-
35
- interface ApiResponse<T> {
36
- ok: boolean;
37
- status: number;
38
- data?: T;
39
- error?: string;
40
- }
41
-
42
- /**
43
- * Calculate delay for exponential backoff with jitter
44
- */
45
- function calculateBackoffDelay(
46
- attempt: number,
47
- baseDelayMs: number,
48
- maxDelayMs: number,
49
- retryAfter?: number
50
- ): number {
51
- // If Retry-After header is present, use it (in seconds)
52
- if (retryAfter !== undefined && retryAfter > 0) {
53
- return Math.min(retryAfter * 1000, maxDelayMs);
54
- }
55
- // Exponential backoff: base * 2^attempt with jitter
56
- const exponentialDelay = baseDelayMs * Math.pow(2, attempt);
57
- const jitter = Math.random() * 0.3 * exponentialDelay; // 0-30% jitter
58
- return Math.min(exponentialDelay + jitter, maxDelayMs);
59
- }
60
-
61
- /**
62
- * Sleep for a specified duration
63
- */
64
- function sleep(ms: number): Promise<void> {
65
- return new Promise(resolve => setTimeout(resolve, ms));
66
- }
67
-
68
- export class VibescopeApiClient {
69
- private apiKey: string;
70
- private baseUrl: string;
71
- private retryConfig: Required<RetryConfig>;
72
-
73
- constructor(config: ApiClientConfig) {
74
- this.apiKey = config.apiKey;
75
- this.baseUrl = config.baseUrl || process.env.VIBESCOPE_API_URL || DEFAULT_API_URL;
76
- this.retryConfig = {
77
- maxRetries: config.retry?.maxRetries ?? DEFAULT_MAX_RETRIES,
78
- baseDelayMs: config.retry?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS,
79
- maxDelayMs: config.retry?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS,
80
- retryStatusCodes: config.retry?.retryStatusCodes ?? DEFAULT_RETRY_STATUS_CODES,
81
- };
82
- }
83
-
84
- private async request<T>(method: string, path: string, body?: unknown, options?: { timeoutMs?: number }): Promise<ApiResponse<T>> {
85
- const url = `${this.baseUrl}${path}`;
86
- const { maxRetries, baseDelayMs, maxDelayMs, retryStatusCodes } = this.retryConfig;
87
- let lastError: Error | null = null;
88
- let lastResponse: Response | null = null;
89
-
90
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
91
- let timeoutId: ReturnType<typeof setTimeout> | undefined;
92
- try {
93
- const fetchOptions: RequestInit = {
94
- method,
95
- headers: {
96
- 'Content-Type': 'application/json',
97
- 'X-API-Key': this.apiKey
98
- },
99
- body: body ? JSON.stringify(body) : undefined,
100
- };
101
-
102
- if (options?.timeoutMs) {
103
- const controller = new AbortController();
104
- timeoutId = setTimeout(() => controller.abort(), options.timeoutMs);
105
- fetchOptions.signal = controller.signal;
106
- }
107
-
108
- const response = await fetch(url, fetchOptions);
109
- if (timeoutId) clearTimeout(timeoutId);
110
-
111
- // Check if we should retry this status code
112
- if (retryStatusCodes.includes(response.status) && attempt < maxRetries) {
113
- lastResponse = response;
114
- // Parse Retry-After header if present (can be seconds or HTTP-date)
115
- const retryAfterHeader = response.headers.get('Retry-After');
116
- let retryAfter: number | undefined;
117
- if (retryAfterHeader) {
118
- const seconds = parseInt(retryAfterHeader, 10);
119
- if (!isNaN(seconds)) {
120
- retryAfter = seconds;
121
- }
122
- }
123
- const delay = calculateBackoffDelay(attempt, baseDelayMs, maxDelayMs, retryAfter);
124
- await sleep(delay);
125
- continue;
126
- }
127
-
128
- const data = await response.json();
129
-
130
- if (!response.ok) {
131
- return {
132
- ok: false,
133
- status: response.status,
134
- error: data.error || `HTTP ${response.status}`,
135
- data // Include full response data for additional error context
136
- };
137
- }
138
-
139
- return {
140
- ok: true,
141
- status: response.status,
142
- data
143
- };
144
- } catch (err) {
145
- if (timeoutId) clearTimeout(timeoutId);
146
- // Detect AbortError from timeout
147
- if (err instanceof Error && err.name === 'AbortError' && options?.timeoutMs) {
148
- lastError = new Error(`Request timed out after ${options.timeoutMs}ms`);
149
- } else {
150
- lastError = err instanceof Error ? err : new Error('Network error');
151
- }
152
- // Retry on network errors (connection failures, timeouts)
153
- if (attempt < maxRetries) {
154
- const delay = calculateBackoffDelay(attempt, baseDelayMs, maxDelayMs);
155
- await sleep(delay);
156
- continue;
157
- }
158
- }
159
- }
160
-
161
- // All retries exhausted
162
- if (lastResponse) {
163
- // We had a response but it was a retryable error status
164
- try {
165
- const data = await lastResponse.json();
166
- return {
167
- ok: false,
168
- status: lastResponse.status,
169
- error: data.error || `HTTP ${lastResponse.status} after ${maxRetries} retries`,
170
- data
171
- };
172
- } catch {
173
- return {
174
- ok: false,
175
- status: lastResponse.status,
176
- error: `HTTP ${lastResponse.status} after ${maxRetries} retries`
177
- };
178
- }
179
- }
180
-
181
- return {
182
- ok: false,
183
- status: 0,
184
- error: lastError?.message || 'Network error after retries'
185
- };
186
- }
187
-
188
- // Auth endpoints
189
- async validateAuth(options?: { timeoutMs?: number }): Promise<ApiResponse<{
190
- valid: boolean;
191
- user_id: string;
192
- api_key_id: string;
193
- key_name: string;
194
- }>> {
195
- return this.request('POST', '/api/mcp/auth/validate', {
196
- api_key: this.apiKey
197
- }, options);
198
- }
199
-
200
- // Session endpoints
201
- async startSession(params: {
202
- project_id?: string;
203
- git_url?: string;
204
- mode?: 'lite' | 'full';
205
- model?: string; // Open-ended - any model name accepted
206
- role?: string; // Open-ended - any role name accepted
207
- hostname?: string; // Machine hostname for worktree tracking
208
- agent_type?: string; // Open-ended - any agent type accepted
209
- agent_name?: string; // Explicit name for cloud/remote agents (skips persona pool)
210
- }): Promise<ApiResponse<{
211
- session_started: boolean;
212
- session_id?: string;
213
- persona?: string;
214
- role?: string;
215
- project?: {
216
- id: string;
217
- name: string;
218
- description?: string;
219
- goal?: string;
220
- status?: string;
221
- git_url?: string;
222
- agent_instructions?: string;
223
- tech_stack?: string[];
224
- git_workflow?: string;
225
- git_main_branch?: string;
226
- git_develop_branch?: string;
227
- git_auto_branch?: boolean;
228
- validation_required?: boolean;
229
- auto_merge_on_approval?: boolean;
230
- fallback_activities_enabled?: boolean;
231
- require_pr_for_validation?: boolean;
232
- };
233
- active_tasks?: Array<{
234
- id: string;
235
- title: string;
236
- status: string;
237
- priority: number;
238
- progress_percentage?: number;
239
- estimated_minutes?: number;
240
- }>;
241
- blockers?: Array<{
242
- id: string;
243
- description: string;
244
- status: string;
245
- }>;
246
- next_task?: {
247
- id: string;
248
- title: string;
249
- priority: number;
250
- estimated_minutes?: number;
251
- } | null;
252
- pending_requests?: Array<{
253
- id: string;
254
- request_type: string;
255
- message: string;
256
- created_at: string;
257
- }>;
258
- pending_requests_count?: number;
259
- URGENT_QUESTIONS?: {
260
- count: number;
261
- oldest_waiting_minutes: number;
262
- action_required: string;
263
- requests: Array<{
264
- id: string;
265
- message: string;
266
- waiting_minutes: number;
267
- }>;
268
- };
269
- directive?: string;
270
- blockers_count?: number;
271
- validation_count?: number;
272
- project_not_found?: boolean;
273
- message?: string;
274
- suggestion?: {
275
- action: string;
276
- example: string;
277
- note: string;
278
- };
279
- // Agent onboarding - returned when a new agent type connects to an existing project
280
- agent_setup?: {
281
- agent_type: string;
282
- is_new_agent_type: boolean;
283
- setup_required: boolean;
284
- instructions: string;
285
- config_file: string; // e.g., '.gemini/GEMINI.md'
286
- template_url?: string;
287
- steps: string[];
288
- };
289
- // Stale worktrees that need cleanup
290
- stale_worktrees?: Array<{
291
- task_id: string;
292
- task_title: string;
293
- worktree_path: string;
294
- worktree_hostname?: string | null;
295
- }>;
296
- stale_worktrees_count?: number;
297
- cleanup_action?: string;
298
- // Validation info for validators
299
- awaiting_validation?: Array<{
300
- id: string;
301
- title?: string;
302
- }>;
303
- validation_priority?: boolean;
304
- next_action?: string;
305
- error?: string;
306
- }>> {
307
- return this.request('POST', '/api/mcp/sessions/start', {
308
- ...params,
309
- instance_id: PROCESS_INSTANCE_ID,
310
- });
311
- }
312
-
313
- async heartbeat(sessionId: string, options?: {
314
- current_worktree_path?: string | null;
315
- hostname?: string; // Machine hostname for worktree tracking
316
- }): Promise<ApiResponse<{
317
- success: boolean;
318
- session_id: string;
319
- timestamp: string;
320
- }>> {
321
- return this.request('POST', '/api/mcp/sessions/heartbeat', {
322
- session_id: sessionId,
323
- ...options
324
- });
325
- }
326
-
327
- async endSession(sessionId: string): Promise<ApiResponse<{
328
- success: boolean;
329
- ended_session_id?: string;
330
- session_summary?: {
331
- agent_name: string;
332
- tasks_completed_this_session: number;
333
- tasks_awaiting_validation: number;
334
- tasks_released: number;
335
- };
336
- reminders?: string[];
337
- }>> {
338
- return this.request('POST', '/api/mcp/sessions/end', {
339
- session_id: sessionId
340
- });
341
- }
342
-
343
- async signalIdle(sessionId: string): Promise<ApiResponse<{
344
- success: boolean;
345
- session_id: string;
346
- status: string;
347
- message: string;
348
- }>> {
349
- return this.request('POST', '/api/mcp/sessions/idle', {
350
- session_id: sessionId
351
- });
352
- }
353
-
354
- // Project endpoints
355
- async listProjects(): Promise<ApiResponse<{
356
- projects: Array<{
357
- id: string;
358
- name: string;
359
- description?: string;
360
- status: string;
361
- git_url?: string;
362
- goal?: string;
363
- tech_stack?: string[];
364
- }>;
365
- }>> {
366
- return this.request('GET', '/api/mcp/projects');
367
- }
368
-
369
- async createProject(params: {
370
- name: string;
371
- description?: string;
372
- goal?: string;
373
- git_url?: string;
374
- tech_stack?: string[];
375
- }): Promise<ApiResponse<{
376
- success: boolean;
377
- project: {
378
- id: string;
379
- name: string;
380
- };
381
- }>> {
382
- return this.request('POST', '/api/mcp/projects', params);
383
- }
384
-
385
- async getProject(projectId: string, gitUrl?: string): Promise<ApiResponse<{
386
- found: boolean;
387
- project?: {
388
- id: string;
389
- name: string;
390
- description?: string;
391
- goal?: string;
392
- status: string;
393
- git_url?: string;
394
- agent_instructions?: string;
395
- tech_stack?: string[];
396
- git_workflow?: string;
397
- git_main_branch?: string;
398
- git_develop_branch?: string;
399
- git_auto_branch?: boolean;
400
- git_auto_tag?: boolean;
401
- deployment_instructions?: string;
402
- };
403
- active_tasks?: Array<{
404
- id: string;
405
- title: string;
406
- description?: string;
407
- priority: number;
408
- status: string;
409
- progress_percentage?: number;
410
- estimated_minutes?: number;
411
- }>;
412
- open_blockers?: Array<{
413
- id: string;
414
- description: string;
415
- }>;
416
- recent_decisions?: Array<{
417
- id: string;
418
- title: string;
419
- description?: string;
420
- }>;
421
- message?: string;
422
- }>> {
423
- const url = gitUrl
424
- ? `/api/mcp/projects/${projectId}?git_url=${encodeURIComponent(gitUrl)}`
425
- : `/api/mcp/projects/${projectId}`;
426
- return this.request('GET', url);
427
- }
428
-
429
- async updateProject(projectId: string, updates: {
430
- name?: string;
431
- description?: string;
432
- goal?: string;
433
- git_url?: string;
434
- tech_stack?: string[];
435
- status?: string;
436
- git_workflow?: string;
437
- git_main_branch?: string;
438
- git_develop_branch?: string;
439
- git_auto_branch?: boolean;
440
- git_auto_tag?: boolean;
441
- deployment_instructions?: string;
442
- agent_instructions?: string;
443
- // New project settings columns
444
- git_delete_branch_on_merge?: boolean;
445
- require_pr_for_validation?: boolean;
446
- auto_merge_on_approval?: boolean;
447
- validation_required?: boolean;
448
- default_task_priority?: number;
449
- require_time_estimates?: boolean;
450
- fallback_activities_enabled?: boolean;
451
- preferred_fallback_activities?: string[];
452
- allow_local_agent_task_creation?: boolean;
453
- allow_cloud_agent_task_creation?: boolean;
454
- }): Promise<ApiResponse<{
455
- success: boolean;
456
- project_id: string;
457
- }>> {
458
- return this.request('PATCH', `/api/mcp/projects/${projectId}`, updates);
459
- }
460
-
461
- // Task endpoints
462
- async getTasks(projectId: string, params?: {
463
- status?: string;
464
- limit?: number;
465
- offset?: number;
466
- include_subtasks?: boolean;
467
- search_query?: string;
468
- include_metadata?: boolean; // When true, returns all task fields; when false (default), only id/title/priority/status
469
- }): Promise<ApiResponse<{
470
- tasks: Array<{
471
- id: string;
472
- title: string;
473
- description?: string;
474
- priority: number;
475
- status: string;
476
- progress_percentage?: number;
477
- estimated_minutes?: number;
478
- started_at?: string;
479
- completed_at?: string;
480
- parent_task_id?: string;
481
- }>;
482
- total_count: number;
483
- has_more: boolean;
484
- }>> {
485
- const queryParams = new URLSearchParams();
486
- if (params?.status) queryParams.set('status', params.status);
487
- if (params?.limit) queryParams.set('limit', params.limit.toString());
488
- if (params?.offset) queryParams.set('offset', params.offset.toString());
489
- if (params?.include_subtasks) queryParams.set('include_subtasks', 'true');
490
- if (params?.search_query) queryParams.set('search_query', params.search_query);
491
- if (params?.include_metadata) queryParams.set('include_metadata', 'true');
492
-
493
- const query = queryParams.toString();
494
- const url = `/api/mcp/projects/${projectId}/tasks${query ? `?${query}` : ''}`;
495
- return this.request('GET', url);
496
- }
497
-
498
- async createTask(projectId: string, params: {
499
- title: string;
500
- description?: string;
501
- priority?: number;
502
- estimated_minutes?: number;
503
- blocking?: boolean;
504
- session_id?: string;
505
- task_type?: string;
506
- }): Promise<ApiResponse<{
507
- success: boolean;
508
- task_id: string;
509
- title: string;
510
- blocking?: boolean;
511
- message?: string;
512
- }>> {
513
- return this.request('POST', `/api/mcp/projects/${projectId}/tasks`, params);
514
- }
515
-
516
- async getNextTask(projectId: string, sessionId?: string): Promise<ApiResponse<{
517
- task?: {
518
- id: string;
519
- title: string;
520
- description?: string;
521
- priority: number;
522
- estimated_minutes?: number;
523
- blocking?: boolean;
524
- } | null;
525
- blocking_task?: boolean;
526
- deployment_blocks_tasks?: boolean;
527
- deployment?: {
528
- id: string;
529
- status: string;
530
- env: string;
531
- };
532
- awaiting_validation?: Array<{
533
- id: string;
534
- title: string;
535
- }>;
536
- validation_priority?: string;
537
- all_claimed?: boolean;
538
- is_subtask?: boolean;
539
- suggested_activity?: string;
540
- directive?: string;
541
- message?: string;
542
- action?: string;
543
- }>> {
544
- const url = sessionId
545
- ? `/api/mcp/projects/${projectId}/next-task?session_id=${sessionId}`
546
- : `/api/mcp/projects/${projectId}/next-task`;
547
- return this.request('GET', url);
548
- }
549
-
550
- async getTask(taskId: string): Promise<ApiResponse<{
551
- task: {
552
- id: string;
553
- title: string;
554
- description?: string;
555
- priority: number;
556
- status: string;
557
- progress_percentage?: number;
558
- estimated_minutes?: number;
559
- started_at?: string;
560
- completed_at?: string;
561
- git_branch?: string;
562
- blocking?: boolean;
563
- references?: Array<{ url: string; label?: string }>;
564
- parent_task_id?: string;
565
- working_agent_session_id?: string;
566
- };
567
- }>> {
568
- return this.request('GET', `/api/mcp/tasks/${taskId}`);
569
- }
570
-
571
- /**
572
- * Get a single task by ID with optional subtasks and milestones
573
- */
574
- async getTaskById(taskId: string, params?: {
575
- include_subtasks?: boolean;
576
- include_milestones?: boolean;
577
- }): Promise<ApiResponse<{
578
- task: {
579
- id: string;
580
- title: string;
581
- description?: string;
582
- priority: number;
583
- status: string;
584
- progress_percentage?: number;
585
- estimated_minutes?: number;
586
- started_at?: string;
587
- completed_at?: string;
588
- git_branch?: string;
589
- references?: Array<{ url: string; label?: string }>;
590
- };
591
- subtasks?: Array<{
592
- id: string;
593
- title: string;
594
- status: string;
595
- progress_percentage?: number;
596
- }>;
597
- milestones?: Array<{
598
- id: string;
599
- title: string;
600
- status: string;
601
- order_index: number;
602
- }>;
603
- }>> {
604
- return this.proxy('get_task', {
605
- task_id: taskId,
606
- include_subtasks: params?.include_subtasks,
607
- include_milestones: params?.include_milestones,
608
- });
609
- }
610
-
611
- /**
612
- * Search tasks by text query with pagination
613
- */
614
- async searchTasks(projectId: string, params: {
615
- query: string;
616
- status?: string[];
617
- limit?: number;
618
- offset?: number;
619
- }): Promise<ApiResponse<{
620
- tasks: Array<{
621
- id: string;
622
- title: string;
623
- status: string;
624
- priority: number;
625
- snippet?: string;
626
- }>;
627
- total_matches: number;
628
- hint?: string;
629
- }>> {
630
- return this.proxy('search_tasks', {
631
- project_id: projectId,
632
- query: params.query,
633
- status: params.status,
634
- limit: params.limit,
635
- offset: params.offset,
636
- });
637
- }
638
-
639
- /**
640
- * Get tasks filtered by priority with pagination
641
- */
642
- async getTasksByPriority(projectId: string, params?: {
643
- priority?: number;
644
- priority_max?: number;
645
- status?: string;
646
- limit?: number;
647
- offset?: number;
648
- }): Promise<ApiResponse<{
649
- tasks: Array<{
650
- id: string;
651
- title: string;
652
- priority: number;
653
- status: string;
654
- estimated_minutes?: number;
655
- }>;
656
- total_count: number;
657
- }>> {
658
- return this.proxy('get_tasks_by_priority', {
659
- project_id: projectId,
660
- priority: params?.priority,
661
- priority_max: params?.priority_max,
662
- status: params?.status,
663
- limit: params?.limit,
664
- offset: params?.offset,
665
- });
666
- }
667
-
668
- /**
669
- * Get recent tasks (newest or oldest) with pagination
670
- */
671
- async getRecentTasks(projectId: string, params?: {
672
- order?: 'newest' | 'oldest';
673
- status?: string;
674
- limit?: number;
675
- offset?: number;
676
- }): Promise<ApiResponse<{
677
- tasks: Array<{
678
- id: string;
679
- title: string;
680
- status: string;
681
- priority: number;
682
- created_at: string;
683
- age?: string;
684
- }>;
685
- total_count: number;
686
- }>> {
687
- return this.proxy('get_recent_tasks', {
688
- project_id: projectId,
689
- order: params?.order,
690
- status: params?.status,
691
- limit: params?.limit,
692
- offset: params?.offset,
693
- });
694
- }
695
-
696
- /**
697
- * Get task statistics for a project
698
- */
699
- async getTaskStats(projectId: string): Promise<ApiResponse<{
700
- total: number;
701
- by_status: {
702
- backlog: number;
703
- pending: number;
704
- in_progress: number;
705
- completed: number;
706
- cancelled: number;
707
- };
708
- by_priority: {
709
- 1: number;
710
- 2: number;
711
- 3: number;
712
- 4: number;
713
- 5: number;
714
- };
715
- awaiting_validation: number;
716
- oldest_pending_days: number | null;
717
- }>> {
718
- return this.proxy('get_task_stats', {
719
- project_id: projectId,
720
- });
721
- }
722
-
723
- async updateTask(taskId: string, updates: {
724
- title?: string;
725
- description?: string;
726
- priority?: number;
727
- status?: string;
728
- progress_percentage?: number;
729
- progress_note?: string;
730
- estimated_minutes?: number;
731
- git_branch?: string;
732
- worktree_path?: string;
733
- session_id?: string;
734
- }): Promise<ApiResponse<{
735
- success: boolean;
736
- task_id: string;
737
- git_workflow?: {
738
- workflow: string;
739
- base_branch: string;
740
- suggested_branch: string;
741
- worktree_required: boolean;
742
- };
743
- worktree_setup?: {
744
- message: string;
745
- commands: string[];
746
- worktree_path: string;
747
- branch_name: string;
748
- cleanup_command: string;
749
- };
750
- next_step?: string;
751
- }>> {
752
- return this.request('PATCH', `/api/mcp/tasks/${taskId}`, updates);
753
- }
754
-
755
- async completeTask(taskId: string, params: {
756
- summary?: string;
757
- session_id?: string;
758
- commit_hash?: string;
759
- check_results?: Array<{ command: string; passed: boolean; output?: string }>;
760
- }): Promise<ApiResponse<{
761
- success: boolean;
762
- directive: string;
763
- auto_continue: boolean;
764
- completed_task_id: string;
765
- next_task?: {
766
- id: string;
767
- title: string;
768
- priority: number;
769
- estimated_minutes?: number;
770
- } | null;
771
- context?: {
772
- validation?: number;
773
- blockers?: number;
774
- deployment?: string;
775
- worktree_path?: string;
776
- };
777
- next_action: string;
778
- warnings?: string[];
779
- }>> {
780
- // Use proxy endpoint for consistency - direct endpoint had routing issues on Vercel
781
- return this.proxy('complete_task', {
782
- task_id: taskId,
783
- summary: params.summary,
784
- ...(params.commit_hash ? { commit_hash: params.commit_hash } : {}),
785
- ...(params.check_results ? { check_results: params.check_results } : {}),
786
- }, params.session_id ? { session_id: params.session_id, persona: null, instance_id: '' } : undefined);
787
- }
788
-
789
- async deleteTask(taskId: string): Promise<ApiResponse<{
790
- success: boolean;
791
- deleted_id: string;
792
- }>> {
793
- return this.request('DELETE', `/api/mcp/tasks/${taskId}`);
794
- }
795
-
796
- /**
797
- * Release a task back to pending status.
798
- * Clears session assignment, git branch, and worktree info.
799
- */
800
- async releaseTask(taskId: string, params?: {
801
- reason?: string;
802
- session_id?: string;
803
- }): Promise<ApiResponse<{
804
- success: boolean;
805
- task_id: string;
806
- message: string;
807
- reason?: string;
808
- hint: string;
809
- }>> {
810
- return this.proxy('release_task', {
811
- task_id: taskId,
812
- reason: params?.reason,
813
- }, params?.session_id ? {
814
- session_id: params.session_id,
815
- persona: null,
816
- instance_id: '',
817
- } : undefined);
818
- }
819
-
820
- /**
821
- * Cancel a task with an optional reason.
822
- * Task remains visible with cancelled status for historical tracking.
823
- */
824
- async cancelTask(taskId: string, params?: {
825
- cancelled_reason?: 'pr_closed' | 'superseded' | 'user_cancelled' | 'validation_failed' | 'obsolete' | 'blocked';
826
- cancellation_note?: string;
827
- session_id?: string;
828
- }): Promise<ApiResponse<{
829
- success: boolean;
830
- task_id: string;
831
- cancelled_reason: string | null;
832
- message: string;
833
- }>> {
834
- return this.proxy('cancel_task', {
835
- task_id: taskId,
836
- cancelled_reason: params?.cancelled_reason,
837
- cancellation_note: params?.cancellation_note,
838
- }, params?.session_id ? {
839
- session_id: params.session_id,
840
- persona: null,
841
- instance_id: '',
842
- } : undefined);
843
- }
844
-
845
- // Progress endpoints
846
- async logProgress(projectId: string, params: {
847
- summary: string;
848
- details?: string;
849
- task_id?: string;
850
- session_id?: string;
851
- }): Promise<ApiResponse<{
852
- success: boolean;
853
- progress_id: string;
854
- }>> {
855
- return this.request('POST', `/api/mcp/projects/${projectId}/progress`, params);
856
- }
857
-
858
- // Git workflow endpoint
859
- async getGitWorkflow(projectId: string, taskId?: string): Promise<ApiResponse<{
860
- workflow: string;
861
- main_branch: string;
862
- develop_branch?: string | null;
863
- auto_branch?: boolean;
864
- auto_tag?: boolean;
865
- instructions: string[];
866
- task?: {
867
- id: string;
868
- title: string;
869
- current_branch?: string;
870
- suggested_branch?: string | null;
871
- };
872
- }>> {
873
- const url = taskId
874
- ? `/api/mcp/projects/${projectId}/git-workflow?task_id=${taskId}`
875
- : `/api/mcp/projects/${projectId}/git-workflow`;
876
- return this.request('GET', url);
877
- }
878
-
879
- // ============================================================================
880
- // Proxy endpoint - Generic method for all operations
881
- // ============================================================================
882
- async proxy<T>(operation: string, args: Record<string, unknown>, sessionContext?: {
883
- session_id: string | null;
884
- persona: string | null;
885
- instance_id: string;
886
- }): Promise<ApiResponse<T>> {
887
- return this.request('POST', '/api/mcp/proxy', {
888
- operation,
889
- args,
890
- session_context: sessionContext
891
- });
892
- }
893
-
894
- // ============================================================================
895
- // Blocker endpoints
896
- // ============================================================================
897
- async getBlockers(projectId: string, params?: {
898
- status?: string;
899
- limit?: number;
900
- offset?: number;
901
- search_query?: string;
902
- }): Promise<ApiResponse<{
903
- blockers: Array<{
904
- id: string;
905
- description: string;
906
- status: string;
907
- resolution_note?: string;
908
- created_at: string;
909
- resolved_at?: string;
910
- }>;
911
- total_count?: number;
912
- has_more?: boolean;
913
- }>> {
914
- return this.proxy('get_blockers', {
915
- project_id: projectId,
916
- ...params
917
- });
918
- }
919
-
920
- async addBlocker(projectId: string, description: string, sessionId?: string): Promise<ApiResponse<{
921
- success: boolean;
922
- blocker_id: string;
923
- }>> {
924
- return this.proxy('add_blocker', { project_id: projectId, description }, sessionId ? {
925
- session_id: sessionId,
926
- persona: null,
927
- instance_id: ''
928
- } : undefined);
929
- }
930
-
931
- async resolveBlocker(blockerId: string, resolutionNote?: string): Promise<ApiResponse<{
932
- success: boolean;
933
- blocker_id: string;
934
- }>> {
935
- return this.proxy('resolve_blocker', {
936
- blocker_id: blockerId,
937
- resolution_note: resolutionNote
938
- });
939
- }
940
-
941
- async deleteBlocker(blockerId: string): Promise<ApiResponse<{
942
- success: boolean;
943
- }>> {
944
- return this.proxy('delete_blocker', { blocker_id: blockerId });
945
- }
946
-
947
- async getBlockersStats(projectId: string): Promise<ApiResponse<{
948
- total: number;
949
- by_status: Record<string, number>;
950
- }>> {
951
- return this.proxy('get_blockers_stats', {
952
- project_id: projectId
953
- });
954
- }
955
-
956
- // ============================================================================
957
- // Decision endpoints
958
- // ============================================================================
959
- async getDecisions(projectId: string, options?: {
960
- limit?: number;
961
- offset?: number;
962
- search_query?: string;
963
- }): Promise<ApiResponse<{
964
- decisions: Array<{
965
- id: string;
966
- title: string;
967
- description: string;
968
- rationale?: string;
969
- alternatives_considered?: string[];
970
- created_at: string;
971
- }>;
972
- }>> {
973
- return this.proxy('get_decisions', { project_id: projectId, ...options });
974
- }
975
-
976
- async logDecision(projectId: string, params: {
977
- title: string;
978
- description: string;
979
- rationale?: string;
980
- alternatives_considered?: string[];
981
- }, sessionId?: string): Promise<ApiResponse<{
982
- success: boolean;
983
- decision_id: string;
984
- }>> {
985
- return this.proxy('log_decision', {
986
- project_id: projectId,
987
- ...params
988
- }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
989
- }
990
-
991
- async deleteDecision(decisionId: string): Promise<ApiResponse<{
992
- success: boolean;
993
- }>> {
994
- return this.proxy('delete_decision', { decision_id: decisionId });
995
- }
996
-
997
- async getDecisionsStats(projectId: string): Promise<ApiResponse<{
998
- total: number;
999
- }>> {
1000
- return this.proxy('get_decisions_stats', {
1001
- project_id: projectId
1002
- });
1003
- }
1004
-
1005
- // ============================================================================
1006
- // Idea endpoints
1007
- // ============================================================================
1008
- async getIdeas(projectId: string, params?: {
1009
- status?: string;
1010
- limit?: number;
1011
- offset?: number;
1012
- search_query?: string;
1013
- }): Promise<ApiResponse<{
1014
- ideas: Array<{
1015
- id: string;
1016
- title: string;
1017
- description?: string;
1018
- status: string;
1019
- doc_url?: string;
1020
- created_at: string;
1021
- }>;
1022
- }>> {
1023
- return this.proxy('get_ideas', {
1024
- project_id: projectId,
1025
- ...params
1026
- });
1027
- }
1028
-
1029
- async addIdea(projectId: string, params: {
1030
- title: string;
1031
- description?: string;
1032
- status?: string;
1033
- }, sessionId?: string): Promise<ApiResponse<{
1034
- success: boolean;
1035
- idea_id: string;
1036
- }>> {
1037
- return this.proxy('add_idea', {
1038
- project_id: projectId,
1039
- ...params
1040
- }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1041
- }
1042
-
1043
- async updateIdea(ideaId: string, updates: {
1044
- title?: string;
1045
- description?: string;
1046
- status?: string;
1047
- doc_url?: string;
1048
- }): Promise<ApiResponse<{
1049
- success: boolean;
1050
- idea_id: string;
1051
- }>> {
1052
- return this.proxy('update_idea', {
1053
- idea_id: ideaId,
1054
- ...updates
1055
- });
1056
- }
1057
-
1058
- async deleteIdea(ideaId: string): Promise<ApiResponse<{
1059
- success: boolean;
1060
- }>> {
1061
- return this.proxy('delete_idea', { idea_id: ideaId });
1062
- }
1063
-
1064
- async convertIdeaToTask(ideaId: string, params?: {
1065
- priority?: number;
1066
- estimated_minutes?: number;
1067
- update_status?: boolean;
1068
- }): Promise<ApiResponse<{
1069
- success: boolean;
1070
- task_id?: string;
1071
- task_title?: string;
1072
- idea_id?: string;
1073
- idea_status?: string;
1074
- message?: string;
1075
- error?: string;
1076
- existing_task_id?: string;
1077
- }>> {
1078
- return this.proxy('convert_idea_to_task', {
1079
- idea_id: ideaId,
1080
- ...params
1081
- });
1082
- }
1083
-
1084
- // ============================================================================
1085
- // Finding endpoints
1086
- // ============================================================================
1087
- async getFindings(projectId: string, params?: {
1088
- category?: string;
1089
- severity?: string;
1090
- status?: string;
1091
- limit?: number;
1092
- offset?: number;
1093
- search_query?: string;
1094
- summary_only?: boolean;
1095
- }): Promise<ApiResponse<{
1096
- findings: Array<{
1097
- id: string;
1098
- title: string;
1099
- description?: string;
1100
- category: string;
1101
- severity: string;
1102
- status: string;
1103
- file_path?: string;
1104
- line_number?: number;
1105
- resolution_note?: string;
1106
- created_at: string;
1107
- }>;
1108
- total_count?: number;
1109
- has_more?: boolean;
1110
- }>> {
1111
- return this.proxy('get_findings', {
1112
- project_id: projectId,
1113
- ...params
1114
- });
1115
- }
1116
-
1117
- async getFindingsStats(projectId: string): Promise<ApiResponse<{
1118
- total: number;
1119
- by_status: Record<string, number>;
1120
- by_severity: Record<string, number>;
1121
- by_category: Record<string, number>;
1122
- }>> {
1123
- return this.proxy('get_findings_stats', {
1124
- project_id: projectId
1125
- });
1126
- }
1127
-
1128
- async addFinding(projectId: string, params: {
1129
- title: string;
1130
- description?: string;
1131
- category?: string;
1132
- severity?: string;
1133
- file_path?: string;
1134
- line_number?: number;
1135
- related_task_id?: string;
1136
- }, sessionId?: string): Promise<ApiResponse<{
1137
- success: boolean;
1138
- finding_id: string;
1139
- }>> {
1140
- return this.proxy('add_finding', {
1141
- project_id: projectId,
1142
- ...params
1143
- }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1144
- }
1145
-
1146
- async updateFinding(findingId: string, updates: {
1147
- title?: string;
1148
- description?: string;
1149
- severity?: string;
1150
- status?: string;
1151
- resolution_note?: string;
1152
- }): Promise<ApiResponse<{
1153
- success: boolean;
1154
- finding_id: string;
1155
- }>> {
1156
- return this.proxy('update_finding', {
1157
- finding_id: findingId,
1158
- ...updates
1159
- });
1160
- }
1161
-
1162
- async deleteFinding(findingId: string): Promise<ApiResponse<{
1163
- success: boolean;
1164
- }>> {
1165
- return this.proxy('delete_finding', { finding_id: findingId });
1166
- }
1167
-
1168
- // ============================================================================
1169
- // Milestone endpoints
1170
- // ============================================================================
1171
- async getMilestones(taskId: string): Promise<ApiResponse<{
1172
- milestones: Array<{
1173
- id: string;
1174
- title: string;
1175
- description?: string;
1176
- status: string;
1177
- order_index: number;
1178
- created_at: string;
1179
- completed_at?: string;
1180
- }>;
1181
- stats: {
1182
- total: number;
1183
- completed: number;
1184
- progress_percentage: number;
1185
- };
1186
- }>> {
1187
- return this.proxy('get_milestones', { task_id: taskId });
1188
- }
1189
-
1190
- async addMilestone(taskId: string, params: {
1191
- title: string;
1192
- description?: string;
1193
- order_index?: number;
1194
- }, sessionId?: string): Promise<ApiResponse<{
1195
- success: boolean;
1196
- milestone_id: string;
1197
- }>> {
1198
- return this.proxy('add_milestone', {
1199
- task_id: taskId,
1200
- ...params
1201
- }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1202
- }
1203
-
1204
- async updateMilestone(milestoneId: string, updates: {
1205
- title?: string;
1206
- description?: string;
1207
- status?: string;
1208
- order_index?: number;
1209
- }): Promise<ApiResponse<{
1210
- success: boolean;
1211
- milestone: {
1212
- id: string;
1213
- title: string;
1214
- status: string;
1215
- };
1216
- }>> {
1217
- return this.proxy('update_milestone', {
1218
- milestone_id: milestoneId,
1219
- ...updates
1220
- });
1221
- }
1222
-
1223
- async completeMilestone(milestoneId: string): Promise<ApiResponse<{
1224
- success: boolean;
1225
- milestone: {
1226
- id: string;
1227
- title: string;
1228
- status: string;
1229
- };
1230
- }>> {
1231
- return this.proxy('complete_milestone', { milestone_id: milestoneId });
1232
- }
1233
-
1234
- async deleteMilestone(milestoneId: string): Promise<ApiResponse<{
1235
- success: boolean;
1236
- }>> {
1237
- return this.proxy('delete_milestone', { milestone_id: milestoneId });
1238
- }
1239
-
1240
- // ============================================================================
1241
- // Request endpoints
1242
- // ============================================================================
1243
- async getPendingRequests(projectId: string, sessionId?: string, limit?: number, offset?: number): Promise<ApiResponse<{
1244
- requests: Array<{
1245
- id: string;
1246
- request_type: string;
1247
- message: string;
1248
- created_at: string;
1249
- }>;
1250
- total_count: number;
1251
- has_more: boolean;
1252
- }>> {
1253
- return this.proxy('get_pending_requests', {
1254
- project_id: projectId,
1255
- ...(limit !== undefined && { limit }),
1256
- ...(offset !== undefined && { offset }),
1257
- }, sessionId ? {
1258
- session_id: sessionId,
1259
- persona: null,
1260
- instance_id: ''
1261
- } : undefined);
1262
- }
1263
-
1264
- async acknowledgeRequest(requestId: string, sessionId?: string): Promise<ApiResponse<{
1265
- success: boolean;
1266
- }>> {
1267
- return this.proxy('acknowledge_request', { request_id: requestId }, sessionId ? {
1268
- session_id: sessionId,
1269
- persona: null,
1270
- instance_id: ''
1271
- } : undefined);
1272
- }
1273
-
1274
- async answerQuestion(requestId: string, answer: string, sessionId?: string): Promise<ApiResponse<{
1275
- success: boolean;
1276
- }>> {
1277
- return this.proxy('answer_question', {
1278
- request_id: requestId,
1279
- answer
1280
- }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1281
- }
1282
-
1283
- // ============================================================================
1284
- // Validation endpoints
1285
- // ============================================================================
1286
- async getTasksAwaitingValidation(projectId: string): Promise<ApiResponse<{
1287
- tasks: Array<{
1288
- id: string;
1289
- title: string;
1290
- completed_at?: string;
1291
- completed_by_session_id?: string;
1292
- }>;
1293
- }>> {
1294
- return this.proxy('get_tasks_awaiting_validation', { project_id: projectId });
1295
- }
1296
-
1297
- async claimValidation(taskId: string, sessionId?: string): Promise<ApiResponse<{
1298
- success: boolean;
1299
- task_id: string;
1300
- }>> {
1301
- return this.proxy('claim_validation', { task_id: taskId }, sessionId ? {
1302
- session_id: sessionId,
1303
- persona: null,
1304
- instance_id: ''
1305
- } : undefined);
1306
- }
1307
-
1308
- async validateTask(taskId: string, params: {
1309
- approved: boolean;
1310
- validation_notes?: string;
1311
- skip_pr_check?: boolean;
1312
- pr_checks_passing?: boolean;
1313
- }, sessionId?: string): Promise<ApiResponse<{
1314
- success: boolean;
1315
- approved: boolean;
1316
- task_id: string;
1317
- message?: string;
1318
- workflow?: string;
1319
- }>> {
1320
- return this.proxy('validate_task', {
1321
- task_id: taskId,
1322
- ...params
1323
- }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1324
- }
1325
-
1326
- // ============================================================================
1327
- // Fallback activity endpoints
1328
- // ============================================================================
1329
- async startFallbackActivity(projectId: string, activity: string, sessionId?: string): Promise<ApiResponse<{
1330
- success: boolean;
1331
- activity: string;
1332
- message: string;
1333
- git_workflow?: {
1334
- workflow: string;
1335
- base_branch: string;
1336
- worktree_recommended: boolean;
1337
- note: string;
1338
- };
1339
- worktree_setup?: {
1340
- message: string;
1341
- commands: string[];
1342
- worktree_path: string;
1343
- branch_name: string;
1344
- cleanup_command: string;
1345
- report_worktree: string;
1346
- };
1347
- next_step?: string;
1348
- }>> {
1349
- return this.proxy('start_fallback_activity', {
1350
- project_id: projectId,
1351
- activity
1352
- }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1353
- }
1354
-
1355
- async stopFallbackActivity(projectId: string, summary?: string, sessionId?: string): Promise<ApiResponse<{
1356
- success: boolean;
1357
- }>> {
1358
- return this.proxy('stop_fallback_activity', {
1359
- project_id: projectId,
1360
- summary
1361
- }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1362
- }
1363
-
1364
- async getActivityHistory(projectId: string, params?: {
1365
- activity_type?: string;
1366
- limit?: number;
1367
- }): Promise<ApiResponse<{
1368
- history: Array<{
1369
- id: string;
1370
- activity_type: string;
1371
- completed_at: string;
1372
- summary?: string;
1373
- }>;
1374
- latest_by_type: Record<string, unknown>;
1375
- count: number;
1376
- }>> {
1377
- return this.proxy('get_activity_history', {
1378
- project_id: projectId,
1379
- ...params
1380
- });
1381
- }
1382
-
1383
- async getActivitySchedules(projectId: string, params?: {
1384
- limit?: number;
1385
- offset?: number;
1386
- }): Promise<ApiResponse<{
1387
- schedules: Array<{
1388
- id: string;
1389
- activity_type: string;
1390
- schedule_type: string;
1391
- next_run_at?: string;
1392
- enabled: boolean;
1393
- }>;
1394
- total_count: number;
1395
- has_more: boolean;
1396
- }>> {
1397
- return this.proxy('get_activity_schedules', {
1398
- project_id: projectId,
1399
- ...params
1400
- });
1401
- }
1402
-
1403
- // ============================================================================
1404
- // Subtask endpoints
1405
- // ============================================================================
1406
- async addSubtask(parentTaskId: string, params: {
1407
- title: string;
1408
- description?: string;
1409
- priority?: number;
1410
- estimated_minutes?: number;
1411
- }, sessionId?: string): Promise<ApiResponse<{
1412
- success: boolean;
1413
- subtask_id: string;
1414
- parent_task_id: string;
1415
- }>> {
1416
- return this.proxy('add_subtask', {
1417
- parent_task_id: parentTaskId,
1418
- ...params
1419
- }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1420
- }
1421
-
1422
- async getSubtasks(parentTaskId: string, status?: string): Promise<ApiResponse<{
1423
- subtasks: Array<{
1424
- id: string;
1425
- title: string;
1426
- description?: string;
1427
- priority: number;
1428
- status: string;
1429
- progress_percentage?: number;
1430
- estimated_minutes?: number;
1431
- }>;
1432
- stats: {
1433
- total: number;
1434
- completed: number;
1435
- progress_percentage: number;
1436
- };
1437
- }>> {
1438
- return this.proxy('get_subtasks', {
1439
- parent_task_id: parentTaskId,
1440
- status
1441
- });
1442
- }
1443
-
1444
- // ============================================================================
1445
- // Activity feed endpoint
1446
- // ============================================================================
1447
- async getActivityFeed(projectId: string, params?: {
1448
- limit?: number;
1449
- since?: string;
1450
- types?: string[];
1451
- created_by?: string;
1452
- }): Promise<ApiResponse<{
1453
- activities: Array<{
1454
- type: string;
1455
- data: unknown;
1456
- timestamp: string;
1457
- }>;
1458
- }>> {
1459
- return this.proxy('get_activity_feed', {
1460
- project_id: projectId,
1461
- ...params
1462
- });
1463
- }
1464
-
1465
- // ============================================================================
1466
- // Deployment endpoints
1467
- // ============================================================================
1468
- async requestDeployment(projectId: string, params?: {
1469
- environment?: string;
1470
- version_bump?: string;
1471
- git_ref?: string;
1472
- notes?: string;
1473
- }, sessionId?: string): Promise<ApiResponse<{
1474
- success: boolean;
1475
- deployment_id: string;
1476
- status: string;
1477
- }>> {
1478
- return this.proxy('request_deployment', {
1479
- project_id: projectId,
1480
- ...params
1481
- }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1482
- }
1483
-
1484
- async checkDeploymentStatus(projectId: string): Promise<ApiResponse<{
1485
- active_deployment: boolean;
1486
- deployment?: {
1487
- id: string;
1488
- status: string;
1489
- environment: string;
1490
- git_ref?: string;
1491
- created_at: string;
1492
- };
1493
- }>> {
1494
- return this.proxy('check_deployment_status', { project_id: projectId });
1495
- }
1496
-
1497
- async claimDeploymentValidation(projectId: string, sessionId?: string): Promise<ApiResponse<{
1498
- success: boolean;
1499
- deployment_id: string;
1500
- message: string;
1501
- }>> {
1502
- return this.proxy('claim_deployment_validation', { project_id: projectId }, sessionId ? {
1503
- session_id: sessionId,
1504
- persona: null,
1505
- instance_id: ''
1506
- } : undefined);
1507
- }
1508
-
1509
- async reportValidation(projectId: string, params: {
1510
- build_passed: boolean;
1511
- tests_passed: boolean;
1512
- error_message?: string;
1513
- }): Promise<ApiResponse<{
1514
- success: boolean;
1515
- deployment_id: string;
1516
- status: string;
1517
- }>> {
1518
- return this.proxy('report_validation', {
1519
- project_id: projectId,
1520
- ...params
1521
- });
1522
- }
1523
-
1524
- async startDeployment(projectId: string, sessionId?: string): Promise<ApiResponse<{
1525
- success: boolean;
1526
- deployment_id: string;
1527
- instructions: string;
1528
- }>> {
1529
- return this.proxy('start_deployment', { project_id: projectId }, sessionId ? {
1530
- session_id: sessionId,
1531
- persona: null,
1532
- instance_id: ''
1533
- } : undefined);
1534
- }
1535
-
1536
- async completeDeployment(projectId: string, params: {
1537
- success: boolean;
1538
- summary?: string;
1539
- }): Promise<ApiResponse<{
1540
- success: boolean;
1541
- deployment_id: string;
1542
- status: string;
1543
- }>> {
1544
- return this.proxy('complete_deployment', {
1545
- project_id: projectId,
1546
- ...params
1547
- });
1548
- }
1549
-
1550
- async cancelDeployment(projectId: string, reason?: string): Promise<ApiResponse<{
1551
- success: boolean;
1552
- }>> {
1553
- return this.proxy('cancel_deployment', {
1554
- project_id: projectId,
1555
- reason
1556
- });
1557
- }
1558
-
1559
- // ============================================================================
1560
- // Task reference endpoints
1561
- // ============================================================================
1562
- async addTaskReference(taskId: string, url: string, label?: string): Promise<ApiResponse<{
1563
- success: boolean;
1564
- reference: { url: string; label?: string };
1565
- }>> {
1566
- return this.proxy('add_task_reference', {
1567
- task_id: taskId,
1568
- url,
1569
- label
1570
- });
1571
- }
1572
-
1573
- async removeTaskReference(taskId: string, url: string): Promise<ApiResponse<{
1574
- success: boolean;
1575
- }>> {
1576
- return this.proxy('remove_task_reference', {
1577
- task_id: taskId,
1578
- url
1579
- });
1580
- }
1581
-
1582
- // ============================================================================
1583
- // Knowledge base endpoint
1584
- // ============================================================================
1585
- async queryKnowledgeBase(projectId: string, params?: {
1586
- scope?: 'summary' | 'detailed';
1587
- categories?: string[];
1588
- limit?: number;
1589
- search_query?: string;
1590
- }): Promise<ApiResponse<{
1591
- findings?: Array<{ id: string; title: string; category: string; severity: string; description?: string }>;
1592
- decisions?: Array<{ id: string; title: string; description: string; rationale?: string }>;
1593
- completed_tasks?: Array<{ id: string; title: string; completed_at: string; summary?: string }>;
1594
- resolved_blockers?: Array<{ id: string; description: string; resolution_note?: string }>;
1595
- progress?: Array<{ id: string; summary: string; details?: string }>;
1596
- qa?: Array<{ id: string; question: string; answer: string }>;
1597
- }>> {
1598
- return this.proxy('query_knowledge_base', {
1599
- project_id: projectId,
1600
- ...params
1601
- });
1602
- }
1603
-
1604
- // ============================================================================
1605
- // Session sync endpoint
1606
- // ============================================================================
1607
- async syncSession(sessionId: string, params?: {
1608
- total_tokens?: number;
1609
- token_breakdown?: Record<string, unknown>;
1610
- model_usage?: Record<string, unknown>;
1611
- }): Promise<ApiResponse<{
1612
- success: boolean;
1613
- }>> {
1614
- return this.proxy('sync_session', {
1615
- session_id: sessionId,
1616
- ...params
1617
- });
1618
- }
1619
-
1620
- /**
1621
- * Report actual API token usage for accurate cost tracking.
1622
- * This records token usage to the session and attributes cost to the current task.
1623
- */
1624
- async reportTokenUsage(sessionId: string, params: {
1625
- input_tokens: number;
1626
- output_tokens: number;
1627
- model?: string; // Open-ended - any model name accepted
1628
- }): Promise<ApiResponse<{
1629
- success: boolean;
1630
- reported: {
1631
- session_id: string;
1632
- model: string;
1633
- input_tokens: number;
1634
- output_tokens: number;
1635
- total_tokens: number;
1636
- estimated_cost_usd: number;
1637
- };
1638
- task_attributed: boolean;
1639
- task_id?: string;
1640
- }>> {
1641
- return this.proxy('report_token_usage', {
1642
- session_id: sessionId,
1643
- ...params
1644
- });
1645
- }
1646
-
1647
- // ============================================================================
1648
- // Organization endpoints
1649
- // ============================================================================
1650
- async listOrganizations(): Promise<ApiResponse<{
1651
- organizations: Array<{
1652
- id: string;
1653
- name: string;
1654
- slug: string;
1655
- description?: string;
1656
- role: string;
1657
- }>;
1658
- }>> {
1659
- return this.proxy('list_organizations', {});
1660
- }
1661
-
1662
- async createOrganization(params: {
1663
- name: string;
1664
- slug?: string;
1665
- description?: string;
1666
- }): Promise<ApiResponse<{
1667
- success: boolean;
1668
- organization: { id: string; name: string; slug: string };
1669
- }>> {
1670
- return this.proxy('create_organization', params);
1671
- }
1672
-
1673
- async updateOrganization(organizationId: string, updates: {
1674
- name?: string;
1675
- description?: string;
1676
- logo_url?: string;
1677
- }): Promise<ApiResponse<{
1678
- success: boolean;
1679
- organization_id: string;
1680
- }>> {
1681
- return this.proxy('update_organization', {
1682
- organization_id: organizationId,
1683
- ...updates
1684
- });
1685
- }
1686
-
1687
- async deleteOrganization(organizationId: string): Promise<ApiResponse<{
1688
- success: boolean;
1689
- }>> {
1690
- return this.proxy('delete_organization', { organization_id: organizationId });
1691
- }
1692
-
1693
- async listOrgMembers(organizationId: string): Promise<ApiResponse<{
1694
- members: Array<{
1695
- user_id: string;
1696
- email: string;
1697
- role: string;
1698
- joined_at: string;
1699
- }>;
1700
- }>> {
1701
- return this.proxy('list_org_members', { organization_id: organizationId });
1702
- }
1703
-
1704
- async inviteMember(organizationId: string, email: string, role?: string): Promise<ApiResponse<{
1705
- success: boolean;
1706
- invite_id: string;
1707
- }>> {
1708
- return this.proxy('invite_member', {
1709
- organization_id: organizationId,
1710
- email,
1711
- role
1712
- });
1713
- }
1714
-
1715
- async updateMemberRole(organizationId: string, userId: string, role: string): Promise<ApiResponse<{
1716
- success: boolean;
1717
- }>> {
1718
- return this.proxy('update_member_role', {
1719
- organization_id: organizationId,
1720
- user_id: userId,
1721
- role
1722
- });
1723
- }
1724
-
1725
- async removeMember(organizationId: string, userId: string): Promise<ApiResponse<{
1726
- success: boolean;
1727
- }>> {
1728
- return this.proxy('remove_member', {
1729
- organization_id: organizationId,
1730
- user_id: userId
1731
- });
1732
- }
1733
-
1734
- async shareProjectWithOrg(projectId: string, organizationId: string, permission?: string): Promise<ApiResponse<{
1735
- success: boolean;
1736
- }>> {
1737
- return this.proxy('share_project_with_org', {
1738
- project_id: projectId,
1739
- organization_id: organizationId,
1740
- permission
1741
- });
1742
- }
1743
-
1744
- async unshareProject(projectId: string, organizationId: string): Promise<ApiResponse<{
1745
- success: boolean;
1746
- }>> {
1747
- return this.proxy('unshare_project', {
1748
- project_id: projectId,
1749
- organization_id: organizationId
1750
- });
1751
- }
1752
-
1753
- async leaveOrganization(organizationId: string): Promise<ApiResponse<{
1754
- success: boolean;
1755
- message: string;
1756
- }>> {
1757
- return this.proxy('leave_organization', { organization_id: organizationId });
1758
- }
1759
-
1760
- async updateProjectShare(projectId: string, organizationId: string, permission: string): Promise<ApiResponse<{
1761
- success: boolean;
1762
- share: { permission: string };
1763
- }>> {
1764
- return this.proxy('update_project_share', {
1765
- project_id: projectId,
1766
- organization_id: organizationId,
1767
- permission
1768
- });
1769
- }
1770
-
1771
- async listProjectShares(projectId: string): Promise<ApiResponse<{
1772
- shares: Array<{
1773
- id: string;
1774
- permission: string;
1775
- shared_at: string;
1776
- organization: { id: string; name: string; slug: string };
1777
- }>;
1778
- count: number;
1779
- }>> {
1780
- return this.proxy('list_project_shares', { project_id: projectId });
1781
- }
1782
-
1783
- // ============================================================================
1784
- // Body of work endpoints
1785
- // ============================================================================
1786
- async createBodyOfWork(projectId: string, params: {
1787
- title: string;
1788
- description?: string;
1789
- auto_deploy_on_completion?: boolean;
1790
- deploy_environment?: string;
1791
- deploy_version_bump?: string;
1792
- deploy_trigger?: string;
1793
- }): Promise<ApiResponse<{
1794
- success: boolean;
1795
- body_of_work_id: string;
1796
- }>> {
1797
- return this.proxy('create_body_of_work', {
1798
- project_id: projectId,
1799
- ...params
1800
- });
1801
- }
1802
-
1803
- async getBodyOfWork(bodyOfWorkId: string): Promise<ApiResponse<{
1804
- body_of_work: {
1805
- id: string;
1806
- title: string;
1807
- description?: string;
1808
- status: string;
1809
- auto_deploy_on_completion: boolean;
1810
- };
1811
- tasks: {
1812
- pre: Array<{ id: string; title: string; status: string }>;
1813
- core: Array<{ id: string; title: string; status: string }>;
1814
- post: Array<{ id: string; title: string; status: string }>;
1815
- };
1816
- }>> {
1817
- return this.proxy('get_body_of_work', { body_of_work_id: bodyOfWorkId });
1818
- }
1819
-
1820
- async getBodiesOfWork(projectId: string, params?: {
1821
- status?: string;
1822
- limit?: number;
1823
- offset?: number;
1824
- search_query?: string;
1825
- }): Promise<ApiResponse<{
1826
- bodies_of_work: Array<{
1827
- id: string;
1828
- title: string;
1829
- description?: string;
1830
- status: string;
1831
- task_counts: { pre: number; core: number; post: number };
1832
- }>;
1833
- }>> {
1834
- return this.proxy('get_bodies_of_work', {
1835
- project_id: projectId,
1836
- ...params
1837
- });
1838
- }
1839
-
1840
- async updateBodyOfWork(bodyOfWorkId: string, updates: {
1841
- title?: string;
1842
- description?: string;
1843
- auto_deploy_on_completion?: boolean;
1844
- deploy_environment?: string;
1845
- deploy_version_bump?: string;
1846
- deploy_trigger?: string;
1847
- }): Promise<ApiResponse<{
1848
- success: boolean;
1849
- }>> {
1850
- return this.proxy('update_body_of_work', {
1851
- body_of_work_id: bodyOfWorkId,
1852
- ...updates
1853
- });
1854
- }
1855
-
1856
- async deleteBodyOfWork(bodyOfWorkId: string): Promise<ApiResponse<{
1857
- success: boolean;
1858
- }>> {
1859
- return this.proxy('delete_body_of_work', { body_of_work_id: bodyOfWorkId });
1860
- }
1861
-
1862
- async addTaskToBodyOfWork(bodyOfWorkId: string, taskId: string, phase?: string, orderIndex?: number): Promise<ApiResponse<{
1863
- success: boolean;
1864
- }>> {
1865
- return this.proxy('add_task_to_body_of_work', {
1866
- body_of_work_id: bodyOfWorkId,
1867
- task_id: taskId,
1868
- phase,
1869
- order_index: orderIndex
1870
- });
1871
- }
1872
-
1873
- async removeTaskFromBodyOfWork(taskId: string): Promise<ApiResponse<{
1874
- success: boolean;
1875
- }>> {
1876
- return this.proxy('remove_task_from_body_of_work', { task_id: taskId });
1877
- }
1878
-
1879
- async activateBodyOfWork(bodyOfWorkId: string): Promise<ApiResponse<{
1880
- success: boolean;
1881
- status: string;
1882
- }>> {
1883
- return this.proxy('activate_body_of_work', { body_of_work_id: bodyOfWorkId });
1884
- }
1885
-
1886
- async addTaskDependency(bodyOfWorkId: string, taskId: string, dependsOnTaskId: string): Promise<ApiResponse<{
1887
- success: boolean;
1888
- }>> {
1889
- return this.proxy('add_task_dependency', {
1890
- body_of_work_id: bodyOfWorkId,
1891
- task_id: taskId,
1892
- depends_on_task_id: dependsOnTaskId
1893
- });
1894
- }
1895
-
1896
- async removeTaskDependency(taskId: string, dependsOnTaskId: string): Promise<ApiResponse<{
1897
- success: boolean;
1898
- }>> {
1899
- return this.proxy('remove_task_dependency', {
1900
- task_id: taskId,
1901
- depends_on_task_id: dependsOnTaskId
1902
- });
1903
- }
1904
-
1905
- async getTaskDependencies(params: {
1906
- body_of_work_id?: string;
1907
- task_id?: string;
1908
- }): Promise<ApiResponse<{
1909
- dependencies: Array<{
1910
- id: string;
1911
- task_id: string;
1912
- depends_on_task_id: string;
1913
- created_at: string;
1914
- }>;
1915
- }>> {
1916
- return this.proxy('get_task_dependencies', params);
1917
- }
1918
-
1919
- async getNextBodyOfWorkTask(bodyOfWorkId: string): Promise<ApiResponse<{
1920
- task?: {
1921
- id: string;
1922
- title: string;
1923
- priority: number;
1924
- phase: string;
1925
- } | null;
1926
- message?: string;
1927
- }>> {
1928
- return this.proxy('get_next_body_of_work_task', { body_of_work_id: bodyOfWorkId });
1929
- }
1930
-
1931
- // ============================================================================
1932
- // Git issues endpoints
1933
- // ============================================================================
1934
- async addGitIssue(projectId: string, params: {
1935
- issue_type: string;
1936
- branch: string;
1937
- target_branch?: string;
1938
- pr_url?: string;
1939
- conflicting_files?: string[];
1940
- error_message?: string;
1941
- task_id?: string;
1942
- }, sessionId?: string): Promise<ApiResponse<{
1943
- success: boolean;
1944
- git_issue_id: string;
1945
- }>> {
1946
- return this.proxy('add_git_issue', {
1947
- project_id: projectId,
1948
- ...params
1949
- }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1950
- }
1951
-
1952
- async resolveGitIssue(gitIssueId: string, params?: {
1953
- resolution_note?: string;
1954
- auto_resolved?: boolean;
1955
- }, sessionId?: string): Promise<ApiResponse<{
1956
- success: boolean;
1957
- git_issue_id: string;
1958
- }>> {
1959
- return this.proxy('resolve_git_issue', {
1960
- git_issue_id: gitIssueId,
1961
- ...params
1962
- }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1963
- }
1964
-
1965
- async getGitIssues(projectId: string, params?: {
1966
- status?: string;
1967
- issue_type?: string;
1968
- branch?: string;
1969
- limit?: number;
1970
- offset?: number;
1971
- }): Promise<ApiResponse<{
1972
- git_issues: Array<{
1973
- id: string;
1974
- issue_type: string;
1975
- branch: string;
1976
- target_branch?: string;
1977
- pr_url?: string;
1978
- conflicting_files?: string[];
1979
- error_message?: string;
1980
- status: string;
1981
- created_at: string;
1982
- resolved_at?: string;
1983
- }>;
1984
- }>> {
1985
- return this.proxy('get_git_issues', {
1986
- project_id: projectId,
1987
- ...params
1988
- });
1989
- }
1990
-
1991
- async deleteGitIssue(gitIssueId: string): Promise<ApiResponse<{
1992
- success: boolean;
1993
- }>> {
1994
- return this.proxy('delete_git_issue', { git_issue_id: gitIssueId });
1995
- }
1996
-
1997
- // ============================================================================
1998
- // Cost tracking endpoints
1999
- // ============================================================================
2000
- async getCostSummary(projectId: string, params?: {
2001
- period?: 'daily' | 'weekly' | 'monthly';
2002
- limit?: number;
2003
- }): Promise<ApiResponse<{
2004
- summaries: Array<{
2005
- date: string;
2006
- total_cost: number;
2007
- input_tokens: number;
2008
- output_tokens: number;
2009
- }>;
2010
- }>> {
2011
- return this.proxy('get_cost_summary', {
2012
- project_id: projectId,
2013
- ...params
2014
- });
2015
- }
2016
-
2017
- async getCostAlerts(): Promise<ApiResponse<{
2018
- alerts: Array<{
2019
- id: string;
2020
- threshold_amount: number;
2021
- threshold_period: string;
2022
- alert_type: string;
2023
- enabled: boolean;
2024
- }>;
2025
- }>> {
2026
- return this.proxy('get_cost_alerts', {});
2027
- }
2028
-
2029
- async addCostAlert(params: {
2030
- threshold_amount: number;
2031
- threshold_period: 'daily' | 'weekly' | 'monthly';
2032
- alert_type?: 'warning' | 'critical';
2033
- project_id?: string;
2034
- }): Promise<ApiResponse<{
2035
- success: boolean;
2036
- alert_id: string;
2037
- }>> {
2038
- return this.proxy('add_cost_alert', params);
2039
- }
2040
-
2041
- async updateCostAlert(alertId: string, updates: {
2042
- threshold_amount?: number;
2043
- threshold_period?: string;
2044
- alert_type?: string;
2045
- enabled?: boolean;
2046
- }): Promise<ApiResponse<{
2047
- success: boolean;
2048
- }>> {
2049
- return this.proxy('update_cost_alert', {
2050
- alert_id: alertId,
2051
- ...updates
2052
- });
2053
- }
2054
-
2055
- async deleteCostAlert(alertId: string): Promise<ApiResponse<{
2056
- success: boolean;
2057
- }>> {
2058
- return this.proxy('delete_cost_alert', { alert_id: alertId });
2059
- }
2060
-
2061
- async getTaskCosts(projectId: string, limit?: number): Promise<ApiResponse<{
2062
- task_costs: Array<{
2063
- task_id: string;
2064
- title: string;
2065
- total_cost: number;
2066
- input_tokens: number;
2067
- output_tokens: number;
2068
- }>;
2069
- }>> {
2070
- return this.proxy('get_task_costs', {
2071
- project_id: projectId,
2072
- limit
2073
- });
2074
- }
2075
-
2076
- async getBodyOfWorkCosts(params: {
2077
- body_of_work_id?: string;
2078
- project_id?: string;
2079
- }): Promise<ApiResponse<{
2080
- bodies_of_work: Array<{
2081
- body_of_work_id: string;
2082
- title: string;
2083
- project_id: string;
2084
- status: string;
2085
- task_count: number;
2086
- total_cost_usd: number;
2087
- total_tokens: number;
2088
- pre_phase_cost_usd: number;
2089
- core_phase_cost_usd: number;
2090
- post_phase_cost_usd: number;
2091
- model_breakdown: Record<string, { input: number; output: number }>;
2092
- }>;
2093
- count: number;
2094
- totals: {
2095
- total_cost_usd: number;
2096
- total_tokens: number;
2097
- total_tasks: number;
2098
- };
2099
- }>> {
2100
- return this.proxy('get_body_of_work_costs', params);
2101
- }
2102
-
2103
- async getSprintCosts(params: {
2104
- sprint_id?: string;
2105
- project_id?: string;
2106
- }): Promise<ApiResponse<{
2107
- sprints: Array<{
2108
- sprint_id: string;
2109
- title: string;
2110
- project_id: string;
2111
- status: string;
2112
- sprint_number: number;
2113
- task_count: number;
2114
- total_cost_usd: number;
2115
- total_tokens: number;
2116
- cost_per_story_point: number | null;
2117
- committed_points: number;
2118
- velocity_points: number;
2119
- model_breakdown: Record<string, { input: number; output: number }>;
2120
- }>;
2121
- count: number;
2122
- totals: {
2123
- total_cost_usd: number;
2124
- total_tokens: number;
2125
- total_tasks: number;
2126
- total_velocity_points: number;
2127
- avg_cost_per_point: number | null;
2128
- };
2129
- }>> {
2130
- return this.proxy('get_sprint_costs', params);
2131
- }
2132
-
2133
- async getTokenUsage(): Promise<ApiResponse<{
2134
- session_tokens: number;
2135
- estimated_cost: number;
2136
- }>> {
2137
- return this.proxy('get_token_usage', {});
2138
- }
2139
-
2140
- // ============================================================================
2141
- // Batch operation endpoints
2142
- // ============================================================================
2143
- async batchUpdateTasks(updates: Array<{
2144
- task_id: string;
2145
- status?: string;
2146
- priority?: number;
2147
- progress_percentage?: number;
2148
- progress_note?: string;
2149
- }>): Promise<ApiResponse<{
2150
- success: boolean;
2151
- updated_count: number;
2152
- }>> {
2153
- return this.proxy('batch_update_tasks', { updates });
2154
- }
2155
-
2156
- async batchCompleteTasks(completions: Array<{
2157
- task_id: string;
2158
- summary?: string;
2159
- }>): Promise<ApiResponse<{
2160
- success: boolean;
2161
- completed_count: number;
2162
- next_task?: { id: string; title: string } | null;
2163
- }>> {
2164
- return this.proxy('batch_complete_tasks', { completions });
2165
- }
2166
-
2167
- // ============================================================================
2168
- // Deployment requirements and scheduling endpoints
2169
- // ============================================================================
2170
- async addDeploymentRequirement(projectId: string, params: {
2171
- type: string;
2172
- title: string;
2173
- description?: string;
2174
- file_path?: string;
2175
- stage?: string;
2176
- blocking?: boolean;
2177
- recurring?: boolean;
2178
- }): Promise<ApiResponse<{
2179
- success: boolean;
2180
- requirement_id: string;
2181
- }>> {
2182
- return this.proxy('add_deployment_requirement', {
2183
- project_id: projectId,
2184
- ...params
2185
- });
2186
- }
2187
-
2188
- async getDeploymentRequirements(projectId: string, params?: {
2189
- status?: string;
2190
- stage?: string;
2191
- limit?: number;
2192
- offset?: number;
2193
- }): Promise<ApiResponse<{
2194
- requirements: Array<{
2195
- id: string;
2196
- type: string;
2197
- title: string;
2198
- description?: string;
2199
- status: string;
2200
- stage: string;
2201
- }>;
2202
- total_count: number;
2203
- has_more: boolean;
2204
- }>> {
2205
- return this.proxy('get_deployment_requirements', {
2206
- project_id: projectId,
2207
- ...params
2208
- });
2209
- }
2210
-
2211
- async getDeploymentRequirementsStats(projectId: string): Promise<ApiResponse<{
2212
- total: number;
2213
- by_status: Record<string, number>;
2214
- by_stage: Record<string, number>;
2215
- by_type: Record<string, number>;
2216
- }>> {
2217
- return this.proxy('get_deployment_requirements_stats', {
2218
- project_id: projectId
2219
- });
2220
- }
2221
-
2222
- async completeDeploymentRequirement(requirementId: string): Promise<ApiResponse<{
2223
- success: boolean;
2224
- }>> {
2225
- return this.proxy('complete_deployment_requirement', { requirement_id: requirementId });
2226
- }
2227
-
2228
- async reorderDeploymentRequirements(projectId: string, params: {
2229
- stage: string;
2230
- requirement_ids: string[];
2231
- }): Promise<ApiResponse<{ success: boolean; reordered: number }>> {
2232
- return this.proxy('reorder_deployment_requirements', {
2233
- project_id: projectId,
2234
- ...params,
2235
- });
2236
- }
2237
-
2238
- async scheduleDeployment(projectId: string, params: {
2239
- scheduled_at: string;
2240
- schedule_type?: string;
2241
- environment?: string;
2242
- version_bump?: string;
2243
- auto_trigger?: boolean;
2244
- hours_interval?: number;
2245
- notes?: string;
2246
- git_ref?: string;
2247
- }): Promise<ApiResponse<{
2248
- success: boolean;
2249
- schedule_id: string;
2250
- }>> {
2251
- return this.proxy('schedule_deployment', {
2252
- project_id: projectId,
2253
- ...params
2254
- });
2255
- }
2256
-
2257
- async getScheduledDeployments(projectId: string, params?: {
2258
- includeDisabled?: boolean;
2259
- limit?: number;
2260
- offset?: number;
2261
- }): Promise<ApiResponse<{
2262
- schedules: Array<{
2263
- id: string;
2264
- scheduled_at: string;
2265
- schedule_type: string;
2266
- hours_interval: number;
2267
- environment: string;
2268
- version_bump: string;
2269
- auto_trigger: boolean;
2270
- enabled: boolean;
2271
- git_ref?: string;
2272
- notes?: string;
2273
- }>;
2274
- total_count: number;
2275
- has_more: boolean;
2276
- }>> {
2277
- return this.proxy('get_scheduled_deployments', {
2278
- project_id: projectId,
2279
- include_disabled: params?.includeDisabled,
2280
- limit: params?.limit,
2281
- offset: params?.offset
2282
- });
2283
- }
2284
-
2285
- async updateScheduledDeployment(scheduleId: string, updates: {
2286
- scheduled_at?: string;
2287
- schedule_type?: string;
2288
- hours_interval?: number;
2289
- environment?: string;
2290
- version_bump?: string;
2291
- auto_trigger?: boolean;
2292
- enabled?: boolean;
2293
- git_ref?: string;
2294
- notes?: string;
2295
- }): Promise<ApiResponse<{
2296
- success: boolean;
2297
- schedule_id: string;
2298
- }>> {
2299
- return this.proxy('update_scheduled_deployment', {
2300
- schedule_id: scheduleId,
2301
- ...updates
2302
- });
2303
- }
2304
-
2305
- async deleteScheduledDeployment(scheduleId: string): Promise<ApiResponse<{
2306
- success: boolean;
2307
- }>> {
2308
- return this.proxy('delete_scheduled_deployment', { schedule_id: scheduleId });
2309
- }
2310
-
2311
- async triggerScheduledDeployment(scheduleId: string, sessionId?: string): Promise<ApiResponse<{
2312
- success: boolean;
2313
- deployment_id?: string;
2314
- schedule_id: string;
2315
- schedule_type: string;
2316
- next_scheduled_at?: string;
2317
- message?: string;
2318
- }>> {
2319
- return this.proxy('trigger_scheduled_deployment', { schedule_id: scheduleId }, sessionId ? {
2320
- session_id: sessionId,
2321
- persona: null,
2322
- instance_id: ''
2323
- } : undefined);
2324
- }
2325
-
2326
- async checkDueDeployments(projectId: string): Promise<ApiResponse<{
2327
- due_schedules: Array<{
2328
- id: string;
2329
- scheduled_at: string;
2330
- environment: string;
2331
- version_bump: string;
2332
- schedule_type: string;
2333
- }>;
2334
- count: number;
2335
- }>> {
2336
- return this.proxy('check_due_deployments', { project_id: projectId });
2337
- }
2338
-
2339
- // ============================================================================
2340
- // Project README endpoint
2341
- // ============================================================================
2342
- async updateProjectReadme(projectId: string, readmeContent: string): Promise<ApiResponse<{
2343
- success: boolean;
2344
- project_id: string;
2345
- }>> {
2346
- return this.proxy('update_project_readme', {
2347
- project_id: projectId,
2348
- readme_content: readmeContent
2349
- });
2350
- }
2351
-
2352
- async getProjectSummary(projectId: string): Promise<ApiResponse<{
2353
- project: {
2354
- id: string;
2355
- name: string;
2356
- description?: string;
2357
- goal?: string;
2358
- status: string;
2359
- git_url?: string;
2360
- tech_stack?: string[];
2361
- git_workflow?: string;
2362
- };
2363
- tasks: {
2364
- total: number;
2365
- by_status: Record<string, number>;
2366
- };
2367
- blockers: {
2368
- total: number;
2369
- open: number;
2370
- resolved: number;
2371
- };
2372
- findings: {
2373
- total: number;
2374
- by_severity: Record<string, number>;
2375
- by_status: Record<string, number>;
2376
- };
2377
- agents: {
2378
- active_count: number;
2379
- total_tokens_this_session: number;
2380
- total_tool_calls: number;
2381
- };
2382
- activity: {
2383
- tasks_completed_today: number;
2384
- progress_entries_today: number;
2385
- };
2386
- deployment: {
2387
- active: boolean;
2388
- status: string | null;
2389
- last_deployment_at: string | null;
2390
- };
2391
- planning: {
2392
- active_sprints: number;
2393
- active_bodies_of_work: number;
2394
- pending_deployment_requirements: number;
2395
- };
2396
- }>> {
2397
- return this.proxy('get_project_summary', { project_id: projectId });
2398
- }
2399
-
2400
- // ============================================================================
2401
- // Help Topics (database-backed)
2402
- // ============================================================================
2403
- async getHelpTopic(slug: string): Promise<ApiResponse<{
2404
- slug: string;
2405
- title: string;
2406
- content: string;
2407
- } | null>> {
2408
- return this.proxy('get_help_topic', { slug });
2409
- }
2410
-
2411
- async getHelpTopics(): Promise<ApiResponse<Array<{
2412
- slug: string;
2413
- title: string;
2414
- }>>> {
2415
- return this.proxy('get_help_topics', {});
2416
- }
2417
-
2418
- // ============================================================================
2419
- // File Checkout endpoints (multi-agent coordination)
2420
- // ============================================================================
2421
- async checkoutFile(projectId: string, filePath: string, reason?: string, sessionId?: string): Promise<ApiResponse<{
2422
- success: boolean;
2423
- checkout_id: string;
2424
- file_path: string;
2425
- already_checked_out?: boolean;
2426
- message: string;
2427
- }>> {
2428
- return this.proxy('checkout_file', {
2429
- project_id: projectId,
2430
- file_path: filePath,
2431
- reason
2432
- }, sessionId ? {
2433
- session_id: sessionId,
2434
- persona: null,
2435
- instance_id: ''
2436
- } : undefined);
2437
- }
2438
-
2439
- async checkinFile(params: {
2440
- checkout_id?: string;
2441
- project_id?: string;
2442
- file_path?: string;
2443
- summary?: string;
2444
- }, sessionId?: string): Promise<ApiResponse<{
2445
- success: boolean;
2446
- checkout_id: string;
2447
- message: string;
2448
- }>> {
2449
- return this.proxy('checkin_file', params, sessionId ? {
2450
- session_id: sessionId,
2451
- persona: null,
2452
- instance_id: ''
2453
- } : undefined);
2454
- }
2455
-
2456
- async getFileCheckouts(projectId: string, options?: {
2457
- status?: string;
2458
- file_path?: string;
2459
- limit?: number;
2460
- offset?: number;
2461
- }): Promise<ApiResponse<{
2462
- checkouts: Array<{
2463
- id: string;
2464
- file_path: string;
2465
- status: string;
2466
- checked_out_at: string;
2467
- checkout_reason?: string;
2468
- checked_in_at?: string;
2469
- checkin_summary?: string;
2470
- checked_out_by?: string;
2471
- }>;
2472
- }>> {
2473
- return this.proxy('get_file_checkouts', {
2474
- project_id: projectId,
2475
- ...options
2476
- });
2477
- }
2478
-
2479
- async abandonCheckout(params: {
2480
- checkout_id?: string;
2481
- project_id?: string;
2482
- file_path?: string;
2483
- }): Promise<ApiResponse<{
2484
- success: boolean;
2485
- checkout_id: string;
2486
- message: string;
2487
- }>> {
2488
- return this.proxy('abandon_checkout', params);
2489
- }
2490
-
2491
- async getFileCheckoutsStats(projectId: string): Promise<ApiResponse<{
2492
- total: number;
2493
- by_status: Record<string, number>;
2494
- }>> {
2495
- return this.proxy('get_file_checkouts_stats', { project_id: projectId });
2496
- }
2497
-
2498
- // ============================================================================
2499
- // Worktree Management
2500
- // ============================================================================
2501
-
2502
- async getStaleWorktrees(projectId: string, params?: {
2503
- hostname?: string;
2504
- limit?: number;
2505
- offset?: number;
2506
- }): Promise<ApiResponse<{
2507
- project_id: string;
2508
- project_name: string;
2509
- hostname_filter: string | null;
2510
- stale_worktrees: Array<{
2511
- task_id: string;
2512
- task_title: string;
2513
- worktree_path: string;
2514
- worktree_hostname: string | null;
2515
- git_branch: string | null;
2516
- status: string;
2517
- completed_at: string | null;
2518
- updated_at: string;
2519
- pr_url: string | null;
2520
- stale_reason: 'task_finished' | 'potentially_abandoned';
2521
- can_cleanup_locally: boolean;
2522
- }>;
2523
- count: number;
2524
- local_count: number;
2525
- remote_count: number;
2526
- total_count: number;
2527
- has_more: boolean;
2528
- cleanup_instructions: string[] | null;
2529
- remote_worktree_note: string | null;
2530
- }>> {
2531
- const queryParams = new URLSearchParams({ project_id: projectId });
2532
- if (params?.hostname !== undefined) queryParams.set('hostname', params.hostname);
2533
- if (params?.limit !== undefined) queryParams.set('limit', String(params.limit));
2534
- if (params?.offset !== undefined) queryParams.set('offset', String(params.offset));
2535
- return this.request('GET', `/api/mcp/worktrees/stale?${queryParams.toString()}`);
2536
- }
2537
-
2538
- async clearWorktreePath(taskId: string): Promise<ApiResponse<{
2539
- success: boolean;
2540
- task_id: string;
2541
- }>> {
2542
- return this.request('PATCH', `/api/mcp/tasks/${taskId}`, { worktree_path: null });
2543
- }
2544
-
2545
- // ============================================================================
2546
- // Connector endpoints
2547
- // ============================================================================
2548
-
2549
- async getConnectors(projectId: string, params?: {
2550
- type?: string;
2551
- status?: string;
2552
- limit?: number;
2553
- offset?: number;
2554
- }): Promise<ApiResponse<{
2555
- connectors: Array<{
2556
- id: string;
2557
- name: string;
2558
- type: string;
2559
- description?: string;
2560
- status: string;
2561
- events: Record<string, boolean>;
2562
- events_sent: number;
2563
- last_triggered_at?: string;
2564
- last_error?: string;
2565
- last_error_at?: string;
2566
- created_at: string;
2567
- }>;
2568
- total_count: number;
2569
- has_more: boolean;
2570
- }>> {
2571
- return this.proxy('get_connectors', {
2572
- project_id: projectId,
2573
- ...params
2574
- });
2575
- }
2576
-
2577
- async getConnector(connectorId: string): Promise<ApiResponse<{
2578
- connector: {
2579
- id: string;
2580
- name: string;
2581
- type: string;
2582
- description?: string;
2583
- config: Record<string, unknown>;
2584
- events: Record<string, boolean>;
2585
- status: string;
2586
- events_sent: number;
2587
- last_triggered_at?: string;
2588
- last_error?: string;
2589
- last_error_at?: string;
2590
- created_at: string;
2591
- };
2592
- }>> {
2593
- return this.proxy('get_connector', { connector_id: connectorId });
2594
- }
2595
-
2596
- async addConnector(projectId: string, params: {
2597
- name: string;
2598
- type: string;
2599
- description?: string;
2600
- config?: Record<string, unknown>;
2601
- events?: Record<string, boolean>;
2602
- }): Promise<ApiResponse<{
2603
- success: boolean;
2604
- connector_id: string;
2605
- }>> {
2606
- return this.proxy('add_connector', {
2607
- project_id: projectId,
2608
- ...params
2609
- });
2610
- }
2611
-
2612
- async updateConnector(connectorId: string, updates: {
2613
- name?: string;
2614
- description?: string;
2615
- config?: Record<string, unknown>;
2616
- events?: Record<string, boolean>;
2617
- status?: string;
2618
- }): Promise<ApiResponse<{
2619
- success: boolean;
2620
- connector_id: string;
2621
- }>> {
2622
- return this.proxy('update_connector', {
2623
- connector_id: connectorId,
2624
- ...updates
2625
- });
2626
- }
2627
-
2628
- async deleteConnector(connectorId: string): Promise<ApiResponse<{
2629
- success: boolean;
2630
- }>> {
2631
- return this.proxy('delete_connector', { connector_id: connectorId });
2632
- }
2633
-
2634
- async testConnector(connectorId: string): Promise<ApiResponse<{
2635
- success: boolean;
2636
- event_id: string;
2637
- status?: number;
2638
- error?: string;
2639
- }>> {
2640
- return this.proxy('test_connector', { connector_id: connectorId });
2641
- }
2642
-
2643
- async getConnectorEvents(params: {
2644
- connector_id?: string;
2645
- project_id?: string;
2646
- status?: string;
2647
- limit?: number;
2648
- offset?: number;
2649
- }): Promise<ApiResponse<{
2650
- events: Array<{
2651
- id: string;
2652
- connector_id: string;
2653
- event_type: string;
2654
- status: string;
2655
- response_status?: number;
2656
- error_message?: string;
2657
- attempts: number;
2658
- created_at: string;
2659
- sent_at?: string;
2660
- }>;
2661
- total_count: number;
2662
- has_more: boolean;
2663
- }>> {
2664
- return this.proxy('get_connector_events', params);
2665
- }
2666
-
2667
- // ============================================================================
2668
- // Agent onboarding endpoints
2669
- // ============================================================================
2670
-
2671
- /**
2672
- * Confirm that agent setup is complete for a project.
2673
- * This marks the agent type as onboarded so future sessions don't receive setup instructions.
2674
- */
2675
- async confirmAgentSetup(projectId: string, agentType: string): Promise<ApiResponse<{
2676
- success: boolean;
2677
- project_id: string;
2678
- agent_type: string;
2679
- }>> {
2680
- return this.proxy('confirm_agent_setup', {
2681
- project_id: projectId,
2682
- agent_type: agentType
2683
- });
2684
- }
2685
-
2686
- async sendProjectMessage(projectId: string, message: string, sessionId?: string): Promise<ApiResponse<{
2687
- success: boolean;
2688
- message_id: string;
2689
- sent_at: string;
2690
- }>> {
2691
- return this.proxy('send_project_message', { project_id: projectId, message }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
2692
- }
2693
-
2694
- async getProjectMessages(projectId: string, limit?: number): Promise<ApiResponse<{
2695
- messages: Array<{
2696
- id: string;
2697
- sender_type: string;
2698
- sender_name: string | null;
2699
- content: string;
2700
- created_at: string;
2701
- }>;
2702
- count: number;
2703
- }>> {
2704
- return this.proxy('get_project_messages', {
2705
- project_id: projectId,
2706
- ...(limit !== undefined && { limit }),
2707
- });
2708
- }
2709
- }
2710
-
2711
- // Singleton instance
2712
- let apiClient: VibescopeApiClient | null = null;
2713
-
2714
- export function getApiClient(): VibescopeApiClient {
2715
- if (!apiClient) {
2716
- const apiKey = process.env.VIBESCOPE_API_KEY;
2717
- if (!apiKey) {
2718
- throw new Error('VIBESCOPE_API_KEY environment variable is required');
2719
- }
2720
- apiClient = new VibescopeApiClient({ apiKey });
2721
- }
2722
- return apiClient;
2723
- }
2724
-
2725
- export function initApiClient(config: ApiClientConfig): VibescopeApiClient {
2726
- apiClient = new VibescopeApiClient(config);
2727
- return apiClient;
2728
- }
1
+ /**
2
+ * Vibescope API Client
3
+ *
4
+ * HTTP client for communicating with the Vibescope API.
5
+ * All database operations are handled server-side through these endpoints.
6
+ */
7
+
8
+ import crypto from 'crypto';
9
+
10
+ const DEFAULT_API_URL = 'https://vibescope.dev';
11
+
12
+ // Stable instance ID for this process — persists across start_work_session calls
13
+ // so the API can recognise reconnections after context clears
14
+ const PROCESS_INSTANCE_ID = crypto.randomUUID();
15
+
16
+ // Retry configuration defaults
17
+ const DEFAULT_RETRY_STATUS_CODES = [429, 503, 504];
18
+ const DEFAULT_MAX_RETRIES = 3;
19
+ const DEFAULT_BASE_DELAY_MS = 1000;
20
+ const DEFAULT_MAX_DELAY_MS = 30000;
21
+
22
+ interface RetryConfig {
23
+ maxRetries?: number;
24
+ baseDelayMs?: number;
25
+ maxDelayMs?: number;
26
+ retryStatusCodes?: number[];
27
+ }
28
+
29
+ interface ApiClientConfig {
30
+ apiKey: string;
31
+ baseUrl?: string;
32
+ retry?: RetryConfig;
33
+ }
34
+
35
+ interface ApiResponse<T> {
36
+ ok: boolean;
37
+ status: number;
38
+ data?: T;
39
+ error?: string;
40
+ }
41
+
42
+ /**
43
+ * Calculate delay for exponential backoff with jitter
44
+ */
45
+ function calculateBackoffDelay(
46
+ attempt: number,
47
+ baseDelayMs: number,
48
+ maxDelayMs: number,
49
+ retryAfter?: number
50
+ ): number {
51
+ // If Retry-After header is present, use it (in seconds)
52
+ if (retryAfter !== undefined && retryAfter > 0) {
53
+ return Math.min(retryAfter * 1000, maxDelayMs);
54
+ }
55
+ // Exponential backoff: base * 2^attempt with jitter
56
+ const exponentialDelay = baseDelayMs * Math.pow(2, attempt);
57
+ const jitter = Math.random() * 0.3 * exponentialDelay; // 0-30% jitter
58
+ return Math.min(exponentialDelay + jitter, maxDelayMs);
59
+ }
60
+
61
+ /**
62
+ * Sleep for a specified duration
63
+ */
64
+ function sleep(ms: number): Promise<void> {
65
+ return new Promise(resolve => setTimeout(resolve, ms));
66
+ }
67
+
68
+ export class VibescopeApiClient {
69
+ private apiKey: string;
70
+ private baseUrl: string;
71
+ private retryConfig: Required<RetryConfig>;
72
+
73
+ constructor(config: ApiClientConfig) {
74
+ this.apiKey = config.apiKey;
75
+ this.baseUrl = config.baseUrl || process.env.VIBESCOPE_API_URL || DEFAULT_API_URL;
76
+ this.retryConfig = {
77
+ maxRetries: config.retry?.maxRetries ?? DEFAULT_MAX_RETRIES,
78
+ baseDelayMs: config.retry?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS,
79
+ maxDelayMs: config.retry?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS,
80
+ retryStatusCodes: config.retry?.retryStatusCodes ?? DEFAULT_RETRY_STATUS_CODES,
81
+ };
82
+ }
83
+
84
+ private async request<T>(method: string, path: string, body?: unknown, options?: { timeoutMs?: number }): Promise<ApiResponse<T>> {
85
+ const url = `${this.baseUrl}${path}`;
86
+ const { maxRetries, baseDelayMs, maxDelayMs, retryStatusCodes } = this.retryConfig;
87
+ let lastError: Error | null = null;
88
+ let lastResponse: Response | null = null;
89
+
90
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
91
+ let timeoutId: ReturnType<typeof setTimeout> | undefined;
92
+ try {
93
+ const fetchOptions: RequestInit = {
94
+ method,
95
+ headers: {
96
+ 'Content-Type': 'application/json',
97
+ 'X-API-Key': this.apiKey
98
+ },
99
+ body: body ? JSON.stringify(body) : undefined,
100
+ };
101
+
102
+ if (options?.timeoutMs) {
103
+ const controller = new AbortController();
104
+ timeoutId = setTimeout(() => controller.abort(), options.timeoutMs);
105
+ fetchOptions.signal = controller.signal;
106
+ }
107
+
108
+ const response = await fetch(url, fetchOptions);
109
+ if (timeoutId) clearTimeout(timeoutId);
110
+
111
+ // Check if we should retry this status code
112
+ if (retryStatusCodes.includes(response.status) && attempt < maxRetries) {
113
+ lastResponse = response;
114
+ // Parse Retry-After header if present (can be seconds or HTTP-date)
115
+ const retryAfterHeader = response.headers.get('Retry-After');
116
+ let retryAfter: number | undefined;
117
+ if (retryAfterHeader) {
118
+ const seconds = parseInt(retryAfterHeader, 10);
119
+ if (!isNaN(seconds)) {
120
+ retryAfter = seconds;
121
+ }
122
+ }
123
+ const delay = calculateBackoffDelay(attempt, baseDelayMs, maxDelayMs, retryAfter);
124
+ await sleep(delay);
125
+ continue;
126
+ }
127
+
128
+ const data = await response.json();
129
+
130
+ if (!response.ok) {
131
+ return {
132
+ ok: false,
133
+ status: response.status,
134
+ error: data.error || `HTTP ${response.status}`,
135
+ data // Include full response data for additional error context
136
+ };
137
+ }
138
+
139
+ return {
140
+ ok: true,
141
+ status: response.status,
142
+ data
143
+ };
144
+ } catch (err) {
145
+ if (timeoutId) clearTimeout(timeoutId);
146
+ // Detect AbortError from timeout
147
+ if (err instanceof Error && err.name === 'AbortError' && options?.timeoutMs) {
148
+ lastError = new Error(`Request timed out after ${options.timeoutMs}ms`);
149
+ } else {
150
+ lastError = err instanceof Error ? err : new Error('Network error');
151
+ }
152
+ // Retry on network errors (connection failures, timeouts)
153
+ if (attempt < maxRetries) {
154
+ const delay = calculateBackoffDelay(attempt, baseDelayMs, maxDelayMs);
155
+ await sleep(delay);
156
+ continue;
157
+ }
158
+ }
159
+ }
160
+
161
+ // All retries exhausted
162
+ if (lastResponse) {
163
+ // We had a response but it was a retryable error status
164
+ try {
165
+ const data = await lastResponse.json();
166
+ return {
167
+ ok: false,
168
+ status: lastResponse.status,
169
+ error: data.error || `HTTP ${lastResponse.status} after ${maxRetries} retries`,
170
+ data
171
+ };
172
+ } catch {
173
+ return {
174
+ ok: false,
175
+ status: lastResponse.status,
176
+ error: `HTTP ${lastResponse.status} after ${maxRetries} retries`
177
+ };
178
+ }
179
+ }
180
+
181
+ return {
182
+ ok: false,
183
+ status: 0,
184
+ error: lastError?.message || 'Network error after retries'
185
+ };
186
+ }
187
+
188
+ // Auth endpoints
189
+ async validateAuth(options?: { timeoutMs?: number }): Promise<ApiResponse<{
190
+ valid: boolean;
191
+ user_id: string;
192
+ api_key_id: string;
193
+ key_name: string;
194
+ }>> {
195
+ return this.request('POST', '/api/mcp/auth/validate', {
196
+ api_key: this.apiKey
197
+ }, options);
198
+ }
199
+
200
+ // Session endpoints
201
+ async startSession(params: {
202
+ project_id?: string;
203
+ git_url?: string;
204
+ mode?: 'lite' | 'full';
205
+ model?: string; // Open-ended - any model name accepted
206
+ role?: string; // Open-ended - any role name accepted
207
+ hostname?: string; // Machine hostname for worktree tracking
208
+ agent_type?: string; // Open-ended - any agent type accepted
209
+ agent_name?: string; // Explicit name for cloud/remote agents (skips persona pool)
210
+ }): Promise<ApiResponse<{
211
+ session_started: boolean;
212
+ session_id?: string;
213
+ persona?: string;
214
+ role?: string;
215
+ project?: {
216
+ id: string;
217
+ name: string;
218
+ description?: string;
219
+ goal?: string;
220
+ status?: string;
221
+ git_url?: string;
222
+ agent_instructions?: string;
223
+ tech_stack?: string[];
224
+ git_workflow?: string;
225
+ git_main_branch?: string;
226
+ git_develop_branch?: string;
227
+ git_auto_branch?: boolean;
228
+ validation_required?: boolean;
229
+ auto_merge_on_approval?: boolean;
230
+ fallback_activities_enabled?: boolean;
231
+ require_pr_for_validation?: boolean;
232
+ };
233
+ active_tasks?: Array<{
234
+ id: string;
235
+ title: string;
236
+ status: string;
237
+ priority: number;
238
+ progress_percentage?: number;
239
+ estimated_minutes?: number;
240
+ }>;
241
+ blockers?: Array<{
242
+ id: string;
243
+ description: string;
244
+ status: string;
245
+ }>;
246
+ next_task?: {
247
+ id: string;
248
+ title: string;
249
+ priority: number;
250
+ estimated_minutes?: number;
251
+ } | null;
252
+ pending_requests?: Array<{
253
+ id: string;
254
+ request_type: string;
255
+ message: string;
256
+ created_at: string;
257
+ }>;
258
+ pending_requests_count?: number;
259
+ URGENT_QUESTIONS?: {
260
+ count: number;
261
+ oldest_waiting_minutes: number;
262
+ action_required: string;
263
+ requests: Array<{
264
+ id: string;
265
+ message: string;
266
+ waiting_minutes: number;
267
+ }>;
268
+ };
269
+ directive?: string;
270
+ blockers_count?: number;
271
+ validation_count?: number;
272
+ project_not_found?: boolean;
273
+ message?: string;
274
+ suggestion?: {
275
+ action: string;
276
+ example: string;
277
+ note: string;
278
+ };
279
+ // Agent onboarding - returned when a new agent type connects to an existing project
280
+ agent_setup?: {
281
+ agent_type: string;
282
+ is_new_agent_type: boolean;
283
+ setup_required: boolean;
284
+ instructions: string;
285
+ config_file: string; // e.g., '.gemini/GEMINI.md'
286
+ template_url?: string;
287
+ steps: string[];
288
+ };
289
+ // Stale worktrees that need cleanup
290
+ stale_worktrees?: Array<{
291
+ task_id: string;
292
+ task_title: string;
293
+ worktree_path: string;
294
+ worktree_hostname?: string | null;
295
+ }>;
296
+ stale_worktrees_count?: number;
297
+ cleanup_action?: string;
298
+ // Validation info for validators
299
+ awaiting_validation?: Array<{
300
+ id: string;
301
+ title?: string;
302
+ }>;
303
+ validation_priority?: boolean;
304
+ next_action?: string;
305
+ error?: string;
306
+ }>> {
307
+ return this.request('POST', '/api/mcp/sessions/start', {
308
+ ...params,
309
+ instance_id: PROCESS_INSTANCE_ID,
310
+ });
311
+ }
312
+
313
+ async heartbeat(sessionId: string, options?: {
314
+ current_worktree_path?: string | null;
315
+ hostname?: string; // Machine hostname for worktree tracking
316
+ }): Promise<ApiResponse<{
317
+ success: boolean;
318
+ session_id: string;
319
+ timestamp: string;
320
+ }>> {
321
+ return this.request('POST', '/api/mcp/sessions/heartbeat', {
322
+ session_id: sessionId,
323
+ ...options
324
+ });
325
+ }
326
+
327
+ async endSession(sessionId: string): Promise<ApiResponse<{
328
+ success: boolean;
329
+ ended_session_id?: string;
330
+ session_summary?: {
331
+ agent_name: string;
332
+ tasks_completed_this_session: number;
333
+ tasks_awaiting_validation: number;
334
+ tasks_released: number;
335
+ };
336
+ reminders?: string[];
337
+ }>> {
338
+ return this.request('POST', '/api/mcp/sessions/end', {
339
+ session_id: sessionId
340
+ });
341
+ }
342
+
343
+ async signalIdle(sessionId: string): Promise<ApiResponse<{
344
+ success: boolean;
345
+ session_id: string;
346
+ status: string;
347
+ message: string;
348
+ }>> {
349
+ return this.request('POST', '/api/mcp/sessions/idle', {
350
+ session_id: sessionId
351
+ });
352
+ }
353
+
354
+ // Project endpoints
355
+ async listProjects(): Promise<ApiResponse<{
356
+ projects: Array<{
357
+ id: string;
358
+ name: string;
359
+ description?: string;
360
+ status: string;
361
+ git_url?: string;
362
+ goal?: string;
363
+ tech_stack?: string[];
364
+ }>;
365
+ }>> {
366
+ return this.request('GET', '/api/mcp/projects');
367
+ }
368
+
369
+ async createProject(params: {
370
+ name: string;
371
+ description?: string;
372
+ goal?: string;
373
+ git_url?: string;
374
+ tech_stack?: string[];
375
+ }): Promise<ApiResponse<{
376
+ success: boolean;
377
+ project: {
378
+ id: string;
379
+ name: string;
380
+ };
381
+ }>> {
382
+ return this.request('POST', '/api/mcp/projects', params);
383
+ }
384
+
385
+ async getProject(projectId: string, gitUrl?: string): Promise<ApiResponse<{
386
+ found: boolean;
387
+ project?: {
388
+ id: string;
389
+ name: string;
390
+ description?: string;
391
+ goal?: string;
392
+ status: string;
393
+ git_url?: string;
394
+ agent_instructions?: string;
395
+ tech_stack?: string[];
396
+ git_workflow?: string;
397
+ git_main_branch?: string;
398
+ git_develop_branch?: string;
399
+ git_auto_branch?: boolean;
400
+ git_auto_tag?: boolean;
401
+ deployment_instructions?: string;
402
+ };
403
+ active_tasks?: Array<{
404
+ id: string;
405
+ title: string;
406
+ description?: string;
407
+ priority: number;
408
+ status: string;
409
+ progress_percentage?: number;
410
+ estimated_minutes?: number;
411
+ }>;
412
+ open_blockers?: Array<{
413
+ id: string;
414
+ description: string;
415
+ }>;
416
+ recent_decisions?: Array<{
417
+ id: string;
418
+ title: string;
419
+ description?: string;
420
+ }>;
421
+ message?: string;
422
+ }>> {
423
+ const url = gitUrl
424
+ ? `/api/mcp/projects/${projectId}?git_url=${encodeURIComponent(gitUrl)}`
425
+ : `/api/mcp/projects/${projectId}`;
426
+ return this.request('GET', url);
427
+ }
428
+
429
+ async updateProject(projectId: string, updates: {
430
+ name?: string;
431
+ description?: string;
432
+ goal?: string;
433
+ git_url?: string;
434
+ tech_stack?: string[];
435
+ status?: string;
436
+ git_workflow?: string;
437
+ git_main_branch?: string;
438
+ git_develop_branch?: string;
439
+ git_auto_branch?: boolean;
440
+ git_auto_tag?: boolean;
441
+ deployment_instructions?: string;
442
+ agent_instructions?: string;
443
+ // New project settings columns
444
+ git_delete_branch_on_merge?: boolean;
445
+ require_pr_for_validation?: boolean;
446
+ auto_merge_on_approval?: boolean;
447
+ validation_required?: boolean;
448
+ default_task_priority?: number;
449
+ require_time_estimates?: boolean;
450
+ fallback_activities_enabled?: boolean;
451
+ preferred_fallback_activities?: string[];
452
+ allow_local_agent_task_creation?: boolean;
453
+ allow_cloud_agent_task_creation?: boolean;
454
+ }): Promise<ApiResponse<{
455
+ success: boolean;
456
+ project_id: string;
457
+ }>> {
458
+ return this.request('PATCH', `/api/mcp/projects/${projectId}`, updates);
459
+ }
460
+
461
+ // Task endpoints
462
+ async getTasks(projectId: string, params?: {
463
+ status?: string;
464
+ limit?: number;
465
+ offset?: number;
466
+ include_subtasks?: boolean;
467
+ search_query?: string;
468
+ include_metadata?: boolean; // When true, returns all task fields; when false (default), only id/title/priority/status
469
+ }): Promise<ApiResponse<{
470
+ tasks: Array<{
471
+ id: string;
472
+ title: string;
473
+ description?: string;
474
+ priority: number;
475
+ status: string;
476
+ progress_percentage?: number;
477
+ estimated_minutes?: number;
478
+ started_at?: string;
479
+ completed_at?: string;
480
+ parent_task_id?: string;
481
+ }>;
482
+ total_count: number;
483
+ has_more: boolean;
484
+ }>> {
485
+ const queryParams = new URLSearchParams();
486
+ if (params?.status) queryParams.set('status', params.status);
487
+ if (params?.limit) queryParams.set('limit', params.limit.toString());
488
+ if (params?.offset) queryParams.set('offset', params.offset.toString());
489
+ if (params?.include_subtasks) queryParams.set('include_subtasks', 'true');
490
+ if (params?.search_query) queryParams.set('search_query', params.search_query);
491
+ if (params?.include_metadata) queryParams.set('include_metadata', 'true');
492
+
493
+ const query = queryParams.toString();
494
+ const url = `/api/mcp/projects/${projectId}/tasks${query ? `?${query}` : ''}`;
495
+ return this.request('GET', url);
496
+ }
497
+
498
+ async createTask(projectId: string, params: {
499
+ title: string;
500
+ description?: string;
501
+ priority?: number;
502
+ estimated_minutes?: number;
503
+ blocking?: boolean;
504
+ session_id?: string;
505
+ task_type?: string;
506
+ }): Promise<ApiResponse<{
507
+ success: boolean;
508
+ task_id: string;
509
+ title: string;
510
+ blocking?: boolean;
511
+ message?: string;
512
+ }>> {
513
+ return this.request('POST', `/api/mcp/projects/${projectId}/tasks`, params);
514
+ }
515
+
516
+ async getNextTask(projectId: string, sessionId?: string): Promise<ApiResponse<{
517
+ task?: {
518
+ id: string;
519
+ title: string;
520
+ description?: string;
521
+ priority: number;
522
+ estimated_minutes?: number;
523
+ blocking?: boolean;
524
+ } | null;
525
+ blocking_task?: boolean;
526
+ deployment_blocks_tasks?: boolean;
527
+ deployment?: {
528
+ id: string;
529
+ status: string;
530
+ env: string;
531
+ };
532
+ awaiting_validation?: Array<{
533
+ id: string;
534
+ title: string;
535
+ }>;
536
+ validation_priority?: string;
537
+ all_claimed?: boolean;
538
+ is_subtask?: boolean;
539
+ suggested_activity?: string;
540
+ directive?: string;
541
+ message?: string;
542
+ action?: string;
543
+ }>> {
544
+ const url = sessionId
545
+ ? `/api/mcp/projects/${projectId}/next-task?session_id=${sessionId}`
546
+ : `/api/mcp/projects/${projectId}/next-task`;
547
+ return this.request('GET', url);
548
+ }
549
+
550
+ async getTask(taskId: string): Promise<ApiResponse<{
551
+ task: {
552
+ id: string;
553
+ title: string;
554
+ description?: string;
555
+ priority: number;
556
+ status: string;
557
+ progress_percentage?: number;
558
+ estimated_minutes?: number;
559
+ started_at?: string;
560
+ completed_at?: string;
561
+ git_branch?: string;
562
+ blocking?: boolean;
563
+ references?: Array<{ url: string; label?: string }>;
564
+ parent_task_id?: string;
565
+ working_agent_session_id?: string;
566
+ };
567
+ }>> {
568
+ return this.request('GET', `/api/mcp/tasks/${taskId}`);
569
+ }
570
+
571
+ /**
572
+ * Get a single task by ID with optional subtasks and milestones
573
+ */
574
+ async getTaskById(taskId: string, params?: {
575
+ include_subtasks?: boolean;
576
+ include_milestones?: boolean;
577
+ }): Promise<ApiResponse<{
578
+ task: {
579
+ id: string;
580
+ title: string;
581
+ description?: string;
582
+ priority: number;
583
+ status: string;
584
+ progress_percentage?: number;
585
+ estimated_minutes?: number;
586
+ started_at?: string;
587
+ completed_at?: string;
588
+ git_branch?: string;
589
+ references?: Array<{ url: string; label?: string }>;
590
+ };
591
+ subtasks?: Array<{
592
+ id: string;
593
+ title: string;
594
+ status: string;
595
+ progress_percentage?: number;
596
+ }>;
597
+ milestones?: Array<{
598
+ id: string;
599
+ title: string;
600
+ status: string;
601
+ order_index: number;
602
+ }>;
603
+ }>> {
604
+ return this.proxy('get_task', {
605
+ task_id: taskId,
606
+ include_subtasks: params?.include_subtasks,
607
+ include_milestones: params?.include_milestones,
608
+ });
609
+ }
610
+
611
+ /**
612
+ * Search tasks by text query with pagination
613
+ */
614
+ async searchTasks(projectId: string, params: {
615
+ query: string;
616
+ status?: string[];
617
+ limit?: number;
618
+ offset?: number;
619
+ }): Promise<ApiResponse<{
620
+ tasks: Array<{
621
+ id: string;
622
+ title: string;
623
+ status: string;
624
+ priority: number;
625
+ snippet?: string;
626
+ }>;
627
+ total_matches: number;
628
+ hint?: string;
629
+ }>> {
630
+ return this.proxy('search_tasks', {
631
+ project_id: projectId,
632
+ query: params.query,
633
+ status: params.status,
634
+ limit: params.limit,
635
+ offset: params.offset,
636
+ });
637
+ }
638
+
639
+ /**
640
+ * Get tasks filtered by priority with pagination
641
+ */
642
+ async getTasksByPriority(projectId: string, params?: {
643
+ priority?: number;
644
+ priority_max?: number;
645
+ status?: string;
646
+ limit?: number;
647
+ offset?: number;
648
+ }): Promise<ApiResponse<{
649
+ tasks: Array<{
650
+ id: string;
651
+ title: string;
652
+ priority: number;
653
+ status: string;
654
+ estimated_minutes?: number;
655
+ }>;
656
+ total_count: number;
657
+ }>> {
658
+ return this.proxy('get_tasks_by_priority', {
659
+ project_id: projectId,
660
+ priority: params?.priority,
661
+ priority_max: params?.priority_max,
662
+ status: params?.status,
663
+ limit: params?.limit,
664
+ offset: params?.offset,
665
+ });
666
+ }
667
+
668
+ /**
669
+ * Get recent tasks (newest or oldest) with pagination
670
+ */
671
+ async getRecentTasks(projectId: string, params?: {
672
+ order?: 'newest' | 'oldest';
673
+ status?: string;
674
+ limit?: number;
675
+ offset?: number;
676
+ }): Promise<ApiResponse<{
677
+ tasks: Array<{
678
+ id: string;
679
+ title: string;
680
+ status: string;
681
+ priority: number;
682
+ created_at: string;
683
+ age?: string;
684
+ }>;
685
+ total_count: number;
686
+ }>> {
687
+ return this.proxy('get_recent_tasks', {
688
+ project_id: projectId,
689
+ order: params?.order,
690
+ status: params?.status,
691
+ limit: params?.limit,
692
+ offset: params?.offset,
693
+ });
694
+ }
695
+
696
+ /**
697
+ * Get task statistics for a project
698
+ */
699
+ async getTaskStats(projectId: string): Promise<ApiResponse<{
700
+ total: number;
701
+ by_status: {
702
+ backlog: number;
703
+ pending: number;
704
+ in_progress: number;
705
+ completed: number;
706
+ cancelled: number;
707
+ };
708
+ by_priority: {
709
+ 1: number;
710
+ 2: number;
711
+ 3: number;
712
+ 4: number;
713
+ 5: number;
714
+ };
715
+ awaiting_validation: number;
716
+ oldest_pending_days: number | null;
717
+ }>> {
718
+ return this.proxy('get_task_stats', {
719
+ project_id: projectId,
720
+ });
721
+ }
722
+
723
+ async updateTask(taskId: string, updates: {
724
+ title?: string;
725
+ description?: string;
726
+ priority?: number;
727
+ status?: string;
728
+ progress_percentage?: number;
729
+ progress_note?: string;
730
+ estimated_minutes?: number;
731
+ git_branch?: string;
732
+ worktree_path?: string;
733
+ session_id?: string;
734
+ }): Promise<ApiResponse<{
735
+ success: boolean;
736
+ task_id: string;
737
+ git_workflow?: {
738
+ workflow: string;
739
+ base_branch: string;
740
+ suggested_branch: string;
741
+ worktree_required: boolean;
742
+ };
743
+ worktree_setup?: {
744
+ message: string;
745
+ commands: string[];
746
+ worktree_path: string;
747
+ branch_name: string;
748
+ cleanup_command: string;
749
+ };
750
+ next_step?: string;
751
+ }>> {
752
+ return this.request('PATCH', `/api/mcp/tasks/${taskId}`, updates);
753
+ }
754
+
755
+ async completeTask(taskId: string, params: {
756
+ summary?: string;
757
+ session_id?: string;
758
+ commit_hash?: string;
759
+ check_results?: Array<{ command: string; passed: boolean; output?: string }>;
760
+ }): Promise<ApiResponse<{
761
+ success: boolean;
762
+ directive: string;
763
+ auto_continue: boolean;
764
+ completed_task_id: string;
765
+ next_task?: {
766
+ id: string;
767
+ title: string;
768
+ priority: number;
769
+ estimated_minutes?: number;
770
+ } | null;
771
+ context?: {
772
+ validation?: number;
773
+ blockers?: number;
774
+ deployment?: string;
775
+ worktree_path?: string;
776
+ };
777
+ next_action: string;
778
+ warnings?: string[];
779
+ }>> {
780
+ // Use proxy endpoint for consistency - direct endpoint had routing issues on Vercel
781
+ return this.proxy('complete_task', {
782
+ task_id: taskId,
783
+ summary: params.summary,
784
+ ...(params.commit_hash ? { commit_hash: params.commit_hash } : {}),
785
+ ...(params.check_results ? { check_results: params.check_results } : {}),
786
+ }, params.session_id ? { session_id: params.session_id, persona: null, instance_id: '' } : undefined);
787
+ }
788
+
789
+ async deleteTask(taskId: string): Promise<ApiResponse<{
790
+ success: boolean;
791
+ deleted_id: string;
792
+ }>> {
793
+ return this.request('DELETE', `/api/mcp/tasks/${taskId}`);
794
+ }
795
+
796
+ /**
797
+ * Release a task back to pending status.
798
+ * Clears session assignment, git branch, and worktree info.
799
+ */
800
+ async releaseTask(taskId: string, params?: {
801
+ reason?: string;
802
+ session_id?: string;
803
+ }): Promise<ApiResponse<{
804
+ success: boolean;
805
+ task_id: string;
806
+ message: string;
807
+ reason?: string;
808
+ hint: string;
809
+ }>> {
810
+ return this.proxy('release_task', {
811
+ task_id: taskId,
812
+ reason: params?.reason,
813
+ }, params?.session_id ? {
814
+ session_id: params.session_id,
815
+ persona: null,
816
+ instance_id: '',
817
+ } : undefined);
818
+ }
819
+
820
+ /**
821
+ * Cancel a task with an optional reason.
822
+ * Task remains visible with cancelled status for historical tracking.
823
+ */
824
+ async cancelTask(taskId: string, params?: {
825
+ cancelled_reason?: 'pr_closed' | 'superseded' | 'user_cancelled' | 'validation_failed' | 'obsolete' | 'blocked';
826
+ cancellation_note?: string;
827
+ session_id?: string;
828
+ }): Promise<ApiResponse<{
829
+ success: boolean;
830
+ task_id: string;
831
+ cancelled_reason: string | null;
832
+ message: string;
833
+ }>> {
834
+ return this.proxy('cancel_task', {
835
+ task_id: taskId,
836
+ cancelled_reason: params?.cancelled_reason,
837
+ cancellation_note: params?.cancellation_note,
838
+ }, params?.session_id ? {
839
+ session_id: params.session_id,
840
+ persona: null,
841
+ instance_id: '',
842
+ } : undefined);
843
+ }
844
+
845
+ // Progress endpoints
846
+ async logProgress(projectId: string, params: {
847
+ summary: string;
848
+ details?: string;
849
+ task_id?: string;
850
+ session_id?: string;
851
+ }): Promise<ApiResponse<{
852
+ success: boolean;
853
+ progress_id: string;
854
+ }>> {
855
+ return this.request('POST', `/api/mcp/projects/${projectId}/progress`, params);
856
+ }
857
+
858
+ // Git workflow endpoint
859
+ async getGitWorkflow(projectId: string, taskId?: string): Promise<ApiResponse<{
860
+ workflow: string;
861
+ main_branch: string;
862
+ develop_branch?: string | null;
863
+ auto_branch?: boolean;
864
+ auto_tag?: boolean;
865
+ instructions: string[];
866
+ task?: {
867
+ id: string;
868
+ title: string;
869
+ current_branch?: string;
870
+ suggested_branch?: string | null;
871
+ };
872
+ }>> {
873
+ const url = taskId
874
+ ? `/api/mcp/projects/${projectId}/git-workflow?task_id=${taskId}`
875
+ : `/api/mcp/projects/${projectId}/git-workflow`;
876
+ return this.request('GET', url);
877
+ }
878
+
879
+ // ============================================================================
880
+ // Proxy endpoint - Generic method for all operations
881
+ // ============================================================================
882
+ async proxy<T>(operation: string, args: Record<string, unknown>, sessionContext?: {
883
+ session_id: string | null;
884
+ persona: string | null;
885
+ instance_id: string;
886
+ }): Promise<ApiResponse<T>> {
887
+ return this.request('POST', '/api/mcp/proxy', {
888
+ operation,
889
+ args,
890
+ session_context: sessionContext
891
+ });
892
+ }
893
+
894
+ // ============================================================================
895
+ // Blocker endpoints
896
+ // ============================================================================
897
+ async getBlockers(projectId: string, params?: {
898
+ status?: string;
899
+ limit?: number;
900
+ offset?: number;
901
+ search_query?: string;
902
+ }): Promise<ApiResponse<{
903
+ blockers: Array<{
904
+ id: string;
905
+ description: string;
906
+ status: string;
907
+ resolution_note?: string;
908
+ created_at: string;
909
+ resolved_at?: string;
910
+ }>;
911
+ total_count?: number;
912
+ has_more?: boolean;
913
+ }>> {
914
+ return this.proxy('get_blockers', {
915
+ project_id: projectId,
916
+ ...params
917
+ });
918
+ }
919
+
920
+ async addBlocker(projectId: string, description: string, sessionId?: string): Promise<ApiResponse<{
921
+ success: boolean;
922
+ blocker_id: string;
923
+ }>> {
924
+ return this.proxy('add_blocker', { project_id: projectId, description }, sessionId ? {
925
+ session_id: sessionId,
926
+ persona: null,
927
+ instance_id: ''
928
+ } : undefined);
929
+ }
930
+
931
+ async resolveBlocker(blockerId: string, resolutionNote?: string): Promise<ApiResponse<{
932
+ success: boolean;
933
+ blocker_id: string;
934
+ }>> {
935
+ return this.proxy('resolve_blocker', {
936
+ blocker_id: blockerId,
937
+ resolution_note: resolutionNote
938
+ });
939
+ }
940
+
941
+ async deleteBlocker(blockerId: string): Promise<ApiResponse<{
942
+ success: boolean;
943
+ }>> {
944
+ return this.proxy('delete_blocker', { blocker_id: blockerId });
945
+ }
946
+
947
+ async getBlockersStats(projectId: string): Promise<ApiResponse<{
948
+ total: number;
949
+ by_status: Record<string, number>;
950
+ }>> {
951
+ return this.proxy('get_blockers_stats', {
952
+ project_id: projectId
953
+ });
954
+ }
955
+
956
+ // ============================================================================
957
+ // Decision endpoints
958
+ // ============================================================================
959
+ async getDecisions(projectId: string, options?: {
960
+ limit?: number;
961
+ offset?: number;
962
+ search_query?: string;
963
+ }): Promise<ApiResponse<{
964
+ decisions: Array<{
965
+ id: string;
966
+ title: string;
967
+ description: string;
968
+ rationale?: string;
969
+ alternatives_considered?: string[];
970
+ created_at: string;
971
+ }>;
972
+ }>> {
973
+ return this.proxy('get_decisions', { project_id: projectId, ...options });
974
+ }
975
+
976
+ async logDecision(projectId: string, params: {
977
+ title: string;
978
+ description: string;
979
+ rationale?: string;
980
+ alternatives_considered?: string[];
981
+ }, sessionId?: string): Promise<ApiResponse<{
982
+ success: boolean;
983
+ decision_id: string;
984
+ }>> {
985
+ return this.proxy('log_decision', {
986
+ project_id: projectId,
987
+ ...params
988
+ }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
989
+ }
990
+
991
+ async deleteDecision(decisionId: string): Promise<ApiResponse<{
992
+ success: boolean;
993
+ }>> {
994
+ return this.proxy('delete_decision', { decision_id: decisionId });
995
+ }
996
+
997
+ async getDecisionsStats(projectId: string): Promise<ApiResponse<{
998
+ total: number;
999
+ }>> {
1000
+ return this.proxy('get_decisions_stats', {
1001
+ project_id: projectId
1002
+ });
1003
+ }
1004
+
1005
+ // ============================================================================
1006
+ // Idea endpoints
1007
+ // ============================================================================
1008
+ async getIdeas(projectId: string, params?: {
1009
+ status?: string;
1010
+ limit?: number;
1011
+ offset?: number;
1012
+ search_query?: string;
1013
+ }): Promise<ApiResponse<{
1014
+ ideas: Array<{
1015
+ id: string;
1016
+ title: string;
1017
+ description?: string;
1018
+ status: string;
1019
+ doc_url?: string;
1020
+ created_at: string;
1021
+ }>;
1022
+ }>> {
1023
+ return this.proxy('get_ideas', {
1024
+ project_id: projectId,
1025
+ ...params
1026
+ });
1027
+ }
1028
+
1029
+ async addIdea(projectId: string, params: {
1030
+ title: string;
1031
+ description?: string;
1032
+ status?: string;
1033
+ }, sessionId?: string): Promise<ApiResponse<{
1034
+ success: boolean;
1035
+ idea_id: string;
1036
+ }>> {
1037
+ return this.proxy('add_idea', {
1038
+ project_id: projectId,
1039
+ ...params
1040
+ }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1041
+ }
1042
+
1043
+ async updateIdea(ideaId: string, updates: {
1044
+ title?: string;
1045
+ description?: string;
1046
+ status?: string;
1047
+ doc_url?: string;
1048
+ }): Promise<ApiResponse<{
1049
+ success: boolean;
1050
+ idea_id: string;
1051
+ }>> {
1052
+ return this.proxy('update_idea', {
1053
+ idea_id: ideaId,
1054
+ ...updates
1055
+ });
1056
+ }
1057
+
1058
+ async deleteIdea(ideaId: string): Promise<ApiResponse<{
1059
+ success: boolean;
1060
+ }>> {
1061
+ return this.proxy('delete_idea', { idea_id: ideaId });
1062
+ }
1063
+
1064
+ async convertIdeaToTask(ideaId: string, params?: {
1065
+ priority?: number;
1066
+ estimated_minutes?: number;
1067
+ update_status?: boolean;
1068
+ }): Promise<ApiResponse<{
1069
+ success: boolean;
1070
+ task_id?: string;
1071
+ task_title?: string;
1072
+ idea_id?: string;
1073
+ idea_status?: string;
1074
+ message?: string;
1075
+ error?: string;
1076
+ existing_task_id?: string;
1077
+ }>> {
1078
+ return this.proxy('convert_idea_to_task', {
1079
+ idea_id: ideaId,
1080
+ ...params
1081
+ });
1082
+ }
1083
+
1084
+ // ============================================================================
1085
+ // Finding endpoints
1086
+ // ============================================================================
1087
+ async getFindings(projectId: string, params?: {
1088
+ category?: string;
1089
+ severity?: string;
1090
+ status?: string;
1091
+ limit?: number;
1092
+ offset?: number;
1093
+ search_query?: string;
1094
+ summary_only?: boolean;
1095
+ }): Promise<ApiResponse<{
1096
+ findings: Array<{
1097
+ id: string;
1098
+ title: string;
1099
+ description?: string;
1100
+ category: string;
1101
+ severity: string;
1102
+ status: string;
1103
+ file_path?: string;
1104
+ line_number?: number;
1105
+ resolution_note?: string;
1106
+ created_at: string;
1107
+ }>;
1108
+ total_count?: number;
1109
+ has_more?: boolean;
1110
+ }>> {
1111
+ return this.proxy('get_findings', {
1112
+ project_id: projectId,
1113
+ ...params
1114
+ });
1115
+ }
1116
+
1117
+ async getFindingsStats(projectId: string): Promise<ApiResponse<{
1118
+ total: number;
1119
+ by_status: Record<string, number>;
1120
+ by_severity: Record<string, number>;
1121
+ by_category: Record<string, number>;
1122
+ }>> {
1123
+ return this.proxy('get_findings_stats', {
1124
+ project_id: projectId
1125
+ });
1126
+ }
1127
+
1128
+ async addFinding(projectId: string, params: {
1129
+ title: string;
1130
+ description?: string;
1131
+ category?: string;
1132
+ severity?: string;
1133
+ file_path?: string;
1134
+ line_number?: number;
1135
+ related_task_id?: string;
1136
+ }, sessionId?: string): Promise<ApiResponse<{
1137
+ success: boolean;
1138
+ finding_id: string;
1139
+ }>> {
1140
+ return this.proxy('add_finding', {
1141
+ project_id: projectId,
1142
+ ...params
1143
+ }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1144
+ }
1145
+
1146
+ async updateFinding(findingId: string, updates: {
1147
+ title?: string;
1148
+ description?: string;
1149
+ severity?: string;
1150
+ status?: string;
1151
+ resolution_note?: string;
1152
+ }): Promise<ApiResponse<{
1153
+ success: boolean;
1154
+ finding_id: string;
1155
+ }>> {
1156
+ return this.proxy('update_finding', {
1157
+ finding_id: findingId,
1158
+ ...updates
1159
+ });
1160
+ }
1161
+
1162
+ async deleteFinding(findingId: string): Promise<ApiResponse<{
1163
+ success: boolean;
1164
+ }>> {
1165
+ return this.proxy('delete_finding', { finding_id: findingId });
1166
+ }
1167
+
1168
+ // ============================================================================
1169
+ // Milestone endpoints
1170
+ // ============================================================================
1171
+ async getMilestones(taskId: string): Promise<ApiResponse<{
1172
+ milestones: Array<{
1173
+ id: string;
1174
+ title: string;
1175
+ description?: string;
1176
+ status: string;
1177
+ order_index: number;
1178
+ created_at: string;
1179
+ completed_at?: string;
1180
+ }>;
1181
+ stats: {
1182
+ total: number;
1183
+ completed: number;
1184
+ progress_percentage: number;
1185
+ };
1186
+ }>> {
1187
+ return this.proxy('get_milestones', { task_id: taskId });
1188
+ }
1189
+
1190
+ async addMilestone(taskId: string, params: {
1191
+ title: string;
1192
+ description?: string;
1193
+ order_index?: number;
1194
+ }, sessionId?: string): Promise<ApiResponse<{
1195
+ success: boolean;
1196
+ milestone_id: string;
1197
+ }>> {
1198
+ return this.proxy('add_milestone', {
1199
+ task_id: taskId,
1200
+ ...params
1201
+ }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1202
+ }
1203
+
1204
+ async updateMilestone(milestoneId: string, updates: {
1205
+ title?: string;
1206
+ description?: string;
1207
+ status?: string;
1208
+ order_index?: number;
1209
+ }): Promise<ApiResponse<{
1210
+ success: boolean;
1211
+ milestone: {
1212
+ id: string;
1213
+ title: string;
1214
+ status: string;
1215
+ };
1216
+ }>> {
1217
+ return this.proxy('update_milestone', {
1218
+ milestone_id: milestoneId,
1219
+ ...updates
1220
+ });
1221
+ }
1222
+
1223
+ async completeMilestone(milestoneId: string): Promise<ApiResponse<{
1224
+ success: boolean;
1225
+ milestone: {
1226
+ id: string;
1227
+ title: string;
1228
+ status: string;
1229
+ };
1230
+ }>> {
1231
+ return this.proxy('complete_milestone', { milestone_id: milestoneId });
1232
+ }
1233
+
1234
+ async deleteMilestone(milestoneId: string): Promise<ApiResponse<{
1235
+ success: boolean;
1236
+ }>> {
1237
+ return this.proxy('delete_milestone', { milestone_id: milestoneId });
1238
+ }
1239
+
1240
+ // ============================================================================
1241
+ // Request endpoints
1242
+ // ============================================================================
1243
+ async getPendingRequests(projectId: string, sessionId?: string, limit?: number, offset?: number): Promise<ApiResponse<{
1244
+ requests: Array<{
1245
+ id: string;
1246
+ request_type: string;
1247
+ message: string;
1248
+ created_at: string;
1249
+ }>;
1250
+ total_count: number;
1251
+ has_more: boolean;
1252
+ }>> {
1253
+ return this.proxy('get_pending_requests', {
1254
+ project_id: projectId,
1255
+ ...(limit !== undefined && { limit }),
1256
+ ...(offset !== undefined && { offset }),
1257
+ }, sessionId ? {
1258
+ session_id: sessionId,
1259
+ persona: null,
1260
+ instance_id: ''
1261
+ } : undefined);
1262
+ }
1263
+
1264
+ async acknowledgeRequest(requestId: string, sessionId?: string): Promise<ApiResponse<{
1265
+ success: boolean;
1266
+ }>> {
1267
+ return this.proxy('acknowledge_request', { request_id: requestId }, sessionId ? {
1268
+ session_id: sessionId,
1269
+ persona: null,
1270
+ instance_id: ''
1271
+ } : undefined);
1272
+ }
1273
+
1274
+ async answerQuestion(requestId: string, answer: string, sessionId?: string): Promise<ApiResponse<{
1275
+ success: boolean;
1276
+ }>> {
1277
+ return this.proxy('answer_question', {
1278
+ request_id: requestId,
1279
+ answer
1280
+ }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1281
+ }
1282
+
1283
+ // ============================================================================
1284
+ // Validation endpoints
1285
+ // ============================================================================
1286
+ async getTasksAwaitingValidation(projectId: string): Promise<ApiResponse<{
1287
+ tasks: Array<{
1288
+ id: string;
1289
+ title: string;
1290
+ completed_at?: string;
1291
+ completed_by_session_id?: string;
1292
+ }>;
1293
+ }>> {
1294
+ return this.proxy('get_tasks_awaiting_validation', { project_id: projectId });
1295
+ }
1296
+
1297
+ async claimValidation(taskId: string, sessionId?: string): Promise<ApiResponse<{
1298
+ success: boolean;
1299
+ task_id: string;
1300
+ }>> {
1301
+ return this.proxy('claim_validation', { task_id: taskId }, sessionId ? {
1302
+ session_id: sessionId,
1303
+ persona: null,
1304
+ instance_id: ''
1305
+ } : undefined);
1306
+ }
1307
+
1308
+ async validateTask(taskId: string, params: {
1309
+ approved: boolean;
1310
+ validation_notes?: string;
1311
+ skip_pr_check?: boolean;
1312
+ pr_checks_passing?: boolean;
1313
+ }, sessionId?: string): Promise<ApiResponse<{
1314
+ success: boolean;
1315
+ approved: boolean;
1316
+ task_id: string;
1317
+ message?: string;
1318
+ workflow?: string;
1319
+ }>> {
1320
+ return this.proxy('validate_task', {
1321
+ task_id: taskId,
1322
+ ...params
1323
+ }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1324
+ }
1325
+
1326
+ // ============================================================================
1327
+ // Fallback activity endpoints
1328
+ // ============================================================================
1329
+ async startFallbackActivity(projectId: string, activity: string, sessionId?: string): Promise<ApiResponse<{
1330
+ success: boolean;
1331
+ activity: string;
1332
+ message: string;
1333
+ git_workflow?: {
1334
+ workflow: string;
1335
+ base_branch: string;
1336
+ worktree_recommended: boolean;
1337
+ note: string;
1338
+ };
1339
+ worktree_setup?: {
1340
+ message: string;
1341
+ commands: string[];
1342
+ worktree_path: string;
1343
+ branch_name: string;
1344
+ cleanup_command: string;
1345
+ report_worktree: string;
1346
+ };
1347
+ next_step?: string;
1348
+ }>> {
1349
+ return this.proxy('start_fallback_activity', {
1350
+ project_id: projectId,
1351
+ activity
1352
+ }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1353
+ }
1354
+
1355
+ async stopFallbackActivity(projectId: string, summary?: string, sessionId?: string): Promise<ApiResponse<{
1356
+ success: boolean;
1357
+ }>> {
1358
+ return this.proxy('stop_fallback_activity', {
1359
+ project_id: projectId,
1360
+ summary
1361
+ }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1362
+ }
1363
+
1364
+ async getActivityHistory(projectId: string, params?: {
1365
+ activity_type?: string;
1366
+ limit?: number;
1367
+ }): Promise<ApiResponse<{
1368
+ history: Array<{
1369
+ id: string;
1370
+ activity_type: string;
1371
+ completed_at: string;
1372
+ summary?: string;
1373
+ }>;
1374
+ latest_by_type: Record<string, unknown>;
1375
+ count: number;
1376
+ }>> {
1377
+ return this.proxy('get_activity_history', {
1378
+ project_id: projectId,
1379
+ ...params
1380
+ });
1381
+ }
1382
+
1383
+ async getActivitySchedules(projectId: string, params?: {
1384
+ limit?: number;
1385
+ offset?: number;
1386
+ }): Promise<ApiResponse<{
1387
+ schedules: Array<{
1388
+ id: string;
1389
+ activity_type: string;
1390
+ schedule_type: string;
1391
+ next_run_at?: string;
1392
+ enabled: boolean;
1393
+ }>;
1394
+ total_count: number;
1395
+ has_more: boolean;
1396
+ }>> {
1397
+ return this.proxy('get_activity_schedules', {
1398
+ project_id: projectId,
1399
+ ...params
1400
+ });
1401
+ }
1402
+
1403
+ // ============================================================================
1404
+ // Subtask endpoints
1405
+ // ============================================================================
1406
+ async addSubtask(parentTaskId: string, params: {
1407
+ title: string;
1408
+ description?: string;
1409
+ priority?: number;
1410
+ estimated_minutes?: number;
1411
+ }, sessionId?: string): Promise<ApiResponse<{
1412
+ success: boolean;
1413
+ subtask_id: string;
1414
+ parent_task_id: string;
1415
+ }>> {
1416
+ return this.proxy('add_subtask', {
1417
+ parent_task_id: parentTaskId,
1418
+ ...params
1419
+ }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1420
+ }
1421
+
1422
+ async getSubtasks(parentTaskId: string, status?: string): Promise<ApiResponse<{
1423
+ subtasks: Array<{
1424
+ id: string;
1425
+ title: string;
1426
+ description?: string;
1427
+ priority: number;
1428
+ status: string;
1429
+ progress_percentage?: number;
1430
+ estimated_minutes?: number;
1431
+ }>;
1432
+ stats: {
1433
+ total: number;
1434
+ completed: number;
1435
+ progress_percentage: number;
1436
+ };
1437
+ }>> {
1438
+ return this.proxy('get_subtasks', {
1439
+ parent_task_id: parentTaskId,
1440
+ status
1441
+ });
1442
+ }
1443
+
1444
+ // ============================================================================
1445
+ // Activity feed endpoint
1446
+ // ============================================================================
1447
+ async getActivityFeed(projectId: string, params?: {
1448
+ limit?: number;
1449
+ since?: string;
1450
+ types?: string[];
1451
+ created_by?: string;
1452
+ }): Promise<ApiResponse<{
1453
+ activities: Array<{
1454
+ type: string;
1455
+ data: unknown;
1456
+ timestamp: string;
1457
+ }>;
1458
+ }>> {
1459
+ return this.proxy('get_activity_feed', {
1460
+ project_id: projectId,
1461
+ ...params
1462
+ });
1463
+ }
1464
+
1465
+ // ============================================================================
1466
+ // Deployment endpoints
1467
+ // ============================================================================
1468
+ async requestDeployment(projectId: string, params?: {
1469
+ environment?: string;
1470
+ version_bump?: string;
1471
+ git_ref?: string;
1472
+ notes?: string;
1473
+ }, sessionId?: string): Promise<ApiResponse<{
1474
+ success: boolean;
1475
+ deployment_id: string;
1476
+ status: string;
1477
+ }>> {
1478
+ return this.proxy('request_deployment', {
1479
+ project_id: projectId,
1480
+ ...params
1481
+ }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1482
+ }
1483
+
1484
+ async checkDeploymentStatus(projectId: string): Promise<ApiResponse<{
1485
+ active_deployment: boolean;
1486
+ deployment?: {
1487
+ id: string;
1488
+ status: string;
1489
+ environment: string;
1490
+ git_ref?: string;
1491
+ created_at: string;
1492
+ };
1493
+ }>> {
1494
+ return this.proxy('check_deployment_status', { project_id: projectId });
1495
+ }
1496
+
1497
+ async claimDeploymentValidation(projectId: string, sessionId?: string): Promise<ApiResponse<{
1498
+ success: boolean;
1499
+ deployment_id: string;
1500
+ message: string;
1501
+ }>> {
1502
+ return this.proxy('claim_deployment_validation', { project_id: projectId }, sessionId ? {
1503
+ session_id: sessionId,
1504
+ persona: null,
1505
+ instance_id: ''
1506
+ } : undefined);
1507
+ }
1508
+
1509
+ async reportValidation(projectId: string, params: {
1510
+ build_passed: boolean;
1511
+ tests_passed: boolean;
1512
+ error_message?: string;
1513
+ }): Promise<ApiResponse<{
1514
+ success: boolean;
1515
+ deployment_id: string;
1516
+ status: string;
1517
+ }>> {
1518
+ return this.proxy('report_validation', {
1519
+ project_id: projectId,
1520
+ ...params
1521
+ });
1522
+ }
1523
+
1524
+ async startDeployment(projectId: string, sessionId?: string): Promise<ApiResponse<{
1525
+ success: boolean;
1526
+ deployment_id: string;
1527
+ instructions: string;
1528
+ }>> {
1529
+ return this.proxy('start_deployment', { project_id: projectId }, sessionId ? {
1530
+ session_id: sessionId,
1531
+ persona: null,
1532
+ instance_id: ''
1533
+ } : undefined);
1534
+ }
1535
+
1536
+ async completeDeployment(projectId: string, params: {
1537
+ success: boolean;
1538
+ summary?: string;
1539
+ }): Promise<ApiResponse<{
1540
+ success: boolean;
1541
+ deployment_id: string;
1542
+ status: string;
1543
+ }>> {
1544
+ return this.proxy('complete_deployment', {
1545
+ project_id: projectId,
1546
+ ...params
1547
+ });
1548
+ }
1549
+
1550
+ async cancelDeployment(projectId: string, reason?: string): Promise<ApiResponse<{
1551
+ success: boolean;
1552
+ }>> {
1553
+ return this.proxy('cancel_deployment', {
1554
+ project_id: projectId,
1555
+ reason
1556
+ });
1557
+ }
1558
+
1559
+ // ============================================================================
1560
+ // Task reference endpoints
1561
+ // ============================================================================
1562
+ async addTaskReference(taskId: string, url: string, label?: string): Promise<ApiResponse<{
1563
+ success: boolean;
1564
+ reference: { url: string; label?: string };
1565
+ }>> {
1566
+ return this.proxy('add_task_reference', {
1567
+ task_id: taskId,
1568
+ url,
1569
+ label
1570
+ });
1571
+ }
1572
+
1573
+ async removeTaskReference(taskId: string, url: string): Promise<ApiResponse<{
1574
+ success: boolean;
1575
+ }>> {
1576
+ return this.proxy('remove_task_reference', {
1577
+ task_id: taskId,
1578
+ url
1579
+ });
1580
+ }
1581
+
1582
+ // ============================================================================
1583
+ // Knowledge base endpoint
1584
+ // ============================================================================
1585
+ async queryKnowledgeBase(projectId: string, params?: {
1586
+ scope?: 'summary' | 'detailed';
1587
+ categories?: string[];
1588
+ limit?: number;
1589
+ search_query?: string;
1590
+ }): Promise<ApiResponse<{
1591
+ findings?: Array<{ id: string; title: string; category: string; severity: string; description?: string }>;
1592
+ decisions?: Array<{ id: string; title: string; description: string; rationale?: string }>;
1593
+ completed_tasks?: Array<{ id: string; title: string; completed_at: string; summary?: string }>;
1594
+ resolved_blockers?: Array<{ id: string; description: string; resolution_note?: string }>;
1595
+ progress?: Array<{ id: string; summary: string; details?: string }>;
1596
+ qa?: Array<{ id: string; question: string; answer: string }>;
1597
+ }>> {
1598
+ return this.proxy('query_knowledge_base', {
1599
+ project_id: projectId,
1600
+ ...params
1601
+ });
1602
+ }
1603
+
1604
+ // ============================================================================
1605
+ // Session sync endpoint
1606
+ // ============================================================================
1607
+ async syncSession(sessionId: string, params?: {
1608
+ total_tokens?: number;
1609
+ token_breakdown?: Record<string, unknown>;
1610
+ model_usage?: Record<string, unknown>;
1611
+ }): Promise<ApiResponse<{
1612
+ success: boolean;
1613
+ }>> {
1614
+ return this.proxy('sync_session', {
1615
+ session_id: sessionId,
1616
+ ...params
1617
+ });
1618
+ }
1619
+
1620
+ /**
1621
+ * Report actual API token usage for accurate cost tracking.
1622
+ * This records token usage to the session and attributes cost to the current task.
1623
+ */
1624
+ async reportTokenUsage(sessionId: string, params: {
1625
+ input_tokens: number;
1626
+ output_tokens: number;
1627
+ model?: string; // Open-ended - any model name accepted
1628
+ }): Promise<ApiResponse<{
1629
+ success: boolean;
1630
+ reported: {
1631
+ session_id: string;
1632
+ model: string;
1633
+ input_tokens: number;
1634
+ output_tokens: number;
1635
+ total_tokens: number;
1636
+ estimated_cost_usd: number;
1637
+ };
1638
+ task_attributed: boolean;
1639
+ task_id?: string;
1640
+ }>> {
1641
+ return this.proxy('report_token_usage', {
1642
+ session_id: sessionId,
1643
+ ...params
1644
+ });
1645
+ }
1646
+
1647
+ // ============================================================================
1648
+ // Organization endpoints
1649
+ // ============================================================================
1650
+ async listOrganizations(): Promise<ApiResponse<{
1651
+ organizations: Array<{
1652
+ id: string;
1653
+ name: string;
1654
+ slug: string;
1655
+ description?: string;
1656
+ role: string;
1657
+ }>;
1658
+ }>> {
1659
+ return this.proxy('list_organizations', {});
1660
+ }
1661
+
1662
+ async createOrganization(params: {
1663
+ name: string;
1664
+ slug?: string;
1665
+ description?: string;
1666
+ }): Promise<ApiResponse<{
1667
+ success: boolean;
1668
+ organization: { id: string; name: string; slug: string };
1669
+ }>> {
1670
+ return this.proxy('create_organization', params);
1671
+ }
1672
+
1673
+ async updateOrganization(organizationId: string, updates: {
1674
+ name?: string;
1675
+ description?: string;
1676
+ logo_url?: string;
1677
+ }): Promise<ApiResponse<{
1678
+ success: boolean;
1679
+ organization_id: string;
1680
+ }>> {
1681
+ return this.proxy('update_organization', {
1682
+ organization_id: organizationId,
1683
+ ...updates
1684
+ });
1685
+ }
1686
+
1687
+ async deleteOrganization(organizationId: string): Promise<ApiResponse<{
1688
+ success: boolean;
1689
+ }>> {
1690
+ return this.proxy('delete_organization', { organization_id: organizationId });
1691
+ }
1692
+
1693
+ async listOrgMembers(organizationId: string): Promise<ApiResponse<{
1694
+ members: Array<{
1695
+ user_id: string;
1696
+ email: string;
1697
+ role: string;
1698
+ joined_at: string;
1699
+ }>;
1700
+ }>> {
1701
+ return this.proxy('list_org_members', { organization_id: organizationId });
1702
+ }
1703
+
1704
+ async inviteMember(organizationId: string, email: string, role?: string): Promise<ApiResponse<{
1705
+ success: boolean;
1706
+ invite_id: string;
1707
+ }>> {
1708
+ return this.proxy('invite_member', {
1709
+ organization_id: organizationId,
1710
+ email,
1711
+ role
1712
+ });
1713
+ }
1714
+
1715
+ async updateMemberRole(organizationId: string, userId: string, role: string): Promise<ApiResponse<{
1716
+ success: boolean;
1717
+ }>> {
1718
+ return this.proxy('update_member_role', {
1719
+ organization_id: organizationId,
1720
+ user_id: userId,
1721
+ role
1722
+ });
1723
+ }
1724
+
1725
+ async removeMember(organizationId: string, userId: string): Promise<ApiResponse<{
1726
+ success: boolean;
1727
+ }>> {
1728
+ return this.proxy('remove_member', {
1729
+ organization_id: organizationId,
1730
+ user_id: userId
1731
+ });
1732
+ }
1733
+
1734
+ async shareProjectWithOrg(projectId: string, organizationId: string, permission?: string): Promise<ApiResponse<{
1735
+ success: boolean;
1736
+ }>> {
1737
+ return this.proxy('share_project_with_org', {
1738
+ project_id: projectId,
1739
+ organization_id: organizationId,
1740
+ permission
1741
+ });
1742
+ }
1743
+
1744
+ async unshareProject(projectId: string, organizationId: string): Promise<ApiResponse<{
1745
+ success: boolean;
1746
+ }>> {
1747
+ return this.proxy('unshare_project', {
1748
+ project_id: projectId,
1749
+ organization_id: organizationId
1750
+ });
1751
+ }
1752
+
1753
+ async leaveOrganization(organizationId: string): Promise<ApiResponse<{
1754
+ success: boolean;
1755
+ message: string;
1756
+ }>> {
1757
+ return this.proxy('leave_organization', { organization_id: organizationId });
1758
+ }
1759
+
1760
+ async updateProjectShare(projectId: string, organizationId: string, permission: string): Promise<ApiResponse<{
1761
+ success: boolean;
1762
+ share: { permission: string };
1763
+ }>> {
1764
+ return this.proxy('update_project_share', {
1765
+ project_id: projectId,
1766
+ organization_id: organizationId,
1767
+ permission
1768
+ });
1769
+ }
1770
+
1771
+ async listProjectShares(projectId: string): Promise<ApiResponse<{
1772
+ shares: Array<{
1773
+ id: string;
1774
+ permission: string;
1775
+ shared_at: string;
1776
+ organization: { id: string; name: string; slug: string };
1777
+ }>;
1778
+ count: number;
1779
+ }>> {
1780
+ return this.proxy('list_project_shares', { project_id: projectId });
1781
+ }
1782
+
1783
+ // ============================================================================
1784
+ // Body of work endpoints
1785
+ // ============================================================================
1786
+ async createBodyOfWork(projectId: string, params: {
1787
+ title: string;
1788
+ description?: string;
1789
+ auto_deploy_on_completion?: boolean;
1790
+ deploy_environment?: string;
1791
+ deploy_version_bump?: string;
1792
+ deploy_trigger?: string;
1793
+ }): Promise<ApiResponse<{
1794
+ success: boolean;
1795
+ body_of_work_id: string;
1796
+ }>> {
1797
+ return this.proxy('create_body_of_work', {
1798
+ project_id: projectId,
1799
+ ...params
1800
+ });
1801
+ }
1802
+
1803
+ async getBodyOfWork(bodyOfWorkId: string): Promise<ApiResponse<{
1804
+ body_of_work: {
1805
+ id: string;
1806
+ title: string;
1807
+ description?: string;
1808
+ status: string;
1809
+ auto_deploy_on_completion: boolean;
1810
+ };
1811
+ tasks: {
1812
+ pre: Array<{ id: string; title: string; status: string }>;
1813
+ core: Array<{ id: string; title: string; status: string }>;
1814
+ post: Array<{ id: string; title: string; status: string }>;
1815
+ };
1816
+ }>> {
1817
+ return this.proxy('get_body_of_work', { body_of_work_id: bodyOfWorkId });
1818
+ }
1819
+
1820
+ async getBodiesOfWork(projectId: string, params?: {
1821
+ status?: string;
1822
+ limit?: number;
1823
+ offset?: number;
1824
+ search_query?: string;
1825
+ }): Promise<ApiResponse<{
1826
+ bodies_of_work: Array<{
1827
+ id: string;
1828
+ title: string;
1829
+ description?: string;
1830
+ status: string;
1831
+ task_counts: { pre: number; core: number; post: number };
1832
+ }>;
1833
+ }>> {
1834
+ return this.proxy('get_bodies_of_work', {
1835
+ project_id: projectId,
1836
+ ...params
1837
+ });
1838
+ }
1839
+
1840
+ async updateBodyOfWork(bodyOfWorkId: string, updates: {
1841
+ title?: string;
1842
+ description?: string;
1843
+ auto_deploy_on_completion?: boolean;
1844
+ deploy_environment?: string;
1845
+ deploy_version_bump?: string;
1846
+ deploy_trigger?: string;
1847
+ }): Promise<ApiResponse<{
1848
+ success: boolean;
1849
+ }>> {
1850
+ return this.proxy('update_body_of_work', {
1851
+ body_of_work_id: bodyOfWorkId,
1852
+ ...updates
1853
+ });
1854
+ }
1855
+
1856
+ async deleteBodyOfWork(bodyOfWorkId: string): Promise<ApiResponse<{
1857
+ success: boolean;
1858
+ }>> {
1859
+ return this.proxy('delete_body_of_work', { body_of_work_id: bodyOfWorkId });
1860
+ }
1861
+
1862
+ async addTaskToBodyOfWork(bodyOfWorkId: string, taskId: string, phase?: string, orderIndex?: number): Promise<ApiResponse<{
1863
+ success: boolean;
1864
+ }>> {
1865
+ return this.proxy('add_task_to_body_of_work', {
1866
+ body_of_work_id: bodyOfWorkId,
1867
+ task_id: taskId,
1868
+ phase,
1869
+ order_index: orderIndex
1870
+ });
1871
+ }
1872
+
1873
+ async removeTaskFromBodyOfWork(taskId: string): Promise<ApiResponse<{
1874
+ success: boolean;
1875
+ }>> {
1876
+ return this.proxy('remove_task_from_body_of_work', { task_id: taskId });
1877
+ }
1878
+
1879
+ async activateBodyOfWork(bodyOfWorkId: string): Promise<ApiResponse<{
1880
+ success: boolean;
1881
+ status: string;
1882
+ }>> {
1883
+ return this.proxy('activate_body_of_work', { body_of_work_id: bodyOfWorkId });
1884
+ }
1885
+
1886
+ async addTaskDependency(bodyOfWorkId: string, taskId: string, dependsOnTaskId: string): Promise<ApiResponse<{
1887
+ success: boolean;
1888
+ }>> {
1889
+ return this.proxy('add_task_dependency', {
1890
+ body_of_work_id: bodyOfWorkId,
1891
+ task_id: taskId,
1892
+ depends_on_task_id: dependsOnTaskId
1893
+ });
1894
+ }
1895
+
1896
+ async removeTaskDependency(taskId: string, dependsOnTaskId: string): Promise<ApiResponse<{
1897
+ success: boolean;
1898
+ }>> {
1899
+ return this.proxy('remove_task_dependency', {
1900
+ task_id: taskId,
1901
+ depends_on_task_id: dependsOnTaskId
1902
+ });
1903
+ }
1904
+
1905
+ async getTaskDependencies(params: {
1906
+ body_of_work_id?: string;
1907
+ task_id?: string;
1908
+ }): Promise<ApiResponse<{
1909
+ dependencies: Array<{
1910
+ id: string;
1911
+ task_id: string;
1912
+ depends_on_task_id: string;
1913
+ created_at: string;
1914
+ }>;
1915
+ }>> {
1916
+ return this.proxy('get_task_dependencies', params);
1917
+ }
1918
+
1919
+ async getNextBodyOfWorkTask(bodyOfWorkId: string): Promise<ApiResponse<{
1920
+ task?: {
1921
+ id: string;
1922
+ title: string;
1923
+ priority: number;
1924
+ phase: string;
1925
+ } | null;
1926
+ message?: string;
1927
+ }>> {
1928
+ return this.proxy('get_next_body_of_work_task', { body_of_work_id: bodyOfWorkId });
1929
+ }
1930
+
1931
+ // ============================================================================
1932
+ // Git issues endpoints
1933
+ // ============================================================================
1934
+ async addGitIssue(projectId: string, params: {
1935
+ issue_type: string;
1936
+ branch: string;
1937
+ target_branch?: string;
1938
+ pr_url?: string;
1939
+ conflicting_files?: string[];
1940
+ error_message?: string;
1941
+ task_id?: string;
1942
+ }, sessionId?: string): Promise<ApiResponse<{
1943
+ success: boolean;
1944
+ git_issue_id: string;
1945
+ }>> {
1946
+ return this.proxy('add_git_issue', {
1947
+ project_id: projectId,
1948
+ ...params
1949
+ }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1950
+ }
1951
+
1952
+ async resolveGitIssue(gitIssueId: string, params?: {
1953
+ resolution_note?: string;
1954
+ auto_resolved?: boolean;
1955
+ }, sessionId?: string): Promise<ApiResponse<{
1956
+ success: boolean;
1957
+ git_issue_id: string;
1958
+ }>> {
1959
+ return this.proxy('resolve_git_issue', {
1960
+ git_issue_id: gitIssueId,
1961
+ ...params
1962
+ }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
1963
+ }
1964
+
1965
+ async getGitIssues(projectId: string, params?: {
1966
+ status?: string;
1967
+ issue_type?: string;
1968
+ branch?: string;
1969
+ limit?: number;
1970
+ offset?: number;
1971
+ }): Promise<ApiResponse<{
1972
+ git_issues: Array<{
1973
+ id: string;
1974
+ issue_type: string;
1975
+ branch: string;
1976
+ target_branch?: string;
1977
+ pr_url?: string;
1978
+ conflicting_files?: string[];
1979
+ error_message?: string;
1980
+ status: string;
1981
+ created_at: string;
1982
+ resolved_at?: string;
1983
+ }>;
1984
+ }>> {
1985
+ return this.proxy('get_git_issues', {
1986
+ project_id: projectId,
1987
+ ...params
1988
+ });
1989
+ }
1990
+
1991
+ async deleteGitIssue(gitIssueId: string): Promise<ApiResponse<{
1992
+ success: boolean;
1993
+ }>> {
1994
+ return this.proxy('delete_git_issue', { git_issue_id: gitIssueId });
1995
+ }
1996
+
1997
+ // ============================================================================
1998
+ // Cost tracking endpoints
1999
+ // ============================================================================
2000
+ async getCostSummary(projectId: string, params?: {
2001
+ period?: 'daily' | 'weekly' | 'monthly';
2002
+ limit?: number;
2003
+ }): Promise<ApiResponse<{
2004
+ summaries: Array<{
2005
+ date: string;
2006
+ total_cost: number;
2007
+ input_tokens: number;
2008
+ output_tokens: number;
2009
+ }>;
2010
+ }>> {
2011
+ return this.proxy('get_cost_summary', {
2012
+ project_id: projectId,
2013
+ ...params
2014
+ });
2015
+ }
2016
+
2017
+ async getCostAlerts(): Promise<ApiResponse<{
2018
+ alerts: Array<{
2019
+ id: string;
2020
+ threshold_amount: number;
2021
+ threshold_period: string;
2022
+ alert_type: string;
2023
+ enabled: boolean;
2024
+ }>;
2025
+ }>> {
2026
+ return this.proxy('get_cost_alerts', {});
2027
+ }
2028
+
2029
+ async addCostAlert(params: {
2030
+ threshold_amount: number;
2031
+ threshold_period: 'daily' | 'weekly' | 'monthly';
2032
+ alert_type?: 'warning' | 'critical';
2033
+ project_id?: string;
2034
+ }): Promise<ApiResponse<{
2035
+ success: boolean;
2036
+ alert_id: string;
2037
+ }>> {
2038
+ return this.proxy('add_cost_alert', params);
2039
+ }
2040
+
2041
+ async updateCostAlert(alertId: string, updates: {
2042
+ threshold_amount?: number;
2043
+ threshold_period?: string;
2044
+ alert_type?: string;
2045
+ enabled?: boolean;
2046
+ }): Promise<ApiResponse<{
2047
+ success: boolean;
2048
+ }>> {
2049
+ return this.proxy('update_cost_alert', {
2050
+ alert_id: alertId,
2051
+ ...updates
2052
+ });
2053
+ }
2054
+
2055
+ async deleteCostAlert(alertId: string): Promise<ApiResponse<{
2056
+ success: boolean;
2057
+ }>> {
2058
+ return this.proxy('delete_cost_alert', { alert_id: alertId });
2059
+ }
2060
+
2061
+ async getTaskCosts(projectId: string, limit?: number): Promise<ApiResponse<{
2062
+ task_costs: Array<{
2063
+ task_id: string;
2064
+ title: string;
2065
+ total_cost: number;
2066
+ input_tokens: number;
2067
+ output_tokens: number;
2068
+ }>;
2069
+ }>> {
2070
+ return this.proxy('get_task_costs', {
2071
+ project_id: projectId,
2072
+ limit
2073
+ });
2074
+ }
2075
+
2076
+ async getBodyOfWorkCosts(params: {
2077
+ body_of_work_id?: string;
2078
+ project_id?: string;
2079
+ }): Promise<ApiResponse<{
2080
+ bodies_of_work: Array<{
2081
+ body_of_work_id: string;
2082
+ title: string;
2083
+ project_id: string;
2084
+ status: string;
2085
+ task_count: number;
2086
+ total_cost_usd: number;
2087
+ total_tokens: number;
2088
+ pre_phase_cost_usd: number;
2089
+ core_phase_cost_usd: number;
2090
+ post_phase_cost_usd: number;
2091
+ model_breakdown: Record<string, { input: number; output: number }>;
2092
+ }>;
2093
+ count: number;
2094
+ totals: {
2095
+ total_cost_usd: number;
2096
+ total_tokens: number;
2097
+ total_tasks: number;
2098
+ };
2099
+ }>> {
2100
+ return this.proxy('get_body_of_work_costs', params);
2101
+ }
2102
+
2103
+ async getSprintCosts(params: {
2104
+ sprint_id?: string;
2105
+ project_id?: string;
2106
+ }): Promise<ApiResponse<{
2107
+ sprints: Array<{
2108
+ sprint_id: string;
2109
+ title: string;
2110
+ project_id: string;
2111
+ status: string;
2112
+ sprint_number: number;
2113
+ task_count: number;
2114
+ total_cost_usd: number;
2115
+ total_tokens: number;
2116
+ cost_per_story_point: number | null;
2117
+ committed_points: number;
2118
+ velocity_points: number;
2119
+ model_breakdown: Record<string, { input: number; output: number }>;
2120
+ }>;
2121
+ count: number;
2122
+ totals: {
2123
+ total_cost_usd: number;
2124
+ total_tokens: number;
2125
+ total_tasks: number;
2126
+ total_velocity_points: number;
2127
+ avg_cost_per_point: number | null;
2128
+ };
2129
+ }>> {
2130
+ return this.proxy('get_sprint_costs', params);
2131
+ }
2132
+
2133
+ async getTokenUsage(): Promise<ApiResponse<{
2134
+ session_tokens: number;
2135
+ estimated_cost: number;
2136
+ }>> {
2137
+ return this.proxy('get_token_usage', {});
2138
+ }
2139
+
2140
+ // ============================================================================
2141
+ // Batch operation endpoints
2142
+ // ============================================================================
2143
+ async batchUpdateTasks(updates: Array<{
2144
+ task_id: string;
2145
+ status?: string;
2146
+ priority?: number;
2147
+ progress_percentage?: number;
2148
+ progress_note?: string;
2149
+ }>): Promise<ApiResponse<{
2150
+ success: boolean;
2151
+ updated_count: number;
2152
+ }>> {
2153
+ return this.proxy('batch_update_tasks', { updates });
2154
+ }
2155
+
2156
+ async batchCompleteTasks(completions: Array<{
2157
+ task_id: string;
2158
+ summary?: string;
2159
+ }>): Promise<ApiResponse<{
2160
+ success: boolean;
2161
+ completed_count: number;
2162
+ next_task?: { id: string; title: string } | null;
2163
+ }>> {
2164
+ return this.proxy('batch_complete_tasks', { completions });
2165
+ }
2166
+
2167
+ // ============================================================================
2168
+ // Deployment requirements and scheduling endpoints
2169
+ // ============================================================================
2170
+ async addDeploymentRequirement(projectId: string, params: {
2171
+ type: string;
2172
+ title: string;
2173
+ description?: string;
2174
+ file_path?: string;
2175
+ stage?: string;
2176
+ blocking?: boolean;
2177
+ recurring?: boolean;
2178
+ }): Promise<ApiResponse<{
2179
+ success: boolean;
2180
+ requirement_id: string;
2181
+ }>> {
2182
+ return this.proxy('add_deployment_requirement', {
2183
+ project_id: projectId,
2184
+ ...params
2185
+ });
2186
+ }
2187
+
2188
+ async getDeploymentRequirements(projectId: string, params?: {
2189
+ status?: string;
2190
+ stage?: string;
2191
+ limit?: number;
2192
+ offset?: number;
2193
+ }): Promise<ApiResponse<{
2194
+ requirements: Array<{
2195
+ id: string;
2196
+ type: string;
2197
+ title: string;
2198
+ description?: string;
2199
+ status: string;
2200
+ stage: string;
2201
+ }>;
2202
+ total_count: number;
2203
+ has_more: boolean;
2204
+ }>> {
2205
+ return this.proxy('get_deployment_requirements', {
2206
+ project_id: projectId,
2207
+ ...params
2208
+ });
2209
+ }
2210
+
2211
+ async getDeploymentRequirementsStats(projectId: string): Promise<ApiResponse<{
2212
+ total: number;
2213
+ by_status: Record<string, number>;
2214
+ by_stage: Record<string, number>;
2215
+ by_type: Record<string, number>;
2216
+ }>> {
2217
+ return this.proxy('get_deployment_requirements_stats', {
2218
+ project_id: projectId
2219
+ });
2220
+ }
2221
+
2222
+ async completeDeploymentRequirement(requirementId: string): Promise<ApiResponse<{
2223
+ success: boolean;
2224
+ }>> {
2225
+ return this.proxy('complete_deployment_requirement', { requirement_id: requirementId });
2226
+ }
2227
+
2228
+ async reorderDeploymentRequirements(projectId: string, params: {
2229
+ stage: string;
2230
+ requirement_ids: string[];
2231
+ }): Promise<ApiResponse<{ success: boolean; reordered: number }>> {
2232
+ return this.proxy('reorder_deployment_requirements', {
2233
+ project_id: projectId,
2234
+ ...params,
2235
+ });
2236
+ }
2237
+
2238
+ async scheduleDeployment(projectId: string, params: {
2239
+ scheduled_at: string;
2240
+ schedule_type?: string;
2241
+ environment?: string;
2242
+ version_bump?: string;
2243
+ auto_trigger?: boolean;
2244
+ hours_interval?: number;
2245
+ notes?: string;
2246
+ git_ref?: string;
2247
+ }): Promise<ApiResponse<{
2248
+ success: boolean;
2249
+ schedule_id: string;
2250
+ }>> {
2251
+ return this.proxy('schedule_deployment', {
2252
+ project_id: projectId,
2253
+ ...params
2254
+ });
2255
+ }
2256
+
2257
+ async getScheduledDeployments(projectId: string, params?: {
2258
+ includeDisabled?: boolean;
2259
+ limit?: number;
2260
+ offset?: number;
2261
+ }): Promise<ApiResponse<{
2262
+ schedules: Array<{
2263
+ id: string;
2264
+ scheduled_at: string;
2265
+ schedule_type: string;
2266
+ hours_interval: number;
2267
+ environment: string;
2268
+ version_bump: string;
2269
+ auto_trigger: boolean;
2270
+ enabled: boolean;
2271
+ git_ref?: string;
2272
+ notes?: string;
2273
+ }>;
2274
+ total_count: number;
2275
+ has_more: boolean;
2276
+ }>> {
2277
+ return this.proxy('get_scheduled_deployments', {
2278
+ project_id: projectId,
2279
+ include_disabled: params?.includeDisabled,
2280
+ limit: params?.limit,
2281
+ offset: params?.offset
2282
+ });
2283
+ }
2284
+
2285
+ async updateScheduledDeployment(scheduleId: string, updates: {
2286
+ scheduled_at?: string;
2287
+ schedule_type?: string;
2288
+ hours_interval?: number;
2289
+ environment?: string;
2290
+ version_bump?: string;
2291
+ auto_trigger?: boolean;
2292
+ enabled?: boolean;
2293
+ git_ref?: string;
2294
+ notes?: string;
2295
+ }): Promise<ApiResponse<{
2296
+ success: boolean;
2297
+ schedule_id: string;
2298
+ }>> {
2299
+ return this.proxy('update_scheduled_deployment', {
2300
+ schedule_id: scheduleId,
2301
+ ...updates
2302
+ });
2303
+ }
2304
+
2305
+ async deleteScheduledDeployment(scheduleId: string): Promise<ApiResponse<{
2306
+ success: boolean;
2307
+ }>> {
2308
+ return this.proxy('delete_scheduled_deployment', { schedule_id: scheduleId });
2309
+ }
2310
+
2311
+ async triggerScheduledDeployment(scheduleId: string, sessionId?: string): Promise<ApiResponse<{
2312
+ success: boolean;
2313
+ deployment_id?: string;
2314
+ schedule_id: string;
2315
+ schedule_type: string;
2316
+ next_scheduled_at?: string;
2317
+ message?: string;
2318
+ }>> {
2319
+ return this.proxy('trigger_scheduled_deployment', { schedule_id: scheduleId }, sessionId ? {
2320
+ session_id: sessionId,
2321
+ persona: null,
2322
+ instance_id: ''
2323
+ } : undefined);
2324
+ }
2325
+
2326
+ async checkDueDeployments(projectId: string): Promise<ApiResponse<{
2327
+ due_schedules: Array<{
2328
+ id: string;
2329
+ scheduled_at: string;
2330
+ environment: string;
2331
+ version_bump: string;
2332
+ schedule_type: string;
2333
+ }>;
2334
+ count: number;
2335
+ }>> {
2336
+ return this.proxy('check_due_deployments', { project_id: projectId });
2337
+ }
2338
+
2339
+ // ============================================================================
2340
+ // Project README endpoint
2341
+ // ============================================================================
2342
+ async updateProjectReadme(projectId: string, readmeContent: string): Promise<ApiResponse<{
2343
+ success: boolean;
2344
+ project_id: string;
2345
+ }>> {
2346
+ return this.proxy('update_project_readme', {
2347
+ project_id: projectId,
2348
+ readme_content: readmeContent
2349
+ });
2350
+ }
2351
+
2352
+ async getProjectSummary(projectId: string): Promise<ApiResponse<{
2353
+ project: {
2354
+ id: string;
2355
+ name: string;
2356
+ description?: string;
2357
+ goal?: string;
2358
+ status: string;
2359
+ git_url?: string;
2360
+ tech_stack?: string[];
2361
+ git_workflow?: string;
2362
+ };
2363
+ tasks: {
2364
+ total: number;
2365
+ by_status: Record<string, number>;
2366
+ };
2367
+ blockers: {
2368
+ total: number;
2369
+ open: number;
2370
+ resolved: number;
2371
+ };
2372
+ findings: {
2373
+ total: number;
2374
+ by_severity: Record<string, number>;
2375
+ by_status: Record<string, number>;
2376
+ };
2377
+ agents: {
2378
+ active_count: number;
2379
+ total_tokens_this_session: number;
2380
+ total_tool_calls: number;
2381
+ };
2382
+ activity: {
2383
+ tasks_completed_today: number;
2384
+ progress_entries_today: number;
2385
+ };
2386
+ deployment: {
2387
+ active: boolean;
2388
+ status: string | null;
2389
+ last_deployment_at: string | null;
2390
+ };
2391
+ planning: {
2392
+ active_sprints: number;
2393
+ active_bodies_of_work: number;
2394
+ pending_deployment_requirements: number;
2395
+ };
2396
+ }>> {
2397
+ return this.proxy('get_project_summary', { project_id: projectId });
2398
+ }
2399
+
2400
+ // ============================================================================
2401
+ // Help Topics (database-backed)
2402
+ // ============================================================================
2403
+ async getHelpTopic(slug: string): Promise<ApiResponse<{
2404
+ slug: string;
2405
+ title: string;
2406
+ content: string;
2407
+ } | null>> {
2408
+ return this.proxy('get_help_topic', { slug });
2409
+ }
2410
+
2411
+ async getHelpTopics(): Promise<ApiResponse<Array<{
2412
+ slug: string;
2413
+ title: string;
2414
+ }>>> {
2415
+ return this.proxy('get_help_topics', {});
2416
+ }
2417
+
2418
+ // ============================================================================
2419
+ // File Checkout endpoints (multi-agent coordination)
2420
+ // ============================================================================
2421
+ async checkoutFile(projectId: string, filePath: string, reason?: string, sessionId?: string): Promise<ApiResponse<{
2422
+ success: boolean;
2423
+ checkout_id: string;
2424
+ file_path: string;
2425
+ already_checked_out?: boolean;
2426
+ message: string;
2427
+ }>> {
2428
+ return this.proxy('checkout_file', {
2429
+ project_id: projectId,
2430
+ file_path: filePath,
2431
+ reason
2432
+ }, sessionId ? {
2433
+ session_id: sessionId,
2434
+ persona: null,
2435
+ instance_id: ''
2436
+ } : undefined);
2437
+ }
2438
+
2439
+ async checkinFile(params: {
2440
+ checkout_id?: string;
2441
+ project_id?: string;
2442
+ file_path?: string;
2443
+ summary?: string;
2444
+ }, sessionId?: string): Promise<ApiResponse<{
2445
+ success: boolean;
2446
+ checkout_id: string;
2447
+ message: string;
2448
+ }>> {
2449
+ return this.proxy('checkin_file', params, sessionId ? {
2450
+ session_id: sessionId,
2451
+ persona: null,
2452
+ instance_id: ''
2453
+ } : undefined);
2454
+ }
2455
+
2456
+ async getFileCheckouts(projectId: string, options?: {
2457
+ status?: string;
2458
+ file_path?: string;
2459
+ limit?: number;
2460
+ offset?: number;
2461
+ }): Promise<ApiResponse<{
2462
+ checkouts: Array<{
2463
+ id: string;
2464
+ file_path: string;
2465
+ status: string;
2466
+ checked_out_at: string;
2467
+ checkout_reason?: string;
2468
+ checked_in_at?: string;
2469
+ checkin_summary?: string;
2470
+ checked_out_by?: string;
2471
+ }>;
2472
+ }>> {
2473
+ return this.proxy('get_file_checkouts', {
2474
+ project_id: projectId,
2475
+ ...options
2476
+ });
2477
+ }
2478
+
2479
+ async abandonCheckout(params: {
2480
+ checkout_id?: string;
2481
+ project_id?: string;
2482
+ file_path?: string;
2483
+ }): Promise<ApiResponse<{
2484
+ success: boolean;
2485
+ checkout_id: string;
2486
+ message: string;
2487
+ }>> {
2488
+ return this.proxy('abandon_checkout', params);
2489
+ }
2490
+
2491
+ async getFileCheckoutsStats(projectId: string): Promise<ApiResponse<{
2492
+ total: number;
2493
+ by_status: Record<string, number>;
2494
+ }>> {
2495
+ return this.proxy('get_file_checkouts_stats', { project_id: projectId });
2496
+ }
2497
+
2498
+ // ============================================================================
2499
+ // Worktree Management
2500
+ // ============================================================================
2501
+
2502
+ async getStaleWorktrees(projectId: string, params?: {
2503
+ hostname?: string;
2504
+ limit?: number;
2505
+ offset?: number;
2506
+ }): Promise<ApiResponse<{
2507
+ project_id: string;
2508
+ project_name: string;
2509
+ hostname_filter: string | null;
2510
+ stale_worktrees: Array<{
2511
+ task_id: string;
2512
+ task_title: string;
2513
+ worktree_path: string;
2514
+ worktree_hostname: string | null;
2515
+ git_branch: string | null;
2516
+ status: string;
2517
+ completed_at: string | null;
2518
+ updated_at: string;
2519
+ pr_url: string | null;
2520
+ stale_reason: 'task_finished' | 'potentially_abandoned';
2521
+ can_cleanup_locally: boolean;
2522
+ }>;
2523
+ count: number;
2524
+ local_count: number;
2525
+ remote_count: number;
2526
+ total_count: number;
2527
+ has_more: boolean;
2528
+ cleanup_instructions: string[] | null;
2529
+ remote_worktree_note: string | null;
2530
+ }>> {
2531
+ const queryParams = new URLSearchParams({ project_id: projectId });
2532
+ if (params?.hostname !== undefined) queryParams.set('hostname', params.hostname);
2533
+ if (params?.limit !== undefined) queryParams.set('limit', String(params.limit));
2534
+ if (params?.offset !== undefined) queryParams.set('offset', String(params.offset));
2535
+ return this.request('GET', `/api/mcp/worktrees/stale?${queryParams.toString()}`);
2536
+ }
2537
+
2538
+ async clearWorktreePath(taskId: string): Promise<ApiResponse<{
2539
+ success: boolean;
2540
+ task_id: string;
2541
+ }>> {
2542
+ return this.request('PATCH', `/api/mcp/tasks/${taskId}`, { worktree_path: null });
2543
+ }
2544
+
2545
+ // ============================================================================
2546
+ // Connector endpoints
2547
+ // ============================================================================
2548
+
2549
+ async getConnectors(projectId: string, params?: {
2550
+ type?: string;
2551
+ status?: string;
2552
+ limit?: number;
2553
+ offset?: number;
2554
+ }): Promise<ApiResponse<{
2555
+ connectors: Array<{
2556
+ id: string;
2557
+ name: string;
2558
+ type: string;
2559
+ description?: string;
2560
+ status: string;
2561
+ events: Record<string, boolean>;
2562
+ events_sent: number;
2563
+ last_triggered_at?: string;
2564
+ last_error?: string;
2565
+ last_error_at?: string;
2566
+ created_at: string;
2567
+ }>;
2568
+ total_count: number;
2569
+ has_more: boolean;
2570
+ }>> {
2571
+ return this.proxy('get_connectors', {
2572
+ project_id: projectId,
2573
+ ...params
2574
+ });
2575
+ }
2576
+
2577
+ async getConnector(connectorId: string): Promise<ApiResponse<{
2578
+ connector: {
2579
+ id: string;
2580
+ name: string;
2581
+ type: string;
2582
+ description?: string;
2583
+ config: Record<string, unknown>;
2584
+ events: Record<string, boolean>;
2585
+ status: string;
2586
+ events_sent: number;
2587
+ last_triggered_at?: string;
2588
+ last_error?: string;
2589
+ last_error_at?: string;
2590
+ created_at: string;
2591
+ };
2592
+ }>> {
2593
+ return this.proxy('get_connector', { connector_id: connectorId });
2594
+ }
2595
+
2596
+ async addConnector(projectId: string, params: {
2597
+ name: string;
2598
+ type: string;
2599
+ description?: string;
2600
+ config?: Record<string, unknown>;
2601
+ events?: Record<string, boolean>;
2602
+ }): Promise<ApiResponse<{
2603
+ success: boolean;
2604
+ connector_id: string;
2605
+ }>> {
2606
+ return this.proxy('add_connector', {
2607
+ project_id: projectId,
2608
+ ...params
2609
+ });
2610
+ }
2611
+
2612
+ async updateConnector(connectorId: string, updates: {
2613
+ name?: string;
2614
+ description?: string;
2615
+ config?: Record<string, unknown>;
2616
+ events?: Record<string, boolean>;
2617
+ status?: string;
2618
+ }): Promise<ApiResponse<{
2619
+ success: boolean;
2620
+ connector_id: string;
2621
+ }>> {
2622
+ return this.proxy('update_connector', {
2623
+ connector_id: connectorId,
2624
+ ...updates
2625
+ });
2626
+ }
2627
+
2628
+ async deleteConnector(connectorId: string): Promise<ApiResponse<{
2629
+ success: boolean;
2630
+ }>> {
2631
+ return this.proxy('delete_connector', { connector_id: connectorId });
2632
+ }
2633
+
2634
+ async testConnector(connectorId: string): Promise<ApiResponse<{
2635
+ success: boolean;
2636
+ event_id: string;
2637
+ status?: number;
2638
+ error?: string;
2639
+ }>> {
2640
+ return this.proxy('test_connector', { connector_id: connectorId });
2641
+ }
2642
+
2643
+ async getConnectorEvents(params: {
2644
+ connector_id?: string;
2645
+ project_id?: string;
2646
+ status?: string;
2647
+ limit?: number;
2648
+ offset?: number;
2649
+ }): Promise<ApiResponse<{
2650
+ events: Array<{
2651
+ id: string;
2652
+ connector_id: string;
2653
+ event_type: string;
2654
+ status: string;
2655
+ response_status?: number;
2656
+ error_message?: string;
2657
+ attempts: number;
2658
+ created_at: string;
2659
+ sent_at?: string;
2660
+ }>;
2661
+ total_count: number;
2662
+ has_more: boolean;
2663
+ }>> {
2664
+ return this.proxy('get_connector_events', params);
2665
+ }
2666
+
2667
+ // ============================================================================
2668
+ // Agent onboarding endpoints
2669
+ // ============================================================================
2670
+
2671
+ /**
2672
+ * Confirm that agent setup is complete for a project.
2673
+ * This marks the agent type as onboarded so future sessions don't receive setup instructions.
2674
+ */
2675
+ async confirmAgentSetup(projectId: string, agentType: string): Promise<ApiResponse<{
2676
+ success: boolean;
2677
+ project_id: string;
2678
+ agent_type: string;
2679
+ }>> {
2680
+ return this.proxy('confirm_agent_setup', {
2681
+ project_id: projectId,
2682
+ agent_type: agentType
2683
+ });
2684
+ }
2685
+
2686
+ async sendProjectMessage(projectId: string, message: string, sessionId?: string): Promise<ApiResponse<{
2687
+ success: boolean;
2688
+ message_id: string;
2689
+ sent_at: string;
2690
+ }>> {
2691
+ return this.proxy('send_project_message', { project_id: projectId, message }, sessionId ? { session_id: sessionId, persona: null, instance_id: '' } : undefined);
2692
+ }
2693
+
2694
+ async getProjectMessages(projectId: string, limit?: number): Promise<ApiResponse<{
2695
+ messages: Array<{
2696
+ id: string;
2697
+ sender_type: string;
2698
+ sender_name: string | null;
2699
+ content: string;
2700
+ created_at: string;
2701
+ }>;
2702
+ count: number;
2703
+ }>> {
2704
+ return this.proxy('get_project_messages', {
2705
+ project_id: projectId,
2706
+ ...(limit !== undefined && { limit }),
2707
+ });
2708
+ }
2709
+ }
2710
+
2711
+ // Singleton instance
2712
+ let apiClient: VibescopeApiClient | null = null;
2713
+
2714
+ export function getApiClient(): VibescopeApiClient {
2715
+ if (!apiClient) {
2716
+ const apiKey = process.env.VIBESCOPE_API_KEY;
2717
+ if (!apiKey) {
2718
+ throw new Error('VIBESCOPE_API_KEY environment variable is required');
2719
+ }
2720
+ apiClient = new VibescopeApiClient({ apiKey });
2721
+ }
2722
+ return apiClient;
2723
+ }
2724
+
2725
+ export function initApiClient(config: ApiClientConfig): VibescopeApiClient {
2726
+ apiClient = new VibescopeApiClient(config);
2727
+ return apiClient;
2728
+ }