Files
stack/packages/mosaic/framework/tools/prdy/prdy-validate.sh
Jason Woltje b38cfac760
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
feat: integrate framework files into monorepo under packages/mosaic/framework/
Moves all Mosaic framework runtime files from the separate bootstrap repo
into the monorepo as canonical source. The @mosaic/mosaic npm package now
ships the complete framework — bin scripts, runtime configs, tools, and
templates — enabling standalone installation via npm install.

Structure:
  packages/mosaic/framework/
  ├── bin/          28 CLI scripts (mosaic, mosaic-doctor, mosaic-sync-skills, etc.)
  ├── runtime/      Runtime adapters (claude, codex, opencode, pi, mcp)
  ├── tools/        Shell tooling (git, prdy, orchestrator, quality, etc.)
  ├── templates/    Agent and repo templates
  ├── defaults/     Default identity files (AGENTS.md, STANDARDS.md, SOUL.md, etc.)
  ├── install.sh    Legacy bash installer
  └── remote-install.sh  One-liner remote installer

Key files with Pi support and recent fixes:
- bin/mosaic: launch_pi() with skills-local loop
- bin/mosaic-doctor: --fix auto-wiring for all 4 harnesses
- bin/mosaic-sync-skills: Pi as 4th link target, symlink-aware find
- bin/mosaic-link-runtime-assets: Pi settings.json patching
- bin/mosaic-migrate-local-skills: Pi skill roots, symlink find
- runtime/pi/RUNTIME.md + mosaic-extension.ts

Package ships 251 framework files in the npm tarball (278KB compressed).
2026-04-01 21:19:21 -05:00

171 lines
5.1 KiB
Bash
Executable File

#!/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 grep -qiE "$pattern" <<< "$PRD_CONTENT"; 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 ! grep -qiE "^- \*\*${field}:\*\*|^- ${field}:" <<< "$PRD_CONTENT"; 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=$(grep -cE '^\- FR-[0-9]+:|^FR-[0-9]+:' <<< "$PRD_CONTENT" || true)
US_COUNT=$(grep -cE '^#{1,4} US-[0-9]+' <<< "$PRD_CONTENT" || true)
AC_COUNT=$(grep -cE '^\s*- \[ \]' <<< "$PRD_CONTENT" || true)
ASSUMPTION_COUNT=$(grep -c 'ASSUMPTION:' <<< "$PRD_CONTENT" || 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