@vibescope/mcp-server 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +1 -1
  2. package/dist/api-client.d.ts +120 -2
  3. package/dist/api-client.js +51 -5
  4. package/dist/handlers/bodies-of-work.js +84 -50
  5. package/dist/handlers/cost.js +62 -54
  6. package/dist/handlers/decisions.js +29 -16
  7. package/dist/handlers/deployment.js +114 -107
  8. package/dist/handlers/discovery.d.ts +3 -0
  9. package/dist/handlers/discovery.js +55 -657
  10. package/dist/handlers/fallback.js +42 -28
  11. package/dist/handlers/file-checkouts.d.ts +18 -0
  12. package/dist/handlers/file-checkouts.js +101 -0
  13. package/dist/handlers/findings.d.ts +14 -1
  14. package/dist/handlers/findings.js +104 -28
  15. package/dist/handlers/git-issues.js +36 -32
  16. package/dist/handlers/ideas.js +44 -26
  17. package/dist/handlers/index.d.ts +2 -0
  18. package/dist/handlers/index.js +6 -0
  19. package/dist/handlers/milestones.js +34 -27
  20. package/dist/handlers/organizations.js +86 -78
  21. package/dist/handlers/progress.js +22 -11
  22. package/dist/handlers/project.js +62 -22
  23. package/dist/handlers/requests.js +15 -11
  24. package/dist/handlers/roles.d.ts +18 -0
  25. package/dist/handlers/roles.js +130 -0
  26. package/dist/handlers/session.js +52 -15
  27. package/dist/handlers/sprints.js +78 -65
  28. package/dist/handlers/tasks.js +135 -74
  29. package/dist/handlers/tool-docs.d.ts +4 -3
  30. package/dist/handlers/tool-docs.js +252 -5
  31. package/dist/handlers/validation.js +30 -14
  32. package/dist/index.js +25 -7
  33. package/dist/tools.js +417 -4
  34. package/package.json +1 -1
  35. package/src/api-client.ts +161 -8
  36. package/src/handlers/__test-setup__.ts +12 -0
  37. package/src/handlers/bodies-of-work.ts +127 -111
  38. package/src/handlers/cost.test.ts +34 -44
  39. package/src/handlers/cost.ts +77 -92
  40. package/src/handlers/decisions.test.ts +3 -2
  41. package/src/handlers/decisions.ts +32 -27
  42. package/src/handlers/deployment.ts +144 -190
  43. package/src/handlers/discovery.test.ts +4 -5
  44. package/src/handlers/discovery.ts +60 -746
  45. package/src/handlers/fallback.test.ts +78 -0
  46. package/src/handlers/fallback.ts +51 -38
  47. package/src/handlers/file-checkouts.test.ts +477 -0
  48. package/src/handlers/file-checkouts.ts +127 -0
  49. package/src/handlers/findings.test.ts +274 -2
  50. package/src/handlers/findings.ts +123 -57
  51. package/src/handlers/git-issues.ts +40 -80
  52. package/src/handlers/ideas.ts +56 -54
  53. package/src/handlers/index.ts +6 -0
  54. package/src/handlers/milestones.test.ts +1 -1
  55. package/src/handlers/milestones.ts +47 -45
  56. package/src/handlers/organizations.ts +104 -129
  57. package/src/handlers/progress.ts +24 -22
  58. package/src/handlers/project.ts +89 -57
  59. package/src/handlers/requests.ts +18 -14
  60. package/src/handlers/roles.test.ts +303 -0
  61. package/src/handlers/roles.ts +208 -0
  62. package/src/handlers/session.test.ts +37 -2
  63. package/src/handlers/session.ts +64 -21
  64. package/src/handlers/sprints.ts +114 -134
  65. package/src/handlers/tasks.test.ts +61 -0
  66. package/src/handlers/tasks.ts +170 -139
  67. package/src/handlers/tool-docs.ts +1024 -0
  68. package/src/handlers/validation.test.ts +53 -1
  69. package/src/handlers/validation.ts +32 -21
  70. package/src/index.ts +25 -7
  71. package/src/tools.ts +417 -4
  72. package/dist/config/tool-categories.d.ts +0 -31
  73. package/dist/config/tool-categories.js +0 -253
  74. package/dist/knowledge.d.ts +0 -6
  75. package/dist/knowledge.js +0 -218
  76. package/src/knowledge.ts +0 -230
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * MIGRATED: Uses Vibescope API client instead of direct Supabase
11
11
  */
12
- import { validateRequired, validateUUID } from '../validators.js';
12
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
13
13
  import { FALLBACK_ACTIVITIES } from '../utils.js';
14
14
  import { getApiClient } from '../api-client.js';
