@rigstate/mcp 0.7.4 → 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 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.0";
1105
+ var SERVER_VERSION = "0.7.5";
1106
1106
 
1107
1107
  // src/lib/tool-registry.ts
1108
1108
  init_esm_shims();
@@ -1192,10 +1192,13 @@ import { z as z2 } from "zod";
1192
1192
  var QueryGlobalAntidotesSchema = z2.object({
1193
1193
  categories: z2.array(z2.string()).optional().describe("Filter by categories (SECURITY, ARCHITECTURE, UX, PERFORMANCE, ACCESSIBILITY, MAINTAINABILITY)"),
1194
1194
  severities: z2.array(z2.string()).optional().describe("Filter by severity (CRITICAL, HIGH, MEDIUM, LOW)"),
1195
- framework_tags: z2.array(z2.string()).optional().describe('Filter by framework tags (e.g., ["nextjs", "react", "supabase"])'),
1196
- min_trust_score: z2.number().optional().describe("Minimum trust score (0-100)"),
1195
+ framework_tags: z2.preprocess(
1196
+ (val) => typeof val === "string" ? JSON.parse(val) : val,
1197
+ z2.array(z2.string())
1198
+ ).optional().describe('Filter by framework tags (e.g., ["nextjs", "react", "supabase"])'),
1199
+ min_trust_score: z2.coerce.number().optional().describe("Minimum trust score (0-100)"),
1197
1200
  search_text: z2.string().optional().describe("Search in title and instruction"),
1198
- limit: z2.number().optional().describe("Max results (default: 20)")
1201
+ limit: z2.coerce.number().optional().describe("Max results (default: 20)")
1199
1202
  });
