@rigstate/mcp 0.7.5 → 0.7.6
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 +265 -101
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/arch-analysis.ts +171 -0
- package/src/lib/curator/actions/submit.ts +8 -9
- package/src/lib/project-context-utils.ts +139 -0
- package/src/lib/types-roadmap.ts +43 -0
- package/src/lib/types.ts +41 -25
- package/src/server/types.ts +1 -1
- package/src/tools/analyze-database-performance.ts +12 -0
- package/src/tools/archaeological-scan.ts +37 -214
- package/src/tools/audit-integrity-gate.ts +16 -4
- package/src/tools/check-agent-bridge.ts +11 -0
- package/src/tools/check-rules-sync.ts +13 -0
- package/src/tools/complete-roadmap-task.ts +12 -0
- package/src/tools/generate-professional-pdf.ts +17 -2
- package/src/tools/get-next-roadmap-step.ts +5 -1
- package/src/tools/get-project-context.ts +17 -79
- package/src/tools/list-features.ts +13 -3
- package/src/tools/list-roadmap-tasks.ts +6 -1
- package/src/tools/pending-tasks.ts +22 -0
- package/src/tools/query-brain.ts +8 -9
- package/src/tools/research-tools.ts +9 -8
- package/src/tools/run-architecture-audit.ts +8 -9
- package/src/tools/save-decision.ts +15 -9
- package/src/tools/security-tools.ts +26 -2
- package/src/tools/submit-idea.ts +15 -9
- package/src/tools/sync-ide-rules.ts +16 -3
- package/src/tools/teacher-mode.ts +6 -7
- package/src/tools/update-roadmap.ts +14 -8
package/dist/index.js
CHANGED
|
@@ -1102,7 +1102,7 @@ import { ListToolsRequestSchema, ListResourcesRequestSchema } from "@modelcontex
|
|
|
1102
1102
|
// src/server/types.ts
|
|
1103
1103
|
init_esm_shims();
|
|
1104
1104
|
var SERVER_NAME = "rigstate-mcp";
|
|
1105
|
-
var SERVER_VERSION = "0.5
|
|
1105
|
+
var SERVER_VERSION = "0.7.5";
|
|
1106
1106
|
|
|
1107
1107
|
// src/lib/tool-registry.ts
|
|
1108
1108
|
init_esm_shims();
|
|
@@ -1275,8 +1275,11 @@ ${formatted}
|
|
|
1275
1275
|
// src/lib/curator/actions/submit.ts
|
|
1276
1276
|
init_esm_shims();
|
|
1277
1277
|
async function submitSignal(supabase, userId, input) {
|
|
1278
|
-
const { data:
|
|
1279
|
-
|
|
1278
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
1279
|
+
p_project_id: input.projectId,
|
|
1280
|
+
p_user_id: userId
|
|
1281
|
+
});
|
|
1282
|
+
if (accessError || !hasAccess) {
|
|
1280
1283
|
throw new Error("Project not found or access denied");
|
|
1281
1284
|
}
|
|
1282
1285
|
const fingerprintBase = `${input.instruction}::${input.category}`.toLowerCase();
|
|
@@ -1561,8 +1564,11 @@ var GetLearnedInstructionsSchema = z3.object({
|
|
|
1561
1564
|
async function refineLogic(supabase, userId, args) {
|
|
1562
1565
|
const { projectId, originalReasoning, userCorrection, scope } = args;
|
|
1563
1566
|
const traceId = uuidv4();
|
|
1564
|
-
const { data:
|
|
1565
|
-
|
|
1567
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
1568
|
+
p_project_id: projectId,
|
|
1569
|
+
p_user_id: userId
|
|
1570
|
+
});
|
|
1571
|
+
if (accessError || !hasAccess) {
|
|
1566
1572
|
throw new Error(`Project access denied or not found: ${projectId}`);
|
|
1567
1573
|
}
|
|
1568
1574
|
const { data: instruction, error: insertError } = await supabase.from("ai_instructions").insert({
|
|
@@ -1895,6 +1901,119 @@ var CompleteRoadmapTaskInputSchema = z4.object({
|
|
|
1895
1901
|
integrityGate: z4.any().optional()
|
|
1896
1902
|
});
|
|
1897
1903
|
|
|
1904
|
+
// src/lib/project-context-utils.ts
|
|
1905
|
+
init_esm_shims();
|
|
1906
|
+
async function buildProjectSummary(project, techStack, activeTask, nextTask, agentTasks, roadmapItems, stackDef, supabase) {
|
|
1907
|
+
const summaryParts = [];
|
|
1908
|
+
summaryParts.push(`Project Type: ${project.project_type?.toUpperCase() || "UNKNOWN"}`);
|
|
1909
|
+
if (stackDef) {
|
|
1910
|
+
summaryParts.push("\n=== ACTIVE MISSION PARAMETERS ===");
|
|
1911
|
+
if (activeTask) {
|
|
1912
|
+
summaryParts.push(`\u26A0\uFE0F CURRENT OBJECTIVE: T-${activeTask.step_number}: ${activeTask.title}`);
|
|
1913
|
+
summaryParts.push(` Role: ${activeTask.role || "Developer"}`);
|
|
1914
|
+
const detailedInstructions = activeTask.prompt_content || activeTask.instruction_set;
|
|
1915
|
+
if (detailedInstructions) {
|
|
1916
|
+
summaryParts.push(` Instructions: ${detailedInstructions.substring(0, 1e3)}...`);
|
|
1917
|
+
}
|
|
1918
|
+
if (activeTask.architectural_brief) {
|
|
1919
|
+
summaryParts.push(`
|
|
1920
|
+
Architectural Brief: ${activeTask.architectural_brief}`);
|
|
1921
|
+
}
|
|
1922
|
+
if (activeTask.context_summary) {
|
|
1923
|
+
summaryParts.push(`
|
|
1924
|
+
Context: ${activeTask.context_summary}`);
|
|
1925
|
+
}
|
|
1926
|
+
if (activeTask.checklist && activeTask.checklist.length > 0) {
|
|
1927
|
+
summaryParts.push("\n Checklist (DoD):");
|
|
1928
|
+
activeTask.checklist.forEach((item) => {
|
|
1929
|
+
summaryParts.push(` [ ] ${typeof item === "string" ? item : item.task}`);
|
|
1930
|
+
});
|
|
1931
|
+
}
|
|
1932
|
+
if (activeTask.metadata && Object.keys(activeTask.metadata).length > 0) {
|
|
1933
|
+
summaryParts.push(`
|
|
1934
|
+
Technical Metadata: ${JSON.stringify(activeTask.metadata)}`);
|
|
1935
|
+
}
|
|
1936
|
+
if (activeTask.tags && activeTask.tags.length > 0) {
|
|
1937
|
+
summaryParts.push(` Tags: ${activeTask.tags.join(", ")}`);
|
|
1938
|
+
}
|
|
1939
|
+
if (activeTask.feature_id) {
|
|
1940
|
+
const { data: feature } = await supabase.from("project_features").select("name, description").eq("id", activeTask.feature_id).single();
|
|
1941
|
+
if (feature) {
|
|
1942
|
+
summaryParts.push(`
|
|
1943
|
+
Parent Feature: ${feature.name}`);
|
|
1944
|
+
summaryParts.push(` Feature Vision: ${feature.description}`);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
summaryParts.push("\n ACTION: Focus ALL coding efforts on completing this task.");
|
|
1948
|
+
} else if (nextTask) {
|
|
1949
|
+
summaryParts.push(`\u23F8 SYSTEM IDLE (Waiting for command)`);
|
|
1950
|
+
summaryParts.push(` Suggested Next Mission: T-${nextTask.step_number}: ${nextTask.title}`);
|
|
1951
|
+
if (nextTask.tags && nextTask.tags.length > 0) {
|
|
1952
|
+
summaryParts.push(` Scope: ${nextTask.tags.join(", ")}`);
|
|
1953
|
+
}
|
|
1954
|
+
summaryParts.push(` ACTION: Ask the user "Shall we start T-${nextTask.step_number}?"`);
|
|
1955
|
+
} else {
|
|
1956
|
+
summaryParts.push("\u2705 ALL MISSIONS COMPLETE. Awaiting new roadmap items.");
|
|
1957
|
+
}
|
|
1958
|
+
summaryParts.push("\n=== AI BEHAVIORAL INSTRUCTIONS ===");
|
|
1959
|
+
if (activeTask) {
|
|
1960
|
+
summaryParts.push(`1. FOCUS: The user is working on T-${activeTask.step_number}. Help them complete it.`);
|
|
1961
|
+
summaryParts.push(`2. COMPLIANCE: Ensure all code follows project standards.`);
|
|
1962
|
+
} else if (nextTask) {
|
|
1963
|
+
summaryParts.push(`1. NUDGE: No active task found. Suggest starting T-${nextTask.step_number} (${nextTask.title}).`);
|
|
1964
|
+
summaryParts.push(`2. PROACTIVE: Instead of asking "How can I help?", ask "Shall we start on T-${nextTask.step_number}?"`);
|
|
1965
|
+
}
|
|
1966
|
+
summaryParts.push("\n=== CURRENT STACK ===");
|
|
1967
|
+
if (stackDef.frontend) summaryParts.push(`Frontend: ${stackDef.frontend.framework} (${stackDef.frontend.language})`);
|
|
1968
|
+
if (stackDef.backend) summaryParts.push(`Backend: ${stackDef.backend.service} (${stackDef.backend.database})`);
|
|
1969
|
+
if (stackDef.styling) summaryParts.push(`Styling: ${stackDef.styling.framework} ${stackDef.styling.library || ""}`);
|
|
1970
|
+
if (stackDef.hosting) summaryParts.push(`Infrastructure: ${stackDef.hosting.provider}`);
|
|
1971
|
+
} else {
|
|
1972
|
+
if (techStack.framework) summaryParts.push(`Framework: ${techStack.framework}`);
|
|
1973
|
+
if (techStack.orm) summaryParts.push(`ORM: ${techStack.orm}`);
|
|
1974
|
+
}
|
|
1975
|
+
if (project.description) {
|
|
1976
|
+
summaryParts.push(`
|
|
1977
|
+
Description: ${project.description}`);
|
|
1978
|
+
}
|
|
1979
|
+
if (project.functional_spec) {
|
|
1980
|
+
summaryParts.push("\n=== STRATEGIC PROJECT SPECIFICATION (NUANCES) ===");
|
|
1981
|
+
const spec = typeof project.functional_spec === "string" ? JSON.parse(project.functional_spec) : project.functional_spec;
|
|
1982
|
+
if (spec.projectDescription) summaryParts.push(`Vision: ${spec.projectDescription}`);
|
|
1983
|
+
if (spec.targetAudience) summaryParts.push(`Audience: ${spec.targetAudience}`);
|
|
1984
|
+
if (spec.coreProblem) summaryParts.push(`Core Problem: ${spec.coreProblem}`);
|
|
1985
|
+
if (spec.featureList && Array.isArray(spec.featureList)) {
|
|
1986
|
+
summaryParts.push("\nKey Features & Nuances:");
|
|
1987
|
+
spec.featureList.filter((f) => f.priority === "MVP").forEach((f) => {
|
|
1988
|
+
summaryParts.push(`- ${f.name}: ${f.description}`);
|
|
1989
|
+
});
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
summaryParts.push("\n=== RIGSTATE TOOLING GUIDELINES ===");
|
|
1993
|
+
summaryParts.push("You have access to specialized MCP tools. USE THEM TO SUCCEED:");
|
|
1994
|
+
summaryParts.push("1. NEVER guess about architecture. Use `query_brain` to search project documentation.");
|
|
1995
|
+
summaryParts.push("2. BEFORE coding, check `get_learned_instructions` to see if you have been corrected before.");
|
|
1996
|
+
summaryParts.push("3. When finishing a task, ALWAYS update the roadmap using `update_roadmap`.");
|
|
1997
|
+
summaryParts.push("4. If you discover a reusable pattern, submit it with `submit_curator_signal`.");
|
|
1998
|
+
summaryParts.push("5. For large refactors, use `run_architecture_audit` to check against rules.");
|
|
1999
|
+
summaryParts.push("6. Store major decisions using `save_decision` (ADR).");
|
|
2000
|
+
summaryParts.push("\n=== RECENT ACTIVITY DIGEST ===");
|
|
2001
|
+
if (agentTasks && agentTasks.length > 0) {
|
|
2002
|
+
summaryParts.push("\nLatest AI Executions:");
|
|
2003
|
+
agentTasks.forEach((t) => {
|
|
2004
|
+
const time = t.completed_at ? new Date(t.completed_at).toLocaleString() : "Recently";
|
|
2005
|
+
summaryParts.push(`- [${time}] ${t.roadmap_title || "Task"}: ${t.execution_summary || "Completed"}`);
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
if (roadmapItems && roadmapItems.length > 0) {
|
|
2009
|
+
summaryParts.push("\nRoadmap Updates:");
|
|
2010
|
+
roadmapItems.forEach((i) => {
|
|
2011
|
+
summaryParts.push(`- ${i.title} is now ${i.status}`);
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
return summaryParts.join("\n") || "No project context available.";
|
|
2015
|
+
}
|
|
2016
|
+
|
|
1898
2017
|
// src/tools/get-project-context.ts
|
|
1899
2018
|
registry.register({
|
|
1900
2019
|
name: "get_project_context",
|
|
@@ -1940,10 +2059,11 @@ async function getProjectContext(supabase, userId, projectId) {
|
|
|
1940
2059
|
created_at: projectRow.created_at,
|
|
1941
2060
|
last_indexed_at: projectRow.last_indexed_at,
|
|
1942
2061
|
detected_stack: projectRow.detected_stack,
|
|
1943
|
-
repository_tree: projectRow.repository_tree
|
|
2062
|
+
repository_tree: projectRow.repository_tree,
|
|
2063
|
+
functional_spec: projectRow.functional_spec
|
|
1944
2064
|
};
|
|
1945
2065
|
const stackDef = projectRow.architectural_dna?.stack_definition;
|
|
1946
|
-
const { data: allChunks
|
|
2066
|
+
const { data: allChunks } = await supabase.rpc("get_roadmap_chunks_secure", {
|
|
1947
2067
|
p_project_id: projectId,
|
|
1948
2068
|
p_user_id: userId
|
|
1949
2069
|
});
|
|
@@ -1986,67 +2106,16 @@ async function getProjectContext(supabase, userId, projectId) {
|
|
|
1986
2106
|
project.repository_tree.filter((t) => t.type === "tree" && !t.path.includes("/")).map((t) => t.path)
|
|
1987
2107
|
)].slice(0, 10);
|
|
1988
2108
|
}
|
|
1989
|
-
const
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
summaryParts.push(" ACTION: Focus ALL coding efforts on completing this task.");
|
|
2000
|
-
} else if (nextTask) {
|
|
2001
|
-
summaryParts.push(`\u23F8 SYSTEM IDLE (Waiting for command)`);
|
|
2002
|
-
summaryParts.push(` Suggested Next Mission: T-${nextTask.step_number}: ${nextTask.title}`);
|
|
2003
|
-
summaryParts.push(` ACTION: Ask the user "Shall we start T-${nextTask.step_number}?"`);
|
|
2004
|
-
} else {
|
|
2005
|
-
summaryParts.push("\u2705 ALL MISSIONS COMPLETE. Awaiting new roadmap items.");
|
|
2006
|
-
}
|
|
2007
|
-
summaryParts.push("\n=== AI BEHAVIORAL INSTRUCTIONS ===");
|
|
2008
|
-
if (activeTask) {
|
|
2009
|
-
summaryParts.push(`1. FOCUS: The user is working on T-${activeTask.step_number}. Help them complete it.`);
|
|
2010
|
-
summaryParts.push(`2. COMPLIANCE: Ensure all code follows project standards.`);
|
|
2011
|
-
} else if (nextTask) {
|
|
2012
|
-
summaryParts.push(`1. NUDGE: No active task found. Suggest starting T-${nextTask.step_number} (${nextTask.title}).`);
|
|
2013
|
-
summaryParts.push(`2. PROACTIVE: Instead of asking "How can I help?", ask "Shall we start on T-${nextTask.step_number}?"`);
|
|
2014
|
-
}
|
|
2015
|
-
summaryParts.push("\n=== CURRENT STACK ===");
|
|
2016
|
-
if (stackDef.frontend) summaryParts.push(`Frontend: ${stackDef.frontend.framework} (${stackDef.frontend.language})`);
|
|
2017
|
-
if (stackDef.backend) summaryParts.push(`Backend: ${stackDef.backend.service} (${stackDef.backend.database})`);
|
|
2018
|
-
if (stackDef.styling) summaryParts.push(`Styling: ${stackDef.styling.framework} ${stackDef.styling.library || ""}`);
|
|
2019
|
-
if (stackDef.hosting) summaryParts.push(`Infrastructure: ${stackDef.hosting.provider}`);
|
|
2020
|
-
} else {
|
|
2021
|
-
if (techStack.framework) summaryParts.push(`Framework: ${techStack.framework}`);
|
|
2022
|
-
if (techStack.orm) summaryParts.push(`ORM: ${techStack.orm}`);
|
|
2023
|
-
}
|
|
2024
|
-
if (project.description) {
|
|
2025
|
-
summaryParts.push(`
|
|
2026
|
-
Description: ${project.description}`);
|
|
2027
|
-
}
|
|
2028
|
-
summaryParts.push("\n=== RIGSTATE TOOLING GUIDELINES ===");
|
|
2029
|
-
summaryParts.push("You have access to specialized MCP tools. USE THEM TO SUCCEED:");
|
|
2030
|
-
summaryParts.push("1. NEVER guess about architecture. Use `query_brain` to search project documentation.");
|
|
2031
|
-
summaryParts.push("2. BEFORE coding, check `get_learned_instructions` to see if you have been corrected before.");
|
|
2032
|
-
summaryParts.push("3. When finishing a task, ALWAYS update the roadmap using `update_roadmap`.");
|
|
2033
|
-
summaryParts.push("4. If you discover a reusable pattern, submit it with `submit_curator_signal`.");
|
|
2034
|
-
summaryParts.push("5. For large refactors, use `run_architecture_audit` to check against rules.");
|
|
2035
|
-
summaryParts.push("6. Store major decisions using `save_decision` (ADR).");
|
|
2036
|
-
summaryParts.push("\n=== RECENT ACTIVITY DIGEST ===");
|
|
2037
|
-
if (agentTasks && agentTasks.length > 0) {
|
|
2038
|
-
summaryParts.push("\nLatest AI Executions:");
|
|
2039
|
-
agentTasks.forEach((t) => {
|
|
2040
|
-
const time = t.completed_at ? new Date(t.completed_at).toLocaleString() : "Recently";
|
|
2041
|
-
summaryParts.push(`- [${time}] ${t.roadmap_title || "Task"}: ${t.execution_summary || "Completed"}`);
|
|
2042
|
-
});
|
|
2043
|
-
}
|
|
2044
|
-
if (roadmapItems && roadmapItems.length > 0) {
|
|
2045
|
-
summaryParts.push("\nRoadmap Updates:");
|
|
2046
|
-
roadmapItems.forEach((i) => {
|
|
2047
|
-
summaryParts.push(`- ${i.title} is now ${i.status}`);
|
|
2048
|
-
});
|
|
2049
|
-
}
|
|
2109
|
+
const summary = await buildProjectSummary(
|
|
2110
|
+
project,
|
|
2111
|
+
techStack,
|
|
2112
|
+
activeTask,
|
|
2113
|
+
nextTask,
|
|
2114
|
+
agentTasks || [],
|
|
2115
|
+
roadmapItems || [],
|
|
2116
|
+
stackDef,
|
|
2117
|
+
supabase
|
|
2118
|
+
);
|
|
2050
2119
|
const response = {
|
|
2051
2120
|
project: {
|
|
2052
2121
|
id: project.id,
|
|
@@ -2057,7 +2126,7 @@ Description: ${project.description}`);
|
|
|
2057
2126
|
lastIndexedAt: project.last_indexed_at
|
|
2058
2127
|
},
|
|
2059
2128
|
techStack,
|
|
2060
|
-
summary
|
|
2129
|
+
summary
|
|
2061
2130
|
};
|
|
2062
2131
|
try {
|
|
2063
2132
|
const curatorContext = await injectGlobalContext(supabase, userId, {
|
|
@@ -2126,8 +2195,11 @@ async function generateQueryEmbedding(query) {
|
|
|
2126
2195
|
}
|
|
2127
2196
|
}
|
|
2128
2197
|
async function queryBrain(supabase, userId, projectId, query, limit = 8, threshold = 0.5) {
|
|
2129
|
-
const { data:
|
|
2130
|
-
|
|
2198
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
2199
|
+
p_project_id: projectId,
|
|
2200
|
+
p_user_id: userId
|
|
2201
|
+
});
|
|
2202
|
+
if (accessError || !hasAccess) {
|
|
2131
2203
|
throw new Error("Project not found or access denied");
|
|
2132
2204
|
}
|
|
2133
2205
|
const embedding = await generateQueryEmbedding(query);
|
|
@@ -2320,10 +2392,14 @@ High-importance memory for architectural decisions.`,
|
|
|
2320
2392
|
}
|
|
2321
2393
|
});
|
|
2322
2394
|
async function saveDecision(supabase, userId, projectId, title, decision, rationale, category = "decision", tags = []) {
|
|
2323
|
-
const { data:
|
|
2324
|
-
|
|
2395
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
2396
|
+
p_project_id: projectId,
|
|
2397
|
+
p_user_id: userId
|
|
2398
|
+
});
|
|
2399
|
+
if (accessError || !hasAccess) {
|
|
2325
2400
|
throw new Error("Project not found or access denied");
|
|
2326
2401
|
}
|
|
2402
|
+
const { data: project } = await supabase.from("projects").select("name").eq("id", projectId).single();
|
|
2327
2403
|
const contentParts = [`# ${title}`, "", decision];
|
|
2328
2404
|
if (rationale) {
|
|
2329
2405
|
contentParts.push("", "## Rationale", rationale);
|
|
@@ -2354,7 +2430,7 @@ async function saveDecision(supabase, userId, projectId, title, decision, ration
|
|
|
2354
2430
|
return {
|
|
2355
2431
|
success: true,
|
|
2356
2432
|
memoryId: memory.id,
|
|
2357
|
-
message: `\u2705 Decision "${title}" saved to project "${project
|
|
2433
|
+
message: `\u2705 Decision "${title}" saved to project "${project?.name || projectId}" with importance 9/10`
|
|
2358
2434
|
};
|
|
2359
2435
|
}
|
|
2360
2436
|
|
|
@@ -2379,10 +2455,14 @@ Ideas can later be reviewed, analyzed, and promoted to features.`,
|
|
|
2379
2455
|
}
|
|
2380
2456
|
});
|
|
2381
2457
|
async function submitIdea(supabase, userId, projectId, title, description, category = "feature", tags = []) {
|
|
2382
|
-
const { data:
|
|
2383
|
-
|
|
2458
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
2459
|
+
p_project_id: projectId,
|
|
2460
|
+
p_user_id: userId
|
|
2461
|
+
});
|
|
2462
|
+
if (accessError || !hasAccess) {
|
|
2384
2463
|
throw new Error("Project not found or access denied");
|
|
2385
2464
|
}
|
|
2465
|
+
const { data: project } = await supabase.from("projects").select("name").eq("id", projectId).single();
|
|
2386
2466
|
const { data: idea, error: insertError } = await supabase.from("saved_ideas").insert({
|
|
2387
2467
|
project_id: projectId,
|
|
2388
2468
|
title,
|
|
@@ -2408,7 +2488,7 @@ async function submitIdea(supabase, userId, projectId, title, description, categ
|
|
|
2408
2488
|
return {
|
|
2409
2489
|
success: true,
|
|
2410
2490
|
ideaId: idea.id,
|
|
2411
|
-
message: `\u{1F4A1} Idea "${title}" submitted to Idea Lab for project "${project
|
|
2491
|
+
message: `\u{1F4A1} Idea "${title}" submitted to Idea Lab for project "${project?.name || projectId}". Status: Draft (awaiting review)`
|
|
2412
2492
|
};
|
|
2413
2493
|
}
|
|
2414
2494
|
|
|
@@ -2432,10 +2512,14 @@ Can search by chunk ID or by title.`,
|
|
|
2432
2512
|
}
|
|
2433
2513
|
});
|
|
2434
2514
|
async function updateRoadmap(supabase, userId, projectId, status, chunkId, title) {
|
|
2435
|
-
const { data:
|
|
2436
|
-
|
|
2515
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
2516
|
+
p_project_id: projectId,
|
|
2517
|
+
p_user_id: userId
|
|
2518
|
+
});
|
|
2519
|
+
if (accessError || !hasAccess) {
|
|
2437
2520
|
throw new Error("Project not found or access denied");
|
|
2438
2521
|
}
|
|
2522
|
+
const { data: project } = await supabase.from("projects").select("name").eq("id", projectId).single();
|
|
2439
2523
|
let targetChunk = null;
|
|
2440
2524
|
if (chunkId) {
|
|
2441
2525
|
const { data, error } = await supabase.from("roadmap_chunks").select("id, title, status").eq("id", chunkId).eq("project_id", projectId).single();
|
|
@@ -2564,8 +2648,11 @@ Returns violations or "Pass" status.`,
|
|
|
2564
2648
|
}
|
|
2565
2649
|
});
|
|
2566
2650
|
async function runArchitectureAudit(supabase, userId, projectId, filePath, content) {
|
|
2567
|
-
const { data:
|
|
2568
|
-
|
|
2651
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
2652
|
+
p_project_id: projectId,
|
|
2653
|
+
p_user_id: userId
|
|
2654
|
+
});
|
|
2655
|
+
if (accessError || !hasAccess) {
|
|
2569
2656
|
throw new Error("Project not found or access denied");
|
|
2570
2657
|
}
|
|
2571
2658
|
const violations = [];
|
|
@@ -2656,7 +2743,7 @@ registry.register({
|
|
|
2656
2743
|
based on project context and user settings.`,
|
|
2657
2744
|
schema: GenerateCursorRulesInputSchema,
|
|
2658
2745
|
handler: async (args, context) => {
|
|
2659
|
-
const result = await syncIdeRules(context.supabase, args.projectId);
|
|
2746
|
+
const result = await syncIdeRules(context.supabase, context.userId, args.projectId);
|
|
2660
2747
|
let responseText = `FileName: ${result.fileName}
|
|
2661
2748
|
|
|
2662
2749
|
Content:
|
|
@@ -2674,10 +2761,17 @@ Note: Please ensure these modular files are also updated if your IDE is followin
|
|
|
2674
2761
|
return { content: [{ type: "text", text: responseText }] };
|
|
2675
2762
|
}
|
|
2676
2763
|
});
|
|
2677
|
-
async function syncIdeRules(supabase, projectId) {
|
|
2764
|
+
async function syncIdeRules(supabase, userId, projectId) {
|
|
2765
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
2766
|
+
p_project_id: projectId,
|
|
2767
|
+
p_user_id: userId
|
|
2768
|
+
});
|
|
2769
|
+
if (accessError || !hasAccess) {
|
|
2770
|
+
throw new Error("Project not found or access denied");
|
|
2771
|
+
}
|
|
2678
2772
|
const { data: project, error: projectError } = await supabase.from("projects").select("*").eq("id", projectId).single();
|
|
2679
2773
|
if (projectError || !project) {
|
|
2680
|
-
throw new Error(`Project ${projectId} not found.`);
|
|
2774
|
+
throw new Error(`Project ${projectId} details not found.`);
|
|
2681
2775
|
}
|
|
2682
2776
|
const ide = project.preferred_ide || "cursor";
|
|
2683
2777
|
const [stack, roadmapRes, legacyStats, activeAgents, dbMetadataRes] = await Promise.all([
|
|
@@ -2724,10 +2818,17 @@ var listFeaturesTool = {
|
|
|
2724
2818
|
Useful for understanding the strategic context and major milestones.`,
|
|
2725
2819
|
schema: InputSchema,
|
|
2726
2820
|
handler: async ({ projectId }, { supabase, userId }) => {
|
|
2727
|
-
const { data:
|
|
2728
|
-
|
|
2821
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
2822
|
+
p_project_id: projectId,
|
|
2823
|
+
p_user_id: userId
|
|
2824
|
+
});
|
|
2825
|
+
if (accessError || !hasAccess) {
|
|
2729
2826
|
throw new Error("Project not found or access denied");
|
|
2730
2827
|
}
|
|
2828
|
+
const { data: project, error: projectError } = await supabase.from("projects").select("id, functional_spec").eq("id", projectId).single();
|
|
2829
|
+
if (projectError || !project) {
|
|
2830
|
+
throw new Error("Project details not found");
|
|
2831
|
+
}
|
|
2731
2832
|
const { data: dbFeatures, error: dbError } = await supabase.from("project_features").select("id, name, description, status").eq("project_id", projectId).neq("status", "shadow").order("created_at", { ascending: false });
|
|
2732
2833
|
let featuresList = [];
|
|
2733
2834
|
let source = "DB";
|
|
@@ -2810,7 +2911,12 @@ async function listRoadmapTasks(supabase, userId, projectId) {
|
|
|
2810
2911
|
priority: t.priority,
|
|
2811
2912
|
status: t.status,
|
|
2812
2913
|
step_number: t.step_number,
|
|
2813
|
-
prompt_content: t.prompt_content
|
|
2914
|
+
prompt_content: t.prompt_content,
|
|
2915
|
+
architectural_brief: t.architectural_brief,
|
|
2916
|
+
context_summary: t.context_summary,
|
|
2917
|
+
metadata: t.metadata,
|
|
2918
|
+
checklist: t.checklist,
|
|
2919
|
+
tags: t.tags
|
|
2814
2920
|
})),
|
|
2815
2921
|
formatted
|
|
2816
2922
|
};
|
|
@@ -2865,7 +2971,11 @@ async function getNextRoadmapStep(supabase, userId, projectId, currentStepId) {
|
|
|
2865
2971
|
};
|
|
2866
2972
|
}
|
|
2867
2973
|
return {
|
|
2868
|
-
nextStep
|
|
2974
|
+
nextStep: {
|
|
2975
|
+
...nextStep,
|
|
2976
|
+
architectural_brief: nextStep.architectural_brief,
|
|
2977
|
+
context_summary: nextStep.context_summary
|
|
2978
|
+
},
|
|
2869
2979
|
message: `Next step found: [Step ${nextStep.step_number}] ${nextStep.title}`
|
|
2870
2980
|
};
|
|
2871
2981
|
}
|
|
@@ -2879,6 +2989,7 @@ registry.register({
|
|
|
2879
2989
|
handler: async (args, context) => {
|
|
2880
2990
|
const result = await checkRulesSync(
|
|
2881
2991
|
context.supabase,
|
|
2992
|
+
context.userId,
|
|
2882
2993
|
args.projectId,
|
|
2883
2994
|
args.currentRulesContent
|
|
2884
2995
|
);
|
|
@@ -2895,7 +3006,7 @@ var SAFETY_CACHE_RULES = `
|
|
|
2895
3006
|
4. **Error Handling**: Use try/catch blocks for all external API calls.
|
|
2896
3007
|
5. **No Blind Deletes**: Never delete data without explicit confirmation or soft-deletes.
|
|
2897
3008
|
`;
|
|
2898
|
-
async function checkRulesSync(supabase, projectId, currentRulesContent) {
|
|
3009
|
+
async function checkRulesSync(supabase, userId, projectId, currentRulesContent) {
|
|
2899
3010
|
if (!currentRulesContent) {
|
|
2900
3011
|
return {
|
|
2901
3012
|
synced: false,
|
|
@@ -2915,6 +3026,13 @@ async function checkRulesSync(supabase, projectId, currentRulesContent) {
|
|
|
2915
3026
|
};
|
|
2916
3027
|
}
|
|
2917
3028
|
try {
|
|
3029
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
3030
|
+
p_project_id: projectId,
|
|
3031
|
+
p_user_id: userId
|
|
3032
|
+
});
|
|
3033
|
+
if (accessError || !hasAccess) {
|
|
3034
|
+
throw new Error("Project not found or access denied");
|
|
3035
|
+
}
|
|
2918
3036
|
const { data: project, error } = await supabase.from("projects").select("name").eq("id", projectId).single();
|
|
2919
3037
|
if (error) throw error;
|
|
2920
3038
|
if (project) {
|
|
@@ -2951,7 +3069,14 @@ init_esm_shims();
|
|
|
2951
3069
|
init_esm_shims();
|
|
2952
3070
|
import fs from "fs/promises";
|
|
2953
3071
|
import path2 from "path";
|
|
2954
|
-
async function analyzeDatabasePerformance(supabase, input) {
|
|
3072
|
+
async function analyzeDatabasePerformance(supabase, userId, input) {
|
|
3073
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
3074
|
+
p_project_id: input.projectId,
|
|
3075
|
+
p_user_id: userId
|
|
3076
|
+
});
|
|
3077
|
+
if (accessError || !hasAccess) {
|
|
3078
|
+
throw new Error("Project not found or access denied");
|
|
3079
|
+
}
|
|
2955
3080
|
const issues = [];
|
|
2956
3081
|
const { data: rawMetadata, error } = await supabase.rpc("get_table_metadata", {
|
|
2957
3082
|
p_project_id: input.projectId
|
|
@@ -3302,7 +3427,7 @@ registry.register({
|
|
|
3302
3427
|
description: `Sven's Tool: Security Shield. Audits the database to ensure Row Level Security (RLS) is enforced.`,
|
|
3303
3428
|
schema: AuditRlsStatusInputSchema,
|
|
3304
3429
|
handler: async (args, context) => {
|
|
3305
|
-
const result = await auditRlsStatus(context.supabase, args);
|
|
3430
|
+
const result = await auditRlsStatus(context.supabase, context.userId, args);
|
|
3306
3431
|
return { content: [{ type: "text", text: result.summary || "No summary available" }] };
|
|
3307
3432
|
}
|
|
3308
3433
|
});
|
|
@@ -3311,11 +3436,18 @@ registry.register({
|
|
|
3311
3436
|
description: `Frank's Tool: Security Oracle. Performs a diagnostic security audit against the Fortress Matrix.`,
|
|
3312
3437
|
schema: AuditSecurityIntegrityInputSchema,
|
|
3313
3438
|
handler: async (args, context) => {
|
|
3314
|
-
const result = await auditSecurityIntegrity(context.supabase, args);
|
|
3439
|
+
const result = await auditSecurityIntegrity(context.supabase, context.userId, args);
|
|
3315
3440
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
3316
3441
|
}
|
|
3317
3442
|
});
|
|
3318
|
-
async function auditRlsStatus(supabase, input) {
|
|
3443
|
+
async function auditRlsStatus(supabase, userId, input) {
|
|
3444
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
3445
|
+
p_project_id: input.projectId,
|
|
3446
|
+
p_user_id: userId
|
|
3447
|
+
});
|
|
3448
|
+
if (accessError || !hasAccess) {
|
|
3449
|
+
throw new Error("Project not found or access denied");
|
|
3450
|
+
}
|
|
3319
3451
|
try {
|
|
3320
3452
|
const { data, error } = await supabase.rpc("execute_sql", {
|
|
3321
3453
|
query: `
|
|
@@ -3358,7 +3490,14 @@ async function auditRlsStatus(supabase, input) {
|
|
|
3358
3490
|
};
|
|
3359
3491
|
}
|
|
3360
3492
|
}
|
|
3361
|
-
async function auditSecurityIntegrity(supabase, input) {
|
|
3493
|
+
async function auditSecurityIntegrity(supabase, userId, input) {
|
|
3494
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
3495
|
+
p_project_id: input.projectId,
|
|
3496
|
+
p_user_id: userId
|
|
3497
|
+
});
|
|
3498
|
+
if (accessError || !hasAccess) {
|
|
3499
|
+
throw new Error("Project not found or access denied");
|
|
3500
|
+
}
|
|
3362
3501
|
const violations = [];
|
|
3363
3502
|
const { content, filePath } = input;
|
|
3364
3503
|
const sqlViolation = checkSqlInjection(content);
|
|
@@ -3405,15 +3544,22 @@ registry.register({
|
|
|
3405
3544
|
Can trigger a SOFT LOCK if critical issues are found.`,
|
|
3406
3545
|
schema: AuditIntegrityGateInputSchema,
|
|
3407
3546
|
handler: async (args, context) => {
|
|
3408
|
-
const result = await runAuditIntegrityGate(context.supabase, args);
|
|
3547
|
+
const result = await runAuditIntegrityGate(context.supabase, context.userId, args);
|
|
3409
3548
|
return { content: [{ type: "text", text: result.summary }] };
|
|
3410
3549
|
}
|
|
3411
3550
|
});
|
|
3412
|
-
async function runAuditIntegrityGate(supabase, input) {
|
|
3551
|
+
async function runAuditIntegrityGate(supabase, userId, input) {
|
|
3552
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
3553
|
+
p_project_id: input.projectId,
|
|
3554
|
+
p_user_id: userId
|
|
3555
|
+
});
|
|
3556
|
+
if (accessError || !hasAccess) {
|
|
3557
|
+
throw new Error("Project not found or access denied");
|
|
3558
|
+
}
|
|
3413
3559
|
const checks = [];
|
|
3414
3560
|
let isSoftLocked = false;
|
|
3415
3561
|
try {
|
|
3416
|
-
const rlsResult = await auditRlsStatus(supabase, { projectId: input.projectId });
|
|
3562
|
+
const rlsResult = await auditRlsStatus(supabase, userId, { projectId: input.projectId });
|
|
3417
3563
|
const unsecuredTables = rlsResult.unsecuredTables || [];
|
|
3418
3564
|
if (unsecuredTables.length > 0) {
|
|
3419
3565
|
isSoftLocked = true;
|
|
@@ -3442,7 +3588,7 @@ async function runAuditIntegrityGate(supabase, input) {
|
|
|
3442
3588
|
try {
|
|
3443
3589
|
const fs3 = await import("fs/promises");
|
|
3444
3590
|
const content = await fs3.readFile(path4, "utf-8");
|
|
3445
|
-
const securityResult = await auditSecurityIntegrity(supabase, {
|
|
3591
|
+
const securityResult = await auditSecurityIntegrity(supabase, userId, {
|
|
3446
3592
|
projectId: input.projectId,
|
|
3447
3593
|
filePath: path4,
|
|
3448
3594
|
content
|
|
@@ -3473,7 +3619,7 @@ async function runAuditIntegrityGate(supabase, input) {
|
|
|
3473
3619
|
}
|
|
3474
3620
|
if (input.filePaths && input.filePaths.length > 0) {
|
|
3475
3621
|
try {
|
|
3476
|
-
const perfResult = await analyzeDatabasePerformance(supabase, {
|
|
3622
|
+
const perfResult = await analyzeDatabasePerformance(supabase, userId, {
|
|
3477
3623
|
projectId: input.projectId,
|
|
3478
3624
|
filePaths: input.filePaths
|
|
3479
3625
|
});
|
|
@@ -3532,6 +3678,7 @@ registry.register({
|
|
|
3532
3678
|
handler: async (args, context) => {
|
|
3533
3679
|
const result = await completeRoadmapTask(
|
|
3534
3680
|
context.supabase,
|
|
3681
|
+
context.userId,
|
|
3535
3682
|
args.projectId,
|
|
3536
3683
|
args.summary,
|
|
3537
3684
|
args.taskId,
|
|
@@ -3541,7 +3688,14 @@ registry.register({
|
|
|
3541
3688
|
return { content: [{ type: "text", text: result.message }] };
|
|
3542
3689
|
}
|
|
3543
3690
|
});
|
|
3544
|
-
async function completeRoadmapTask(supabase, projectId, summary, taskId, gitDiff, integrityGate) {
|
|
3691
|
+
async function completeRoadmapTask(supabase, userId, projectId, summary, taskId, gitDiff, integrityGate) {
|
|
3692
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
3693
|
+
p_project_id: projectId,
|
|
3694
|
+
p_user_id: userId
|
|
3695
|
+
});
|
|
3696
|
+
if (accessError || !hasAccess) {
|
|
3697
|
+
throw new Error("Project not found or access denied");
|
|
3698
|
+
}
|
|
3545
3699
|
let targetTaskId = taskId;
|
|
3546
3700
|
if (!targetTaskId) {
|
|
3547
3701
|
const { data: activeTask } = await supabase.from("roadmap_chunks").select("id, title").eq("project_id", projectId).in("status", ["IN_PROGRESS", "ACTIVE"]).order("step_number", { ascending: true }).limit(1).single();
|
|
@@ -3958,8 +4112,18 @@ function interpolateScribePrompt(basePrompt, vars) {
|
|
|
3958
4112
|
// src/tools/generate-professional-pdf.ts
|
|
3959
4113
|
async function generateProfessionalPdf(supabase, userId, projectId, reportType) {
|
|
3960
4114
|
console.error(`\u{1F58B}\uFE0F The Scribe is preparing a ${reportType} briefing for project ${projectId}...`);
|
|
4115
|
+
const { data: hasAccess, error: accessError } = await supabase.rpc("check_project_access_secure", {
|
|
4116
|
+
p_project_id: projectId,
|
|
4117
|
+
p_user_id: userId
|
|
4118
|
+
});
|
|
4119
|
+
if (accessError || !hasAccess) {
|
|
4120
|
+
throw new Error("Project not found or access denied");
|
|
4121
|
+
}
|
|
3961
4122
|
const persona = await getScribePersona(supabase);
|
|
3962
|
-
const { data: project } = await supabase.from("projects").select("name, description, project_type, detected_stack").eq("id", projectId).single();
|
|
4123
|
+
const { data: project, error: projectError } = await supabase.from("projects").select("name, description, project_type, detected_stack").eq("id", projectId).single();
|
|
4124
|
+
if (projectError || !project) {
|
|
4125
|
+
throw new Error("Project details not found");
|
|
4126
|
+
}
|
|
3963
4127
|
const projectName = project?.name || "Rigstate Project";
|
|
3964
4128
|
const projectDescription = project?.description || "A cutting-edge software project built with modern architecture.";
|
|
3965
4129
|
const projectType = project?.project_type || "Web Application";
|