From c9bf578396b14f2de32abc57a40cfa16209f7690 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Mon, 23 Feb 2026 11:04:35 -0600 Subject: [PATCH] feat: add mosaic prdy command for PRD creation and validation Adds `mosaic prdy {init|update|validate}` subcommand: - init: launches yolo Claude session with PRD-focused system prompt - update: launches session to modify existing docs/PRD.md - validate: bash-only completeness checker (15 checks against PRD guide) Co-Authored-By: Claude Opus 4.6 --- bin/mosaic | 45 ++++++ tools/prdy/_lib.sh | 265 ++++++++++++++++++++++++++++++++++++ tools/prdy/prdy-init.sh | 87 ++++++++++++ tools/prdy/prdy-update.sh | 75 ++++++++++ tools/prdy/prdy-validate.sh | 170 +++++++++++++++++++++++ 5 files changed, 642 insertions(+) create mode 100644 tools/prdy/_lib.sh create mode 100644 tools/prdy/prdy-init.sh create mode 100644 tools/prdy/prdy-update.sh create mode 100644 tools/prdy/prdy-validate.sh diff --git a/bin/mosaic b/bin/mosaic index f905947..4afdedc 100755 --- a/bin/mosaic +++ b/bin/mosaic @@ -51,6 +51,12 @@ Management: release-upgrade [...] Upgrade installed Mosaic release project-upgrade [...] Clean up stale SOUL.md/CLAUDE.md in a project +PRD: + prdy PRD creation and validation + init Create docs/PRD.md via Claude session + update Update existing PRD via Claude session + validate Check PRD completeness (bash-only) + Coordinator (r0): coord Manual coordinator tools init Initialize a new mission @@ -478,6 +484,44 @@ _check_resumable_session() { fi } +run_prdy() { + check_mosaic_home + local subcmd="${1:-help}" + shift || true + + local tool_dir="$MOSAIC_HOME/tools/prdy" + + case "$subcmd" in + init) + exec bash "$tool_dir/prdy-init.sh" "$@" + ;; + update) + exec bash "$tool_dir/prdy-update.sh" "$@" + ;; + validate|check) + exec bash "$tool_dir/prdy-validate.sh" "$@" + ;; + help|*) + cat <] [--name ] Create docs/PRD.md via guided Claude session + update [--project ] Update existing docs/PRD.md via Claude session + validate [--project ] Check PRD completeness against Mosaic guide (bash-only) + +Examples: + mosaic prdy init --name "User Authentication" + mosaic prdy update + mosaic prdy validate + +Output location: docs/PRD.md (per Mosaic PRD guide) + +PRDY_USAGE + ;; + esac +} + run_bootstrap() { check_mosaic_home exec "$MOSAIC_HOME/bin/mosaic-bootstrap-repo" "$@" @@ -550,6 +594,7 @@ case "$command" in sync) run_sync "$@" ;; seq) run_seq "$@" ;; bootstrap) run_bootstrap "$@" ;; + prdy) run_prdy "$@" ;; coord) run_coord "$@" ;; upgrade) run_upgrade "$@" ;; release-upgrade) run_release_upgrade "$@" ;; diff --git a/tools/prdy/_lib.sh b/tools/prdy/_lib.sh new file mode 100644 index 0000000..cf24e21 --- /dev/null +++ b/tools/prdy/_lib.sh @@ -0,0 +1,265 @@ +#!/usr/bin/env bash +# +# _lib.sh — Shared functions for mosaic prdy tools +# +# Usage: source ~/.config/mosaic/tools/prdy/_lib.sh +# +# Provides PRD detection, section validation, and system prompt generation +# for interactive PRD creation/update sessions. + +MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}" +PRD_CANONICAL="docs/PRD.md" +PRD_JSON_ALT="docs/PRD.json" + +# ─── Color support ─────────────────────────────────────────────────────────── + +if [[ -t 1 ]]; then + C_GREEN='\033[0;32m' + C_RED='\033[0;31m' + C_YELLOW='\033[0;33m' + C_CYAN='\033[0;36m' + C_BOLD='\033[1m' + C_DIM='\033[2m' + C_RESET='\033[0m' +else + C_GREEN='' C_RED='' C_YELLOW='' C_CYAN='' C_BOLD='' C_DIM='' C_RESET='' +fi + +ok() { echo -e " ${C_GREEN}✓${C_RESET} $1"; } +warn() { echo -e " ${C_YELLOW}⚠${C_RESET} $1" >&2; } +fail() { echo -e " ${C_RED}✗${C_RESET} $1" >&2; } +info() { echo -e " ${C_CYAN}ℹ${C_RESET} $1"; } +step() { echo -e "\n${C_BOLD}$1${C_RESET}"; } + +# ─── Dependency checks ────────────────────────────────────────────────────── + +_require_cmd() { + local cmd="$1" + if ! command -v "$cmd" &>/dev/null; then + fail "'$cmd' is required but not installed" + return 1 + fi +} + +# ─── PRD detection ─────────────────────────────────────────────────────────── + +# Find the PRD file in a project directory. +# Returns the path (relative to project root) or empty string. +find_prd() { + local project="${1:-.}" + if [[ -f "$project/$PRD_CANONICAL" ]]; then + echo "$project/$PRD_CANONICAL" + elif [[ -f "$project/$PRD_JSON_ALT" ]]; then + echo "$project/$PRD_JSON_ALT" + fi +} + +# ─── Section validation manifest ───────────────────────────────────────────── + +# 10 required sections per ~/.config/mosaic/guides/PRD.md +# Each entry: "label|grep_pattern" (extended regex, case-insensitive via grep -iE) +PRDY_REQUIRED_SECTIONS=( + "Problem Statement|^#{2,3} .*(problem statement|objective)" + "Scope / Non-Goals|^#{2,3} .*(scope|non.goal|out of scope|in.scope)" + "User Stories / Requirements|^#{2,3} .*(user stor|stakeholder|user.*requirement)" + "Functional Requirements|^#{2,3} .*functional requirement" + "Non-Functional Requirements|^#{2,3} .*non.functional" + "Acceptance Criteria|^#{2,3} .*acceptance criteria|\*\*acceptance criteria\*\*|- \[ \]" + "Technical Considerations|^#{2,3} .*(technical consideration|constraint|dependenc)" + "Risks / Open Questions|^#{2,3} .*(risk|open question)" + "Success Metrics / Testing|^#{2,3} .*(success metric|test|verification)" + "Milestones / Delivery|^#{2,3} .*(milestone|delivery|scope version)" +) + +# ─── System prompt builder ─────────────────────────────────────────────────── + +# Build the specialized system prompt for PRD sessions. +# Usage: build_prdy_system_prompt +# mode: "init" or "update" +build_prdy_system_prompt() { + local mode="${1:-init}" + local guide_file="$MOSAIC_HOME/guides/PRD.md" + local template_file="$MOSAIC_HOME/templates/docs/PRD.md.template" + + cat <<'PROMPT_HEADER' +# Mosaic PRD Agent — Behavioral Contract + +You are a specialized PRD (Product Requirements Document) agent. Your sole purpose is to help the user create or update a comprehensive PRD document. + +## Mode Declaration (Hard Gate) + +PROMPT_HEADER + + if [[ "$mode" == "init" ]]; then + cat <<'INIT_MODE' +Your first response MUST start with: "Now initiating PRD Creation mode..." + +You are creating a NEW PRD. The output file is `docs/PRD.md`. + +## Workflow + +1. Read the project directory to understand context (README, package.json, existing docs, source structure) +2. Ask the user 3-5 clarifying questions with lettered options (A/B/C/D) so they can answer "1A, 2C, 3B" +3. Focus questions on: problem/goal, target users, scope boundaries, milestone structure, success criteria +4. After answers (or if the description is sufficiently complete), generate the full PRD +5. Write to `docs/PRD.md` (create `docs/` directory if it doesn't exist) +6. After writing, tell the user: "PRD written to docs/PRD.md. Run `mosaic prdy validate` to verify completeness." + +INIT_MODE + else + cat <<'UPDATE_MODE' +Your first response MUST start with: "Now initiating PRD Update mode..." + +You are UPDATING an existing PRD. Read `docs/PRD.md` (or `docs/PRD.json`) first. + +## Workflow + +1. Read the existing PRD file completely +2. Summarize the current state to the user (title, status, milestone count, open questions) +3. Ask the user what changes or additions are needed +4. Make targeted modifications — do NOT rewrite sections that don't need changes +5. Preserve existing user stories, FRs, and acceptance criteria unless explicitly asked to change them +6. After writing, tell the user: "PRD updated. Run `mosaic prdy validate` to verify completeness." + +UPDATE_MODE + fi + + cat <<'CONSTRAINTS' +## Hard Constraints + +MUST: +- Save output to `docs/PRD.md` only +- Include ALL 10 required sections from the Mosaic PRD guide (included below) +- Number functional requirements as FR-1, FR-2, ... in sequence +- Group user stories under named Milestones (e.g., "Milestone 0.0.4 — Foundation") +- Format user stories as US-NNN with Description and Acceptance Criteria checkboxes +- Mark all guessed decisions with `ASSUMPTION:` and rationale +- Include a Metadata block (Owner, Date, Status, Mission ID, Scope Version) +- Write for junior developers and AI agents — explicit, unambiguous, no jargon +- Include "Typecheck and lint pass" in acceptance criteria for code stories +- Include "Verify in browser using dev-browser skill" for UI stories + +MUST NOT: +- Write any implementation code +- Modify any files other than `docs/PRD.md` +- Skip clarifying questions when the feature description is ambiguous +- Begin implementation of any requirements +- Invent requirements silently — all guesses must be marked with ASSUMPTION + +CONSTRAINTS + + cat <<'STRUCTURE' +## Required PRD Structure (Gold Standard) + +Follow this exact structure. Every section is required. + +``` +# PRD: {Feature/Project Name} + +## Metadata +- **Owner:** {name} +- **Date:** {yyyy-mm-dd} +- **Status:** draft|planning|approved|in-progress|completed +- **Mission ID:** {kebab-case-id-yyyymmdd} +- **Scope Version:** {e.g., 0.0.4 → 0.0.5 → 0.1.0} + +## Introduction +Narrative overview: what this is, the context it lives in, what exists before this work. + +## Problem Statement +Numbered list of specific pain points being solved. + +## Goals +Bullet list of measurable objectives. + +## Milestones +Named milestones (e.g., "Milestone 0.0.4 — Design System Foundation"). +Each milestone has a one-paragraph description of its theme. + +## User Stories (grouped under milestones) + +### Milestone X.Y.Z — {Theme Name} + +#### US-001: {Title} +**Description:** As a {user}, I want {feature} so that {benefit}. + +**Acceptance Criteria:** +- [ ] Specific verifiable criterion +- [ ] Another criterion +- [ ] Typecheck and lint pass +- [ ] [UI stories only] Verify in browser using dev-browser skill + +--- + +## Functional Requirements +Numbered FR-1 through FR-N. Group by subsystem if applicable. +Each: "FR-N: {subject} must {behavior}" + +## Non-Goals (Out of Scope) +Numbered list of explicit exclusions with brief rationale. + +## Design Considerations +- Design references (mockups, existing design systems) +- Key visual/UX elements +- Component hierarchy + +## Technical Considerations + +### Dependencies +Libraries, packages, external services required. + +### Build & CI +Build pipeline, CI gates, quality checks. + +### Deployment +Target infrastructure, deployment method, image tagging strategy. + +### Risks +Technical risks that could affect delivery. + +## Success Metrics +Numbered list of measurable success conditions. + +## Open Questions +Numbered list. For unresolved items, add: +(ASSUMPTION: {best-guess decision} — {rationale}) +``` + +STRUCTURE + + cat <<'QUESTIONS' +## Clarifying Question Format + +When asking clarifying questions, use numbered questions with lettered options: + +``` +1. What is the primary goal? + A. Option one + B. Option two + C. Option three + D. Other: [please specify] + +2. What is the target scope? + A. Minimal viable + B. Full-featured + C. Backend only + D. Frontend only +``` + +Tell the user they can answer with shorthand like "1A, 2C, 3B" for quick iteration. + +QUESTIONS + + # Include the live PRD guide + if [[ -f "$guide_file" ]]; then + printf '\n## Mosaic PRD Guide (Authoritative Rules)\n\n' + cat "$guide_file" + fi + + # Include the template as reference + if [[ -f "$template_file" ]]; then + printf '\n## PRD Template Reference\n\n```markdown\n' + cat "$template_file" + printf '\n```\n' + fi +} diff --git a/tools/prdy/prdy-init.sh b/tools/prdy/prdy-init.sh new file mode 100644 index 0000000..e09455a --- /dev/null +++ b/tools/prdy/prdy-init.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +set -euo pipefail +# +# prdy-init.sh — Create a new PRD via guided Claude session +# +# Usage: +# prdy-init.sh [--project ] [--name ] +# +# Launches a dedicated Claude Code session in yolo mode with a specialized +# system prompt that guides the user through PRD creation. The output is +# written to docs/PRD.md. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/_lib.sh" + +# ─── Parse arguments ───────────────────────────────────────────────────────── + +PROJECT="." +NAME="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --project) PROJECT="$2"; shift 2 ;; + --name) NAME="$2"; shift 2 ;; + -h|--help) + cat <<'USAGE' +prdy-init.sh — Create a new PRD via guided Claude session + +Usage: prdy-init.sh [--project ] [--name ] + +Options: + --project Project directory (default: CWD) + --name Feature or project name (optional, Claude will ask if omitted) + +Launches Claude Code in yolo mode with a PRD-focused system prompt. +The agent will ask clarifying questions, then write docs/PRD.md. + +Examples: + mosaic prdy init + mosaic prdy init --name "User Authentication" + mosaic prdy init --project ~/src/my-app --name "Dashboard Redesign" +USAGE + exit 0 + ;; + *) echo "Unknown option: $1" >&2; exit 1 ;; + esac +done + +# Expand tilde if passed literally (e.g., --project ~/src/foo) +PROJECT="${PROJECT/#\~/$HOME}" + +# ─── Preflight checks ─────────────────────────────────────────────────────── + +_require_cmd "claude" + +# Check for existing PRD +EXISTING="$(find_prd "$PROJECT")" +if [[ -n "$EXISTING" ]]; then + fail "PRD already exists: $EXISTING" + echo -e " Use ${C_CYAN}mosaic prdy update${C_RESET} to modify the existing PRD." + exit 1 +fi + +# Ensure docs/ directory exists +mkdir -p "$PROJECT/docs" + +# ─── Build system prompt ───────────────────────────────────────────────────── + +step "Launching PRD creation session" + +SYSTEM_PROMPT="$(build_prdy_system_prompt "init")" + +# Build kickoff message +if [[ -n "$NAME" ]]; then + KICKOFF="Create docs/PRD.md for the feature: ${NAME}. Read the project context first, then ask clarifying questions before writing the PRD." +else + KICKOFF="Create docs/PRD.md for this project. Read the project context first, then ask the user what they want to build. Ask clarifying questions before writing the PRD." +fi + +# ─── Launch Claude ─────────────────────────────────────────────────────────── + +info "Output target: $PROJECT/$PRD_CANONICAL" +info "Mode: PRD Creation (yolo)" +echo "" + +cd "$PROJECT" +exec claude --dangerously-skip-permissions --append-system-prompt "$SYSTEM_PROMPT" "$KICKOFF" diff --git a/tools/prdy/prdy-update.sh b/tools/prdy/prdy-update.sh new file mode 100644 index 0000000..00dc1f8 --- /dev/null +++ b/tools/prdy/prdy-update.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -euo pipefail +# +# prdy-update.sh — Update an existing PRD via guided Claude session +# +# Usage: +# prdy-update.sh [--project ] +# +# Launches a dedicated Claude Code session in yolo mode with a specialized +# system prompt that reads the existing PRD and guides targeted modifications. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/_lib.sh" + +# ─── Parse arguments ───────────────────────────────────────────────────────── + +PROJECT="." + +while [[ $# -gt 0 ]]; do + case "$1" in + --project) PROJECT="$2"; shift 2 ;; + -h|--help) + cat <<'USAGE' +prdy-update.sh — Update an existing PRD via guided Claude session + +Usage: prdy-update.sh [--project ] + +Options: + --project Project directory (default: CWD) + +Launches Claude Code in yolo mode with a PRD-update system prompt. +The agent will read the existing docs/PRD.md, summarize its state, +and ask what changes are needed. + +Examples: + mosaic prdy update + mosaic prdy update --project ~/src/my-app +USAGE + exit 0 + ;; + *) echo "Unknown option: $1" >&2; exit 1 ;; + esac +done + +# Expand tilde if passed literally (e.g., --project ~/src/foo) +PROJECT="${PROJECT/#\~/$HOME}" + +# ─── Preflight checks ─────────────────────────────────────────────────────── + +_require_cmd "claude" + +# Require existing PRD +EXISTING="$(find_prd "$PROJECT")" +if [[ -z "$EXISTING" ]]; then + fail "No PRD found in $PROJECT/docs/" + echo -e " Run ${C_CYAN}mosaic prdy init${C_RESET} to create one first." + exit 1 +fi + +# ─── Build system prompt ───────────────────────────────────────────────────── + +step "Launching PRD update session" + +SYSTEM_PROMPT="$(build_prdy_system_prompt "update")" + +KICKOFF="Read the existing PRD at ${EXISTING}, summarize its current state, then ask what changes or additions are needed." + +# ─── Launch Claude ─────────────────────────────────────────────────────────── + +info "Updating: $EXISTING" +info "Mode: PRD Update (yolo)" +echo "" + +cd "$PROJECT" +exec claude --dangerously-skip-permissions --append-system-prompt "$SYSTEM_PROMPT" "$KICKOFF" diff --git a/tools/prdy/prdy-validate.sh b/tools/prdy/prdy-validate.sh new file mode 100644 index 0000000..b76d71b --- /dev/null +++ b/tools/prdy/prdy-validate.sh @@ -0,0 +1,170 @@ +#!/usr/bin/env bash +set -euo pipefail +# +# prdy-validate.sh — Check PRD completeness against Mosaic guide requirements +# +# Usage: +# prdy-validate.sh [--project ] +# +# Performs static analysis of docs/PRD.md to verify it meets the minimum +# content requirements defined in the Mosaic PRD guide. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/_lib.sh" + +# ─── Parse arguments ───────────────────────────────────────────────────────── + +PROJECT="." + +while [[ $# -gt 0 ]]; do + case "$1" in + --project) PROJECT="$2"; shift 2 ;; + -h|--help) + cat <<'USAGE' +prdy-validate.sh — Check PRD completeness against Mosaic guide + +Usage: prdy-validate.sh [--project ] + +Options: + --project Project directory (default: CWD) + +Checks: + - docs/PRD.md exists + - All 10 required sections present + - Metadata block (Owner, Date, Status) + - Functional requirements (FR-N) count + - User stories (US-NNN) count + - Acceptance criteria checklist count + - ASSUMPTION markers (informational) + +Exit codes: + 0 All required checks pass + 1 One or more required checks failed +USAGE + exit 0 + ;; + *) echo "Unknown option: $1" >&2; exit 1 ;; + esac +done + +# Expand tilde if passed literally (e.g., --project ~/src/foo) +PROJECT="${PROJECT/#\~/$HOME}" + +# ─── Find PRD ──────────────────────────────────────────────────────────────── + +step "Validating PRD" + +PRD_PATH="$(find_prd "$PROJECT")" +PASS=0 +FAIL_COUNT=0 +WARN_COUNT=0 + +check_pass() { + ok "$1" + PASS=$((PASS + 1)) +} + +check_fail() { + fail "$1" + FAIL_COUNT=$((FAIL_COUNT + 1)) +} + +check_warn() { + warn "$1" + WARN_COUNT=$((WARN_COUNT + 1)) +} + +if [[ -z "$PRD_PATH" ]]; then + check_fail "No PRD found. Expected $PROJECT/$PRD_CANONICAL or $PROJECT/$PRD_JSON_ALT" + echo "" + echo -e "${C_RED}PRD validation failed.${C_RESET} Run ${C_CYAN}mosaic prdy init${C_RESET} to create one." + exit 1 +fi + +check_pass "PRD found: $PRD_PATH" + +# JSON PRDs get a limited check +if [[ "$PRD_PATH" == *.json ]]; then + info "JSON PRD detected — section checks skipped (markdown-only)" + echo "" + echo -e "${C_GREEN}PRD file exists.${C_RESET} JSON validation is limited." + exit 0 +fi + +PRD_CONTENT="$(cat "$PRD_PATH")" + +# ─── Section checks ───────────────────────────────────────────────────────── + +for entry in "${PRDY_REQUIRED_SECTIONS[@]}"; do + label="${entry%%|*}" + pattern="${entry#*|}" + + if echo "$PRD_CONTENT" | grep -qiE "$pattern"; then + check_pass "$label section present" + else + check_fail "$label section MISSING" + fi +done + +# ─── Metadata checks ──────────────────────────────────────────────────────── + +META_PASS=true +for field in "Owner" "Date" "Status"; do + if ! echo "$PRD_CONTENT" | grep -qiE "^- \*\*${field}:\*\*|^- ${field}:"; then + META_PASS=false + fi +done + +if $META_PASS; then + check_pass "Metadata block present (Owner, Date, Status)" +else + check_fail "Metadata block incomplete (need Owner, Date, Status)" +fi + +# ─── Content counts ───────────────────────────────────────────────────────── + +FR_COUNT=$(echo "$PRD_CONTENT" | grep -cE '^\- FR-[0-9]+:|^FR-[0-9]+:' || true) +US_COUNT=$(echo "$PRD_CONTENT" | grep -cE '^#{1,4} US-[0-9]+' || true) +AC_COUNT=$(echo "$PRD_CONTENT" | grep -cE '^\s*- \[ \]' || true) +ASSUMPTION_COUNT=$(echo "$PRD_CONTENT" | grep -c 'ASSUMPTION:' || true) + +if [[ "$FR_COUNT" -gt 0 ]]; then + check_pass "Functional requirements: $FR_COUNT FR items" +else + check_warn "No FR-N items found (expected numbered functional requirements)" +fi + +if [[ "$US_COUNT" -gt 0 ]]; then + check_pass "User stories: $US_COUNT US items" +else + check_warn "No US-NNN items found (expected user stories)" +fi + +if [[ "$AC_COUNT" -gt 0 ]]; then + check_pass "Acceptance criteria: $AC_COUNT checklist items" +else + check_warn "No acceptance criteria checkboxes found" +fi + +if [[ "$ASSUMPTION_COUNT" -gt 0 ]]; then + info "ASSUMPTION markers: $ASSUMPTION_COUNT (informational)" +fi + +# ─── Summary ───────────────────────────────────────────────────────────────── + +TOTAL=$((PASS + FAIL_COUNT)) +echo "" + +if [[ "$FAIL_COUNT" -eq 0 ]]; then + echo -e "${C_GREEN}PRD validation passed.${C_RESET} ${PASS}/${TOTAL} checks OK." + if [[ "$WARN_COUNT" -gt 0 ]]; then + echo -e "${C_YELLOW}${WARN_COUNT} warning(s)${C_RESET} — review recommended." + fi + exit 0 +else + echo -e "${C_RED}PRD validation failed.${C_RESET} ${FAIL_COUNT}/${TOTAL} checks failed." + if [[ "$WARN_COUNT" -gt 0 ]]; then + echo -e "${C_YELLOW}${WARN_COUNT} warning(s)${C_RESET}" + fi + exit 1 +fi