@vibescope/mcp-server 0.2.2 → 0.2.4

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 (80) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +35 -20
  3. package/dist/api-client.d.ts +276 -8
  4. package/dist/api-client.js +128 -9
  5. package/dist/handlers/blockers.d.ts +11 -0
  6. package/dist/handlers/blockers.js +37 -2
  7. package/dist/handlers/bodies-of-work.d.ts +2 -0
  8. package/dist/handlers/bodies-of-work.js +30 -1
  9. package/dist/handlers/connectors.js +2 -2
  10. package/dist/handlers/decisions.d.ts +11 -0
  11. package/dist/handlers/decisions.js +37 -2
  12. package/dist/handlers/deployment.d.ts +6 -0
  13. package/dist/handlers/deployment.js +33 -5
  14. package/dist/handlers/discovery.js +27 -11
  15. package/dist/handlers/fallback.js +12 -6
  16. package/dist/handlers/file-checkouts.d.ts +1 -0
  17. package/dist/handlers/file-checkouts.js +17 -2
  18. package/dist/handlers/findings.d.ts +5 -0
  19. package/dist/handlers/findings.js +19 -2
  20. package/dist/handlers/git-issues.js +4 -2
  21. package/dist/handlers/ideas.d.ts +5 -0
  22. package/dist/handlers/ideas.js +19 -2
  23. package/dist/handlers/progress.js +2 -2
  24. package/dist/handlers/project.d.ts +1 -0
  25. package/dist/handlers/project.js +35 -2
  26. package/dist/handlers/requests.js +6 -3
  27. package/dist/handlers/roles.js +13 -2
  28. package/dist/handlers/session.d.ts +12 -0
  29. package/dist/handlers/session.js +288 -25
  30. package/dist/handlers/sprints.d.ts +2 -0
  31. package/dist/handlers/sprints.js +30 -1
  32. package/dist/handlers/tasks.d.ts +25 -2
  33. package/dist/handlers/tasks.js +228 -35
  34. package/dist/handlers/tool-docs.js +72 -5
  35. package/dist/templates/agent-guidelines.d.ts +18 -0
  36. package/dist/templates/agent-guidelines.js +207 -0
  37. package/dist/tools.js +478 -125
  38. package/dist/utils.d.ts +5 -2
  39. package/dist/utils.js +90 -51
  40. package/package.json +51 -46
  41. package/scripts/version-bump.ts +203 -0
  42. package/src/api-client.test.ts +8 -3
  43. package/src/api-client.ts +376 -13
  44. package/src/handlers/__test-setup__.ts +5 -0
  45. package/src/handlers/blockers.test.ts +76 -0
  46. package/src/handlers/blockers.ts +56 -2
  47. package/src/handlers/bodies-of-work.ts +59 -1
  48. package/src/handlers/connectors.ts +2 -2
  49. package/src/handlers/decisions.test.ts +71 -2
  50. package/src/handlers/decisions.ts +56 -2
  51. package/src/handlers/deployment.test.ts +81 -0
  52. package/src/handlers/deployment.ts +38 -5
  53. package/src/handlers/discovery.ts +27 -11
  54. package/src/handlers/fallback.test.ts +11 -10
  55. package/src/handlers/fallback.ts +14 -8
  56. package/src/handlers/file-checkouts.test.ts +83 -3
  57. package/src/handlers/file-checkouts.ts +22 -2
  58. package/src/handlers/findings.test.ts +2 -2
  59. package/src/handlers/findings.ts +38 -2
  60. package/src/handlers/git-issues.test.ts +2 -2
  61. package/src/handlers/git-issues.ts +4 -2
  62. package/src/handlers/ideas.test.ts +1 -1
  63. package/src/handlers/ideas.ts +34 -2
  64. package/src/handlers/progress.ts +2 -2
  65. package/src/handlers/project.ts +47 -2
  66. package/src/handlers/requests.test.ts +38 -7
  67. package/src/handlers/requests.ts +6 -3
  68. package/src/handlers/roles.test.ts +1 -1
  69. package/src/handlers/roles.ts +20 -2
  70. package/src/handlers/session.test.ts +303 -4
  71. package/src/handlers/session.ts +335 -28
  72. package/src/handlers/sprints.ts +61 -1
  73. package/src/handlers/tasks.test.ts +0 -73
  74. package/src/handlers/tasks.ts +269 -40
  75. package/src/handlers/tool-docs.ts +77 -5
  76. package/src/handlers/types.test.ts +259 -0
  77. package/src/templates/agent-guidelines.ts +210 -0
  78. package/src/tools.ts +479 -125
  79. package/src/utils.test.ts +7 -5
  80. package/src/utils.ts +95 -51
