Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <path>` and `--tasks <path>` with `stackforge init` to copy local planning inputs into the generated repo as `docs/PRD.md` and `docs/TASKS.md`.
Expand Down
5 changes: 3 additions & 2 deletions docs/repo-customisation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
21 changes: 21 additions & 0 deletions scripts/smoke-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ function buildVariables(projectName: string, overrides: string[]): Record<string
GITHUB_REPO: packageSlug,
INSTALL_COMMAND: 'pnpm install',
USAGE_COMMAND: 'pnpm dev',
PRIMARY_VERIFICATION_COMMAND: 'pnpm test',
PRIMARY_VERIFICATION_COMMAND: 'bash scripts/validate.sh',
YEAR: String(new Date().getFullYear()),
LICENSE: 'MIT',
VULNERABILITY_REPORTING_INSTRUCTIONS: 'Ask maintainers for the private security reporting path before sharing details.',
Expand Down
1 change: 1 addition & 0 deletions templates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ For a practical setup flow, see
- `license/`: license text templates.
- `npm-package/`: optional JavaScript npm package starter files.
- `readme/`: generated repository README template.
- `repo-validate/`: local validation script template, including an optional `agent-qc` readiness hook.
- `release/`: changelog, roadmap, release process, and release checklist
templates.
- `security/`: security policy and vulnerability reporting templates.
4 changes: 3 additions & 1 deletion templates/readme/README.template.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ repository.

## Verify

Run the primary local check before opening a pull request:
Run the local validation script before opening a pull request:

```sh
{{PRIMARY_VERIFICATION_COMMAND}}
```

`scripts/validate.sh` runs the repository's standard local checks when they are defined and will also run `agent-qc ready` when `agent-qc` is installed. Missing `agent-qc` is treated as a skip, not a failure.

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution expectations. Changes
Expand Down
121 changes: 120 additions & 1 deletion templates/repo-validate/validate.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$repo_root"

failed=0
ran_checks=0

pass() {
printf 'PASS: %s\n' "$1"
Expand All @@ -15,6 +16,10 @@ fail() {
failed=1
}

note() {
printf 'NOTE: %s\n' "$1"
}

check_file() {
if [ -f "$1" ]; then
pass "required file exists: $1"
Expand All @@ -31,18 +36,132 @@ check_dir() {
fi
}

run_check() {
local label="$1"
shift
ran_checks=$((ran_checks + 1))

if "$@"; then
pass "$label"
else
fail "$label"
fi
}

package_script_exists() {
local script_name="$1"
node -e "const fs=require('node:fs'); const pkg=JSON.parse(fs.readFileSync('package.json','utf8')); process.exit(pkg.scripts && pkg.scripts[process.argv[1]] ? 0 : 1)" "$script_name"
}

choose_package_manager() {
if [ -f "pnpm-lock.yaml" ] && command -v pnpm >/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"
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
Expand Down