@rigstate/mcp 0.7.3 → 0.7.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.
- package/dist/index.js +44 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/tools/get-next-roadmap-step.ts +38 -33
- package/src/tools/get-project-context.ts +26 -37
- package/src/tools/list-roadmap-tasks.ts +20 -20
package/package.json
CHANGED
|
@@ -16,6 +16,7 @@ Useful for transitioning between tasks.`,
|
|
|
16
16
|
handler: async (args, context) => {
|
|
17
17
|
const result = await getNextRoadmapStep(
|
|
18
18
|
context.supabase,
|
|
19
|
+
context.userId,
|
|
19
20
|
args.projectId,
|
|
20
21
|
args.currentStepId
|
|
21
22
|
);
|
|
@@ -30,58 +31,62 @@ export interface GetNextRoadmapStepResponse {
|
|
|
30
31
|
|
|
31
32
|
export async function getNextRoadmapStep(
|
|
32
33
|
supabase: SupabaseClient,
|
|
34
|
+
userId: string,
|
|
33
35
|
projectId: string,
|
|
34
36
|
currentStepId?: string
|
|
35
37
|
): Promise<GetNextRoadmapStepResponse> {
|
|
36
38
|
|
|
39
|
+
// 1. Fetch all tasks via secure RPC
|
|
40
|
+
const { data: allTasks, error: fetchError } = await supabase
|
|
41
|
+
.rpc('get_roadmap_chunks_secure', {
|
|
42
|
+
p_project_id: projectId,
|
|
43
|
+
p_user_id: userId
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (fetchError) {
|
|
47
|
+
throw new Error(`Failed to fetch roadmap data: ${fetchError.message}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const tasks = (allTasks || []) as any[];
|
|
51
|
+
|
|
37
52
|
// Strategy:
|
|
38
53
|
// 1. If currentStepId is provided, look for the step with the next higher step_number.
|
|
39
|
-
// 2. If not, look for the first 'ACTIVE' step.
|
|
40
|
-
// 3. If no ACTIVE,
|
|
54
|
+
// 2. If not, look for the first 'ACTIVE' or 'IN_PROGRESS' step. Baseline.
|
|
55
|
+
// 3. If no ACTIVE, current baseline is 0.
|
|
41
56
|
|
|
42
57
|
let currentStepNumber = 0;
|
|
43
58
|
|
|
44
59
|
if (currentStepId) {
|
|
45
|
-
const
|
|
46
|
-
.from('roadmap_chunks')
|
|
47
|
-
.select('step_number')
|
|
48
|
-
.eq('id', currentStepId)
|
|
49
|
-
.single();
|
|
50
|
-
|
|
60
|
+
const current = tasks.find(t => t.id === currentStepId);
|
|
51
61
|
if (current) {
|
|
52
62
|
currentStepNumber = current.step_number;
|
|
53
63
|
}
|
|
54
64
|
} else {
|
|
55
|
-
//
|
|
56
|
-
const
|
|
57
|
-
.
|
|
58
|
-
.
|
|
59
|
-
.eq('project_id', projectId)
|
|
60
|
-
.in('status', ['ACTIVE', 'IN_PROGRESS'])
|
|
61
|
-
.order('step_number', { ascending: true })
|
|
62
|
-
.limit(1)
|
|
63
|
-
.single();
|
|
65
|
+
// Find baseline: first ACTIVE/IN_PROGRESS
|
|
66
|
+
const active = tasks
|
|
67
|
+
.filter(t => ['ACTIVE', 'IN_PROGRESS'].includes(t.status))
|
|
68
|
+
.sort((a, b) => a.step_number - b.step_number)[0];
|
|
64
69
|
|
|
65
70
|
if (active) {
|
|
66
|
-
|
|
71
|
+
// If we are looking for the "next" step and we have an active one,
|
|
72
|
+
// we usually want to start *from* that active one or its successor.
|
|
73
|
+
// For now, let's keep the baseline logic: if we have an active one, we are *on* it.
|
|
74
|
+
// But get_next is often called to FIND what to do.
|
|
75
|
+
// If we have an active one, THAT is the next thing to work on.
|
|
76
|
+
|
|
77
|
+
// Logic change: if no currentStepId provided, and we have an ACTIVE one,
|
|
78
|
+
// that ACTIVE one IS the next step.
|
|
79
|
+
return {
|
|
80
|
+
nextStep: active as RoadmapChunk,
|
|
81
|
+
message: `Current active step: [Step ${active.step_number}] ${active.title}`
|
|
82
|
+
};
|
|
67
83
|
}
|
|
68
84
|
}
|
|
69
85
|
|
|
70
|
-
// Find the next step > currentStepNumber
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
.
|
|
74
|
-
.select('*')
|
|
75
|
-
.eq('project_id', projectId)
|
|
76
|
-
.gt('step_number', currentStepNumber)
|
|
77
|
-
.neq('status', 'COMPLETED') // We want the next *workable* step
|
|
78
|
-
.order('step_number', { ascending: true })
|
|
79
|
-
.limit(1)
|
|
80
|
-
.single();
|
|
81
|
-
|
|
82
|
-
if (error && error.code !== 'PGRST116') { // PGRST116 is "Row not found" which is fine
|
|
83
|
-
throw new Error(`Failed to fetch next roadmap step: ${error.message}`);
|
|
84
|
-
}
|
|
86
|
+
// Find the next step > currentStepNumber that is not COMPLETED
|
|
87
|
+
const nextStep = tasks
|
|
88
|
+
.filter(t => t.step_number > currentStepNumber && t.status !== 'COMPLETED')
|
|
89
|
+
.sort((a, b) => a.step_number - b.step_number)[0];
|
|
85
90
|
|
|
86
91
|
if (!nextStep) {
|
|
87
92
|
return {
|
|
@@ -94,43 +94,32 @@ export async function getProjectContext(
|
|
|
94
94
|
|
|
95
95
|
|
|
96
96
|
|
|
97
|
-
// Continuity Loop: Fetch Active & Next Tasks
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
.maybeSingle()
|
|
114
|
-
]);
|
|
115
|
-
|
|
116
|
-
const activeTask = activeTaskResult.data;
|
|
117
|
-
const nextTask = nextTaskResult.data;
|
|
118
|
-
|
|
119
|
-
// Fetch Digest Data (Reduced to 2 to save tokens)
|
|
97
|
+
// Continuity Loop: Fetch Active & Next Tasks via secure RPC
|
|
98
|
+
const { data: allChunks, error: chunksError } = await supabase
|
|
99
|
+
.rpc('get_roadmap_chunks_secure', {
|
|
100
|
+
p_project_id: projectId,
|
|
101
|
+
p_user_id: userId
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const tasks = (allChunks || []) as any[];
|
|
105
|
+
|
|
106
|
+
// In-memory filtering to match original logic
|
|
107
|
+
const activeTask = tasks.find(t => ['IN_PROGRESS', 'ACTIVE'].includes(t.status));
|
|
108
|
+
const nextTask = tasks
|
|
109
|
+
.filter(t => ['PENDING', 'LOCKED'].includes(t.status))
|
|
110
|
+
.sort((a, b) => a.step_number - b.step_number)[0];
|
|
111
|
+
|
|
112
|
+
// Fetch Digest Data via secure RPC
|
|
120
113
|
const { data: agentTasks } = await supabase
|
|
121
|
-
.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
.
|
|
130
|
-
.select('title, status, updated_at')
|
|
131
|
-
.eq('project_id', projectId)
|
|
132
|
-
.order('updated_at', { ascending: false })
|
|
133
|
-
.limit(2);
|
|
114
|
+
.rpc('get_agent_bridge_secure', {
|
|
115
|
+
p_project_id: projectId,
|
|
116
|
+
p_user_id: userId,
|
|
117
|
+
p_limit: 2
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const roadmapItems = tasks
|
|
121
|
+
.sort((a, b) => new Date(b.updated_at || b.created_at).getTime() - new Date(a.updated_at || a.created_at).getTime())
|
|
122
|
+
.slice(0, 2);
|
|
134
123
|
|
|
135
124
|
// Parse tech stack from detected_stack
|
|
136
125
|
const techStack: TechStackInfo = {
|
|
@@ -239,7 +228,7 @@ export async function getProjectContext(
|
|
|
239
228
|
summaryParts.push('\nLatest AI Executions:');
|
|
240
229
|
agentTasks.forEach((t: any) => {
|
|
241
230
|
const time = t.completed_at ? new Date(t.completed_at).toLocaleString() : 'Recently';
|
|
242
|
-
summaryParts.push(`- [${time}] ${t.
|
|
231
|
+
summaryParts.push(`- [${time}] ${t.roadmap_title || 'Task'}: ${t.execution_summary || 'Completed'}`);
|
|
243
232
|
});
|
|
244
233
|
}
|
|
245
234
|
|
|
@@ -33,42 +33,42 @@ export async function listRoadmapTasks(
|
|
|
33
33
|
userId: string,
|
|
34
34
|
projectId: string
|
|
35
35
|
): Promise<ListRoadmapTasksResponse> {
|
|
36
|
-
// 1.
|
|
37
|
-
const { data:
|
|
38
|
-
.rpc('
|
|
36
|
+
// 1. Fetch tasks via secure RPC (bypasses RLS)
|
|
37
|
+
const { data: tasks, error } = await supabase
|
|
38
|
+
.rpc('get_roadmap_chunks_secure', {
|
|
39
39
|
p_project_id: projectId,
|
|
40
40
|
p_user_id: userId
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
if (accessError || !hasAccess) {
|
|
44
|
-
throw new Error('Project not found or access denied');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 2. Fetch non-completed tasks
|
|
48
|
-
const { data: tasks, error } = await supabase
|
|
49
|
-
.from('roadmap_chunks')
|
|
50
|
-
.select('id, title, priority, status, step_number, prompt_content')
|
|
51
|
-
.eq('project_id', projectId)
|
|
52
|
-
.neq('status', 'COMPLETED')
|
|
53
|
-
.order('priority', { ascending: false })
|
|
54
|
-
.order('step_number', { ascending: true });
|
|
55
|
-
|
|
56
43
|
if (error) {
|
|
44
|
+
if (error.message.includes('Access Denied')) {
|
|
45
|
+
throw new Error('Project not found or access denied');
|
|
46
|
+
}
|
|
57
47
|
console.error('Failed to fetch roadmap tasks:', error);
|
|
58
48
|
throw new Error('Failed to fetch roadmap tasks');
|
|
59
49
|
}
|
|
60
50
|
|
|
51
|
+
// 2. Filter non-completed in JS (to preserve original logic)
|
|
52
|
+
const activeTasks = (tasks || [])
|
|
53
|
+
.filter((t: any) => t.status !== 'COMPLETED')
|
|
54
|
+
.sort((a: any, b: any) => {
|
|
55
|
+
if (a.priority !== b.priority) {
|
|
56
|
+
const priorityOrder: any = { 'CRITICAL': 3, 'HIGH': 2, 'MEDIUM': 1, 'LOW': 0 };
|
|
57
|
+
return (priorityOrder[b.priority] || 0) - (priorityOrder[a.priority] || 0);
|
|
58
|
+
}
|
|
59
|
+
return a.step_number - b.step_number;
|
|
60
|
+
});
|
|
61
|
+
|
|
61
62
|
// 3. Format response
|
|
62
|
-
const formatted =
|
|
63
|
-
?
|
|
63
|
+
const formatted = activeTasks.length > 0
|
|
64
|
+
? activeTasks.map((t: any) => {
|
|
64
65
|
const statusEmoji = t.status === 'ACTIVE' ? '🔵' : '🔒';
|
|
65
|
-
const priorityStr = t.priority ? `[${t.priority}]` : '';
|
|
66
66
|
return `${statusEmoji} Step ${t.step_number}: ${t.title} (ID: ${t.id})`;
|
|
67
67
|
}).join('\n')
|
|
68
68
|
: 'No active or locked tasks found in the roadmap.';
|
|
69
69
|
|
|
70
70
|
return {
|
|
71
|
-
tasks:
|
|
71
|
+
tasks: activeTasks.map((t: any) => ({
|
|
72
72
|
id: t.id,
|
|
73
73
|
title: t.title,
|
|
74
74
|
priority: t.priority,
|