@rigstate/mcp 0.7.6 → 0.7.7
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 +67 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/project-context-utils.ts +32 -23
- package/src/tools/get-project-context.ts +2 -2
- package/src/tools/list-features.ts +18 -32
- package/src/tools/planning-tools.ts +10 -14
- package/src/tools/query-brain.ts +29 -41
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TechStackInfo } from '../lib/types.js';
|
|
2
2
|
|
|
3
3
|
export async function buildProjectSummary(
|
|
4
4
|
project: any,
|
|
@@ -8,14 +8,15 @@ export async function buildProjectSummary(
|
|
|
8
8
|
agentTasks: any[],
|
|
9
9
|
roadmapItems: any[],
|
|
10
10
|
stackDef: any,
|
|
11
|
-
supabase: any
|
|
11
|
+
supabase: any,
|
|
12
|
+
userId: string
|
|
12
13
|
): Promise<string> {
|
|
13
14
|
const summaryParts: string[] = [];
|
|
14
15
|
|
|
15
16
|
summaryParts.push(`Project Type: ${project.project_type?.toUpperCase() || 'UNKNOWN'}`);
|
|
16
17
|
|
|
17
18
|
// Active Mission Parameters
|
|
18
|
-
if (stackDef) {
|
|
19
|
+
if (stackDef || activeTask || nextTask) {
|
|
19
20
|
summaryParts.push('\n=== ACTIVE MISSION PARAMETERS ===');
|
|
20
21
|
if (activeTask) {
|
|
21
22
|
summaryParts.push(`⚠️ CURRENT OBJECTIVE: T-${activeTask.step_number}: ${activeTask.title}`);
|
|
@@ -44,17 +45,23 @@ export async function buildProjectSummary(
|
|
|
44
45
|
summaryParts.push(` Tags: ${activeTask.tags.join(', ')}`);
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
// Enhanced Feature Context
|
|
48
|
+
// Enhanced Feature Context (Secure)
|
|
48
49
|
if (activeTask.feature_id) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
try {
|
|
51
|
+
const { data: dbFeatures } = await supabase
|
|
52
|
+
.rpc('get_project_features_secure', {
|
|
53
|
+
p_project_id: project.id,
|
|
54
|
+
p_user_id: userId
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const feature = dbFeatures?.find((f: any) => f.id === activeTask.feature_id);
|
|
58
|
+
|
|
59
|
+
if (feature) {
|
|
60
|
+
summaryParts.push(`\n Parent Feature: ${feature.name}`);
|
|
61
|
+
summaryParts.push(` Feature Vision: ${feature.description}`);
|
|
62
|
+
}
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.warn('Feature context fetch failed', e);
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
|
|
@@ -80,13 +87,15 @@ export async function buildProjectSummary(
|
|
|
80
87
|
}
|
|
81
88
|
|
|
82
89
|
summaryParts.push('\n=== CURRENT STACK ===');
|
|
83
|
-
if (stackDef
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
if (stackDef) {
|
|
91
|
+
if (stackDef.frontend) summaryParts.push(`Frontend: ${stackDef.frontend.framework} (${stackDef.frontend.language})`);
|
|
92
|
+
if (stackDef.backend) summaryParts.push(`Backend: ${stackDef.backend.service} (${stackDef.backend.database})`);
|
|
93
|
+
if (stackDef.styling) summaryParts.push(`Styling: ${stackDef.styling.framework} ${stackDef.styling.library || ''}`);
|
|
94
|
+
if (stackDef.hosting) summaryParts.push(`Infrastructure: ${stackDef.hosting.provider}`);
|
|
95
|
+
} else {
|
|
96
|
+
if (techStack.framework) summaryParts.push(`Framework: ${techStack.framework}`);
|
|
97
|
+
if (techStack.orm) summaryParts.push(`ORM: ${techStack.orm}`);
|
|
98
|
+
}
|
|
90
99
|
}
|
|
91
100
|
|
|
92
101
|
if (project.description) {
|
|
@@ -103,8 +112,8 @@ export async function buildProjectSummary(
|
|
|
103
112
|
|
|
104
113
|
if (spec.featureList && Array.isArray(spec.featureList)) {
|
|
105
114
|
summaryParts.push('\nKey Features & Nuances:');
|
|
106
|
-
spec.featureList.filter((f: any) => f.priority === 'MVP').forEach((f: any) => {
|
|
107
|
-
summaryParts.push(`- ${f.name}: ${f.description}`);
|
|
115
|
+
spec.featureList.filter((f: any) => f.priority === 'MVP' || f.priority === 'HIGH').slice(0, 10).forEach((f: any) => {
|
|
116
|
+
summaryParts.push(`- ${f.name}: ${f.description?.substring(0, 200)}`);
|
|
108
117
|
});
|
|
109
118
|
}
|
|
110
119
|
}
|
|
@@ -124,7 +133,7 @@ export async function buildProjectSummary(
|
|
|
124
133
|
summaryParts.push('\nLatest AI Executions:');
|
|
125
134
|
agentTasks.forEach((t: any) => {
|
|
126
135
|
const time = t.completed_at ? new Date(t.completed_at).toLocaleString() : 'Recently';
|
|
127
|
-
summaryParts.push(`- [${time}] ${t.roadmap_title || 'Task'}: ${t.execution_summary || 'Completed'}`);
|
|
136
|
+
summaryParts.push(`- [${time}] ${t.roadmap_title || 'Task'}: ${t.execution_summary?.substring(0, 100) || 'Completed'}`);
|
|
128
137
|
});
|
|
129
138
|
}
|
|
130
139
|
|
|
@@ -165,7 +165,6 @@ export async function getProjectContext(
|
|
|
165
165
|
)].slice(0, 10) as string[];
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
// Build summary via utility
|
|
169
168
|
const summary = await buildProjectSummary(
|
|
170
169
|
project,
|
|
171
170
|
techStack,
|
|
@@ -174,7 +173,8 @@ export async function getProjectContext(
|
|
|
174
173
|
agentTasks || [],
|
|
175
174
|
roadmapItems || [],
|
|
176
175
|
stackDef,
|
|
177
|
-
supabase
|
|
176
|
+
supabase,
|
|
177
|
+
userId
|
|
178
178
|
);
|
|
179
179
|
|
|
180
180
|
const response: ProjectContextResponse = {
|
|
@@ -17,53 +17,41 @@ export const listFeaturesTool: ToolDefinition<typeof InputSchema> = {
|
|
|
17
17
|
Useful for understanding the strategic context and major milestones.`,
|
|
18
18
|
schema: InputSchema,
|
|
19
19
|
handler: async ({ projectId }, { supabase, userId }) => {
|
|
20
|
-
// 1. Verify project access
|
|
21
|
-
const { data:
|
|
22
|
-
.rpc('
|
|
20
|
+
// 1. Verify project access and fetch context
|
|
21
|
+
const { data: projectRow, error: projectError } = await supabase
|
|
22
|
+
.rpc('get_project_context_secure', {
|
|
23
23
|
p_project_id: projectId,
|
|
24
24
|
p_user_id: userId
|
|
25
|
-
})
|
|
25
|
+
})
|
|
26
|
+
.single() as any;
|
|
26
27
|
|
|
27
|
-
if (
|
|
28
|
-
throw new Error('Project not found or access denied');
|
|
28
|
+
if (projectError || !projectRow) {
|
|
29
|
+
throw new Error('Project details not found or access denied');
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
// 2. Fetch
|
|
32
|
-
const { data: project, error: projectError } = await supabase
|
|
33
|
-
.from('projects')
|
|
34
|
-
.select('id, functional_spec')
|
|
35
|
-
.eq('id', projectId)
|
|
36
|
-
.single();
|
|
37
|
-
|
|
38
|
-
if (projectError || !project) {
|
|
39
|
-
throw new Error('Project details not found');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// 2. Primary Strategy: Fetch from 'project_features'
|
|
32
|
+
// 2. Primary Strategy: Fetch from 'project_features' via secure RPC
|
|
43
33
|
const { data: dbFeatures, error: dbError } = await supabase
|
|
44
|
-
.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.order('created_at', { ascending: false });
|
|
34
|
+
.rpc('get_project_features_secure', {
|
|
35
|
+
p_project_id: projectId,
|
|
36
|
+
p_user_id: userId
|
|
37
|
+
});
|
|
49
38
|
|
|
50
39
|
let featuresList: any[] = [];
|
|
51
40
|
let source = 'DB';
|
|
52
41
|
|
|
53
42
|
if (!dbError && dbFeatures && dbFeatures.length > 0) {
|
|
54
|
-
featuresList = dbFeatures.map(f => ({
|
|
43
|
+
featuresList = dbFeatures.map((f: any) => ({
|
|
55
44
|
...f,
|
|
56
|
-
title: f.name
|
|
45
|
+
title: f.name
|
|
57
46
|
}));
|
|
58
47
|
} else {
|
|
59
48
|
// 3. Fallback Strategy: Extract from 'functional_spec'
|
|
60
49
|
source = 'FALLBACK_SPEC';
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
const spec = projectRow.functional_spec as any;
|
|
51
|
+
const features = spec?.featureList || spec?.features;
|
|
63
52
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
featuresList = spec.features.map((f: any) => ({
|
|
53
|
+
if (Array.isArray(features)) {
|
|
54
|
+
featuresList = features.map((f: any) => ({
|
|
67
55
|
id: 'legacy',
|
|
68
56
|
title: f.name || f.title,
|
|
69
57
|
description: f.description,
|
|
@@ -89,5 +77,3 @@ Useful for understanding the strategic context and major milestones.`,
|
|
|
89
77
|
};
|
|
90
78
|
|
|
91
79
|
registry.register(listFeaturesTool);
|
|
92
|
-
|
|
93
|
-
|
|
@@ -60,25 +60,21 @@ export async function saveToProjectBrain(
|
|
|
60
60
|
|
|
61
61
|
const fullContent = `# ${title}\n\n${content}`;
|
|
62
62
|
|
|
63
|
-
const { data, error } = await supabase
|
|
64
|
-
.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
source_type: 'chat_manual'
|
|
73
|
-
})
|
|
74
|
-
.select('id')
|
|
75
|
-
.single();
|
|
63
|
+
const { data: memoryId, error } = await supabase
|
|
64
|
+
.rpc('save_project_memory_secure', {
|
|
65
|
+
p_project_id: projectId,
|
|
66
|
+
p_user_id: userId,
|
|
67
|
+
p_content: fullContent,
|
|
68
|
+
p_category: category.toLowerCase(),
|
|
69
|
+
p_tags: tags || [],
|
|
70
|
+
p_importance: (category === 'DECISION' || category === 'ARCHITECTURE') ? 9 : 5
|
|
71
|
+
});
|
|
76
72
|
|
|
77
73
|
if (error) throw new Error(`Failed to save memory: ${error.message}`);
|
|
78
74
|
|
|
79
75
|
return {
|
|
80
76
|
success: true,
|
|
81
|
-
memoryId:
|
|
77
|
+
memoryId: memoryId,
|
|
82
78
|
message: `✅ Saved [${category}] "${title}" to Project Brain.`
|
|
83
79
|
};
|
|
84
80
|
}
|
package/src/tools/query-brain.ts
CHANGED
|
@@ -87,42 +87,22 @@ export async function queryBrain(
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// Try semantic search first using the match_memories RPC
|
|
90
|
-
// This requires the embedding to be generated, so we'll try
|
|
91
|
-
// Generate embedding if possible for semantic search
|
|
92
90
|
const embedding = await generateQueryEmbedding(query);
|
|
93
91
|
let memories: MemoryRecord[] = [];
|
|
94
92
|
|
|
95
|
-
// Use the
|
|
93
|
+
// Use the secure search RPC
|
|
96
94
|
const { data: searchResults, error: searchError } = await supabase
|
|
97
|
-
.rpc('
|
|
95
|
+
.rpc('query_project_brain_secure', {
|
|
98
96
|
p_project_id: projectId,
|
|
97
|
+
p_user_id: userId,
|
|
99
98
|
p_query: query,
|
|
100
|
-
|
|
101
|
-
p_limit: limit,
|
|
102
|
-
p_similarity_threshold: threshold || 0.1
|
|
99
|
+
p_limit: limit
|
|
103
100
|
});
|
|
104
101
|
|
|
105
|
-
if (searchError) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
.select('id, content, category, tags, importance, created_at')
|
|
110
|
-
.eq('project_id', projectId)
|
|
111
|
-
.eq('is_active', true)
|
|
112
|
-
.order('created_at', { ascending: false })
|
|
113
|
-
.limit(limit);
|
|
114
|
-
|
|
115
|
-
if (recentMemories) {
|
|
116
|
-
memories = recentMemories.map(m => ({
|
|
117
|
-
id: m.id,
|
|
118
|
-
content: m.content,
|
|
119
|
-
category: m.category || 'general',
|
|
120
|
-
tags: m.tags || [],
|
|
121
|
-
netVotes: m.importance || 0,
|
|
122
|
-
createdAt: m.created_at
|
|
123
|
-
}));
|
|
124
|
-
}
|
|
125
|
-
} else if (searchResults) {
|
|
102
|
+
if (searchError || !searchResults) {
|
|
103
|
+
console.error('Brain query failed:', searchError);
|
|
104
|
+
memories = [];
|
|
105
|
+
} else {
|
|
126
106
|
memories = searchResults.map((m: any) => ({
|
|
127
107
|
id: m.id,
|
|
128
108
|
content: m.content,
|
|
@@ -133,31 +113,39 @@ export async function queryBrain(
|
|
|
133
113
|
}));
|
|
134
114
|
}
|
|
135
115
|
|
|
136
|
-
// ---
|
|
137
|
-
// Simple fuzzy search until we vector embedding for features.
|
|
116
|
+
// --- FETCH RELEVANT FEATURES FROM SPEC ---
|
|
138
117
|
let relevantFeatures: any[] = [];
|
|
139
118
|
try {
|
|
140
|
-
const { data:
|
|
141
|
-
.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
.
|
|
146
|
-
|
|
147
|
-
if (
|
|
119
|
+
const { data: projectRow } = await supabase
|
|
120
|
+
.rpc('get_project_context_secure', {
|
|
121
|
+
p_project_id: projectId,
|
|
122
|
+
p_user_id: userId
|
|
123
|
+
})
|
|
124
|
+
.single() as any;
|
|
125
|
+
|
|
126
|
+
if (projectRow?.functional_spec) {
|
|
127
|
+
const spec = typeof projectRow.functional_spec === 'string'
|
|
128
|
+
? JSON.parse(projectRow.functional_spec)
|
|
129
|
+
: projectRow.functional_spec;
|
|
130
|
+
|
|
131
|
+
const features = spec.featureList || spec.features || [];
|
|
132
|
+
relevantFeatures = features
|
|
133
|
+
.filter((f: any) => (f.name || f.title)?.toLowerCase().includes(query.toLowerCase()))
|
|
134
|
+
.slice(0, 3);
|
|
135
|
+
}
|
|
148
136
|
} catch (e) {
|
|
149
137
|
console.warn('Feature fetch failed in brain query', e);
|
|
150
138
|
}
|
|
151
139
|
|
|
152
140
|
// Format memories into a readable context block
|
|
153
|
-
const contextLines = memories.map((m) => {
|
|
141
|
+
const contextLines = memories.map((m: any) => {
|
|
154
142
|
const voteIndicator = m.netVotes && m.netVotes < 0 ? ` [⚠️ POORLY RATED: ${m.netVotes}]` : '';
|
|
155
143
|
const tagStr = m.tags && m.tags.length > 0 ? ` [${m.tags.join(', ')}]` : '';
|
|
156
144
|
const category = m.category ? m.category.toUpperCase() : 'GENERAL';
|
|
157
145
|
return `- [${category}]${tagStr}${voteIndicator}: ${m.content}`;
|
|
158
146
|
});
|
|
159
147
|
|
|
160
|
-
const searchType = embedding ? 'TRIPLE-HYBRID (Vector + FTS + Fuzzy)' : '
|
|
148
|
+
const searchType = embedding ? 'TRIPLE-HYBRID (Vector + FTS + Fuzzy)' : 'SECURE-GATEWAY (Fuzzy)';
|
|
161
149
|
|
|
162
150
|
let formatted = `=== PROJECT BRAIN: RELEVANT MEMORIES ===
|
|
163
151
|
Search Mode: ${searchType}
|
|
@@ -165,7 +153,7 @@ Query: "${query}"`;
|
|
|
165
153
|
|
|
166
154
|
if (relevantFeatures.length > 0) {
|
|
167
155
|
formatted += `\n\n=== RELATED FEATURES ===\n` +
|
|
168
|
-
relevantFeatures.map((f: any) => `- ${f.name} [${f.status}]`).join('\n');
|
|
156
|
+
relevantFeatures.map((f: any) => `- ${f.name || f.title} [${f.status || 'Active'}]`).join('\n');
|
|
169
157
|
}
|
|
170
158
|
|
|
171
159
|
formatted += `\n\nFound ${memories.length} relevant memories:\n\n${contextLines.join('\n')}\n\n==========================================`;
|