@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 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();
@@ -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: project, error: projectError } = await supabase.from("projects").select("id").eq("id", input.projectId).eq("owner_id", userId).single();
1279
- 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) {
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: project, error: projectError } = await supabase.from("projects").select("id, name").eq("id", projectId).eq("owner_id", userId).single();
1565
- 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) {
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, error: chunksError } = await supabase.rpc("get_roadmap_chunks_secure", {
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 summaryParts = [];
1990
- summaryParts.push(`Project Type: ${project.project_type?.toUpperCase() || "UNKNOWN"}`);
1991
- if (stackDef) {
1992
- summaryParts.push("\n=== ACTIVE MISSION PARAMETERS ===");
1993
- if (activeTask) {
1994
- summaryParts.push(`\u26A0\uFE0F CURRENT OBJECTIVE: T-${activeTask.step_number}: ${activeTask.title}`);
1995
- summaryParts.push(` Role: ${activeTask.role || "Developer"}`);
1996
- if (activeTask.instruction_set) {
1997
- summaryParts.push(` Instructions: ${activeTask.instruction_set.substring(0, 200)}...`);
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: summaryParts.join("\n") || "No project context available."
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: project, error: projectError } = await supabase.from("projects").select("id").eq("id", projectId).eq("owner_id", userId).single();
2130
- 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) {
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: project, error: projectError } = await supabase.from("projects").select("id, name").eq("id", projectId).eq("owner_id", userId).single();
2324
- 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) {
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.name}" with importance 9/10`
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: project, error: projectError } = await supabase.from("projects").select("id, name").eq("id", projectId).eq("owner_id", userId).single();
2383
- 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) {
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.name}". Status: Draft (awaiting review)`
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: project, error: projectError } = await supabase.from("projects").select("id, name").eq("id", projectId).eq("owner_id", userId).single();
2436
- 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) {
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: project, error: projectError } = await supabase.from("projects").select("id, name").eq("id", projectId).eq("owner_id", userId).single();
2568
- 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) {
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: project, error: projectError } = await supabase.from("projects").select("id, functional_spec").eq("id", projectId).eq("owner_id", userId).single();
2728
- 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) {
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";