scrum4me-mcp/src/access.ts
Madhura68 fdf3dc4471 feat: M12 idea-job support — version 0.6.0
Adds the 4 new MCP-tools for the Scrum4Me M12 Idea-entity flow + extends
3 existing tools to handle the new ClaudeJobKind discriminator.

New tools:
- get_idea_context: full idea + product + open questions + recent logs
- update_idea_grill_md: save grill-result + status → GRILLED + IdeaLog
- update_idea_plan_md: server-side yaml parser validates frontmatter;
  ok → PLAN_READY, fail → PLAN_FAILED + line-info errors
- log_idea_decision: DECISION/NOTE entries on the timeline

Extended tools:
- ask_user_question: xor schema (story_id | idea_id); idea-questions are
  user-private with productId derived from idea.product_id
- wait_for_job: returns \`kind\` discriminator; IDEA_* payloads include
  idea + prompt_text (from src/prompts/idea/) and skip worktree creation
- update_job_status: failed on IDEA_* auto-transitions idea-status to
  GRILL_FAILED / PLAN_FAILED + IdeaLog{JOB_EVENT}; auto-PR + worktree-
  cleanup skipped for idea-jobs

Other changes:
- Health version now read dynamically from package.json (was hardcoded
  '0.1.0' which caused deploy-sync confusion)
- Schema synced to Scrum4Me M12 (Idea + IdeaLog + enums + ClaudeJob/
  Question nullable-FKs + check-constraints + pg_notify-trigger update)
- New @scrum4me-mcp/lib/idea-plan-parser duplicates Scrum4Me's parser
  (drift detected by vendor schema-watchdog)
- Embedded grill+make-plan prompts copied to src/prompts/idea/
- New userOwnsIdea access helper

Tests: 153/153 green; tsc + build clean.

Migration: requires Scrum4Me M12 migration (20260504172747_add_ideas_and_grill_jobs)
applied on the target DB. See vendor/scrum4me/docs/runbooks/mcp-integration.md
for the updated batch-loop with kind-switch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:12:36 +02:00

40 lines
1.3 KiB
TypeScript

import { prisma } from './prisma.js'
export async function userCanAccessProduct(productId: string, userId: string): Promise<boolean> {
const hit = await prisma.product.findFirst({
where: {
id: productId,
OR: [{ user_id: userId }, { members: { some: { user_id: userId } } }],
},
select: { id: true },
})
return Boolean(hit)
}
export async function userCanAccessTask(taskId: string, userId: string): Promise<boolean> {
const task = await prisma.task.findUnique({
where: { id: taskId },
select: { story: { select: { product_id: true } } },
})
if (!task) return false
return userCanAccessProduct(task.story.product_id, userId)
}
export async function userCanAccessStory(storyId: string, userId: string): Promise<boolean> {
const story = await prisma.story.findUnique({
where: { id: storyId },
select: { product_id: true },
})
if (!story) return false
return userCanAccessProduct(story.product_id, userId)
}
// M12: idee is strikt user_id-only (geen productAccessFilter — Q8).
// Idea-questions, idea-jobs, en idea-md-mutaties scopen op de eigenaar.
export async function userOwnsIdea(ideaId: string, userId: string): Promise<boolean> {
const idea = await prisma.idea.findUnique({
where: { id: ideaId },
select: { user_id: true },
})
return idea !== null && idea.user_id === userId
}