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 <noreply@anthropic.com>
This commit is contained in:
45
bin/mosaic
45
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 <subcommand> 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 <subcommand> 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 <<PRDY_USAGE
|
||||
mosaic prdy — PRD creation and validation tools
|
||||
|
||||
Commands:
|
||||
init [--project <path>] [--name <feature>] Create docs/PRD.md via guided Claude session
|
||||
update [--project <path>] Update existing docs/PRD.md via Claude session
|
||||
validate [--project <path>] 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 "$@" ;;
|
||||
|
||||
265
tools/prdy/_lib.sh
Normal file
265
tools/prdy/_lib.sh
Normal file
@@ -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>
|
||||
# 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
|
||||
}
|
||||
87
tools/prdy/prdy-init.sh
Normal file
87
tools/prdy/prdy-init.sh
Normal file
@@ -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 <path>] [--name <feature>]
|
||||
#
|
||||
# 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 <path>] [--name <feature>]
|
||||
|
||||
Options:
|
||||
--project <path> Project directory (default: CWD)
|
||||
--name <feature> 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"
|
||||
75
tools/prdy/prdy-update.sh
Normal file
75
tools/prdy/prdy-update.sh
Normal file
@@ -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 <path>]
|
||||
#
|
||||
# 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 <path>]
|
||||
|
||||
Options:
|
||||
--project <path> 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"
|
||||
170
tools/prdy/prdy-validate.sh
Normal file
170
tools/prdy/prdy-validate.sh
Normal file
@@ -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 <path>]
|
||||
#
|
||||
# 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 <path>]
|
||||
|
||||
Options:
|
||||
--project <path> 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
|
||||
Reference in New Issue
Block a user