package/src/api-client.ts CHANGED
@@ -85,6 +85,8 @@ export class VibescopeApiClient {
85
85
  mode?: 'lite' | 'full';
86
86
  model?: 'opus' | 'sonnet' | 'haiku';
87
87
  role?: 'developer' | 'validator' | 'deployer' | 'reviewer' | 'maintainer';
88
+ hostname?: string; // Machine hostname for worktree tracking
89
+ agent_type?: 'claude' | 'gemini' | 'cursor' | 'windsurf' | 'other'; // Agent type for onboarding
88
90
  }): Promise<ApiResponse<{
89
91
  session_started: boolean;
90
92
  session_id?: string;
@@ -150,6 +152,32 @@ export class VibescopeApiClient {
150
152
  example: string;
151
153
  note: string;
152
154
  };
155
+ // Agent onboarding - returned when a new agent type connects to an existing project
156
+ agent_setup?: {
157
+ agent_type: string;
158
+ is_new_agent_type: boolean;
159
+ setup_required: boolean;
160
+ instructions: string;
161
+ config_file: string; // e.g., '.gemini/GEMINI.md'
162
+ template_url?: string;
163
+ steps: string[];
164
+ };
165
+ // Stale worktrees that need cleanup
166
+ stale_worktrees?: Array<{
167
+ task_id: string;
168
+ task_title: string;
169
+ worktree_path: string;
170
+ worktree_hostname?: string | null;
171
+ }>;
172
+ stale_worktrees_count?: number;
173
+ cleanup_action?: string;
174
+ // Validation info for validators
175
+ awaiting_validation?: Array<{
176
+ id: string;
177
+ title?: string;
178
+ }>;
179
+ validation_priority?: boolean;
180
+ next_action?: string;
153
181
  error?: string;
154
182
  }>> {
155
183
  return this.request('POST', '/api/mcp/sessions/start', params);
@@ -157,6 +185,7 @@ export class VibescopeApiClient {
157
185
 
158
186
  async heartbeat(sessionId: string, options?: {
159
187
  current_worktree_path?: string | null;
188
+ hostname?: string; // Machine hostname for worktree tracking
160
189
  }): Promise<ApiResponse<{
161
190
  success: boolean;
162
191
  session_id: string;
@@ -273,6 +302,15 @@ export class VibescopeApiClient {
273
302
  git_auto_tag?: boolean;
274
303
  deployment_instructions?: string;
275
304
  agent_instructions?: string;
305
+ // New project settings columns
306
+ git_delete_branch_on_merge?: boolean;
307
+ require_pr_for_validation?: boolean;
308
+ auto_merge_on_approval?: boolean;
309
+ validation_required?: boolean;
310
+ default_task_priority?: number;
311
+ require_time_estimates?: boolean;
312
+ fallback_activities_enabled?: boolean;
313
+ preferred_fallback_activities?: string[];
276
314
  }): Promise<ApiResponse<{
277
315
  success: boolean;
278
316
  project_id: string;
@@ -390,6 +428,158 @@ export class VibescopeApiClient {
390
428
  return this.request('GET', `/api/mcp/tasks/${taskId}`);
391
429
  }
392
430
 
431
+ /**
432
+ * Get a single task by ID with optional subtasks and milestones
433
+ */
434
+ async getTaskById(taskId: string, params?: {
435
+ include_subtasks?: boolean;
436
+ include_milestones?: boolean;
437
+ }): Promise<ApiResponse<{
438
+ task: {
439
+ id: string;
440
+ title: string;
441
+ description?: string;
442
+ priority: number;
443
+ status: string;
444
+ progress_percentage?: number;
445
+ estimated_minutes?: number;
446
+ started_at?: string;
447
+ completed_at?: string;
448
+ git_branch?: string;
449
+ references?: Array<{ url: string; label?: string }>;
450
+ };
451
+ subtasks?: Array<{
452
+ id: string;
453
+ title: string;
454
+ status: string;
455
+ progress_percentage?: number;
456
+ }>;
457
+ milestones?: Array<{
458
+ id: string;
459
+ title: string;
460
+ status: string;
461
+ order_index: number;
462
+ }>;
463
+ }>> {
464
+ return this.proxy('get_task', {
465
+ task_id: taskId,
466
+ include_subtasks: params?.include_subtasks,
467
+ include_milestones: params?.include_milestones,
468
+ });
469
+ }
470
+
471
+ /**
472
+ * Search tasks by text query with pagination
473
+ */
474
+ async searchTasks(projectId: string, params: {
475
+ query: string;
476
+ status?: string[];
477
+ limit?: number;
478
+ offset?: number;
479
+ }): Promise<ApiResponse<{
480
+ tasks: Array<{
481
+ id: string;
482
+ title: string;
483
+ status: string;
484
+ priority: number;
485
+ snippet?: string;
486
+ }>;
487
+ total_matches: number;
488
+ hint?: string;
489
+ }>> {
490
+ return this.proxy('search_tasks', {
491
+ project_id: projectId,
492
+ query: params.query,
493
+ status: params.status,
494
+ limit: params.limit,
495
+ offset: params.offset,
496
+ });
497
+ }
498
+
499
+ /**
500
+ * Get tasks filtered by priority with pagination
501
+ */
502
+ async getTasksByPriority(projectId: string, params?: {
503
+ priority?: number;
504
+ priority_max?: number;
505
+ status?: string;
506
+ limit?: number;
507
+ offset?: number;
508
+ }): Promise<ApiResponse<{
509
+ tasks: Array<{
510
+ id: string;
511
+ title: string;
512
+ priority: number;
513
+ status: string;
514
+ estimated_minutes?: number;
515
+ }>;
516
+ total_count: number;
517
+ }>> {
518
+ return this.proxy('get_tasks_by_priority', {
519
+ project_id: projectId,
520
+ priority: params?.priority,
521
+ priority_max: params?.priority_max,
522
+ status: params?.status,
523
+ limit: params?.limit,
524
+ offset: params?.offset,
525
+ });
526
+ }
527
+
528
+ /**
529
+ * Get recent tasks (newest or oldest) with pagination
530
+ */
531
+ async getRecentTasks(projectId: string, params?: {
532
+ order?: 'newest' | 'oldest';
533
+ status?: string;
534
+ limit?: number;
535
+ offset?: number;
536
+ }): Promise<ApiResponse<{
537
+ tasks: Array<{
538
+ id: string;
539
+ title: string;
540
+ status: string;
541
+ priority: number;
542
+ created_at: string;
543
+ age?: string;
544
+ }>;
545
+ total_count: number;
546
+ }>> {
547
+ return this.proxy('get_recent_tasks', {
548
+ project_id: projectId,
549
+ order: params?.order,
550
+ status: params?.status,
551
+ limit: params?.limit,
552
+ offset: params?.offset,
553
+ });
554
+ }
555
+
556
+ /**
557
+ * Get task statistics for a project
558
+ */
559
+ async getTaskStats(projectId: string): Promise<ApiResponse<{
560
+ total: number;
561
+ by_status: {
562
+ backlog: number;
563
+ pending: number;
564
+ in_progress: number;
565
+ completed: number;
566
+ cancelled: number;
567
+ };
568
+ by_priority: {
569
+ 1: number;
570
+ 2: number;
571
+ 3: number;
572
+ 4: number;
573
+ 5: number;
574
+ };
575
+ awaiting_validation: number;
576
+ oldest_pending_days: number | null;
577
+ }>> {
578
+ return this.proxy('get_task_stats', {
579
+ project_id: projectId,
580
+ });
581
+ }
582
+
393
583
  async updateTask(taskId: string, updates: {
394
584
  title?: string;
395
585
  description?: string;
@@ -444,7 +634,11 @@ export class VibescopeApiClient {
444
634
  next_action: string;
445
635
  warnings?: string[];
446
636
  }>> {
447
- return this.request('POST', `/api/mcp/tasks/${taskId}/complete`, params);
637
+ // Use proxy endpoint for consistency - direct endpoint had routing issues on Vercel
638
+ return this.proxy('complete_task', {
639
+ task_id: taskId,
640
+ summary: params.summary,
641
+ }, params.session_id ? { session_id: params.session_id, persona: null, instance_id: '' } : undefined);
448
642
  }
449
643
 
450
644
  async deleteTask(taskId: string): Promise<ApiResponse<{
@@ -556,6 +750,15 @@ export class VibescopeApiClient {
556
750
  return this.proxy('delete_blocker', { blocker_id: blockerId });
557
751
  }
558
752
 
753
+ async getBlockersStats(projectId: string): Promise<ApiResponse<{
754
+ total: number;
755
+ by_status: Record<string, number>;
756
+ }>> {
757
+ return this.proxy('get_blockers_stats', {
758
+ project_id: projectId
759
+ });
760
+ }
761
+
559
762
  // ============================================================================
560
763
  // Decision endpoints
561
764
  // ============================================================================
@@ -597,6 +800,14 @@ export class VibescopeApiClient {
597
800
  return this.proxy('delete_decision', { decision_id: decisionId });
598
801
  }
599
802
 
803
+ async getDecisionsStats(projectId: string): Promise<ApiResponse<{
804
+ total: number;
805
+ }>> {
806
+ return this.proxy('get_decisions_stats', {
807
+ project_id: projectId
808
+ });
809
+ }
810
+
600
811
  // ============================================================================
601
812
  // Idea endpoints
602
813
  // ============================================================================
@@ -835,15 +1046,21 @@ export class VibescopeApiClient {
835
1046
  // ============================================================================
836
1047
  // Request endpoints
837
1048
  // ============================================================================
838
- async getPendingRequests(projectId: string, sessionId?: string): Promise<ApiResponse<{
1049
+ async getPendingRequests(projectId: string, sessionId?: string, limit?: number, offset?: number): Promise<ApiResponse<{
839
1050
  requests: Array<{
840
1051
  id: string;
841
1052
  request_type: string;
842
1053
  message: string;
843
1054
  created_at: string;
844
1055
  }>;
1056
+ total_count: number;
1057
+ has_more: boolean;
845
1058
  }>> {
846
- return this.proxy('get_pending_requests', { project_id: projectId }, sessionId ? {
1059
+ return this.proxy('get_pending_requests', {
1060
+ project_id: projectId,
1061
+ ...(limit !== undefined && { limit }),
1062
+ ...(offset !== undefined && { offset }),
1063
+ }, sessionId ? {
847
1064
  session_id: sessionId,
848
1065
  persona: null,
849
1066
  instance_id: ''
@@ -968,7 +1185,10 @@ export class VibescopeApiClient {
968
1185
  });
969
1186
  }
970
1187
 
971
- async getActivitySchedules(projectId: string): Promise<ApiResponse<{
1188
+ async getActivitySchedules(projectId: string, params?: {
1189
+ limit?: number;
1190
+ offset?: number;
1191
+ }): Promise<ApiResponse<{
972
1192
  schedules: Array<{
973
1193
  id: string;
974
1194
  activity_type: string;
@@ -976,10 +1196,13 @@ export class VibescopeApiClient {
976
1196
  next_run_at?: string;
977
1197
  enabled: boolean;
978
1198
  }>;
979
- due_activities: string[];
980
- count: number;
1199
+ total_count: number;
1200
+ has_more: boolean;
981
1201
  }>> {
982
- return this.proxy('get_activity_schedules', { project_id: projectId });
1202
+ return this.proxy('get_activity_schedules', {
1203
+ project_id: projectId,
1204
+ ...params
1205
+ });
983
1206
  }
984
1207
 
985
1208
  // ============================================================================
@@ -1199,6 +1422,33 @@ export class VibescopeApiClient {
1199
1422
  });
1200
1423
  }
1201
1424
 
1425
+ /**
1426
+ * Report actual API token usage for accurate cost tracking.
1427
+ * This records token usage to the session and attributes cost to the current task.
1428
+ */
1429
+ async reportTokenUsage(sessionId: string, params: {
1430
+ input_tokens: number;
1431
+ output_tokens: number;
1432
+ model?: 'opus' | 'sonnet' | 'haiku';
1433
+ }): Promise<ApiResponse<{
1434
+ success: boolean;
1435
+ reported: {
1436
+ session_id: string;
1437
+ model: string;
1438
+ input_tokens: number;
1439
+ output_tokens: number;
1440
+ total_tokens: number;
1441
+ estimated_cost_usd: number;
1442
+ };
1443
+ task_attributed: boolean;
1444
+ task_id?: string;
1445
+ }>> {
1446
+ return this.proxy('report_token_usage', {
1447
+ session_id: sessionId,
1448
+ ...params
1449
+ });
1450
+ }
1451
+
1202
1452
  // ============================================================================
1203
1453
  // Organization endpoints
1204
1454
  // ============================================================================
@@ -1522,6 +1772,7 @@ export class VibescopeApiClient {
1522
1772
  issue_type?: string;
1523
1773
  branch?: string;
1524
1774
  limit?: number;
1775
+ offset?: number;
1525
1776
  }): Promise<ApiResponse<{
1526
1777
  git_issues: Array<{
1527
1778
  id: string;
@@ -1742,6 +1993,8 @@ export class VibescopeApiClient {
1742
1993
  async getDeploymentRequirements(projectId: string, params?: {
1743
1994
  status?: string;
1744
1995
  stage?: string;
1996
+ limit?: number;
1997
+ offset?: number;
1745
1998
  }): Promise<ApiResponse<{
1746
1999
  requirements: Array<{
1747
2000
  id: string;
@@ -1751,6 +2004,8 @@ export class VibescopeApiClient {
1751
2004
  status: string;
1752
2005
  stage: string;
1753
2006
  }>;
2007
+ total_count: number;
2008
+ has_more: boolean;
1754
2009
  }>> {
1755
2010
  return this.proxy('get_deployment_requirements', {
1756
2011
  project_id: projectId,
@@ -1758,6 +2013,17 @@ export class VibescopeApiClient {
1758
2013
  });
1759
2014
  }
1760
2015
 
2016
+ async getDeploymentRequirementsStats(projectId: string): Promise<ApiResponse<{
2017
+ total: number;
2018
+ by_status: Record<string, number>;
2019
+ by_stage: Record<string, number>;
2020
+ by_type: Record<string, number>;
2021
+ }>> {
2022
+ return this.proxy('get_deployment_requirements_stats', {
2023
+ project_id: projectId
2024
+ });
2025
+ }
2026
+
1761
2027
  async completeDeploymentRequirement(requirementId: string): Promise<ApiResponse<{
1762
2028
  success: boolean;
1763
2029
  }>> {
@@ -1783,7 +2049,11 @@ export class VibescopeApiClient {
1783
2049
  });
1784
2050
  }
1785
2051
 
1786
- async getScheduledDeployments(projectId: string, includeDisabled?: boolean): Promise<ApiResponse<{
2052
+ async getScheduledDeployments(projectId: string, params?: {
2053
+ includeDisabled?: boolean;
2054
+ limit?: number;
2055
+ offset?: number;
2056
+ }): Promise<ApiResponse<{
1787
2057
  schedules: Array<{
1788
2058
  id: string;
1789
2059
  scheduled_at: string;
@@ -1796,12 +2066,14 @@ export class VibescopeApiClient {
1796
2066
  git_ref?: string;
1797
2067
  notes?: string;
1798
2068
  }>;
1799
- count: number;
1800
- due_count: number;
2069
+ total_count: number;
2070
+ has_more: boolean;
1801
2071
  }>> {
1802
2072
  return this.proxy('get_scheduled_deployments', {
1803
2073
  project_id: projectId,
1804
- include_disabled: includeDisabled
2074
+ include_disabled: params?.includeDisabled,
2075
+ limit: params?.limit,
2076
+ offset: params?.offset
1805
2077
  });
1806
2078
  }
1807
2079
 
@@ -1872,6 +2144,54 @@ export class VibescopeApiClient {
1872
2144
  });
1873
2145
  }
1874
2146
 
2147
+ async getProjectSummary(projectId: string): Promise<ApiResponse<{
2148
+ project: {
2149
+ id: string;
2150
+ name: string;
2151
+ description?: string;
2152
+ goal?: string;
2153
+ status: string;
2154
+ git_url?: string;
2155
+ tech_stack?: string[];
2156
+ git_workflow?: string;
2157
+ };
2158
+ tasks: {
2159
+ total: number;
2160
+ by_status: Record<string, number>;
2161
+ };
2162
+ blockers: {
2163
+ total: number;
2164
+ open: number;
2165
+ resolved: number;
2166
+ };
2167
+ findings: {
2168
+ total: number;
2169
+ by_severity: Record<string, number>;
2170
+ by_status: Record<string, number>;
2171
+ };
2172
+ agents: {
2173
+ active_count: number;
2174
+ total_tokens_this_session: number;
2175
+ total_tool_calls: number;
2176
+ };
2177
+ activity: {
2178
+ tasks_completed_today: number;
2179
+ progress_entries_today: number;
2180
+ };
2181
+ deployment: {
2182
+ active: boolean;
2183
+ status: string | null;
2184
+ last_deployment_at: string | null;
2185
+ };
2186
+ planning: {
2187
+ active_sprints: number;
2188
+ active_bodies_of_work: number;
2189
+ pending_deployment_requirements: number;
2190
+ };
2191
+ }>> {
2192
+ return this.proxy('get_project_summary', { project_id: projectId });
2193
+ }
2194
+
1875
2195
  // ============================================================================
1876
2196
  // Help Topics (database-backed)
1877
2197
  // ============================================================================
@@ -1932,6 +2252,7 @@ export class VibescopeApiClient {
1932
2252
  status?: string;
1933
2253
  file_path?: string;
1934
2254
  limit?: number;
2255
+ offset?: number;
1935
2256
  }): Promise<ApiResponse<{
1936
2257
  checkouts: Array<{
1937
2258
  id: string;
@@ -1962,28 +2283,51 @@ export class VibescopeApiClient {
1962
2283
  return this.proxy('abandon_checkout', params);
1963
2284
  }
1964
2285
 
2286
+ async getFileCheckoutsStats(projectId: string): Promise<ApiResponse<{
2287
+ total: number;
2288
+ by_status: Record<string, number>;
2289
+ }>> {
2290
+ return this.proxy('get_file_checkouts_stats', { project_id: projectId });
2291
+ }
2292
+
1965
2293
  // ============================================================================
1966
2294
  // Worktree Management
1967
2295
  // ============================================================================
1968
2296
 
1969
- async getStaleWorktrees(projectId: string): Promise<ApiResponse<{
2297
+ async getStaleWorktrees(projectId: string, params?: {
2298
+ hostname?: string;
2299
+ limit?: number;
2300
+ offset?: number;
2301
+ }): Promise<ApiResponse<{
1970
2302
  project_id: string;
1971
2303
  project_name: string;
2304
+ hostname_filter: string | null;
1972
2305
  stale_worktrees: Array<{
1973
2306
  task_id: string;
1974
2307
  task_title: string;
1975
2308
  worktree_path: string;
2309
+ worktree_hostname: string | null;
1976
2310
  git_branch: string | null;
1977
2311
  status: string;
1978
2312
  completed_at: string | null;
1979
2313
  updated_at: string;
1980
2314
  pr_url: string | null;
1981
2315
  stale_reason: 'task_finished' | 'potentially_abandoned';
2316
+ can_cleanup_locally: boolean;
1982
2317
  }>;
1983
2318
  count: number;
2319
+ local_count: number;
2320
+ remote_count: number;
2321
+ total_count: number;
2322
+ has_more: boolean;
1984
2323
  cleanup_instructions: string[] | null;
2324
+ remote_worktree_note: string | null;
1985
2325
  }>> {
1986
- return this.request('GET', `/api/mcp/worktrees/stale?project_id=${projectId}`);
2326
+ const queryParams = new URLSearchParams({ project_id: projectId });
2327
+ if (params?.hostname !== undefined) queryParams.set('hostname', params.hostname);
2328
+ if (params?.limit !== undefined) queryParams.set('limit', String(params.limit));
2329
+ if (params?.offset !== undefined) queryParams.set('offset', String(params.offset));
2330
+ return this.request('GET', `/api/mcp/worktrees/stale?${queryParams.toString()}`);
1987
2331
  }
1988
2332
 
1989
2333
  async clearWorktreePath(taskId: string): Promise<ApiResponse<{
@@ -2114,6 +2458,25 @@ export class VibescopeApiClient {
2114
2458
  }>> {
2115
2459
  return this.proxy('get_connector_events', params);
2116
2460
  }
2461
+
2462
+ // ============================================================================
2463
+ // Agent onboarding endpoints
2464
+ // ============================================================================
2465
+
2466
+ /**
2467
+ * Confirm that agent setup is complete for a project.
2468
+ * This marks the agent type as onboarded so future sessions don't receive setup instructions.
2469
+ */
2470
+ async confirmAgentSetup(projectId: string, agentType: 'claude' | 'gemini' | 'cursor' | 'windsurf' | 'other'): Promise<ApiResponse<{
2471
+ success: boolean;
2472
+ project_id: string;
2473
+ agent_type: string;
2474
+ }>> {
2475
+ return this.proxy('confirm_agent_setup', {
2476
+ project_id: projectId,
2477
+ agent_type: agentType
2478
+ });
2479
+ }
2117
2480
  }
2118
2481
 
2119
2482
  // Singleton instance
@@ -26,6 +26,7 @@ export const mockApiClient = {
26
26
  heartbeat: vi.fn(),
27
27
  syncSession: vi.fn(),
28
28
  endSession: vi.fn(),
29
+ reportTokenUsage: vi.fn(),
29
30
 
30
31
  // Project
31
32
  listProjects: vi.fn(),
@@ -60,11 +61,13 @@ export const mockApiClient = {
60
61
  addBlocker: vi.fn(),
61
62
  resolveBlocker: vi.fn(),
62
63
  getBlockers: vi.fn(),
64
+ getBlockersStats: vi.fn(),
63
65
  deleteBlocker: vi.fn(),
64
66
 
65
67
  // Decisions
66
68
  logDecision: vi.fn(),
67
69
  getDecisions: vi.fn(),
70
+ getDecisionsStats: vi.fn(),
68
71
  deleteDecision: vi.fn(),
69
72
 
70
73
  // Ideas
@@ -154,6 +157,7 @@ export const mockApiClient = {
154
157
  addDeploymentRequirement: vi.fn(),
155
158
  completeDeploymentRequirement: vi.fn(),
156
159
  getDeploymentRequirements: vi.fn(),
160
+ getDeploymentRequirementsStats: vi.fn(),
157
161
  scheduleDeployment: vi.fn(),
158
162
  getScheduledDeployments: vi.fn(),
159
163
  updateScheduledDeployment: vi.fn(),
@@ -174,6 +178,7 @@ export const mockApiClient = {
174
178
  checkoutFile: vi.fn(),
175
179
  checkinFile: vi.fn(),
176
180
  getFileCheckouts: vi.fn(),
181
+ getFileCheckoutsStats: vi.fn(),
177
182
  abandonCheckout: vi.fn(),
178
183
 
179
184
  // Connectors
@@ -3,6 +3,7 @@ import {
3
3
  addBlocker,
4
4
  resolveBlocker,
5
5
  getBlockers,
6
+ getBlockersStats,
6
7
  deleteBlocker,
7
8
  } from './blockers.js';
8
9
  import { ValidationError } from '../validators.js';
@@ -390,3 +391,78 @@ describe('deleteBlocker', () => {
390
391
  });
391
392
  });
392
393
  });
394
+
395
+ // ============================================================================
396
+ // getBlockersStats Tests
397
+ // ============================================================================
398
+
399
+ describe('getBlockersStats', () => {
400
+ beforeEach(() => vi.clearAllMocks());
401
+
402
+ it('should throw error for missing project_id', async () => {
403
+ const ctx = createMockContext();
404
+
405
+ await expect(getBlockersStats({}, ctx)).rejects.toThrow(ValidationError);
406
+ });
407
+
408
+ it('should throw error for invalid project_id UUID', async () => {
409
+ const ctx = createMockContext();
410
+
411
+ await expect(
412
+ getBlockersStats({ project_id: 'invalid' }, ctx)
413
+ ).rejects.toThrow(ValidationError);
414
+ });
415
+
416
+ it('should return blockers stats', async () => {
417
+ mockApiClient.getBlockersStats.mockResolvedValue({
418
+ ok: true,
419
+ data: {
420
+ total: 5,
421
+ by_status: { open: 3, resolved: 2 },
422
+ },
423
+ });
424
+ const ctx = createMockContext();
425
+
426
+ const result = await getBlockersStats(
427
+ { project_id: '123e4567-e89b-12d3-a456-426614174000' },
428
+ ctx
429
+ );
430
+
431
+ expect(result.result).toMatchObject({
432
+ total: 5,
433
+ by_status: { open: 3, resolved: 2 },
434
+ });
435
+ });
436
+
437
+ it('should call API client getBlockersStats', async () => {
438
+ mockApiClient.getBlockersStats.mockResolvedValue({
439
+ ok: true,
440
+ data: { total: 0, by_status: {} },
441
+ });
442
+ const ctx = createMockContext();
443
+
444
+ await getBlockersStats(
445
+ { project_id: '123e4567-e89b-12d3-a456-426614174000' },
446
+ ctx
447
+ );
448
+
449
+ expect(mockApiClient.getBlockersStats).toHaveBeenCalledWith(
450
+ '123e4567-e89b-12d3-a456-426614174000'
451
+ );
452
+ });
453
+
454
+ it('should return error when API call fails', async () => {
455
+ mockApiClient.getBlockersStats.mockResolvedValue({
456
+ ok: false,
457
+ error: 'Query failed',
458
+ });
459
+ const ctx = createMockContext();
460
+
461
+ const result = await getBlockersStats({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx);
462
+
463
+ expect(result.isError).toBe(true);
464
+ expect(result.result).toMatchObject({
465
+ error: 'Query failed',
466
+ });
467
+ });
468
+ });