15
15
  const VALID_ACTIVITIES = [
@@ -24,14 +24,25 @@ const VALID_ACTIVITIES = [
24
24
  'dependency_audit',
25
25
  'validate_completed_tasks',
26
26
  ];
27
+ // Argument schemas for type-safe parsing
28
+ const startFallbackActivitySchema = {
29
+ project_id: { type: 'string', required: true, validate: uuidValidator },
30
+ activity: { type: 'string', required: true, validate: createEnumValidator(VALID_ACTIVITIES) },
31
+ };
32
+ const stopFallbackActivitySchema = {
33
+ project_id: { type: 'string', required: true, validate: uuidValidator },
34
+ summary: { type: 'string' },
35
+ };
36
+ const getActivityHistorySchema = {
37
+ project_id: { type: 'string', required: true, validate: uuidValidator },
38
+ activity_type: { type: 'string' },
39
+ limit: { type: 'number', default: 50 },
40
+ };
41
+ const getActivitySchedulesSchema = {
42
+ project_id: { type: 'string', required: true, validate: uuidValidator },
43
+ };
27
44
  export const startFallbackActivity = async (args, ctx) => {
28
- const { project_id, activity } = args;
29
- validateRequired(project_id, 'project_id');
30
- validateUUID(project_id, 'project_id');
31
- validateRequired(activity, 'activity');
32
- if (!VALID_ACTIVITIES.includes(activity)) {
33
- throw new Error(`Invalid activity. Must be one of: ${VALID_ACTIVITIES.join(', ')}`);
34
- }
45
+ const { project_id, activity } = parseArgs(args, startFallbackActivitySchema);
35
46
  const { session } = ctx;
36
47
  const apiClient = getApiClient();
37
48
  const response = await apiClient.startFallbackActivity(project_id, activity, session.currentSessionId || undefined);
@@ -40,21 +51,28 @@ export const startFallbackActivity = async (args, ctx) => {
40
51
  }
41
52
  // Get the activity details for the response
42
53
  const activityInfo = FALLBACK_ACTIVITIES.find((a) => a.activity === activity);
43
- return {
44
- result: {
45
- success: true,
46
- activity,
47
- title: activityInfo?.title || activity,
48
- description: activityInfo?.description || '',
49
- prompt: activityInfo?.prompt || '',
50
- message: response.data?.message || `Started fallback activity: ${activityInfo?.title || activity}`,
51
- },
54
+ const result = {
55
+ success: true,
56
+ activity,
57
+ title: activityInfo?.title || activity,
58
+ description: activityInfo?.description || '',
59
+ prompt: activityInfo?.prompt || '',
60
+ message: response.data?.message || `Started fallback activity: ${activityInfo?.title || activity}`,
52
61
  };
62
+ // Pass through worktree guidance if provided
63
+ if (response.data?.git_workflow) {
64
+ result.git_workflow = response.data.git_workflow;
65
+ }
66
+ if (response.data?.worktree_setup) {
67
+ result.worktree_setup = response.data.worktree_setup;
68
+ }
69
+ if (response.data?.next_step) {
70
+ result.next_step = response.data.next_step;
71
+ }
72
+ return { result };
53
73
  };
54
74
  export const stopFallbackActivity = async (args, ctx) => {
55
- const { project_id, summary } = args;
56
- validateRequired(project_id, 'project_id');
57
- validateUUID(project_id, 'project_id');
75
+ const { project_id, summary } = parseArgs(args, stopFallbackActivitySchema);
58
76
  const { session } = ctx;
59
77
  const apiClient = getApiClient();
60
78
  const response = await apiClient.stopFallbackActivity(project_id, summary, session.currentSessionId || undefined);
@@ -68,10 +86,8 @@ export const stopFallbackActivity = async (args, ctx) => {
68
86
  },
69
87
  };
70
88
  };
71
- export const getActivityHistory = async (args, ctx) => {
72
- const { project_id, activity_type, limit = 50 } = args;
73
- validateRequired(project_id, 'project_id');
74
- validateUUID(project_id, 'project_id');
89
+ export const getActivityHistory = async (args, _ctx) => {
90
+ const { project_id, activity_type, limit } = parseArgs(args, getActivityHistorySchema);
75
91
  const apiClient = getApiClient();
76
92
  // Use proxy for get_activity_history operation
77
93
  const response = await apiClient.proxy('get_activity_history', {
@@ -90,10 +106,8 @@ export const getActivityHistory = async (args, ctx) => {
90
106
  },
91
107
  };
92
108
  };
93
- export const getActivitySchedules = async (args, ctx) => {
94
- const { project_id } = args;
95
- validateRequired(project_id, 'project_id');
96
- validateUUID(project_id, 'project_id');
109
+ export const getActivitySchedules = async (args, _ctx) => {
110
+ const { project_id } = parseArgs(args, getActivitySchedulesSchema);
97
111
  const apiClient = getApiClient();
98
112
  // Use proxy for get_activity_schedules operation
99
113
  const response = await apiClient.proxy('get_activity_schedules', {
@@ -0,0 +1,18 @@
1
+ /**
2
+ * File Checkouts Handlers
3
+ *
4
+ * Handles file checkout/checkin for multi-agent coordination:
5
+ * - checkout_file: Check out a file before editing
6
+ * - checkin_file: Check in a file after editing
7
+ * - get_file_checkouts: Get active checkouts for a project
8
+ * - abandon_checkout: Force release a checkout
9
+ */
10
+ import type { Handler, HandlerRegistry } from './types.js';
11
+ export declare const checkoutFile: Handler;
12
+ export declare const checkinFile: Handler;
13
+ export declare const getFileCheckouts: Handler;
14
+ export declare const abandonCheckout: Handler;
15
+ /**
16
+ * File Checkouts handlers registry
17
+ */
18
+ export declare const fileCheckoutHandlers: HandlerRegistry;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * File Checkouts Handlers
3
+ *
4
+ * Handles file checkout/checkin for multi-agent coordination:
5
+ * - checkout_file: Check out a file before editing
6
+ * - checkin_file: Check in a file after editing
7
+ * - get_file_checkouts: Get active checkouts for a project
8
+ * - abandon_checkout: Force release a checkout
9
+ */
10
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
11
+ import { getApiClient } from '../api-client.js';
12
+ const VALID_CHECKOUT_STATUSES = ['checked_out', 'checked_in', 'abandoned'];
13
+ // Argument schemas for type-safe parsing
14
+ const checkoutFileSchema = {
15
+ project_id: { type: 'string', required: true, validate: uuidValidator },
16
+ file_path: { type: 'string', required: true },
17
+ reason: { type: 'string' },
18
+ };
19
+ const checkinFileSchema = {
20
+ checkout_id: { type: 'string', validate: uuidValidator },
21
+ project_id: { type: 'string', validate: uuidValidator },
22
+ file_path: { type: 'string' },
23
+ summary: { type: 'string' },
24
+ };
25
+ const getFileCheckoutsSchema = {
26
+ project_id: { type: 'string', required: true, validate: uuidValidator },
27
+ status: { type: 'string', validate: createEnumValidator(VALID_CHECKOUT_STATUSES) },
28
+ file_path: { type: 'string' },
29
+ limit: { type: 'number', default: 50 },
30
+ };
31
+ const abandonCheckoutSchema = {
32
+ checkout_id: { type: 'string', validate: uuidValidator },
33
+ project_id: { type: 'string', validate: uuidValidator },
34
+ file_path: { type: 'string' },
35
+ };
36
+ export const checkoutFile = async (args, ctx) => {
37
+ const { project_id, file_path, reason } = parseArgs(args, checkoutFileSchema);
38
+ const apiClient = getApiClient();
39
+ const response = await apiClient.checkoutFile(project_id, file_path, reason, ctx.session.currentSessionId || undefined);
40
+ if (!response.ok) {
41
+ throw new Error(response.error || 'Failed to checkout file');
42
+ }
43
+ return { result: response.data };
44
+ };
45
+ export const checkinFile = async (args, ctx) => {
46
+ const { checkout_id, project_id, file_path, summary } = parseArgs(args, checkinFileSchema);
47
+ // Validate that either checkout_id or both project_id and file_path are provided
48
+ if (!checkout_id && (!project_id || !file_path)) {
49
+ throw new Error('Either checkout_id or both project_id and file_path are required');
50
+ }
51
+ const apiClient = getApiClient();
52
+ const response = await apiClient.checkinFile({
53
+ checkout_id,
54
+ project_id,
55
+ file_path,
56
+ summary
57
+ }, ctx.session.currentSessionId || undefined);
58
+ if (!response.ok) {
59
+ throw new Error(response.error || 'Failed to checkin file');
60
+ }
61
+ return { result: response.data };
62
+ };
63
+ export const getFileCheckouts = async (args, _ctx) => {
64
+ const { project_id, status, file_path, limit } = parseArgs(args, getFileCheckoutsSchema);
65
+ const apiClient = getApiClient();
66
+ const response = await apiClient.getFileCheckouts(project_id, {
67
+ status,
68
+ file_path,
69
+ limit
70
+ });
71
+ if (!response.ok) {
72
+ throw new Error(response.error || 'Failed to get file checkouts');
73
+ }
74
+ return { result: response.data };
75
+ };
76
+ export const abandonCheckout = async (args, _ctx) => {
77
+ const { checkout_id, project_id, file_path } = parseArgs(args, abandonCheckoutSchema);
78
+ // Validate that either checkout_id or both project_id and file_path are provided
79
+ if (!checkout_id && (!project_id || !file_path)) {
80
+ throw new Error('Either checkout_id or both project_id and file_path are required');
81
+ }
82
+ const apiClient = getApiClient();
83
+ const response = await apiClient.abandonCheckout({
84
+ checkout_id,
85
+ project_id,
86
+ file_path
87
+ });
88
+ if (!response.ok) {
89
+ throw new Error(response.error || 'Failed to abandon checkout');
90
+ }
91
+ return { result: response.data };
92
+ };
93
+ /**
94
+ * File Checkouts handlers registry
95
+ */
96
+ export const fileCheckoutHandlers = {
97
+ checkout_file: checkoutFile,
98
+ checkin_file: checkinFile,
99
+ get_file_checkouts: getFileCheckouts,
100
+ abandon_checkout: abandonCheckout,
101
+ };
@@ -3,15 +3,28 @@
3
3
  *
4
4
  * Handles audit findings and knowledge base:
5
5
  * - add_finding
6
- * - get_findings
6
+ * - get_findings (supports summary_only for reduced tokens)
7
+ * - get_findings_stats (aggregate counts for minimal tokens)
7
8
  * - update_finding
8
9
  * - delete_finding
9
10
  */
10
11
  import type { Handler, HandlerRegistry } from './types.js';
11
12
  export declare const addFinding: Handler;
12
13
  export declare const getFindings: Handler;
14
+ /**
15
+ * Get aggregate statistics about findings for a project.
16
+ * Returns counts by category, severity, and status without the actual finding data.
17
+ * This is much more token-efficient than get_findings for understanding the overall state.
18
+ */
19
+ export declare const getFindingsStats: Handler;
13
20
  export declare const updateFinding: Handler;
14
21
  export declare const deleteFinding: Handler;
22
+ /**
23
+ * Query aggregated project knowledge in a single call.
24
+ * Returns findings, Q&A, decisions, completed tasks, and resolved blockers.
25
+ * Use this instead of multiple separate tool calls to reduce token usage.
26
+ */
27
+ export declare const queryKnowledgeBase: Handler;
15
28
  /**
16
29
  * Findings handlers registry
17
30
  */
@@ -3,25 +3,67 @@
3
3
  *
4
4
  * Handles audit findings and knowledge base:
5
5
  * - add_finding
6
- * - get_findings
6
+ * - get_findings (supports summary_only for reduced tokens)
7
+ * - get_findings_stats (aggregate counts for minimal tokens)
7
8
  * - update_finding
8
9
  * - delete_finding
9
10
  */
10
- import { validateRequired, validateUUID } from '../validators.js';
11
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
11
12
  import { getApiClient } from '../api-client.js';
13
+ const VALID_FINDING_CATEGORIES = ['performance', 'security', 'code_quality', 'accessibility', 'documentation', 'architecture', 'testing', 'other'];
14
+ const VALID_FINDING_SEVERITIES = ['info', 'low', 'medium', 'high', 'critical'];
15
+ const VALID_FINDING_STATUSES = ['open', 'addressed', 'dismissed', 'wontfix'];
16
+ // Argument schemas for type-safe parsing
17
+ const addFindingSchema = {
18
+ project_id: { type: 'string', required: true, validate: uuidValidator },
19
+ title: { type: 'string', required: true },
20
+ description: { type: 'string' },
21
+ category: { type: 'string', validate: createEnumValidator(VALID_FINDING_CATEGORIES) },
22
+ severity: { type: 'string', validate: createEnumValidator(VALID_FINDING_SEVERITIES) },
23
+ file_path: { type: 'string' },
24
+ line_number: { type: 'number' },
25
+ related_task_id: { type: 'string', validate: uuidValidator },
26
+ };
27
+ const getFindingsSchema = {
28
+ project_id: { type: 'string', required: true, validate: uuidValidator },
29
+ category: { type: 'string', validate: createEnumValidator(VALID_FINDING_CATEGORIES) },
30
+ severity: { type: 'string', validate: createEnumValidator(VALID_FINDING_SEVERITIES) },
31
+ status: { type: 'string', validate: createEnumValidator(VALID_FINDING_STATUSES) },
32
+ limit: { type: 'number', default: 50 },
33
+ offset: { type: 'number', default: 0 },
34
+ search_query: { type: 'string' },
35
+ summary_only: { type: 'boolean', default: false },
36
+ };
37
+ const getFindingsStatsSchema = {
38
+ project_id: { type: 'string', required: true, validate: uuidValidator },
39
+ };
40
+ const updateFindingSchema = {
41
+ finding_id: { type: 'string', required: true, validate: uuidValidator },
42
+ title: { type: 'string' },
43
+ description: { type: 'string' },
44
+ severity: { type: 'string', validate: createEnumValidator(VALID_FINDING_SEVERITIES) },
45
+ status: { type: 'string', validate: createEnumValidator(VALID_FINDING_STATUSES) },
46
+ resolution_note: { type: 'string' },
47
+ };
48
+ const deleteFindingSchema = {
49
+ finding_id: { type: 'string', required: true, validate: uuidValidator },
50
+ };
51
+ const VALID_SCOPES = ['summary', 'detailed'];
52
+ const queryKnowledgeBaseSchema = {
53
+ project_id: { type: 'string', required: true, validate: uuidValidator },
54
+ scope: { type: 'string', default: 'summary', validate: createEnumValidator(VALID_SCOPES) },
55
+ categories: { type: 'array' },
56
+ limit: { type: 'number', default: 5 },
57
+ search_query: { type: 'string' },
58
+ };
12
59
  export const addFinding = async (args, ctx) => {
13
- const { project_id, category, title, description, severity, file_path, line_number, related_task_id } = args;
14
- validateRequired(project_id, 'project_id');
15
- validateUUID(project_id, 'project_id');
16
- validateRequired(title, 'title');
17
- if (related_task_id)
18
- validateUUID(related_task_id, 'related_task_id');
60
+ const { project_id, title, description, category, severity, file_path, line_number, related_task_id } = parseArgs(args, addFindingSchema);
19
61
  const apiClient = getApiClient();
20
62
  const response = await apiClient.addFinding(project_id, {
21
63
  title,
22
64
  description,
23
- category,
24
- severity,
65
+ category: category,
66
+ severity: severity,
25
67
  file_path,
26
68
  line_number,
27
69
  related_task_id
@@ -31,32 +73,45 @@ export const addFinding = async (args, ctx) => {
31
73
  }
32
74
  return { result: response.data };
33
75
  };
34
- export const getFindings = async (args, ctx) => {
35
- const { project_id, category, severity, status, limit = 50, offset = 0, search_query } = args;
36
- validateRequired(project_id, 'project_id');
37
- validateUUID(project_id, 'project_id');
76
+ export const getFindings = async (args, _ctx) => {
77
+ const { project_id, category, severity, status, limit, offset, search_query, summary_only } = parseArgs(args, getFindingsSchema);
38
78
  const apiClient = getApiClient();
39
79
  const response = await apiClient.getFindings(project_id, {
40
- category,
41
- severity,
42
- status,
43
- limit
80
+ category: category,
81
+ severity: severity,
82
+ status: status,
83
+ limit,
84
+ offset,
85
+ search_query,
86
+ summary_only
44
87
  });
45
88
  if (!response.ok) {
46
89
  throw new Error(response.error || 'Failed to get findings');
47
90
  }
48
91
  return { result: response.data };
49
92
  };
50
- export const updateFinding = async (args, ctx) => {
51
- const { finding_id, status, resolution_note, title, description, severity } = args;
52
- validateRequired(finding_id, 'finding_id');
53
- validateUUID(finding_id, 'finding_id');
93
+ /**
94
+ * Get aggregate statistics about findings for a project.
95
+ * Returns counts by category, severity, and status without the actual finding data.
96
+ * This is much more token-efficient than get_findings for understanding the overall state.
97
+ */
98
+ export const getFindingsStats = async (args, _ctx) => {
99
+ const { project_id } = parseArgs(args, getFindingsStatsSchema);
100
+ const apiClient = getApiClient();
101
+ const response = await apiClient.getFindingsStats(project_id);
102
+ if (!response.ok) {
103
+ throw new Error(response.error || 'Failed to get findings stats');
104
+ }
105
+ return { result: response.data };
106
+ };
107
+ export const updateFinding = async (args, _ctx) => {
108
+ const { finding_id, title, description, severity, status, resolution_note } = parseArgs(args, updateFindingSchema);
54
109
  const apiClient = getApiClient();
55
110
  const response = await apiClient.updateFinding(finding_id, {
56
111
  title,
57
112
  description,
58
- severity,
59
- status,
113
+ severity: severity,
114
+ status: status,
60
115
  resolution_note
61
116
  });
62
117
  if (!response.ok) {
@@ -64,10 +119,8 @@ export const updateFinding = async (args, ctx) => {
64
119
  }
65
120
  return { result: response.data };
66
121
  };
67
- export const deleteFinding = async (args, ctx) => {
68
- const { finding_id } = args;
69
- validateRequired(finding_id, 'finding_id');
70
- validateUUID(finding_id, 'finding_id');
122
+ export const deleteFinding = async (args, _ctx) => {
123
+ const { finding_id } = parseArgs(args, deleteFindingSchema);
71
124
  const apiClient = getApiClient();
72
125
  const response = await apiClient.deleteFinding(finding_id);
73
126
  if (!response.ok) {
@@ -75,12 +128,35 @@ export const deleteFinding = async (args, ctx) => {
75
128
  }
76
129
  return { result: response.data };
77
130
  };
131
+ /**
132
+ * Query aggregated project knowledge in a single call.
133
+ * Returns findings, Q&A, decisions, completed tasks, and resolved blockers.
134
+ * Use this instead of multiple separate tool calls to reduce token usage.
135
+ */
136
+ export const queryKnowledgeBase = async (args, _ctx) => {
137
+ const { project_id, scope, categories, limit, search_query } = parseArgs(args, queryKnowledgeBaseSchema);
138
+ // Validate limit range
139
+ const effectiveLimit = Math.min(Math.max(1, limit ?? 5), 20);
140
+ const apiClient = getApiClient();
141
+ const response = await apiClient.queryKnowledgeBase(project_id, {
142
+ scope: scope,
143
+ categories: categories,
144
+ limit: effectiveLimit,
145
+ search_query
146
+ });
147
+ if (!response.ok) {
148
+ throw new Error(response.error || 'Failed to query knowledge base');
149
+ }
150
+ return { result: response.data };
151
+ };
78
152
  /**
79
153
  * Findings handlers registry
80
154
  */
81
155
  export const findingHandlers = {
82
156
  add_finding: addFinding,
83
157
  get_findings: getFindings,
158
+ get_findings_stats: getFindingsStats,
84
159
  update_finding: updateFinding,
85
160
  delete_finding: deleteFinding,
161
+ query_knowledge_base: queryKnowledgeBase,
86
162
  };
@@ -7,7 +7,7 @@
7
7
  * - get_git_issues: List git issues for a project
8
8
  * - delete_git_issue: Remove a git issue
9
9
  */
10
- import { validateRequired, validateUUID } from '../validators.js';
10
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
11
11
  import { getApiClient } from '../api-client.js';
12
12
  const VALID_GIT_ISSUE_TYPES = [
13
13
  'merge_conflict',
@@ -17,25 +17,41 @@ const VALID_GIT_ISSUE_TYPES = [
17
17
  'pr_not_mergeable',
18
18
  ];
19
19
  const VALID_GIT_ISSUE_STATUSES = ['open', 'resolved'];
20
+ // Argument schemas for type-safe parsing
21
+ const addGitIssueSchema = {
22
+ project_id: { type: 'string', required: true, validate: uuidValidator },
23
+ issue_type: { type: 'string', required: true, validate: createEnumValidator(VALID_GIT_ISSUE_TYPES) },
24
+ branch: { type: 'string', required: true },
25
+ target_branch: { type: 'string' },
26
+ pr_url: { type: 'string' },
27
+ conflicting_files: { type: 'array' },
28
+ error_message: { type: 'string' },
29
+ task_id: { type: 'string', validate: uuidValidator },
30
+ };
31
+ const resolveGitIssueSchema = {
32
+ git_issue_id: { type: 'string', required: true, validate: uuidValidator },
33
+ resolution_note: { type: 'string' },
34
+ auto_resolved: { type: 'boolean' },
35
+ };
36
+ const getGitIssuesSchema = {
37
+ project_id: { type: 'string', required: true, validate: uuidValidator },
38
+ status: { type: 'string', default: 'open', validate: createEnumValidator(VALID_GIT_ISSUE_STATUSES) },
39
+ issue_type: { type: 'string', validate: createEnumValidator(VALID_GIT_ISSUE_TYPES) },
40
+ branch: { type: 'string' },
41
+ limit: { type: 'number', default: 50 },
42
+ };
43
+ const deleteGitIssueSchema = {
44
+ git_issue_id: { type: 'string', required: true, validate: uuidValidator },
45
+ };
20
46
  export const addGitIssue = async (args, ctx) => {
21
- const { project_id, issue_type, branch, target_branch, pr_url, conflicting_files, error_message, task_id, } = args;
22
- validateRequired(project_id, 'project_id');
23
- validateUUID(project_id, 'project_id');
24
- validateRequired(issue_type, 'issue_type');
25
- validateRequired(branch, 'branch');
26
- if (!VALID_GIT_ISSUE_TYPES.includes(issue_type)) {
27
- throw new Error(`Invalid issue_type. Valid types: ${VALID_GIT_ISSUE_TYPES.join(', ')}`);
28
- }
29
- if (task_id) {
30
- validateUUID(task_id, 'task_id');
31
- }
47
+ const { project_id, issue_type, branch, target_branch, pr_url, conflicting_files, error_message, task_id } = parseArgs(args, addGitIssueSchema);
32
48
  const apiClient = getApiClient();
33
49
  const response = await apiClient.addGitIssue(project_id, {
34
50
  issue_type: issue_type,
35
51
  branch,
36
52
  target_branch,
37
53
  pr_url,
38
- conflicting_files,
54
+ conflicting_files: conflicting_files,
39
55
  error_message,
40
56
  task_id
41
57
  }, ctx.session.currentSessionId || undefined);
@@ -45,9 +61,7 @@ export const addGitIssue = async (args, ctx) => {
45
61
  return { result: response.data };
46
62
  };
47
63
  export const resolveGitIssue = async (args, ctx) => {
48
- const { git_issue_id, resolution_note, auto_resolved } = args;
49
- validateRequired(git_issue_id, 'git_issue_id');
50
- validateUUID(git_issue_id, 'git_issue_id');
64
+ const { git_issue_id, resolution_note, auto_resolved } = parseArgs(args, resolveGitIssueSchema);
51
65
  const apiClient = getApiClient();
52
66
  const response = await apiClient.resolveGitIssue(git_issue_id, {
53
67
  resolution_note,
@@ -58,20 +72,12 @@ export const resolveGitIssue = async (args, ctx) => {
58
72
  }
59
73
  return { result: response.data };
60
74
  };
61
- export const getGitIssues = async (args, ctx) => {
62
- const { project_id, status = 'open', issue_type, branch, limit = 50, } = args;
63
- validateRequired(project_id, 'project_id');
64
- validateUUID(project_id, 'project_id');
65
- if (status && !VALID_GIT_ISSUE_STATUSES.includes(status)) {
66
- throw new Error(`Invalid status. Valid statuses: ${VALID_GIT_ISSUE_STATUSES.join(', ')}`);
67
- }
68
- if (issue_type && !VALID_GIT_ISSUE_TYPES.includes(issue_type)) {
69
- throw new Error(`Invalid issue_type. Valid types: ${VALID_GIT_ISSUE_TYPES.join(', ')}`);
70
- }
75
+ export const getGitIssues = async (args, _ctx) => {
76
+ const { project_id, status, issue_type, branch, limit } = parseArgs(args, getGitIssuesSchema);
71
77
  const apiClient = getApiClient();
72
78
  const response = await apiClient.getGitIssues(project_id, {
73
- status,
74
- issue_type,
79
+ status: status,
80
+ issue_type: issue_type,
75
81
  branch,
76
82
  limit
77
83
  });
@@ -80,10 +86,8 @@ export const getGitIssues = async (args, ctx) => {
80
86
  }
81
87
  return { result: response.data };
82
88
  };
83
- export const deleteGitIssue = async (args, ctx) => {
84
- const { git_issue_id } = args;
85
- validateRequired(git_issue_id, 'git_issue_id');
86
- validateUUID(git_issue_id, 'git_issue_id');
89
+ export const deleteGitIssue = async (args, _ctx) => {
90
+ const { git_issue_id } = parseArgs(args, deleteGitIssueSchema);
87
91
  const apiClient = getApiClient();
88
92
  const response = await apiClient.deleteGitIssue(git_issue_id);
89
93
  if (!response.ok) {
@@ -10,34 +10,60 @@
10
10
  *
11
11
  * MIGRATED: Uses Vibescope API client instead of direct Supabase
12
12
  */
13
- import { validateRequired, validateUUID, validatePriority, validateEstimatedMinutes } from '../validators.js';
13
+ import { parseArgs, uuidValidator, priorityValidator, minutesValidator, createEnumValidator, } from '../validators.js';
14
14
  import { getApiClient } from '../api-client.js';
15
+ const VALID_IDEA_STATUSES = ['raw', 'exploring', 'planned', 'in_development', 'shipped'];
16
+ // Argument schemas for type-safe parsing
17
+ const addIdeaSchema = {
18
+ project_id: { type: 'string', required: true, validate: uuidValidator },
19
+ title: { type: 'string', required: true },
20
+ description: { type: 'string' },
21
+ status: { type: 'string', validate: createEnumValidator(VALID_IDEA_STATUSES) },
22
+ };
23
+ const updateIdeaSchema = {
24
+ idea_id: { type: 'string', required: true, validate: uuidValidator },
25
+ title: { type: 'string' },
26
+ description: { type: 'string' },
27
+ status: { type: 'string', validate: createEnumValidator(VALID_IDEA_STATUSES) },
28
+ doc_url: { type: 'string' },
29
+ };
30
+ const getIdeasSchema = {
31
+ project_id: { type: 'string', required: true, validate: uuidValidator },
32
+ status: { type: 'string', validate: createEnumValidator(VALID_IDEA_STATUSES) },
33
+ limit: { type: 'number', default: 50 },
34
+ offset: { type: 'number', default: 0 },
35
+ search_query: { type: 'string' },
36
+ };
37
+ const deleteIdeaSchema = {
38
+ idea_id: { type: 'string', required: true, validate: uuidValidator },
39
+ };
40
+ const convertIdeaToTaskSchema = {
41
+ idea_id: { type: 'string', required: true, validate: uuidValidator },
42
+ priority: { type: 'number', default: 3, validate: priorityValidator },
43
+ estimated_minutes: { type: 'number', validate: minutesValidator },
44
+ update_status: { type: 'boolean', default: true },
45
+ };
15
46
  export const addIdea = async (args, ctx) => {
16
- const { project_id, title, description, status } = args;
17
- validateRequired(project_id, 'project_id');
18
- validateUUID(project_id, 'project_id');
19
- validateRequired(title, 'title');
47
+ const { project_id, title, description, status } = parseArgs(args, addIdeaSchema);
20
48
  const { session } = ctx;
21
49
  const apiClient = getApiClient();
22
50
  const response = await apiClient.addIdea(project_id, {
23
51
  title,
24
52
  description,
25
- status
53
+ status: status
26
54
  }, session.currentSessionId || undefined);
27
55
  if (!response.ok) {
28
56
  throw new Error(`Failed to add idea: ${response.error}`);
29
57
  }
30
58
  return { result: { success: true, idea_id: response.data?.idea_id, title } };
31
59
  };
32
- export const updateIdea = async (args, ctx) => {
33
- const { idea_id, title, description, status, doc_url } = args;
34
- validateRequired(idea_id, 'idea_id');
35
- validateUUID(idea_id, 'idea_id');
60
+ export const updateIdea = async (args, _ctx) => {
61
+ const { idea_id, title, description, status, doc_url } = parseArgs(args, updateIdeaSchema);
36
62
  const apiClient = getApiClient();
37
63
  const response = await apiClient.updateIdea(idea_id, {
38
64
  title,
39
65
  description,
40
- status,
66
+ status: status,
41
67
  doc_url
42
68
  });
43
69
  if (!response.ok) {
@@ -45,13 +71,11 @@ export const updateIdea = async (args, ctx) => {
45
71
  }
46
72
  return { result: { success: true, idea_id } };
47
73
  };
48
- export const getIdeas = async (args, ctx) => {
49
- const { project_id, status, limit = 50, offset = 0, search_query } = args;
50
- validateRequired(project_id, 'project_id');
51
- validateUUID(project_id, 'project_id');
74
+ export const getIdeas = async (args, _ctx) => {
75
+ const { project_id, status, limit, offset, search_query } = parseArgs(args, getIdeasSchema);
52
76
  const apiClient = getApiClient();
53
77
  const response = await apiClient.getIdeas(project_id, {
54
- status,
78
+ status: status,
55
79
  limit,
56
80
  offset,
57
81
  search_query
@@ -65,10 +89,8 @@ export const getIdeas = async (args, ctx) => {
65
89
  },
66
90
  };
67
91
  };
68
- export const deleteIdea = async (args, ctx) => {
69
- const { idea_id } = args;
70
- validateRequired(idea_id, 'idea_id');
71
- validateUUID(idea_id, 'idea_id');
92
+ export const deleteIdea = async (args, _ctx) => {
93
+ const { idea_id } = parseArgs(args, deleteIdeaSchema);
72
94
  const apiClient = getApiClient();
73
95
  const response = await apiClient.deleteIdea(idea_id);
74
96
  if (!response.ok) {
@@ -76,12 +98,8 @@ export const deleteIdea = async (args, ctx) => {
76
98
  }
77
99
  return { result: { success: true } };
78
100
  };
79
- export const convertIdeaToTask = async (args, ctx) => {
80
- const { idea_id, priority = 3, estimated_minutes, update_status = true } = args;
81
- validateRequired(idea_id, 'idea_id');
82
- validateUUID(idea_id, 'idea_id');
83
- validatePriority(priority);
84
- validateEstimatedMinutes(estimated_minutes);
101
+ export const convertIdeaToTask = async (args, _ctx) => {
102
+ const { idea_id, priority, estimated_minutes, update_status } = parseArgs(args, convertIdeaToTaskSchema);
85
103
  const apiClient = getApiClient();
86
104
  // Use proxy for convert_idea_to_task operation
87
105
  const response = await apiClient.proxy('convert_idea_to_task', {