@vibescope/mcp-server 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -98
- package/dist/api-client.d.ts +1114 -0
- package/dist/api-client.js +698 -0
- package/dist/cli.d.ts +1 -6
- package/dist/cli.js +39 -240
- package/dist/config/tool-categories.d.ts +31 -0
- package/dist/config/tool-categories.js +253 -0
- package/dist/handlers/blockers.js +57 -58
- package/dist/handlers/bodies-of-work.d.ts +2 -0
- package/dist/handlers/bodies-of-work.js +106 -476
- package/dist/handlers/cost.d.ts +1 -0
- package/dist/handlers/cost.js +35 -113
- package/dist/handlers/decisions.d.ts +2 -0
- package/dist/handlers/decisions.js +28 -27
- package/dist/handlers/deployment.js +112 -828
- package/dist/handlers/discovery.js +31 -0
- package/dist/handlers/fallback.d.ts +2 -0
- package/dist/handlers/fallback.js +39 -134
- package/dist/handlers/findings.js +43 -67
- package/dist/handlers/git-issues.d.ts +9 -13
- package/dist/handlers/git-issues.js +80 -225
- package/dist/handlers/ideas.d.ts +3 -0
- package/dist/handlers/ideas.js +53 -134
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.js +6 -0
- package/dist/handlers/milestones.d.ts +2 -0
- package/dist/handlers/milestones.js +51 -98
- package/dist/handlers/organizations.js +79 -275
- package/dist/handlers/progress.d.ts +2 -0
- package/dist/handlers/progress.js +25 -123
- package/dist/handlers/project.js +42 -221
- package/dist/handlers/requests.d.ts +2 -0
- package/dist/handlers/requests.js +23 -83
- package/dist/handlers/session.js +99 -585
- package/dist/handlers/sprints.d.ts +32 -0
- package/dist/handlers/sprints.js +274 -0
- package/dist/handlers/tasks.d.ts +7 -10
- package/dist/handlers/tasks.js +230 -900
- package/dist/handlers/tool-docs.d.ts +8 -0
- package/dist/handlers/tool-docs.js +657 -0
- package/dist/handlers/types.d.ts +11 -3
- package/dist/handlers/validation.d.ts +1 -1
- package/dist/handlers/validation.js +26 -153
- package/dist/index.js +473 -160
- package/dist/knowledge.js +106 -9
- package/dist/tools.js +4 -0
- package/dist/validators.d.ts +21 -0
- package/dist/validators.js +91 -0
- package/package.json +2 -3
- package/src/api-client.ts +1752 -0
- package/src/cli.test.ts +128 -302
- package/src/cli.ts +41 -285
- package/src/handlers/__test-setup__.ts +210 -0
- package/src/handlers/__test-utils__.ts +4 -134
- package/src/handlers/blockers.test.ts +114 -124
- package/src/handlers/blockers.ts +68 -70
- package/src/handlers/bodies-of-work.test.ts +236 -831
- package/src/handlers/bodies-of-work.ts +194 -525
- package/src/handlers/cost.test.ts +149 -113
- package/src/handlers/cost.ts +44 -132
- package/src/handlers/decisions.test.ts +111 -209
- package/src/handlers/decisions.ts +35 -27
- package/src/handlers/deployment.test.ts +193 -239
- package/src/handlers/deployment.ts +140 -895
- package/src/handlers/discovery.test.ts +20 -67
- package/src/handlers/discovery.ts +32 -0
- package/src/handlers/fallback.test.ts +128 -361
- package/src/handlers/fallback.ts +62 -148
- package/src/handlers/findings.test.ts +127 -345
- package/src/handlers/findings.ts +49 -66
- package/src/handlers/git-issues.test.ts +623 -0
- package/src/handlers/git-issues.ts +174 -0
- package/src/handlers/ideas.test.ts +229 -343
- package/src/handlers/ideas.ts +69 -143
- package/src/handlers/index.ts +6 -0
- package/src/handlers/milestones.test.ts +167 -281
- package/src/handlers/milestones.ts +54 -93
- package/src/handlers/organizations.test.ts +275 -467
- package/src/handlers/organizations.ts +84 -294
- package/src/handlers/progress.test.ts +112 -218
- package/src/handlers/progress.ts +29 -142
- package/src/handlers/project.test.ts +203 -226
- package/src/handlers/project.ts +48 -238
- package/src/handlers/requests.test.ts +74 -342
- package/src/handlers/requests.ts +25 -83
- package/src/handlers/session.test.ts +241 -206
- package/src/handlers/session.ts +110 -657
- package/src/handlers/sprints.test.ts +711 -0
- package/src/handlers/sprints.ts +497 -0
- package/src/handlers/tasks.test.ts +608 -353
- package/src/handlers/tasks.ts +248 -1025
- package/src/handlers/types.ts +12 -4
- package/src/handlers/validation.test.ts +189 -572
- package/src/handlers/validation.ts +29 -166
- package/src/index.ts +473 -184
- package/src/knowledge.ts +107 -9
- package/src/tools.ts +2506 -0
- package/src/validators.test.ts +223 -223
- package/src/validators.ts +127 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +14 -13
- package/dist/cli.test.d.ts +0 -1
- package/dist/cli.test.js +0 -367
- package/dist/handlers/__test-utils__.d.ts +0 -72
- package/dist/handlers/__test-utils__.js +0 -176
- package/dist/handlers/checkouts.d.ts +0 -37
- package/dist/handlers/checkouts.js +0 -377
- package/dist/handlers/knowledge-query.d.ts +0 -22
- package/dist/handlers/knowledge-query.js +0 -253
- package/dist/handlers/knowledge.d.ts +0 -12
- package/dist/handlers/knowledge.js +0 -108
- package/dist/handlers/roles.d.ts +0 -30
- package/dist/handlers/roles.js +0 -281
- package/dist/handlers/tasks.test.d.ts +0 -1
- package/dist/handlers/tasks.test.js +0 -431
- package/dist/utils.test.d.ts +0 -1
- package/dist/utils.test.js +0 -532
- package/dist/validators.test.d.ts +0 -1
- package/dist/validators.test.js +0 -176
- package/src/tmpclaude-0078-cwd +0 -1
- package/src/tmpclaude-0ee1-cwd +0 -1
- package/src/tmpclaude-2dd5-cwd +0 -1
- package/src/tmpclaude-344c-cwd +0 -1
- package/src/tmpclaude-3860-cwd +0 -1
- package/src/tmpclaude-4b63-cwd +0 -1
- package/src/tmpclaude-5c73-cwd +0 -1
- package/src/tmpclaude-5ee3-cwd +0 -1
- package/src/tmpclaude-6795-cwd +0 -1
- package/src/tmpclaude-709e-cwd +0 -1
- package/src/tmpclaude-9839-cwd +0 -1
- package/src/tmpclaude-d829-cwd +0 -1
- package/src/tmpclaude-e072-cwd +0 -1
- package/src/tmpclaude-f6ee-cwd +0 -1
- package/tmpclaude-0439-cwd +0 -1
- package/tmpclaude-132f-cwd +0 -1
- package/tmpclaude-15bb-cwd +0 -1
- package/tmpclaude-165a-cwd +0 -1
- package/tmpclaude-1ba9-cwd +0 -1
- package/tmpclaude-21a3-cwd +0 -1
- package/tmpclaude-2a38-cwd +0 -1
- package/tmpclaude-2adf-cwd +0 -1
- package/tmpclaude-2f56-cwd +0 -1
- package/tmpclaude-3626-cwd +0 -1
- package/tmpclaude-3727-cwd +0 -1
- package/tmpclaude-40bc-cwd +0 -1
- package/tmpclaude-436f-cwd +0 -1
- package/tmpclaude-4783-cwd +0 -1
- package/tmpclaude-4b6d-cwd +0 -1
- package/tmpclaude-4ba4-cwd +0 -1
- package/tmpclaude-51e6-cwd +0 -1
- package/tmpclaude-5ecf-cwd +0 -1
- package/tmpclaude-6f97-cwd +0 -1
- package/tmpclaude-7fb2-cwd +0 -1
- package/tmpclaude-825c-cwd +0 -1
- package/tmpclaude-8baf-cwd +0 -1
- package/tmpclaude-8d9f-cwd +0 -1
- package/tmpclaude-975c-cwd +0 -1
- package/tmpclaude-9983-cwd +0 -1
- package/tmpclaude-a045-cwd +0 -1
- package/tmpclaude-ac4a-cwd +0 -1
- package/tmpclaude-b593-cwd +0 -1
- package/tmpclaude-b891-cwd +0 -1
- package/tmpclaude-c032-cwd +0 -1
- package/tmpclaude-cf43-cwd +0 -1
- package/tmpclaude-d040-cwd +0 -1
- package/tmpclaude-dcdd-cwd +0 -1
- package/tmpclaude-dcee-cwd +0 -1
- package/tmpclaude-e16b-cwd +0 -1
- package/tmpclaude-ecd2-cwd +0 -1
- package/tmpclaude-f48d-cwd +0 -1
|
@@ -4,141 +4,43 @@
|
|
|
4
4
|
* Handles progress logging and activity feed:
|
|
5
5
|
* - log_progress
|
|
6
6
|
* - get_activity_feed
|
|
7
|
+
*
|
|
8
|
+
* MIGRATED: Uses Vibescope API client instead of direct Supabase
|
|
7
9
|
*/
|
|
8
10
|
import { validateRequired, validateUUID } from '../validators.js';
|
|
11
|
+
import { getApiClient } from '../api-client.js';
|
|
9
12
|
export const logProgress = async (args, ctx) => {
|
|
10
13
|
const { project_id, task_id, summary, details } = args;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
validateRequired(project_id, 'project_id');
|
|
15
|
+
validateUUID(project_id, 'project_id');
|
|
16
|
+
validateRequired(summary, 'summary');
|
|
17
|
+
const { session } = ctx;
|
|
18
|
+
const apiClient = getApiClient();
|
|
19
|
+
const response = await apiClient.logProgress(project_id, {
|
|
17
20
|
summary,
|
|
18
|
-
details
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
details,
|
|
22
|
+
task_id,
|
|
23
|
+
session_id: session.currentSessionId || undefined
|
|
21
24
|
});
|
|
22
|
-
if (
|
|
23
|
-
throw new Error(`Failed to log progress: ${error
|
|
24
|
-
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
throw new Error(`Failed to log progress: ${response.error}`);
|
|
27
|
+
}
|
|
28
|
+
return { result: { success: true, progress_id: response.data?.progress_id } };
|
|
25
29
|
};
|
|
26
30
|
export const getActivityFeed = async (args, ctx) => {
|
|
27
|
-
const { project_id,
|
|
31
|
+
const { project_id, limit = 50, since } = args;
|
|
28
32
|
validateRequired(project_id, 'project_id');
|
|
29
33
|
validateUUID(project_id, 'project_id');
|
|
30
|
-
const
|
|
34
|
+
const apiClient = getApiClient();
|
|
31
35
|
const effectiveLimit = Math.min(limit, 200);
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
let query = supabase
|
|
39
|
-
.from('tasks')
|
|
40
|
-
.select('id, title, status, created_by, created_at, completed_at')
|
|
41
|
-
.eq('project_id', project_id)
|
|
42
|
-
.order('created_at', { ascending: false })
|
|
43
|
-
.limit(perTypeLimit);
|
|
44
|
-
if (created_by)
|
|
45
|
-
query = query.eq('created_by', created_by);
|
|
46
|
-
if (since)
|
|
47
|
-
query = query.gt('created_at', since);
|
|
48
|
-
const { data: tasks } = await query;
|
|
49
|
-
if (tasks) {
|
|
50
|
-
for (const task of tasks) {
|
|
51
|
-
activities.push({
|
|
52
|
-
type: 'task',
|
|
53
|
-
id: task.id,
|
|
54
|
-
title: task.title,
|
|
55
|
-
status: task.status,
|
|
56
|
-
created_by: task.created_by,
|
|
57
|
-
created_at: task.created_at,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
// Fetch progress logs if included
|
|
63
|
-
if (includeTypes.includes('progress')) {
|
|
64
|
-
let query = supabase
|
|
65
|
-
.from('progress_logs')
|
|
66
|
-
.select('id, summary, created_by, created_at')
|
|
67
|
-
.eq('project_id', project_id)
|
|
68
|
-
.order('created_at', { ascending: false })
|
|
69
|
-
.limit(perTypeLimit);
|
|
70
|
-
if (created_by)
|
|
71
|
-
query = query.eq('created_by', created_by);
|
|
72
|
-
if (since)
|
|
73
|
-
query = query.gt('created_at', since);
|
|
74
|
-
const { data: logs } = await query;
|
|
75
|
-
if (logs) {
|
|
76
|
-
for (const log of logs) {
|
|
77
|
-
activities.push({
|
|
78
|
-
type: 'progress',
|
|
79
|
-
id: log.id,
|
|
80
|
-
summary: log.summary,
|
|
81
|
-
created_by: log.created_by,
|
|
82
|
-
created_at: log.created_at,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// Fetch blockers if included
|
|
88
|
-
if (includeTypes.includes('blocker')) {
|
|
89
|
-
let query = supabase
|
|
90
|
-
.from('blockers')
|
|
91
|
-
.select('id, description, status, created_by, created_at')
|
|
92
|
-
.eq('project_id', project_id)
|
|
93
|
-
.order('created_at', { ascending: false })
|
|
94
|
-
.limit(perTypeLimit);
|
|
95
|
-
if (created_by)
|
|
96
|
-
query = query.eq('created_by', created_by);
|
|
97
|
-
if (since)
|
|
98
|
-
query = query.gt('created_at', since);
|
|
99
|
-
const { data: blockers } = await query;
|
|
100
|
-
if (blockers) {
|
|
101
|
-
for (const blocker of blockers) {
|
|
102
|
-
activities.push({
|
|
103
|
-
type: 'blocker',
|
|
104
|
-
id: blocker.id,
|
|
105
|
-
description: blocker.description,
|
|
106
|
-
status: blocker.status,
|
|
107
|
-
created_by: blocker.created_by,
|
|
108
|
-
created_at: blocker.created_at,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// Fetch decisions if included
|
|
114
|
-
if (includeTypes.includes('decision')) {
|
|
115
|
-
let query = supabase
|
|
116
|
-
.from('decisions')
|
|
117
|
-
.select('id, title, created_by, created_at')
|
|
118
|
-
.eq('project_id', project_id)
|
|
119
|
-
.order('created_at', { ascending: false })
|
|
120
|
-
.limit(perTypeLimit);
|
|
121
|
-
if (created_by)
|
|
122
|
-
query = query.eq('created_by', created_by);
|
|
123
|
-
if (since)
|
|
124
|
-
query = query.gt('created_at', since);
|
|
125
|
-
const { data: decisions } = await query;
|
|
126
|
-
if (decisions) {
|
|
127
|
-
for (const decision of decisions) {
|
|
128
|
-
activities.push({
|
|
129
|
-
type: 'decision',
|
|
130
|
-
id: decision.id,
|
|
131
|
-
title: decision.title,
|
|
132
|
-
created_by: decision.created_by,
|
|
133
|
-
created_at: decision.created_at,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
}
|
|
36
|
+
const response = await apiClient.getActivityFeed(project_id, {
|
|
37
|
+
limit: effectiveLimit,
|
|
38
|
+
since
|
|
39
|
+
});
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(`Failed to fetch activity feed: ${response.error}`);
|
|
137
42
|
}
|
|
138
|
-
|
|
139
|
-
activities.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
|
140
|
-
const limitedActivities = activities.slice(0, effectiveLimit);
|
|
141
|
-
return { result: { activities: limitedActivities } };
|
|
43
|
+
return { result: { activities: response.data?.activities || [] } };
|
|
142
44
|
};
|
|
143
45
|
/**
|
|
144
46
|
* Progress handlers registry
|
package/dist/handlers/project.js
CHANGED
|
@@ -9,262 +9,83 @@
|
|
|
9
9
|
* - update_project_readme
|
|
10
10
|
*/
|
|
11
11
|
import { validateRequired, validateUUID, validateProjectStatus } from '../validators.js';
|
|
12
|
-
|
|
13
|
-
* Get user-created items since last sync
|
|
14
|
-
*/
|
|
15
|
-
async function getUserUpdates(supabase, auth, projectId, currentSessionId) {
|
|
16
|
-
let lastSyncedAt;
|
|
17
|
-
if (currentSessionId) {
|
|
18
|
-
const { data: session } = await supabase
|
|
19
|
-
.from('agent_sessions')
|
|
20
|
-
.select('last_synced_at')
|
|
21
|
-
.eq('id', currentSessionId)
|
|
22
|
-
.single();
|
|
23
|
-
lastSyncedAt = session?.last_synced_at || new Date(0).toISOString();
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
const { data: session } = await supabase
|
|
27
|
-
.from('agent_sessions')
|
|
28
|
-
.select('last_synced_at')
|
|
29
|
-
.eq('api_key_id', auth.apiKeyId)
|
|
30
|
-
.eq('project_id', projectId)
|
|
31
|
-
.single();
|
|
32
|
-
lastSyncedAt = session?.last_synced_at || new Date(0).toISOString();
|
|
33
|
-
}
|
|
34
|
-
const [tasksResult, blockersResult, ideasResult] = await Promise.all([
|
|
35
|
-
supabase
|
|
36
|
-
.from('tasks')
|
|
37
|
-
.select('id, title, created_at')
|
|
38
|
-
.eq('project_id', projectId)
|
|
39
|
-
.eq('created_by', 'user')
|
|
40
|
-
.gt('created_at', lastSyncedAt)
|
|
41
|
-
.order('created_at', { ascending: false })
|
|
42
|
-
.limit(5),
|
|
43
|
-
supabase
|
|
44
|
-
.from('blockers')
|
|
45
|
-
.select('id, description, created_at')
|
|
46
|
-
.eq('project_id', projectId)
|
|
47
|
-
.eq('created_by', 'user')
|
|
48
|
-
.gt('created_at', lastSyncedAt)
|
|
49
|
-
.order('created_at', { ascending: false })
|
|
50
|
-
.limit(5),
|
|
51
|
-
supabase
|
|
52
|
-
.from('ideas')
|
|
53
|
-
.select('id, title, created_at')
|
|
54
|
-
.eq('project_id', projectId)
|
|
55
|
-
.eq('created_by', 'user')
|
|
56
|
-
.gt('created_at', lastSyncedAt)
|
|
57
|
-
.order('created_at', { ascending: false })
|
|
58
|
-
.limit(5),
|
|
59
|
-
]);
|
|
60
|
-
const tasks = tasksResult.data || [];
|
|
61
|
-
const blockers = blockersResult.data || [];
|
|
62
|
-
const ideas = ideasResult.data || [];
|
|
63
|
-
if (tasks.length === 0 && blockers.length === 0 && ideas.length === 0) {
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
return { tasks, blockers, ideas };
|
|
67
|
-
}
|
|
12
|
+
import { getApiClient } from '../api-client.js';
|
|
68
13
|
export const getProjectContext = async (args, ctx) => {
|
|
69
14
|
const { project_id, git_url } = args;
|
|
70
|
-
const
|
|
15
|
+
const apiClient = getApiClient();
|
|
71
16
|
// If no project_id or git_url, list all projects
|
|
72
17
|
if (!project_id && !git_url) {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (error)
|
|
79
|
-
throw new Error(`Failed to fetch projects: ${error.message}`);
|
|
80
|
-
return { result: { projects: projects || [] } };
|
|
18
|
+
const response = await apiClient.listProjects();
|
|
19
|
+
if (!response.ok) {
|
|
20
|
+
throw new Error(response.error || 'Failed to fetch projects');
|
|
21
|
+
}
|
|
22
|
+
return { result: { projects: response.data?.projects || [] } };
|
|
81
23
|
}
|
|
82
24
|
// Find project by ID or git_url
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
.eq('user_id', auth.userId);
|
|
87
|
-
if (project_id) {
|
|
88
|
-
query = query.eq('id', project_id);
|
|
89
|
-
}
|
|
90
|
-
else if (git_url) {
|
|
91
|
-
query = query.eq('git_url', git_url);
|
|
25
|
+
const response = await apiClient.getProject(project_id || 'by-git-url', git_url);
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(response.error || 'Failed to fetch project');
|
|
92
28
|
}
|
|
93
|
-
|
|
94
|
-
if (projectError || !project) {
|
|
29
|
+
if (!response.data?.found) {
|
|
95
30
|
return {
|
|
96
31
|
result: {
|
|
97
32
|
found: false,
|
|
98
|
-
message: 'Project not found. Use create_project to create one.',
|
|
33
|
+
message: response.data?.message || 'Project not found. Use create_project to create one.',
|
|
99
34
|
},
|
|
100
35
|
};
|
|
101
36
|
}
|
|
102
|
-
|
|
103
|
-
const [tasksResult, blockersResult, decisionsResult, progressResult] = await Promise.all([
|
|
104
|
-
supabase
|
|
105
|
-
.from('tasks')
|
|
106
|
-
.select('id, title, description, priority, status, estimated_minutes')
|
|
107
|
-
.eq('project_id', project.id)
|
|
108
|
-
.in('status', ['pending', 'in_progress'])
|
|
109
|
-
.order('priority', { ascending: true })
|
|
110
|
-
.limit(10),
|
|
111
|
-
supabase
|
|
112
|
-
.from('blockers')
|
|
113
|
-
.select('id, description')
|
|
114
|
-
.eq('project_id', project.id)
|
|
115
|
-
.eq('status', 'open')
|
|
116
|
-
.limit(5),
|
|
117
|
-
supabase
|
|
118
|
-
.from('decisions')
|
|
119
|
-
.select('title')
|
|
120
|
-
.eq('project_id', project.id)
|
|
121
|
-
.order('created_at', { ascending: false })
|
|
122
|
-
.limit(5),
|
|
123
|
-
supabase
|
|
124
|
-
.from('progress_logs')
|
|
125
|
-
.select('summary')
|
|
126
|
-
.eq('project_id', project.id)
|
|
127
|
-
.order('created_at', { ascending: false })
|
|
128
|
-
.limit(5),
|
|
129
|
-
]);
|
|
130
|
-
const userUpdates = await getUserUpdates(supabase, auth, project.id, session.currentSessionId);
|
|
131
|
-
// Build compact response
|
|
132
|
-
const result = {
|
|
133
|
-
found: true,
|
|
134
|
-
project,
|
|
135
|
-
active_tasks: tasksResult.data || [],
|
|
136
|
-
};
|
|
137
|
-
const blockers = blockersResult.data || [];
|
|
138
|
-
const decisions = decisionsResult.data || [];
|
|
139
|
-
const progress = progressResult.data || [];
|
|
140
|
-
if (blockers.length > 0)
|
|
141
|
-
result.open_blockers = blockers;
|
|
142
|
-
if (decisions.length > 0)
|
|
143
|
-
result.recent_decisions = decisions;
|
|
144
|
-
if (progress.length > 0)
|
|
145
|
-
result.recent_progress = progress;
|
|
146
|
-
return { result, user_updates: userUpdates };
|
|
37
|
+
return { result: response.data };
|
|
147
38
|
};
|
|
148
39
|
export const getGitWorkflow = async (args, ctx) => {
|
|
149
40
|
const { project_id, task_id } = args;
|
|
150
41
|
validateRequired(project_id, 'project_id');
|
|
151
42
|
validateUUID(project_id, 'project_id');
|
|
152
|
-
const
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
.
|
|
156
|
-
.eq('id', project_id)
|
|
157
|
-
.single();
|
|
158
|
-
if (error || !project)
|
|
159
|
-
throw new Error(`Project not found: ${project_id}`);
|
|
160
|
-
let task = null;
|
|
161
|
-
if (task_id) {
|
|
162
|
-
const { data: taskData } = await supabase
|
|
163
|
-
.from('tasks')
|
|
164
|
-
.select('id, title, git_branch')
|
|
165
|
-
.eq('id', task_id)
|
|
166
|
-
.single();
|
|
167
|
-
task = taskData;
|
|
43
|
+
const apiClient = getApiClient();
|
|
44
|
+
const response = await apiClient.getGitWorkflow(project_id, task_id);
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
throw new Error(response.error || 'Failed to get git workflow');
|
|
168
47
|
}
|
|
169
|
-
|
|
170
|
-
const workflow = project.git_workflow || 'none';
|
|
171
|
-
const mainBranch = project.git_main_branch || 'main';
|
|
172
|
-
const developBranch = project.git_develop_branch || 'develop';
|
|
173
|
-
const instructions = {
|
|
174
|
-
'none': [
|
|
175
|
-
'No git workflow configured for this project.',
|
|
176
|
-
'Commit changes directly without branching strategy.',
|
|
177
|
-
],
|
|
178
|
-
'trunk-based': [
|
|
179
|
-
`Work directly on the ${mainBranch} branch.`,
|
|
180
|
-
'Make small, frequent commits.',
|
|
181
|
-
'Ensure all tests pass before committing.',
|
|
182
|
-
`Tag deployments on ${mainBranch} (e.g., git tag v1.0.0).`,
|
|
183
|
-
],
|
|
184
|
-
'github-flow': [
|
|
185
|
-
task ? `Create feature branch: git checkout -b feature/${task.id.slice(0, 8)}-${task.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 30)}` : 'Create a feature branch for each task.',
|
|
186
|
-
'Make commits with descriptive messages.',
|
|
187
|
-
'Push branch and create a pull request.',
|
|
188
|
-
`Merge to ${mainBranch} after review/validation.`,
|
|
189
|
-
project.git_auto_tag ? 'Deployments will be automatically tagged.' : `Tag deployments manually on ${mainBranch}.`,
|
|
190
|
-
],
|
|
191
|
-
'git-flow': [
|
|
192
|
-
task ? `Create feature branch from ${developBranch}: git checkout -b feature/${task.id.slice(0, 8)}-${task.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 30)} ${developBranch}` : `Create feature branches from ${developBranch}.`,
|
|
193
|
-
`Merge completed features back to ${developBranch}.`,
|
|
194
|
-
`Create release branches from ${developBranch} when ready.`,
|
|
195
|
-
`Merge releases to both ${mainBranch} and ${developBranch}.`,
|
|
196
|
-
`Tag releases on ${mainBranch}.`,
|
|
197
|
-
],
|
|
198
|
-
};
|
|
199
|
-
const result = {
|
|
200
|
-
workflow,
|
|
201
|
-
main_branch: mainBranch,
|
|
202
|
-
develop_branch: workflow === 'git-flow' ? developBranch : null,
|
|
203
|
-
auto_branch: project.git_auto_branch,
|
|
204
|
-
auto_tag: project.git_auto_tag,
|
|
205
|
-
instructions: instructions[workflow] || instructions['none'],
|
|
206
|
-
};
|
|
207
|
-
if (task) {
|
|
208
|
-
result.task = {
|
|
209
|
-
id: task.id,
|
|
210
|
-
title: task.title,
|
|
211
|
-
current_branch: task.git_branch,
|
|
212
|
-
suggested_branch: workflow !== 'none' && workflow !== 'trunk-based'
|
|
213
|
-
? `feature/${task.id.slice(0, 8)}-${task.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 30)}`
|
|
214
|
-
: null,
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
return { result };
|
|
48
|
+
return { result: response.data };
|
|
218
49
|
};
|
|
219
50
|
export const createProject = async (args, ctx) => {
|
|
220
51
|
const { name, description, goal, git_url, tech_stack } = args;
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
.insert({
|
|
225
|
-
user_id: auth.userId,
|
|
52
|
+
validateRequired(name, 'name');
|
|
53
|
+
const apiClient = getApiClient();
|
|
54
|
+
const response = await apiClient.createProject({
|
|
226
55
|
name,
|
|
227
|
-
description
|
|
228
|
-
goal
|
|
229
|
-
git_url
|
|
230
|
-
tech_stack
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return { result: { success: true, project: data } };
|
|
56
|
+
description,
|
|
57
|
+
goal,
|
|
58
|
+
git_url,
|
|
59
|
+
tech_stack
|
|
60
|
+
});
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
throw new Error(response.error || 'Failed to create project');
|
|
63
|
+
}
|
|
64
|
+
return { result: response.data };
|
|
237
65
|
};
|
|
238
66
|
export const updateProject = async (args, ctx) => {
|
|
239
67
|
const { project_id, ...updates } = args;
|
|
240
68
|
validateRequired(project_id, 'project_id');
|
|
241
69
|
validateUUID(project_id, 'project_id');
|
|
242
70
|
validateProjectStatus(updates.status);
|
|
243
|
-
const
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
.update
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (error)
|
|
250
|
-
throw new Error(`Failed to update project: ${error.message}`);
|
|
251
|
-
return { result: { success: true, project_id } };
|
|
71
|
+
const apiClient = getApiClient();
|
|
72
|
+
const response = await apiClient.updateProject(project_id, updates);
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
throw new Error(response.error || 'Failed to update project');
|
|
75
|
+
}
|
|
76
|
+
return { result: response.data };
|
|
252
77
|
};
|
|
253
78
|
export const updateProjectReadme = async (args, ctx) => {
|
|
254
79
|
const { project_id, readme_content } = args;
|
|
255
80
|
validateRequired(project_id, 'project_id');
|
|
256
81
|
validateUUID(project_id, 'project_id');
|
|
257
82
|
validateRequired(readme_content, 'readme_content');
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
.eq('id', project_id);
|
|
265
|
-
if (error)
|
|
266
|
-
throw new Error(`Failed to update README: ${error.message}`);
|
|
267
|
-
return { result: { success: true } };
|
|
83
|
+
const apiClient = getApiClient();
|
|
84
|
+
const response = await apiClient.updateProjectReadme(project_id, readme_content);
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
throw new Error(response.error || 'Failed to update README');
|
|
87
|
+
}
|
|
88
|
+
return { result: response.data };
|
|
268
89
|
};
|
|
269
90
|
/**
|
|
270
91
|
* Project handlers registry
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* - get_pending_requests
|
|
6
6
|
* - acknowledge_request
|
|
7
7
|
* - answer_question
|
|
8
|
+
*
|
|
9
|
+
* MIGRATED: Uses Vibescope API client instead of direct Supabase
|
|
8
10
|
*/
|
|
9
11
|
import type { Handler, HandlerRegistry } from './types.js';
|
|
10
12
|
export declare const getPendingRequests: Handler;
|
|
@@ -5,68 +5,25 @@
|
|
|
5
5
|
* - get_pending_requests
|
|
6
6
|
* - acknowledge_request
|
|
7
7
|
* - answer_question
|
|
8
|
+
*
|
|
9
|
+
* MIGRATED: Uses Vibescope API client instead of direct Supabase
|
|
8
10
|
*/
|
|
9
11
|
import { validateRequired, validateUUID } from '../validators.js';
|
|
12
|
+
import { getApiClient } from '../api-client.js';
|
|
10
13
|
export const getPendingRequests = async (args, ctx) => {
|
|
11
14
|
const { project_id } = args;
|
|
12
15
|
validateRequired(project_id, 'project_id');
|
|
13
16
|
validateUUID(project_id, 'project_id');
|
|
14
|
-
const {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
.eq('project_id', project_id)
|
|
21
|
-
.eq('status', 'active');
|
|
22
|
-
const activeSessionIds = new Set((activeSessions || []).map((s) => s.id));
|
|
23
|
-
// Get pending requests for this project:
|
|
24
|
-
// - Unacknowledged requests OR unanswered questions (questions need answers, not just acknowledgment)
|
|
25
|
-
const { data: requests, error } = await supabase
|
|
26
|
-
.from('agent_requests')
|
|
27
|
-
.select('*')
|
|
28
|
-
.eq('project_id', project_id)
|
|
29
|
-
.or('acknowledged_at.is.null,and(request_type.eq.question,answered_at.is.null)')
|
|
30
|
-
.order('created_at', { ascending: false });
|
|
31
|
-
if (error)
|
|
32
|
-
throw error;
|
|
33
|
-
// Filter to requests this agent can handle
|
|
34
|
-
const filteredRequests = (requests || []).filter((r) => {
|
|
35
|
-
// Broadcast requests (session_id is null) - anyone can handle
|
|
36
|
-
if (!r.session_id)
|
|
37
|
-
return true;
|
|
38
|
-
// Targeted to this session
|
|
39
|
-
if (r.session_id === currentSessionId)
|
|
40
|
-
return true;
|
|
41
|
-
// Orphaned questions (targeted session is disconnected) - any agent can answer
|
|
42
|
-
if (r.request_type === 'question' && !activeSessionIds.has(r.session_id))
|
|
43
|
-
return true;
|
|
44
|
-
return false;
|
|
45
|
-
});
|
|
46
|
-
// Sort questions first (highest priority) and add wait times
|
|
47
|
-
const now = new Date();
|
|
48
|
-
const sortedRequests = filteredRequests
|
|
49
|
-
.map((r) => ({
|
|
50
|
-
...r,
|
|
51
|
-
wait_minutes: Math.floor((now.getTime() - new Date(r.created_at).getTime()) / 60000),
|
|
52
|
-
}))
|
|
53
|
-
.sort((a, b) => {
|
|
54
|
-
// Questions first, then by created_at (oldest first for urgency)
|
|
55
|
-
const aIsQuestion = a.request_type === 'question' && !a.answered_at;
|
|
56
|
-
const bIsQuestion = b.request_type === 'question' && !b.answered_at;
|
|
57
|
-
if (aIsQuestion && !bIsQuestion)
|
|
58
|
-
return -1;
|
|
59
|
-
if (!aIsQuestion && bIsQuestion)
|
|
60
|
-
return 1;
|
|
61
|
-
return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
|
|
62
|
-
});
|
|
63
|
-
// Count unanswered questions separately
|
|
64
|
-
const questionsCount = sortedRequests.filter((r) => r.request_type === 'question' && !r.answered_at).length;
|
|
17
|
+
const { session } = ctx;
|
|
18
|
+
const apiClient = getApiClient();
|
|
19
|
+
const response = await apiClient.getPendingRequests(project_id, session.currentSessionId || undefined);
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
throw new Error(`Failed to get pending requests: ${response.error}`);
|
|
22
|
+
}
|
|
65
23
|
return {
|
|
66
24
|
result: {
|
|
67
|
-
requests:
|
|
68
|
-
count:
|
|
69
|
-
questions_count: questionsCount,
|
|
25
|
+
requests: response.data?.requests || [],
|
|
26
|
+
count: response.data?.requests?.length || 0,
|
|
70
27
|
},
|
|
71
28
|
};
|
|
72
29
|
};
|
|
@@ -74,22 +31,15 @@ export const acknowledgeRequest = async (args, ctx) => {
|
|
|
74
31
|
const { request_id } = args;
|
|
75
32
|
validateRequired(request_id, 'request_id');
|
|
76
33
|
validateUUID(request_id, 'request_id');
|
|
77
|
-
const {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
})
|
|
84
|
-
.eq('id', request_id)
|
|
85
|
-
.select()
|
|
86
|
-
.single();
|
|
87
|
-
if (error)
|
|
88
|
-
throw error;
|
|
34
|
+
const { session } = ctx;
|
|
35
|
+
const apiClient = getApiClient();
|
|
36
|
+
const response = await apiClient.acknowledgeRequest(request_id, session.currentSessionId || undefined);
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
throw new Error(`Failed to acknowledge request: ${response.error}`);
|
|
39
|
+
}
|
|
89
40
|
return {
|
|
90
41
|
result: {
|
|
91
42
|
success: true,
|
|
92
|
-
request,
|
|
93
43
|
},
|
|
94
44
|
};
|
|
95
45
|
};
|
|
@@ -98,26 +48,16 @@ export const answerQuestion = async (args, ctx) => {
|
|
|
98
48
|
validateRequired(request_id, 'request_id');
|
|
99
49
|
validateRequired(answer, 'answer');
|
|
100
50
|
validateUUID(request_id, 'request_id');
|
|
101
|
-
const {
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
answered_at: new Date().toISOString(),
|
|
108
|
-
acknowledged_at: new Date().toISOString(),
|
|
109
|
-
acknowledged_by_session_id: session.currentSessionId,
|
|
110
|
-
})
|
|
111
|
-
.eq('id', request_id)
|
|
112
|
-
.select()
|
|
113
|
-
.single();
|
|
114
|
-
if (error)
|
|
115
|
-
throw error;
|
|
51
|
+
const { session } = ctx;
|
|
52
|
+
const apiClient = getApiClient();
|
|
53
|
+
const response = await apiClient.answerQuestion(request_id, answer, session.currentSessionId || undefined);
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
throw new Error(`Failed to answer question: ${response.error}`);
|
|
56
|
+
}
|
|
116
57
|
return {
|
|
117
58
|
result: {
|
|
118
59
|
success: true,
|
|
119
60
|
message: 'Question answered successfully',
|
|
120
|
-
request,
|
|
121
61
|
},
|
|
122
62
|
};
|
|
123
63
|
};
|