1200
1203
  var SubmitSignalSchema = z2.object({
1201
1204
  projectId: z2.string().describe("The UUID of the Rigstate project"),
@@ -1205,7 +1208,10 @@ var SubmitSignalSchema = z2.object({
1205
1208
  severity: z2.enum(["CRITICAL", "HIGH", "MEDIUM", "LOW"]).describe("Severity level"),
1206
1209
  example: z2.string().optional().describe("Good example demonstrating the instruction"),
1207
1210
  anti_example: z2.string().optional().describe("Bad example showing what NOT to do"),
1208
- framework_tags: z2.array(z2.string()).optional().describe("Relevant framework tags"),
1211
+ framework_tags: z2.preprocess(
1212
+ (val) => typeof val === "string" ? [val] : val,
1213
+ z2.array(z2.string())
1214
+ ).optional().describe("Relevant framework tags"),
1209
1215
  reasoning: z2.string().optional().describe("Why this signal should be added"),
1210
1216
  source_type: z2.string().optional().describe("Internal source type override (e.g. TEACHER_MODE)")
1211
1217
  });
@@ -1269,8 +1275,11 @@ ${formatted}
1269
1275
  // src/lib/curator/actions/submit.ts
1270
1276
  init_esm_shims();
1271
1277
  async function submitSignal(supabase, userId, input) {
1272
- const { data: project, error: projectError } = await supabase.from("projects").select("id").eq("id", input.projectId).eq("owner_id", userId).single();
1273
- if (projectError || !project) {
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) {
1274
1283
  throw new Error("Project not found or access denied");
1275
1284
  }
1276
1285
  const fingerprintBase = `${input.instruction}::${input.category}`.toLowerCase();
@@ -1555,8 +1564,11 @@ var GetLearnedInstructionsSchema = z3.object({
1555
1564
  async function refineLogic(supabase, userId, args) {
1556
1565
  const { projectId, originalReasoning, userCorrection, scope } = args;
1557
1566
  const traceId = uuidv4();
1558
- const { data: project, error: projectError } = await supabase.from("projects").select("id, name").eq("id", projectId).eq("owner_id", userId).single();
1559
- if (projectError || !project) {
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) {
1560
1572
  throw new Error(`Project access denied or not found: ${projectId}`);
1561
1573
  }
1562
1574
  const { data: instruction, error: insertError } = await supabase.from("ai_instructions").insert({
@@ -1739,15 +1751,15 @@ import { z as z4 } from "zod";
1739
1751
  var QueryBrainInputSchema = z4.object({
1740
1752
  projectId: z4.string().uuid("Invalid project ID"),
1741
1753
  query: z4.string().min(1, "Query is required"),
1742
- limit: z4.number().min(1).max(20).optional().default(8),
1743
- threshold: z4.number().min(0).max(1).optional().default(0.1)
1754
+ limit: z4.coerce.number().min(1).max(20).optional().default(8),
1755
+ threshold: z4.coerce.number().min(0).max(1).optional().default(0.1)
1744
1756
  });
1745
1757
  var GetProjectContextInputSchema = z4.object({
1746
1758
  projectId: z4.string().uuid("Invalid project ID")
1747
1759
  });
1748
1760
  var GetLatestDecisionsInputSchema = z4.object({
1749
1761
  projectId: z4.string().uuid("Invalid project ID"),
1750
- limit: z4.number().min(1).max(10).optional().default(5)
1762
+ limit: z4.coerce.number().min(1).max(10).optional().default(5)
1751
1763
  });
1752
1764
  var SaveDecisionInputSchema = z4.object({
1753
1765
  projectId: z4.string().uuid("Invalid project ID"),
@@ -1755,14 +1767,14 @@ var SaveDecisionInputSchema = z4.object({
1755
1767
  decision: z4.string().min(1, "Decision content is required"),
1756
1768
  rationale: z4.string().optional(),
1757
1769
  category: z4.enum(["decision", "architecture", "constraint", "tech_stack", "design_rule"]).optional().default("decision"),
1758
- tags: z4.array(z4.string()).optional().default([])
1770
+ tags: z4.preprocess((val) => typeof val === "string" ? [val] : val, z4.array(z4.string())).optional().default([])
1759
1771
  });
1760
1772
  var SubmitIdeaInputSchema = z4.object({
1761
1773
  projectId: z4.string().uuid("Invalid project ID"),
1762
1774
  title: z4.string().min(1, "Title is required").max(200, "Title too long"),
1763
1775
  description: z4.string().min(1, "Description is required"),
1764
1776
  category: z4.enum(["feature", "improvement", "experiment", "pivot"]).optional().default("feature"),
1765
- tags: z4.array(z4.string()).optional().default([])
1777
+ tags: z4.preprocess((val) => typeof val === "string" ? [val] : val, z4.array(z4.string())).optional().default([])
1766
1778
  });
1767
1779
  var UpdateRoadmapInputSchema = z4.object({
1768
1780
  projectId: z4.string().uuid("Invalid project ID"),
@@ -1820,7 +1832,7 @@ var GenerateProfessionalPDFInputSchema = z4.object({
1820
1832
  var ArchaeologicalScanInputSchema = z4.object({
1821
1833
  projectId: z4.string().uuid("Invalid project ID"),
1822
1834
  gitLog: z4.string().describe("Git log output"),
1823
- fileTree: z4.array(z4.string()).describe("File paths")
1835
+ fileTree: z4.preprocess((val) => typeof val === "string" ? [val] : val, z4.array(z4.string())).describe("File paths")
1824
1836
  });
1825
1837
  var ImportGhostFeaturesInputSchema = z4.object({
1826
1838
  projectId: z4.string().uuid("Invalid project ID"),
@@ -1836,7 +1848,7 @@ var AuditSecurityIntegrityInputSchema = z4.object({
1836
1848
  projectId: z4.string().uuid(),
1837
1849
  filePath: z4.string().min(1),
1838
1850
  content: z4.string().min(1),
1839
- rules: z4.array(z4.string()).optional()
1851
+ rules: z4.preprocess((val) => typeof val === "string" ? [val] : val, z4.array(z4.string())).optional()
1840
1852
  });
1841
1853
  var FetchPackageHealthInputSchema = z4.object({
1842
1854
  packageName: z4.string().min(1)
@@ -1846,7 +1858,7 @@ var SaveToProjectBrainInputSchema = z4.object({
1846
1858
  title: z4.string().min(1),
1847
1859
  content: z4.string().min(1),
1848
1860
  category: z4.enum(["DECISION", "ARCHITECTURE", "NOTE", "LESSON_LEARNED"]).default("NOTE"),
1849
- tags: z4.array(z4.string()).optional().default([])
1861
+ tags: z4.preprocess((val) => typeof val === "string" ? [val] : val, z4.array(z4.string())).optional().default([])
1850
1862
  });
1851
1863
  var UpdateRoadmapStatusInputSchema = z4.object({
1852
1864
  projectId: z4.string().uuid(),
@@ -1875,11 +1887,11 @@ var GenerateCursorRulesInputSchema = z4.object({
1875
1887
  });
1876
1888
  var AnalyzeDatabasePerformanceInputSchema = z4.object({
1877
1889
  projectId: z4.string().uuid(),
1878
- filePaths: z4.array(z4.string())
1890
+ filePaths: z4.preprocess((val) => typeof val === "string" ? [val] : val, z4.array(z4.string()))
1879
1891
  });
1880
1892
  var AuditIntegrityGateInputSchema = z4.object({
1881
1893
  projectId: z4.string().uuid(),
1882
- filePaths: z4.array(z4.string()).optional().default([])
1894
+ filePaths: z4.preprocess((val) => typeof val === "string" ? [val] : val, z4.array(z4.string())).optional().default([])
1883
1895
  });
1884
1896
  var CompleteRoadmapTaskInputSchema = z4.object({
1885
1897
  projectId: z4.string().uuid(),
@@ -1889,6 +1901,119 @@ var CompleteRoadmapTaskInputSchema = z4.object({
1889
1901
  integrityGate: z4.any().optional()
1890
1902
  });
1891
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
+
1892
2017
  // src/tools/get-project-context.ts
1893
2018
  registry.register({
1894
2019
  name: "get_project_context",
@@ -1934,10 +2059,11 @@ async function getProjectContext(supabase, userId, projectId) {
1934
2059
  created_at: projectRow.created_at,
1935
2060
  last_indexed_at: projectRow.last_indexed_at,
1936
2061
  detected_stack: projectRow.detected_stack,
1937
- repository_tree: projectRow.repository_tree
2062
+ repository_tree: projectRow.repository_tree,
2063
+ functional_spec: projectRow.functional_spec
1938
2064
  };
1939
2065
  const stackDef = projectRow.architectural_dna?.stack_definition;
1940
- const { data: allChunks, error: chunksError } = await supabase.rpc("get_roadmap_chunks_secure", {
2066
+ const { data: allChunks } = await supabase.rpc("get_roadmap_chunks_secure", {
1941
2067
  p_project_id: projectId,
1942
2068
  p_user_id: userId
1943
2069
  });
@@ -1980,67 +2106,16 @@ async function getProjectContext(supabase, userId, projectId) {
1980
2106
  project.repository_tree.filter((t) => t.type === "tree" && !t.path.includes("/")).map((t) => t.path)
1981
2107
  )].slice(0, 10);
1982
2108
  }
1983
- const summaryParts = [];
1984
- summaryParts.push(`Project Type: ${project.project_type?.toUpperCase() || "UNKNOWN"}`);
1985
- if (stackDef) {
1986
- summaryParts.push("\n=== ACTIVE MISSION PARAMETERS ===");
1987
- if (activeTask) {
1988
- summaryParts.push(`\u26A0\uFE0F CURRENT OBJECTIVE: T-${activeTask.step_number}: ${activeTask.title}`);
1989
- summaryParts.push(` Role: ${activeTask.role || "Developer"}`);
1990
- if (activeTask.instruction_set) {
1991
- summaryParts.push(` Instructions: ${activeTask.instruction_set.substring(0, 200)}...`);
1992
- }
1993
- summaryParts.push(" ACTION: Focus ALL coding efforts on completing this task.");
1994
- } else if (nextTask) {
1995
- summaryParts.push(`\u23F8 SYSTEM IDLE (Waiting for command)`);
1996
- summaryParts.push(` Suggested Next Mission: T-${nextTask.step_number}: ${nextTask.title}`);
1997
- summaryParts.push(` ACTION: Ask the user "Shall we start T-${nextTask.step_number}?"`);
1998
- } else {
1999
- summaryParts.push("\u2705 ALL MISSIONS COMPLETE. Awaiting new roadmap items.");
2000
- }
2001
- summaryParts.push("\n=== AI BEHAVIORAL INSTRUCTIONS ===");
2002
- if (activeTask) {
2003
- summaryParts.push(`1. FOCUS: The user is working on T-${activeTask.step_number}. Help them complete it.`);
2004
- summaryParts.push(`2. COMPLIANCE: Ensure all code follows project standards.`);
2005
- } else if (nextTask) {
2006
- summaryParts.push(`1. NUDGE: No active task found. Suggest starting T-${nextTask.step_number} (${nextTask.title}).`);
2007
- summaryParts.push(`2. PROACTIVE: Instead of asking "How can I help?", ask "Shall we start on T-${nextTask.step_number}?"`);
2008
- }
2009
- summaryParts.push("\n=== CURRENT STACK ===");
2010
- if (stackDef.frontend) summaryParts.push(`Frontend: ${stackDef.frontend.framework} (${stackDef.frontend.language})`);
2011
- if (stackDef.backend) summaryParts.push(`Backend: ${stackDef.backend.service} (${stackDef.backend.database})`);
2012
- if (stackDef.styling) summaryParts.push(`Styling: ${stackDef.styling.framework} ${stackDef.styling.library || ""}`);
2013
- if (stackDef.hosting) summaryParts.push(`Infrastructure: ${stackDef.hosting.provider}`);
2014
- } else {
2015
- if (techStack.framework) summaryParts.push(`Framework: ${techStack.framework}`);
2016
- if (techStack.orm) summaryParts.push(`ORM: ${techStack.orm}`);
2017
- }
2018
- if (project.description) {
2019
- summaryParts.push(`
2020
- Description: ${project.description}`);
2021
- }
2022
- summaryParts.push("\n=== RIGSTATE TOOLING GUIDELINES ===");
2023
- summaryParts.push("You have access to specialized MCP tools. USE THEM TO SUCCEED:");
2024
- summaryParts.push("1. NEVER guess about architecture. Use `query_brain` to search project documentation.");
2025
- summaryParts.push("2. BEFORE coding, check `get_learned_instructions` to see if you have been corrected before.");
2026
- summaryParts.push("3. When finishing a task, ALWAYS update the roadmap using `update_roadmap`.");
2027
- summaryParts.push("4. If you discover a reusable pattern, submit it with `submit_curator_signal`.");
2028
- summaryParts.push("5. For large refactors, use `run_architecture_audit` to check against rules.");
2029
- summaryParts.push("6. Store major decisions using `save_decision` (ADR).");
2030
- summaryParts.push("\n=== RECENT ACTIVITY DIGEST ===");
2031
- if (agentTasks && agentTasks.length > 0) {
2032
- summaryParts.push("\nLatest AI Executions:");
2033
- agentTasks.forEach((t) => {
2034
- const time = t.completed_at ? new Date(t.completed_at).toLocaleString() : "Recently";
2035
- summaryParts.push(`- [${time}] ${t.roadmap_title || "Task"}: ${t.execution_summary || "Completed"}`);
2036
- });
2037
- }
2038
- if (roadmapItems && roadmapItems.length > 0) {
2039
- summaryParts.push("\nRoadmap Updates:");
2040
- roadmapItems.forEach((i) => {
2041
- summaryParts.push(`- ${i.title} is now ${i.status}`);
2042
- });
2043
- }
2109
+ const summary = await buildProjectSummary(
2110
+ project,
2111
+ techStack,
2112
+ activeTask,
2113
+ nextTask,
2114
+ agentTasks || [],
2115
+ roadmapItems || [],
2116
+ stackDef,
2117
+ supabase
2118
+ );
2044
2119
  const response = {
2045
2120
  project: {
2046
2121
  id: project.id,
@@ -2051,7 +2126,7 @@ Description: ${project.description}`);
2051
2126
  lastIndexedAt: project.last_indexed_at
2052
2127
  },
2053
2128
  techStack,
2054
- summary: summaryParts.join("\n") || "No project context available."
2129
+ summary
2055
2130
  };
2056
2131
  try {
2057
2132
  const curatorContext = await injectGlobalContext(supabase, userId, {
@@ -2120,8 +2195,11 @@ async function generateQueryEmbedding(query) {
2120
2195
  }
2121
2196
  }
2122
2197
  async function queryBrain(supabase, userId, projectId, query, limit = 8, threshold = 0.5) {
2123
- const { data: project, error: projectError } = await supabase.from("projects").select("id").eq("id", projectId).eq("owner_id", userId).single();
2124
- if (projectError || !project) {
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) {
2125
2203
  throw new Error("Project not found or access denied");
2126
2204
  }
2127
2205
  const embedding = await generateQueryEmbedding(query);
@@ -2314,10 +2392,14 @@ High-importance memory for architectural decisions.`,
2314
2392
  }
2315
2393
  });
2316
2394
  async function saveDecision(supabase, userId, projectId, title, decision, rationale, category = "decision", tags = []) {
2317
- const { data: project, error: projectError } = await supabase.from("projects").select("id, name").eq("id", projectId).eq("owner_id", userId).single();
2318
- if (projectError || !project) {
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) {
2319
2400
  throw new Error("Project not found or access denied");
2320
2401
  }
2402
+ const { data: project } = await supabase.from("projects").select("name").eq("id", projectId).single();
2321
2403
  const contentParts = [`# ${title}`, "", decision];
2322
2404
  if (rationale) {
2323
2405
  contentParts.push("", "## Rationale", rationale);
@@ -2348,7 +2430,7 @@ async function saveDecision(supabase, userId, projectId, title, decision, ration
2348
2430
  return {
2349
2431
  success: true,
2350
2432
  memoryId: memory.id,
2351
- message: `\u2705 Decision "${title}" saved to project "${project.name}" with importance 9/10`
2433
+ message: `\u2705 Decision "${title}" saved to project "${project?.name || projectId}" with importance 9/10`
2352
2434
  };
2353
2435
  }
2354
2436
 
@@ -2373,10 +2455,14 @@ Ideas can later be reviewed, analyzed, and promoted to features.`,
2373
2455
  }
2374
2456
  });
2375
2457
  async function submitIdea(supabase, userId, projectId, title, description, category = "feature", tags = []) {
2376
- const { data: project, error: projectError } = await supabase.from("projects").select("id, name").eq("id", projectId).eq("owner_id", userId).single();
2377
- if (projectError || !project) {
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) {
2378
2463
  throw new Error("Project not found or access denied");
2379
2464
  }
2465
+ const { data: project } = await supabase.from("projects").select("name").eq("id", projectId).single();
2380
2466
  const { data: idea, error: insertError } = await supabase.from("saved_ideas").insert({
2381
2467
  project_id: projectId,
2382
2468
  title,
@@ -2402,7 +2488,7 @@ async function submitIdea(supabase, userId, projectId, title, description, categ
2402
2488
  return {
2403
2489
  success: true,
2404
2490
  ideaId: idea.id,
2405
- message: `\u{1F4A1} Idea "${title}" submitted to Idea Lab for project "${project.name}". Status: Draft (awaiting review)`
2491
+ message: `\u{1F4A1} Idea "${title}" submitted to Idea Lab for project "${project?.name || projectId}". Status: Draft (awaiting review)`
2406
2492
  };
2407
2493
  }
2408
2494
 
@@ -2426,10 +2512,14 @@ Can search by chunk ID or by title.`,
2426
2512
  }
2427
2513
  });
2428
2514
  async function updateRoadmap(supabase, userId, projectId, status, chunkId, title) {
2429
- const { data: project, error: projectError } = await supabase.from("projects").select("id, name").eq("id", projectId).eq("owner_id", userId).single();
2430
- if (projectError || !project) {
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) {
2431
2520
  throw new Error("Project not found or access denied");
2432
2521
  }
2522
+ const { data: project } = await supabase.from("projects").select("name").eq("id", projectId).single();
2433
2523
  let targetChunk = null;
2434
2524
  if (chunkId) {
2435
2525
  const { data, error } = await supabase.from("roadmap_chunks").select("id, title, status").eq("id", chunkId).eq("project_id", projectId).single();
@@ -2558,8 +2648,11 @@ Returns violations or "Pass" status.`,
2558
2648
  }
2559
2649
  });
2560
2650
  async function runArchitectureAudit(supabase, userId, projectId, filePath, content) {
2561
- const { data: project, error: projectError } = await supabase.from("projects").select("id, name").eq("id", projectId).eq("owner_id", userId).single();
2562
- if (projectError || !project) {
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) {
2563
2656
  throw new Error("Project not found or access denied");
2564
2657
  }
2565
2658
  const violations = [];
@@ -2650,7 +2743,7 @@ registry.register({
2650
2743
  based on project context and user settings.`,
2651
2744
  schema: GenerateCursorRulesInputSchema,
2652
2745
  handler: async (args, context) => {
2653
- const result = await syncIdeRules(context.supabase, args.projectId);
2746
+ const result = await syncIdeRules(context.supabase, context.userId, args.projectId);
2654
2747
  let responseText = `FileName: ${result.fileName}
2655
2748
 
2656
2749
  Content:
@@ -2668,10 +2761,17 @@ Note: Please ensure these modular files are also updated if your IDE is followin
2668
2761
  return { content: [{ type: "text", text: responseText }] };
2669
2762
  }
2670
2763
  });
2671
- 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
+ }
2672
2772
  const { data: project, error: projectError } = await supabase.from("projects").select("*").eq("id", projectId).single();
2673
2773
  if (projectError || !project) {
2674
- throw new Error(`Project ${projectId} not found.`);
2774
+ throw new Error(`Project ${projectId} details not found.`);
2675
2775
  }
2676
2776
  const ide = project.preferred_ide || "cursor";
2677
2777
  const [stack, roadmapRes, legacyStats, activeAgents, dbMetadataRes] = await Promise.all([
@@ -2718,10 +2818,17 @@ var listFeaturesTool = {
2718
2818
  Useful for understanding the strategic context and major milestones.`,
2719
2819
  schema: InputSchema,
2720
2820
  handler: async ({ projectId }, { supabase, userId }) => {
2721
- const { data: project, error: projectError } = await supabase.from("projects").select("id, functional_spec").eq("id", projectId).eq("owner_id", userId).single();
2722
- if (projectError || !project) {
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) {
2723
2826
  throw new Error("Project not found or access denied");
2724
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
+ }
2725
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 });
2726
2833
  let featuresList = [];
2727
2834
  let source = "DB";
@@ -2804,7 +2911,12 @@ async function listRoadmapTasks(supabase, userId, projectId) {
2804
2911
  priority: t.priority,
2805
2912
  status: t.status,
2806
2913
  step_number: t.step_number,
2807
- 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
2808
2920
  })),
2809
2921
  formatted
2810
2922
  };
@@ -2859,7 +2971,11 @@ async function getNextRoadmapStep(supabase, userId, projectId, currentStepId) {
2859
2971
  };
2860
2972
  }
2861
2973
  return {
2862
- nextStep,
2974
+ nextStep: {
2975
+ ...nextStep,
2976
+ architectural_brief: nextStep.architectural_brief,
2977
+ context_summary: nextStep.context_summary
2978
+ },
2863
2979
  message: `Next step found: [Step ${nextStep.step_number}] ${nextStep.title}`
2864
2980
  };
2865
2981
  }
@@ -2873,6 +2989,7 @@ registry.register({
2873
2989
  handler: async (args, context) => {
2874
2990
  const result = await checkRulesSync(
2875
2991
  context.supabase,
2992
+ context.userId,
2876
2993
  args.projectId,
2877
2994
  args.currentRulesContent
2878
2995
  );
@@ -2889,7 +3006,7 @@ var SAFETY_CACHE_RULES = `
2889
3006
  4. **Error Handling**: Use try/catch blocks for all external API calls.
2890
3007
  5. **No Blind Deletes**: Never delete data without explicit confirmation or soft-deletes.
2891
3008
  `;
2892
- async function checkRulesSync(supabase, projectId, currentRulesContent) {
3009
+ async function checkRulesSync(supabase, userId, projectId, currentRulesContent) {
2893
3010
  if (!currentRulesContent) {
2894
3011
  return {
2895
3012
  synced: false,
@@ -2909,6 +3026,13 @@ async function checkRulesSync(supabase, projectId, currentRulesContent) {
2909
3026
  };
2910
3027
  }
2911
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
+ }
2912
3036
  const { data: project, error } = await supabase.from("projects").select("name").eq("id", projectId).single();
2913
3037
  if (error) throw error;
2914
3038
  if (project) {
@@ -2945,7 +3069,14 @@ init_esm_shims();
2945
3069
  init_esm_shims();
2946
3070
  import fs from "fs/promises";
2947
3071
  import path2 from "path";
2948
- 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
+ }
2949
3080
  const issues = [];
2950
3081
  const { data: rawMetadata, error } = await supabase.rpc("get_table_metadata", {
2951
3082
  p_project_id: input.projectId
@@ -3296,7 +3427,7 @@ registry.register({
3296
3427
  description: `Sven's Tool: Security Shield. Audits the database to ensure Row Level Security (RLS) is enforced.`,
3297
3428
  schema: AuditRlsStatusInputSchema,
3298
3429
  handler: async (args, context) => {
3299
- const result = await auditRlsStatus(context.supabase, args);
3430
+ const result = await auditRlsStatus(context.supabase, context.userId, args);
3300
3431
  return { content: [{ type: "text", text: result.summary || "No summary available" }] };
3301
3432
  }
3302
3433
  });
@@ -3305,11 +3436,18 @@ registry.register({
3305
3436
  description: `Frank's Tool: Security Oracle. Performs a diagnostic security audit against the Fortress Matrix.`,
3306
3437
  schema: AuditSecurityIntegrityInputSchema,
3307
3438
  handler: async (args, context) => {
3308
- const result = await auditSecurityIntegrity(context.supabase, args);
3439
+ const result = await auditSecurityIntegrity(context.supabase, context.userId, args);
3309
3440
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
3310
3441
  }
3311
3442
  });
3312
- 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
+ }
3313
3451
  try {
3314
3452
  const { data, error } = await supabase.rpc("execute_sql", {
3315
3453
  query: `
@@ -3352,7 +3490,14 @@ async function auditRlsStatus(supabase, input) {
3352
3490
  };
3353
3491
  }
3354
3492
  }
3355
- 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
+ }
3356
3501
  const violations = [];
3357
3502
  const { content, filePath } = input;
3358
3503
  const sqlViolation = checkSqlInjection(content);
@@ -3399,15 +3544,22 @@ registry.register({
3399
3544
  Can trigger a SOFT LOCK if critical issues are found.`,
3400
3545
  schema: AuditIntegrityGateInputSchema,
3401
3546
  handler: async (args, context) => {
3402
- const result = await runAuditIntegrityGate(context.supabase, args);
3547
+ const result = await runAuditIntegrityGate(context.supabase, context.userId, args);
3403
3548
  return { content: [{ type: "text", text: result.summary }] };
3404
3549
  }
3405
3550
  });
3406
- 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
+ }
3407
3559
  const checks = [];
3408
3560
  let isSoftLocked = false;
3409
3561
  try {
3410
- const rlsResult = await auditRlsStatus(supabase, { projectId: input.projectId });
3562
+ const rlsResult = await auditRlsStatus(supabase, userId, { projectId: input.projectId });
3411
3563
  const unsecuredTables = rlsResult.unsecuredTables || [];
3412
3564
  if (unsecuredTables.length > 0) {
3413
3565
  isSoftLocked = true;
@@ -3436,7 +3588,7 @@ async function runAuditIntegrityGate(supabase, input) {
3436
3588
  try {
3437
3589
  const fs3 = await import("fs/promises");
3438
3590
  const content = await fs3.readFile(path4, "utf-8");
3439
- const securityResult = await auditSecurityIntegrity(supabase, {
3591
+ const securityResult = await auditSecurityIntegrity(supabase, userId, {
3440
3592
  projectId: input.projectId,
3441
3593
  filePath: path4,
3442
3594
  content
@@ -3467,7 +3619,7 @@ async function runAuditIntegrityGate(supabase, input) {
3467
3619
  }
3468
3620
  if (input.filePaths && input.filePaths.length > 0) {
3469
3621
  try {
3470
- const perfResult = await analyzeDatabasePerformance(supabase, {
3622
+ const perfResult = await analyzeDatabasePerformance(supabase, userId, {
3471
3623
  projectId: input.projectId,
3472
3624
  filePaths: input.filePaths
3473
3625
  });
@@ -3526,6 +3678,7 @@ registry.register({
3526
3678
  handler: async (args, context) => {
3527
3679
  const result = await completeRoadmapTask(
3528
3680
  context.supabase,
3681
+ context.userId,
3529
3682
  args.projectId,
3530
3683
  args.summary,
3531
3684
  args.taskId,
@@ -3535,7 +3688,14 @@ registry.register({
3535
3688
  return { content: [{ type: "text", text: result.message }] };
3536
3689
  }
3537
3690
  });
3538
- 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
+ }
3539
3699
  let targetTaskId = taskId;
3540
3700
  if (!targetTaskId) {
3541
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();
@@ -3952,8 +4112,18 @@ function interpolateScribePrompt(basePrompt, vars) {
3952
4112
  // src/tools/generate-professional-pdf.ts
3953
4113
  async function generateProfessionalPdf(supabase, userId, projectId, reportType) {
3954
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
+ }
3955
4122
  const persona = await getScribePersona(supabase);
3956
- 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
+ }
3957
4127
  const projectName = project?.name || "Rigstate Project";
3958
4128
  const projectDescription = project?.description || "A cutting-edge software project built with modern architecture.";
3959
4129
  const projectType = project?.project_type || "Web Application";