@vibescope/mcp-server 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) 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/session.js +3 -1
  7. package/dist/handlers/tasks.js +7 -1
  8. package/dist/handlers/tool-docs.js +1216 -1216
  9. package/dist/index.js +73 -73
  10. package/dist/templates/agent-guidelines.d.ts +1 -1
  11. package/dist/templates/agent-guidelines.js +205 -205
  12. package/dist/templates/help-content.js +1621 -1621
  13. package/dist/tools/bodies-of-work.js +6 -6
  14. package/dist/tools/cloud-agents.js +22 -22
  15. package/dist/tools/milestones.js +2 -2
  16. package/dist/tools/requests.js +1 -1
  17. package/dist/tools/session.js +11 -11
  18. package/dist/tools/sprints.js +9 -9
  19. package/dist/tools/tasks.js +43 -35
  20. package/dist/tools/worktrees.js +14 -14
  21. package/dist/utils.js +11 -11
  22. package/docs/TOOLS.md +2687 -2685
  23. package/package.json +53 -53
  24. package/scripts/generate-docs.ts +212 -212
  25. package/scripts/version-bump.ts +203 -203
  26. package/src/api-client/blockers.ts +86 -86
  27. package/src/api-client/bodies-of-work.ts +194 -194
  28. package/src/api-client/chat.ts +50 -50
  29. package/src/api-client/connectors.ts +152 -152
  30. package/src/api-client/cost.ts +185 -185
  31. package/src/api-client/decisions.ts +87 -87
  32. package/src/api-client/deployment.ts +313 -313
  33. package/src/api-client/discovery.ts +81 -81
  34. package/src/api-client/fallback.ts +52 -52
  35. package/src/api-client/file-checkouts.ts +115 -115
  36. package/src/api-client/findings.ts +100 -100
  37. package/src/api-client/git-issues.ts +88 -88
  38. package/src/api-client/ideas.ts +112 -112
  39. package/src/api-client/index.ts +592 -592
  40. package/src/api-client/milestones.ts +83 -83
  41. package/src/api-client/organizations.ts +185 -185
  42. package/src/api-client/progress.ts +94 -94
  43. package/src/api-client/project.ts +181 -181
  44. package/src/api-client/requests.ts +54 -54
  45. package/src/api-client/session.ts +220 -220
  46. package/src/api-client/sprints.ts +227 -227
  47. package/src/api-client/subtasks.ts +57 -57
  48. package/src/api-client/tasks.ts +451 -450
  49. package/src/api-client/types.ts +32 -32
  50. package/src/api-client/validation.ts +60 -60
  51. package/src/api-client/worktrees.ts +53 -53
  52. package/src/api-client.test.ts +847 -847
  53. package/src/api-client.ts +2728 -2728
  54. package/src/cli-init.ts +558 -558
  55. package/src/cli.test.ts +284 -284
  56. package/src/cli.ts +204 -204
  57. package/src/handlers/__test-setup__.ts +240 -240
  58. package/src/handlers/__test-utils__.ts +89 -89
  59. package/src/handlers/blockers.test.ts +468 -468
  60. package/src/handlers/blockers.ts +172 -172
  61. package/src/handlers/bodies-of-work.test.ts +704 -704
  62. package/src/handlers/bodies-of-work.ts +526 -526
  63. package/src/handlers/chat.test.ts +185 -185
  64. package/src/handlers/chat.ts +101 -101
  65. package/src/handlers/cloud-agents.test.ts +438 -438
  66. package/src/handlers/cloud-agents.ts +156 -156
  67. package/src/handlers/connectors.test.ts +834 -834
  68. package/src/handlers/connectors.ts +229 -229
  69. package/src/handlers/cost.test.ts +462 -462
  70. package/src/handlers/cost.ts +285 -285
  71. package/src/handlers/decisions.test.ts +382 -382
  72. package/src/handlers/decisions.ts +153 -153
  73. package/src/handlers/deployment.test.ts +551 -551
  74. package/src/handlers/deployment.ts +570 -570
  75. package/src/handlers/discovery.test.ts +206 -206
  76. package/src/handlers/discovery.ts +433 -433
  77. package/src/handlers/fallback.test.ts +537 -537
  78. package/src/handlers/fallback.ts +194 -194
  79. package/src/handlers/file-checkouts.test.ts +750 -750
  80. package/src/handlers/file-checkouts.ts +185 -185
  81. package/src/handlers/findings.test.ts +633 -633
  82. package/src/handlers/findings.ts +239 -239
  83. package/src/handlers/git-issues.test.ts +631 -631
  84. package/src/handlers/git-issues.ts +136 -136
  85. package/src/handlers/ideas.test.ts +644 -644
  86. package/src/handlers/ideas.ts +207 -207
  87. package/src/handlers/index.ts +93 -93
  88. package/src/handlers/milestones.test.ts +475 -475
  89. package/src/handlers/milestones.ts +180 -180
  90. package/src/handlers/organizations.test.ts +826 -826
  91. package/src/handlers/organizations.ts +315 -315
  92. package/src/handlers/progress.test.ts +269 -269
  93. package/src/handlers/progress.ts +77 -77
  94. package/src/handlers/project.test.ts +546 -546
  95. package/src/handlers/project.ts +245 -245
  96. package/src/handlers/requests.test.ts +303 -303
  97. package/src/handlers/requests.ts +99 -99
  98. package/src/handlers/roles.test.ts +305 -305
  99. package/src/handlers/roles.ts +219 -219
  100. package/src/handlers/session.test.ts +998 -998
  101. package/src/handlers/session.ts +1107 -1105
  102. package/src/handlers/sprints.test.ts +732 -732
  103. package/src/handlers/sprints.ts +537 -537
  104. package/src/handlers/tasks.test.ts +931 -931
  105. package/src/handlers/tasks.ts +1144 -1137
  106. package/src/handlers/tool-categories.test.ts +66 -66
  107. package/src/handlers/tool-docs.test.ts +511 -511
  108. package/src/handlers/tool-docs.ts +1595 -1595
  109. package/src/handlers/types.test.ts +259 -259
  110. package/src/handlers/types.ts +176 -176
  111. package/src/handlers/validation.test.ts +582 -582
  112. package/src/handlers/validation.ts +164 -164
  113. package/src/handlers/version.ts +63 -63
  114. package/src/index.test.ts +674 -674
  115. package/src/index.ts +884 -884
  116. package/src/setup.test.ts +243 -243
  117. package/src/setup.ts +410 -410
  118. package/src/templates/agent-guidelines.ts +233 -233
  119. package/src/templates/help-content.ts +1751 -1751
  120. package/src/token-tracking.test.ts +463 -463
  121. package/src/token-tracking.ts +167 -167
  122. package/src/tools/blockers.ts +122 -122
  123. package/src/tools/bodies-of-work.ts +283 -283
  124. package/src/tools/chat.ts +72 -72
  125. package/src/tools/cloud-agents.ts +101 -101
  126. package/src/tools/connectors.ts +191 -191
  127. package/src/tools/cost.ts +111 -111
  128. package/src/tools/decisions.ts +111 -111
  129. package/src/tools/deployment.ts +455 -455
  130. package/src/tools/discovery.ts +76 -76
  131. package/src/tools/fallback.ts +111 -111
  132. package/src/tools/features.ts +154 -154
  133. package/src/tools/file-checkouts.ts +145 -145
  134. package/src/tools/findings.ts +101 -101
  135. package/src/tools/git-issues.ts +130 -130
  136. package/src/tools/ideas.ts +162 -162
  137. package/src/tools/index.ts +145 -145
  138. package/src/tools/milestones.ts +118 -118
  139. package/src/tools/organizations.ts +224 -224
  140. package/src/tools/persona-templates.ts +25 -25
  141. package/src/tools/progress.ts +73 -73
  142. package/src/tools/project.ts +210 -210
  143. package/src/tools/requests.ts +68 -68
  144. package/src/tools/roles.ts +112 -112
  145. package/src/tools/session.ts +181 -181
  146. package/src/tools/sprints.ts +298 -298
  147. package/src/tools/tasks.ts +583 -575
  148. package/src/tools/tools.test.ts +222 -222
  149. package/src/tools/types.ts +9 -9
  150. package/src/tools/validation.ts +75 -75
  151. package/src/tools/version.ts +34 -34
  152. package/src/tools/worktrees.ts +66 -66
  153. package/src/tools.test.ts +416 -416
  154. package/src/utils.test.ts +1014 -1014
  155. package/src/utils.ts +586 -586
  156. package/src/validators.test.ts +223 -223
  157. package/src/validators.ts +249 -249
  158. package/src/version.ts +162 -162
  159. package/tsconfig.json +16 -16
  160. package/vitest.config.ts +14 -14
  161. package/dist/tools.d.ts +0 -2
  162. 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
+ }