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>
171 lines
5.1 KiB
Bash
171 lines
5.1 KiB
Bash
#!/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
|