- A phase is now a directory: 00_phase.md (overview + task index) plus small, quick NN_task.md task files; complete/ mirrors todo/ - Task is the unit of execution: run-task.sh (one unit), run-phase.sh (phase to completion incl. 00_phase.md final pass), auto-phase.sh (all units in order); validate.sh gate runs after every task - PHASE_COMMIT commits at phase boundaries (00_phase.md / legacy file moves) - Legacy flat todo/NN_name.md files still execute as a single unit; new migrate-phases-to-tasks.sh converts them to the directory layout - phase-status.sh reports per-task state and the next unit - New task-template.md; phase templates now carry a Tasks index
171 lines
5.1 KiB
Bash
Executable File
171 lines
5.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# project-audit.sh — stack & phased-readiness probe for the Conversion Architect.
|
|
#
|
|
# Prints: project root, version-control state, detected manifests, domain
|
|
# hints, existing .agent/ artifacts, test tooling, and the next free phase
|
|
# number NN (counting .agent/phases/todo/ and complete/ together,
|
|
# zero-padded to 2 digits).
|
|
#
|
|
# Usage: bash project-audit.sh # from anywhere in the project tree
|
|
|
|
set -uo pipefail
|
|
|
|
root="$(pwd)"
|
|
if git rev-parse --show-toplevel >/dev/null 2>&1; then
|
|
root="$(git rev-parse --show-toplevel)"
|
|
fi
|
|
|
|
echo "project root: $root"
|
|
|
|
echo
|
|
echo "version control:"
|
|
if [[ -d "$root/.git" ]]; then
|
|
branch="$(git -C "$root" branch --show-current 2>/dev/null || echo unknown)"
|
|
dirty="$(git -C "$root" status --porcelain 2>/dev/null | wc -l | tr -d ' ')"
|
|
echo " git repository (branch: ${branch:-detached}, uncommitted changes: ${dirty})"
|
|
else
|
|
echo " (not a git repository — the conversion must git init)"
|
|
fi
|
|
|
|
echo
|
|
echo "manifests:"
|
|
found=0
|
|
for m in pyproject.toml uv.lock package.json pnpm-lock.yaml go.mod Cargo.toml requirements.txt setup.py setup.cfg pom.xml build.gradle; do
|
|
if [[ -f "$root/$m" ]]; then
|
|
echo " $m"
|
|
found=1
|
|
fi
|
|
done
|
|
for c in compose.yaml docker-compose.yml Containerfile Dockerfile; do
|
|
if [[ -f "$root/$c" ]]; then
|
|
echo " $c"
|
|
found=1
|
|
fi
|
|
done
|
|
if (( ! found )); then echo " (none detected)"; fi
|
|
|
|
echo
|
|
echo "domain hints:"
|
|
hint=0
|
|
if [[ -f "$root/pyproject.toml" ]]; then
|
|
if grep -q '^\[project\.scripts\]' "$root/pyproject.toml"; then
|
|
echo " cli ([project.scripts] entry points)"
|
|
hint=1
|
|
fi
|
|
if grep -qiE 'fastapi|flask|django|starlette|aiohttp' "$root/pyproject.toml"; then
|
|
echo " web (web framework dependency)"
|
|
hint=1
|
|
fi
|
|
fi
|
|
if [[ -f "$root/py.typed" ]]; then
|
|
echo " library (py.typed marker)"
|
|
hint=1
|
|
fi
|
|
if [[ -d "$root/templates" ]]; then
|
|
echo " web (templates/ directory)"
|
|
hint=1
|
|
fi
|
|
if [[ -f "$root/package.json" ]] && grep -q '"bin"' "$root/package.json"; then
|
|
echo " cli (package.json \"bin\")"
|
|
hint=1
|
|
fi
|
|
if (( ! hint )); then echo " (none — classify from the manifests above)"; fi
|
|
|
|
echo
|
|
echo ".agent state:"
|
|
a=0
|
|
if [[ -f "$root/.agent/PLAN.md" ]]; then
|
|
echo " .agent/PLAN.md: present"
|
|
a=1
|
|
fi
|
|
if [[ -f "$root/AGENTS.md" ]]; then
|
|
echo " AGENTS.md: present"
|
|
a=1
|
|
fi
|
|
if [[ -f "$root/.agent/validate.sh" ]]; then
|
|
echo " .agent/validate.sh: present"
|
|
a=1
|
|
fi
|
|
for d in workflows features user_stories; do
|
|
if [[ -d "$root/.agent/$d" ]]; then
|
|
n="$(find "$root/.agent/$d" -maxdepth 1 -name '*.md' 2>/dev/null | wc -l | tr -d ' ')"
|
|
echo " .agent/$d/: present ($n file(s))"
|
|
a=1
|
|
fi
|
|
done
|
|
todo_dir="$root/.agent/phases/todo"
|
|
complete_dir="$root/.agent/phases/complete"
|
|
list_phases() {
|
|
local dir="$1" entry n t any=0
|
|
for entry in "$dir"/*; do
|
|
if [[ ! -e "$entry" ]]; then continue; fi
|
|
n="$(basename "$entry")"
|
|
if [[ "$n" =~ ^[0-9] ]]; then
|
|
if [[ -d "$entry" ]]; then
|
|
t="$(ls -1 "$entry" 2>/dev/null | grep -cE '\.md$' || true)"
|
|
echo " $n/ ($t file(s): 00_phase.md + tasks)"
|
|
else
|
|
echo " $n (legacy single-file phase)"
|
|
fi
|
|
any=1
|
|
fi
|
|
done
|
|
if (( ! any )); then echo " (empty)"; fi
|
|
}
|
|
if [[ -d "$todo_dir" ]]; then
|
|
echo " .agent/phases/todo:"
|
|
list_phases "$todo_dir"
|
|
a=1
|
|
fi
|
|
if [[ -d "$complete_dir" ]]; then
|
|
echo " .agent/phases/complete:"
|
|
list_phases "$complete_dir"
|
|
a=1
|
|
fi
|
|
if (( ! a )); then echo " (no .agent/ artifacts — full conversion needed)"; fi
|
|
|
|
max=0
|
|
for d in "$todo_dir" "$complete_dir"; do
|
|
[[ -d "$d" ]] || continue
|
|
for entry in "$d"/*; do
|
|
if [[ ! -e "$entry" ]]; then continue; fi
|
|
n="$(basename "$entry" .md)"
|
|
if [[ "$n" =~ ^([0-9]+) ]]; then
|
|
n=$((10#${BASH_REMATCH[1]}))
|
|
if (( n > max )); then max=$n; fi
|
|
fi
|
|
done
|
|
done
|
|
echo " next phase number: $(printf '%02d' $((max + 1)))"
|
|
|
|
echo
|
|
echo "test tooling:"
|
|
t=0
|
|
if [[ -f "$root/pytest.ini" ]] || { [[ -f "$root/pyproject.toml" ]] && grep -q '\[tool\.pytest' "$root/pyproject.toml"; }; then
|
|
echo " pytest configured"
|
|
t=1
|
|
fi
|
|
if [[ -f "$root/conftest.py" ]] || [[ -d "$root/tests" ]]; then
|
|
echo " tests/ or conftest.py present"
|
|
t=1
|
|
fi
|
|
n_py="$(find "$root" \( -name 'test_*.py' -o -name '*_test.py' \) -not -path '*/.git/*' -not -path '*/node_modules/*' -not -path '*/.venv/*' -not -path '*/venv/*' 2>/dev/null | wc -l | tr -d ' ')"
|
|
if (( n_py > 0 )); then echo " $n_py python test file(s)"; t=1; fi
|
|
if [[ -f "$root/package.json" ]] && grep -q '"test"' "$root/package.json"; then
|
|
echo " npm test script"
|
|
t=1
|
|
fi
|
|
if [[ -f "$root/ruff.toml" ]] || { [[ -f "$root/pyproject.toml" ]] && grep -q '\[tool\.ruff\]' "$root/pyproject.toml"; }; then
|
|
echo " ruff configured"
|
|
t=1
|
|
fi
|
|
if [[ -f "$root/pyrightconfig.json" ]] || { [[ -f "$root/pyproject.toml" ]] && grep -q '\[tool\.pyright\]' "$root/pyproject.toml"; }; then
|
|
echo " pyright configured"
|
|
t=1
|
|
fi
|
|
if [[ -f "$root/.coveragerc" ]] || { [[ -f "$root/pyproject.toml" ]] && grep -qE '\[tool\.(coverage|pytest-cov)\]' "$root/pyproject.toml"; }; then
|
|
echo " coverage configured"
|
|
t=1
|
|
fi
|
|
if (( ! t )); then echo " (none detected — the foundation phase must establish a test baseline)"; fi
|