From 617374c71a0d5d58b9eaf07099eeb76a9b25551a Mon Sep 17 00:00:00 2001 From: Roger Chappel Date: Wed, 29 Apr 2026 14:00:26 +1000 Subject: [PATCH] Add optional agent-qc validation hook --- README.md | 2 + docs/repo-customisation.md | 5 +- scripts/smoke-init.sh | 21 +++++ src/index.ts | 2 +- templates/README.md | 1 + templates/readme/README.template.md | 4 +- templates/repo-validate/validate.sh | 121 +++++++++++++++++++++++++++- 7 files changed, 151 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ae55c28..5357365 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,8 @@ StackForge is local-first and review-friendly: - `next-app`: Next.js application - `python-api`: Python API service +Generated repositories include `scripts/validate.sh` as the default local verification path. It runs the repo's normal local package checks when they exist and only runs `agent-qc ready` when `agent-qc` is installed, so `agent-qc` stays optional. + ## Local planning docs Use `--prd ` and `--tasks ` with `stackforge init` to copy local planning inputs into the generated repo as `docs/PRD.md` and `docs/TASKS.md`. diff --git a/docs/repo-customisation.md b/docs/repo-customisation.md index 3dcf867..612c04f 100644 --- a/docs/repo-customisation.md +++ b/docs/repo-customisation.md @@ -22,8 +22,9 @@ the repository in a state that is accurate enough for contributors and agents. remaining placeholder. 8. Run `rg 'template|starter|example|placeholder|TODO'` and remove stale template language that does not apply to the generated repository. -9. Make the first commit as a small identity-only change before adding project - code. +9. Run `bash scripts/validate.sh` after updating the generated repository so the default local checks pass before you open the first pull request. +10. Make the first commit as a small identity-only change before adding project + code. ## Recommended Order diff --git a/scripts/smoke-init.sh b/scripts/smoke-init.sh index 8904fa1..5f1bc19 100755 --- a/scripts/smoke-init.sh +++ b/scripts/smoke-init.sh @@ -74,10 +74,31 @@ test -f smoke-app/docs/PRD.md test -f smoke-app/docs/TASKS.md test -f smoke-app/.github/dependabot.yml test -f smoke-app/.github/workflows/ci.yml +test -f smoke-app/scripts/validate.sh grep -q "# smoke-app" smoke-app/README.md +grep -q "bash scripts/validate.sh" smoke-app/README.md +grep -q "agent-qc" smoke-app/README.md grep -q "Smoke Tester" smoke-app/package.json grep -q "This is a copied PRD" smoke-app/docs/PRD.md grep -q -- "- \[ \] Ship it" smoke-app/docs/TASKS.md +( + cd smoke-app + bash scripts/validate.sh > ../validate-without-agent-qc.log +) +grep -q "agent-qc not installed; skipping optional agent check" validate-without-agent-qc.log +grep -q "PASS: package script: test" validate-without-agent-qc.log +cat <<'EOF' > "$tmp_dir/agent-qc" +#!/usr/bin/env bash +set -euo pipefail +printf 'agent-qc %s\n' "$*" +EOF +chmod +x "$tmp_dir/agent-qc" +( + cd smoke-app + PATH="$tmp_dir:$PATH" bash scripts/validate.sh > ../validate-with-agent-qc.log +) +grep -q "PASS: optional agent-qc ready" validate-with-agent-qc.log +grep -q "agent-qc ready" validate-with-agent-qc.log node "$repo_root/dist/index.js" init oss-cli smoke-app --dry-run --prd local-prd.md --tasks local-tasks.md > dry-run-existing.json taskbrief_workspace="$tmp_dir/taskbrief-workspace" diff --git a/src/index.ts b/src/index.ts index 04a2337..ea0404d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -273,7 +273,7 @@ function buildVariables(projectName: string, overrides: string[]): Record/dev/null 2>&1; then + printf 'pnpm\n' + return + fi + + if [ -f "package-lock.json" ] && command -v npm >/dev/null 2>&1; then + printf 'npm\n' + return + fi + + if [ -f "yarn.lock" ] && command -v yarn >/dev/null 2>&1; then + printf 'yarn\n' + return + fi + + if [ -f "bun.lock" ] && command -v bun >/dev/null 2>&1; then + printf 'bun\n' + return + fi + + if command -v pnpm >/dev/null 2>&1; then + printf 'pnpm\n' + return + fi + + if command -v npm >/dev/null 2>&1; then + printf 'npm\n' + return + fi + + if command -v yarn >/dev/null 2>&1; then + printf 'yarn\n' + return + fi + + if command -v bun >/dev/null 2>&1; then + printf 'bun\n' + return + fi + + return 1 +} + +run_package_script() { + local package_manager="$1" + local script_name="$2" + + case "$package_manager" in + pnpm) + pnpm "$script_name" + ;; + npm) + npm run "$script_name" + ;; + yarn) + yarn "$script_name" + ;; + bun) + bun run "$script_name" + ;; + *) + return 1 + ;; + esac +} + printf 'Checking {{PROJECT_NAME}} required files...\n' check_file "README.md" @@ -38,11 +127,41 @@ check_file "AGENTS.md" check_file "CONTRIBUTING.md" check_file "SECURITY.md" check_file ".github/pull_request_template.md" +check_file "scripts/validate.sh" printf '\nChecking {{PROJECT_NAME}} required directories...\n' check_dir ".github" check_dir "docs" +check_dir "scripts" + +printf '\nRunning local project checks where present...\n' + +if [ -f "package.json" ]; then + if package_manager="$(choose_package_manager)"; then + note "using package manager: $package_manager" + + for script_name in check lint test build; do + if package_script_exists "$script_name"; then + run_check "package script: $script_name" run_package_script "$package_manager" "$script_name" + fi + done + else + fail "package.json exists but no supported package manager was found on PATH" + fi +else + note "no package.json detected; skipping JavaScript package scripts" +fi + +if command -v agent-qc >/dev/null 2>&1; then + run_check "optional agent-qc ready" agent-qc ready +else + note "agent-qc not installed; skipping optional agent check" +fi + +if [ "$ran_checks" -eq 0 ]; then + note "no runnable local checks were detected" +fi if [ "$failed" -ne 0 ]; then printf '\nValidation failed.\n' >&2