From 992a4ad5e1bd6c20a647b00487bb78a7caf1e6fb Mon Sep 17 00:00:00 2001 From: Madhura68 Date: Sun, 26 Apr 2026 23:00:29 +0200 Subject: [PATCH] feat(ST-702): vendor Scrum4Me schema via submodule + sync script - Add vendor/scrum4me as a git submodule (read-only source of truth) - scripts/sync-schema.sh copies schema.prisma into prisma/, stripping the upstream prisma-erd-generator block we don't ship - Track the synced schema in git so a fresh clone works after 'git submodule update --init && npm install' - postinstall runs 'prisma generate' so @prisma/client is ready Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitmodules | 3 + package.json | 1 + prisma/schema.prisma | 240 +++++++++++++++++++++++++++++++++++++++++ scripts/sync-schema.sh | 28 +++++ vendor/scrum4me | 1 + 5 files changed, 273 insertions(+) create mode 100644 .gitmodules create mode 100644 prisma/schema.prisma create mode 100755 scripts/sync-schema.sh create mode 160000 vendor/scrum4me diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5747df4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vendor/scrum4me"] + path = vendor/scrum4me + url = https://github.com/madhura68/Scrum4Me.git diff --git a/package.json b/package.json index a739501..0a9aaa8 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "build": "tsc", "start": "node dist/index.js", "prisma:generate": "prisma generate", + "postinstall": "prisma generate || true", "typecheck": "tsc --noEmit", "sync-schema": "bash scripts/sync-schema.sh" }, diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..5401976 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,240 @@ +generator client { + provider = "prisma-client-js" +} + + +datasource db { + provider = "postgresql" +} + +enum Role { + PRODUCT_OWNER + SCRUM_MASTER + DEVELOPER +} + +enum StoryStatus { + OPEN + IN_SPRINT + DONE +} + +enum TaskStatus { + TO_DO + IN_PROGRESS + REVIEW + DONE +} + +enum LogType { + IMPLEMENTATION_PLAN + TEST_RESULT + COMMIT +} + +enum TestStatus { + PASSED + FAILED +} + +enum SprintStatus { + ACTIVE + COMPLETED +} + +model User { + id String @id @default(cuid()) + username String @unique + email String? @unique + password_hash String + is_demo Boolean @default(false) + bio String? @db.VarChar(160) + bio_detail String? @db.VarChar(2000) + avatar_data Bytes? + created_at DateTime @default(now()) + updated_at DateTime @updatedAt + roles UserRole[] + api_tokens ApiToken[] + products Product[] + todos Todo[] + product_members ProductMember[] + assigned_stories Story[] @relation("StoryAssignee") + + @@map("users") +} + +model UserRole { + id String @id @default(cuid()) + user User @relation(fields: [user_id], references: [id], onDelete: Cascade) + user_id String + role Role + + @@unique([user_id, role]) + @@map("user_roles") +} + +model ApiToken { + id String @id @default(cuid()) + user User @relation(fields: [user_id], references: [id], onDelete: Cascade) + user_id String + token_hash String @unique + label String? + created_at DateTime @default(now()) + revoked_at DateTime? + + @@index([token_hash]) + @@map("api_tokens") +} + +model Product { + id String @id @default(cuid()) + user User @relation(fields: [user_id], references: [id], onDelete: Cascade) + user_id String + name String + code String? @db.VarChar(30) + description String? + repo_url String? + definition_of_done String + archived Boolean @default(false) + created_at DateTime @default(now()) + updated_at DateTime @updatedAt + pbis Pbi[] + sprints Sprint[] + stories Story[] + todos Todo[] + members ProductMember[] + + @@unique([user_id, name]) + @@unique([user_id, code]) + @@index([user_id, archived]) + @@map("products") +} + +model Pbi { + id String @id @default(cuid()) + product Product @relation(fields: [product_id], references: [id], onDelete: Cascade) + product_id String + code String? @db.VarChar(30) + title String + description String? + priority Int + sort_order Float + created_at DateTime @default(now()) + updated_at DateTime @updatedAt + stories Story[] + + @@unique([product_id, code]) + @@index([product_id, priority, sort_order]) + @@map("pbis") +} + +model Story { + id String @id @default(cuid()) + pbi Pbi @relation(fields: [pbi_id], references: [id], onDelete: Cascade) + pbi_id String + product Product @relation(fields: [product_id], references: [id]) + product_id String + sprint Sprint? @relation(fields: [sprint_id], references: [id]) + sprint_id String? + assignee User? @relation("StoryAssignee", fields: [assignee_id], references: [id], onDelete: SetNull) + assignee_id String? + code String? @db.VarChar(30) + title String + description String? + acceptance_criteria String? + priority Int + sort_order Float + status StoryStatus @default(OPEN) + created_at DateTime @default(now()) + updated_at DateTime @updatedAt + logs StoryLog[] + tasks Task[] + + @@unique([product_id, code]) + @@index([pbi_id, priority, sort_order]) + @@index([sprint_id, sort_order]) + @@index([product_id, status]) + @@index([sprint_id, assignee_id]) + @@map("stories") +} + +model StoryLog { + id String @id @default(cuid()) + story Story @relation(fields: [story_id], references: [id], onDelete: Cascade) + story_id String + type LogType + content String + status TestStatus? + commit_hash String? + commit_message String? + created_at DateTime @default(now()) + + @@index([story_id, created_at]) + @@map("story_logs") +} + +model Sprint { + id String @id @default(cuid()) + product Product @relation(fields: [product_id], references: [id], onDelete: Cascade) + product_id String + sprint_goal String + status SprintStatus @default(ACTIVE) + created_at DateTime @default(now()) + completed_at DateTime? + stories Story[] + tasks Task[] + + @@index([product_id, status]) + @@map("sprints") +} + +model Task { + id String @id @default(cuid()) + story Story @relation(fields: [story_id], references: [id], onDelete: Cascade) + story_id String + sprint Sprint? @relation(fields: [sprint_id], references: [id]) + sprint_id String? + title String + description String? + implementation_plan String? + priority Int + sort_order Float + status TaskStatus @default(TO_DO) + created_at DateTime @default(now()) + updated_at DateTime @updatedAt + + @@index([story_id, priority, sort_order]) + @@index([sprint_id, status]) + @@map("tasks") +} + +model ProductMember { + id String @id @default(cuid()) + product Product @relation(fields: [product_id], references: [id], onDelete: Cascade) + product_id String + user User @relation(fields: [user_id], references: [id], onDelete: Cascade) + user_id String + created_at DateTime @default(now()) + + @@unique([product_id, user_id]) + @@index([user_id]) + @@map("product_members") +} + +model Todo { + id String @id @default(cuid()) + user User @relation(fields: [user_id], references: [id], onDelete: Cascade) + user_id String + product Product? @relation(fields: [product_id], references: [id], onDelete: SetNull) + product_id String? + title String + description String? @db.VarChar(2000) + done Boolean @default(false) + archived Boolean @default(false) + created_at DateTime @default(now()) + updated_at DateTime @updatedAt + + @@index([user_id, done, archived]) + @@index([user_id, product_id]) + @@map("todos") +} diff --git a/scripts/sync-schema.sh b/scripts/sync-schema.sh new file mode 100755 index 0000000..30a28e6 --- /dev/null +++ b/scripts/sync-schema.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Sync prisma/schema.prisma from the vendored Scrum4Me submodule. +# +# Strips the `generator erd` block — that generator depends on +# prisma-erd-generator which we don't ship in this MCP package. The +# schema is otherwise byte-identical with upstream. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +SRC="$ROOT/vendor/scrum4me/prisma/schema.prisma" +DST="$ROOT/prisma/schema.prisma" + +if [ ! -f "$SRC" ]; then + echo "error: $SRC not found — run 'git submodule update --init' first" >&2 + exit 1 +fi + +mkdir -p "$ROOT/prisma" + +# Strip the `generator erd { ... }` block (multi-line, balanced braces). +awk ' + /^generator erd \{/ { skip = 1; next } + skip && /^\}/ { skip = 0; next } + skip { next } + { print } +' "$SRC" > "$DST" + +echo "synced schema from $(cd "$ROOT/vendor/scrum4me" && git rev-parse --short HEAD) to prisma/schema.prisma" diff --git a/vendor/scrum4me b/vendor/scrum4me new file mode 160000 index 0000000..a8adac1 --- /dev/null +++ b/vendor/scrum4me @@ -0,0 +1 @@ +Subproject commit a8adac127fc09486d0b3e63544b5ddf8791f3c04