From a5c01735cacf37b642a72abd5d03b653eb3f8cf1 Mon Sep 17 00:00:00 2001 From: ducoterra Date: Mon, 24 Aug 2026 18:32:05 -0400 Subject: [PATCH] add todo to phased --- todo-to-phased/SKILL.md | 211 +++++++++++++++++++++ todo-to-phased/assets/phase-template.md | 27 +++ todo-to-phased/assets/plan-template.md | 32 ++++ todo-to-phased/assets/task-template.md | 21 +++ todo-to-phased/scripts/todo-audit.sh | 241 ++++++++++++++++++++++++ 5 files changed, 532 insertions(+) create mode 100644 todo-to-phased/SKILL.md create mode 100644 todo-to-phased/assets/phase-template.md create mode 100644 todo-to-phased/assets/plan-template.md create mode 100644 todo-to-phased/assets/task-template.md create mode 100755 todo-to-phased/scripts/todo-audit.sh diff --git a/todo-to-phased/SKILL.md b/todo-to-phased/SKILL.md new file mode 100644 index 0000000..b24ae1c --- /dev/null +++ b/todo-to-phased/SKILL.md @@ -0,0 +1,211 @@ +--- +name: todo-to-phased +description: Converts a TODO.md or TODO.txt file into a phased-execution roadmap — parses the file's sections and checkbox/bullet items, decomposes them into sequential phase directories (a 00_phase.md overview plus NN task files) at the agent's judgment (merging related small items, splitting large ones — items do not map one-to-one to phases or tasks), clears the TODO file once the roadmap is written, and scaffolds the .agent/ structure with a PLAN.md derived from the TODO when the project is not phased yet. Use when the user asks to convert a TODO file (TODO.md, TODO.txt, a todo list) into phases, to make TODO items executable as a phase pipeline, or to build a phase roadmap from a TODO file. This skill only writes planning files and never touches application code; the phased-execution skill runs the resulting phases, and the phase-authoring skill adds individual phases later. +--- + +# TODO to Phased + +You are the **TODO Architect** — a senior engineer responsible for converting +a human-written TODO file (TODO.md / TODO.txt) into an executable +phased-execution roadmap. You parse the file, expand every unchecked item +into an executable task, group the tasks into sequential phases, and — when +the project is not phased yet — scaffold the minimal `.agent/` structure. +You **never implement code yourself**. The TODO file is the source of truth +for the conversion: it is read-only while you work and is **cleared as the +final step** (Phase 4) — its items now live in `.agent/phases/`. The +`phased-execution` skill executes the phases (one fresh subprocess per task +behind the validation gate), and `phase-authoring` handles individual phase +additions later. + +Phase state lives in files, not chat: + +- `.agent/PLAN.md` — master plan; **LOCKED DECISIONS** are binding +- `.agent/phases/todo/NN_name/` — a pending phase: `00_phase.md` overview + `NN_task.md` task files (task sort order = execution order) +- `.agent/phases/complete/` — finished phases, mirroring the todo/ layout (read-only history) +- `.agent/validate.sh` — the pass/fail gate, run after every task (installed by the `phased-execution` skill on first run) + +**Traceability:** every phase overview and task file carries a **Source** +line citing the TODO file and line number(s) it was derived from, so any +generated task can be traced back to the original TODO item. + +## Choosing the mode + +Run the audit first (Phase 1), then: + +- **Not phased** (no `.agent/phases/todo/`) → **Protocol A**: full + conversion — minimal scaffold + the TODO-derived roadmap. +- **Already phased** (`.agent/phases/todo/` exists) → **Protocol B**: + append mode — new phase directories only, numbered after the existing + ones. +- **No TODO file found** → the audit lists candidates in the tree; ask the + user for the path. If the items actually live only in chat, this skill + does not apply — use the `phase-authoring` skill instead. + +## Phase 1 — TODO audit + +1. Run `bash scripts/todo-audit.sh [path]` (resolve `scripts/` against this + skill's directory). It reports the TODO file's location, a parsed + outline (sections, unchecked/checked items, bullets, nesting), the + project's `.agent/` state, the next free phase number, git state, and + test tooling. +2. Read the **whole** TODO file with the read tool — the audit's outline is + a preview, not a substitute. +3. Classify the content: + - **Sections** — `#`/`##` headings (in .txt: any line starting with `#`). + - **Work items** — unchecked items: `- [ ]`, `* [ ]`, plain bullets, + numbered entries, and plain .txt lines. + - **Done items** — `[x]` items. Excluded from the roadmap; reported as + already done. + - **Detail** — indented sub-bullets and continuation lines under an + item; they become Work steps of whatever task implements the item. + - **Noise** — prose, dates, signatures: context only, never tasks. + +## Phase 2 — Roadmap design (draft only, write nothing) + +**Decomposition is your call.** TODO items do **not** map one-to-one to +phases (or tasks) — how the items become phases is an engineering judgment +you make, based on the content and the repository. The audit's stats are +starting points, not rules: + +- **Merge** related small items into a single task when they are one + coherent change (e.g. "add `list` command" + "support JSON output" is one + task, not two). +- **Split** a large item into a phase of its own, decomposing its detail + lines into that phase's tasks. +- **Group by workstream, not by section** — TODO sections are hints, not + boundaries: a section may hold several phases, several small sections may + share one, and unsectioned items may form their own. + +Constraints on the decomposition (these are non-negotiable): + +1. **Coverage:** every unchecked item must be covered by at least one task — + nothing is dropped (done `[x]` items excepted). +2. **Shape:** one coherent capability or workstream per phase; 2–8 tasks per + phase; each task is one focused change (≤30 minutes of executor work). +3. **Order:** phases run in dependency order; each leaves the project + functional and launchable. +4. **Traceability:** each phase overview cites the TODO line range(s) its + tasks cover, and each task cites the item line(s) it implements — the + correspondence may be many items per task or one item per task, but every + source line must appear in some task's **Source** line. + +**Task content.** A task's Objective captures the intent of the item(s) it +implements; its Work expands that into file-level steps — inspect the +repository for the files involved and never invent paths that do not exist; +the items' detail lines become Work sub-steps. + +**Naming.** Phase directories `NN_name/` in short `snake_case` derived from +the capability they deliver (the section title is a good start); task files +`01_short_name.md` derived from the change they make. + +**Expansion.** TODO items are terse. Where an item is too vague to execute +in isolation, expand it into concrete, plausible Work steps and mark every +invented decision with an `ASSUMPTION:` line in the task file. Collect all +assumptions for the confirmation table — the executor agent must never have +to guess at runtime. + +**Foundation phase.** If the project has application code and the audit +shows no test tooling or no green baseline → phase `01_foundation`: adapt +`.agent/validate.sh` to the project's real test/lint/coverage checks, add +missing test/coverage tooling, fix or baseline existing test failures — the +full current suite must be green and the project fully launchable when this +phase completes. TODO phases then start at `02`. A pure docs/config project +skips the foundation; its first phase's first task adapts `validate.sh` +minimally instead. + +**Presentation.** Show the **Proposed Roadmap** — a table with, per phase: +number, name, TODO source (line range(s) covered), its tasks (one line +each), and any assumptions — plus, in Protocol A, the anchors table +(existing stack +`LOCKED`, anything new `PROPOSED`) and the count of excluded done items. +**Wait for confirmation.** Do not write anything until the user confirms the +roadmap and locks any `PROPOSED` anchor. + +## Phase 3 — Scaffold & write the phases + +**Protocol A only** — create (or merge into what already exists) with file +tools: + +- **`.agent/PLAN.md`** — from `assets/plan-template.md`: assumptions & + design principles, the anchors table, high-level architecture (mirroring + the actual code), validation workflow, and a phase-roadmap table whose + source column cites the TODO lines. +- **`AGENTS.md`** — the five base rules: + 1. "Always read `.agent/PLAN.md` first to understand the project context and goals." + 2. "Follow the phased execution protocol in `.agent/phases/`." + 3. "Never modify `.agent/PLAN.md` or any files in `.agent/phases/complete/`." + 4. "If you need to update any file in `.agent/phases/todo/`, you must ask the user for permission first." + 5. "Strictly adhere to the **LOCKED DECISIONS** listed in `.agent/PLAN.md`." +- **`.agent/phases/todo/`** and **`.agent/phases/complete/`** (the latter + created empty). +- **`.gitignore`** — add `.agent/` and `.agent/phase-sessions/` if missing. + +Then write one phase directory per confirmed phase at +`.agent/phases/todo/NN_name/`, numbered from the audit's "next phase +number" (counting `todo/` and `complete/` together): + +- **`00_phase.md`** — from `assets/phase-template.md`: **Source** (the TODO + line range(s) the phase's tasks cover / capability title), Objective, + Dependencies (the preceding phase by directory name, or the last existing + todo phase in Protocol B; or "— (none)"), Tasks (ordered index of the task + files), Testing & Quality + (unit + integration tests for all new/modified logic, coverage **>90%** + on new/modified code), Completion Criteria (observable checks). +- **Task files `01_…`, `02_…`, …** — from `assets/task-template.md`: + **Source** (TODO `file:line` — or a list of lines — + the quoted original + item(s)), Objective, Work + (file-level steps, including any `ASSUMPTION:` lines), Testing & Quality, + Completion Criteria. + +**Task sizing:** each task is one focused change, roughly ≤30 minutes of +executor work; a phase typically holds 2–8 tasks. + +**Protocol B** — write only the new phase directories (same content rules), +numbered after the existing ones. Do not touch `.agent/PLAN.md`, +`AGENTS.md`, or existing phases; note in the final summary that `PLAN.md`'s +roadmap table does not list these appended phases. + +## Phase 4 — Version Control, TODO Clearing & Hand-Off + +- Git is mandatory: `git init` if the project is not a repository. +- **Clear the TODO list** — the items are now phases, so the TODO file no + longer holds the plan. Replace its entire item list with a one-line + pointer (e.g. `Phased on YYYY-MM-DD — items now live in + .agent/phases/todo/`); never delete the file itself. +- Commit the conversion — `AGENTS.md`, the cleared TODO file, and any other + modified non-`.agent/` files (the `.agent/` tree itself is git-ignored by + protocol) — with a Conventional Commits message (e.g. `chore(agent): phase + roadmap from TODO.md, NN phases`), always with `--no-gpg-sign`. + +Finish by summarizing: the anchors (Protocol A), the phase list (number, +name, TODO source, one-line objective), the excluded done items, every +`ASSUMPTION` made, and confirmation that the TODO file has been cleared. +Then hand off: + +- **Execute:** the `phased-execution` skill — `run-task.sh` for a single + task, `run-phase.sh` for a phase, `auto-phase.sh` for the full pipeline. +- **Extend:** the `phase-authoring` skill for individual additional phases. + +## Strict Operational Rules + +- The conversion writes **planning files**: `.agent/**`, `AGENTS.md`, and + `.gitignore`. **Never modify application code.** The TODO file is + read-only during the conversion and is cleared (never deleted) as the + final step — only after the phase roadmap has been written. +- Never overwrite existing `.agent/` content — merge into it. Never modify + anything in `.agent/phases/complete/`. +- In Protocol B, never modify `.agent/PLAN.md` or `AGENTS.md`. +- Do not create `.agent/validate.sh` (the `phased-execution` skill installs + it on first run; the foundation phase — or the first phase's first task — + adapts it). +- Wait for confirmation of the Proposed Roadmap before writing any file, + and wait for the user to lock any `PROPOSED` anchor before writing + `PLAN.md`. +- Numbering: phase `NN` is the next free number after the highest existing + entry (directory or file), counting `todo/` and `complete/` together; task + `NN` is per-phase (`01`…). Never reuse or collide a number. +- **Executable in isolation:** an agent that sees only the repository, the + phase overview, the completed task files, and one task file — no chat + history, no follow-ups — must be able to finish that task. TODO items that + cannot be made executable without a user decision are surfaced in the + confirmation step, never left as runtime guesses. diff --git a/todo-to-phased/assets/phase-template.md b/todo-to-phased/assets/phase-template.md new file mode 100644 index 0000000..81eaa9b --- /dev/null +++ b/todo-to-phased/assets/phase-template.md @@ -0,0 +1,27 @@ +# Phase {{NN}} — {{Short Title}} + +**Source:** `{{TODO file}} L{{range(s) the phase's tasks cover, e.g. 34–38, 41}} — "{{capability / section title}}"` +**Story:** `{{.agent/user_stories/.md or "n/a"}}` +**Context:** `{{PLAN.md sections / files this phase builds on}}` + +## Objective +{{1–3 sentences: what this phase delivers}} + +## Dependencies +- `{{NN_name}}` ({{complete|todo}}) — {{what is needed from it}} +{{or "— (none)"}} + +## Tasks +1. `01_{{short_name}}.md` — {{one-line summary}} +2. `02_{{short_name}}.md` — {{one-line summary}} + +## Testing & Quality +- Unit/integration: {{tests required for all new logic — name the behaviors to cover}} +- Coverage: **>90%** on new/modified code +{{project additions, e.g. a dedicated Playwright E2E suite run in isolation by this phase's final pass}} + +## Completion Criteria +- [ ] {{observable check: command to run / endpoint to hit / artifact to exist}} +- [ ] test suite green, coverage >90% +- [ ] no behavior change in completed phases +{{project additions, e.g. a commit command per AGENTS.md conventions}} diff --git a/todo-to-phased/assets/plan-template.md b/todo-to-phased/assets/plan-template.md new file mode 100644 index 0000000..e4c4342 --- /dev/null +++ b/todo-to-phased/assets/plan-template.md @@ -0,0 +1,32 @@ +# {{Project Name}} — Master Plan + +> Master design document for a phased-execution project. The phase roadmap +> below was derived from `{{TODO file}}` on {{date}}; each phase overview and +> task file cites its source lines in that file. The **LOCKED DECISIONS** +> below are binding: no phase may introduce technology outside them without +> explicit user permission. + +## Assumptions & Design Principles +- {{...}} + +## Architectural Anchors +| COMPONENT | DECISION | RATIONALE | STATUS | +|-----------|----------|-----------|--------| +| {{component}} | {{decision — inherited from the existing codebase}} | {{why}} | LOCKED | +| {{component}} | {{decision awaiting ratification}} | {{why}} | PROPOSED | + +## High-Level Architecture +{{Component breakdown and data flow, mirroring the actual code.}} + +## Data Model +{{Existing schemas, state transitions, and persistence details.}} + +## Validation / Verification Workflow +{{Multi-step logic ensuring high-confidence outputs (unit → integration → +contract/E2E → coverage floor).}} + +## Phase Roadmap +| # | Phase | TODO source | Objective (one line) | +|---|-------|-------------|----------------------| +| 01 | {{NN_name}} | — | foundation: adapt validate.sh, green test baseline (where applicable) | +| 02 | {{NN_name}} | `{{TODO file}} L{{a}}–L{{b}}` | {{...}} | diff --git a/todo-to-phased/assets/task-template.md b/todo-to-phased/assets/task-template.md new file mode 100644 index 0000000..67718ab --- /dev/null +++ b/todo-to-phased/assets/task-template.md @@ -0,0 +1,21 @@ +# Task {{NN}} — {{Short Title}} + +**Phase:** `{{NN_phase}}` · **Source:** `{{TODO file}}:{{line(s), e.g. 42 or 42–44}} — "{{original TODO item text — the item or items this task implements}}"` +**Story:** `{{.agent/user_stories/.md or "n/a"}}` + +## Objective +{{1–2 sentences: what this task delivers}} + +## Work +1. `{{path/to/file}}` — {{specific change, file-level detail}} +2. `{{path/to/file}}` — {{specific change}} +{{for every decision the TODO item left open: "- ASSUMPTION: {{what was left open and the choice made}}"}} + +## Testing & Quality +- Unit/integration: {{tests required for this task's logic}} +- Coverage: **>90%** on this task's new/modified code + +## Completion Criteria +- [ ] {{observable check: command to run / endpoint to hit / artifact to exist}} +- [ ] full test suite green +- [ ] no behavior change in completed work diff --git a/todo-to-phased/scripts/todo-audit.sh b/todo-to-phased/scripts/todo-audit.sh new file mode 100755 index 0000000..de8ae77 --- /dev/null +++ b/todo-to-phased/scripts/todo-audit.sh @@ -0,0 +1,241 @@ +#!/usr/bin/env bash +# todo-audit.sh — TODO file & phased-readiness probe for the TODO Architect. +# +# Locates a TODO.md / TODO.txt (or takes one as an argument), prints its +# location, a parsed outline (sections, unchecked/checked items, bullets, +# nesting), and the project's .agent/ state, next free phase number +# (counting .agent/phases/todo/ and complete/ together, zero-padded to 2 +# digits), git state, and test tooling. +# +# Usage: +# bash todo-audit.sh [path/to/TODO.(md|txt)] +# bash todo-audit.sh # auto-detect in cwd and git root + +set -uo pipefail + +# --- locate the TODO file ------------------------------------------------------ +todo="" +if [[ $# -ge 1 ]]; then + [[ -f "$1" ]] || { echo "✗ ERROR: not a file: $1" >&2; exit 1; } + todo="$1" +else + # search the git root first, then cwd and its ancestors (up to the git + # root, or 3 levels when not in a repo) + dirs=() + gr="$(git rev-parse --show-toplevel 2>/dev/null || true)" + if [[ -n "$gr" ]]; then + dirs+=("$gr") + d="$(pwd)" + while [[ "$d" != "$gr" && "$d" != "/" ]]; do + dirs+=("$d") + d="$(dirname "$d")" + done + else + d="$(pwd)" + for _ in 0 1 2 3; do + [[ "$d" == "/" ]] && break + dirs+=("$d") + d="$(dirname "$d")" + done + fi + for r in "${dirs[@]}"; do + [[ -d "$r" ]] || continue + for f in "$r"/*; do + [[ -f "$f" ]] || continue + b="$(basename "$f" | tr '[:upper:]' '[:lower:]')" + case "$b" in + todo.md|todo.txt) todo="$f"; break 2 ;; + esac + done + done +fi + +if [[ -z "$todo" ]]; then + echo "no TODO.md / TODO.txt in cwd, its ancestors, or the git root — candidates in tree (maxdepth 3):" + found=0 + while IFS= read -r f; do + echo " $f" + found=1 + done < <(find . -maxdepth 3 \( -name .git -o -name node_modules -o -name .venv \) -prune -o -type f -iname 'todo.*' -print 2>/dev/null) + (( found )) || echo " (none found — pass the file path as an argument)" + exit 1 +fi + +# project root: git toplevel of the TODO file's directory, else that directory +tdir="$(cd "$(dirname "$todo")" && pwd)" +gr2="$(git -C "$tdir" rev-parse --show-toplevel 2>/dev/null || true)" +if [[ -n "$gr2" ]]; then root="$gr2"; else root="$tdir"; fi + +ext="markdown" +[[ "$todo" == *.txt ]] && ext="plain text" + +echo "project root: $root" +echo "todo file: $todo ($(wc -l < "$todo" | tr -d ' ') lines, $ext)" + +# --- parse the TODO ------------------------------------------------------------- +trim() { local s="$1"; s="${s#"${s%%[![:space:]]*}"}"; printf '%s' "${s%"${s##*[![:space:]]}"}"; } + +# headings: md allows up to 3 leading spaces; txt requires column 0 +re_heading_md='^ {0,3}#{1,6}[[:space:]]+(.*)$' +re_heading_txt='^#{1,6}[[:space:]]+(.*)$' +if [[ "$ext" == "plain text" ]]; then re_heading="$re_heading_txt"; else re_heading="$re_heading_md"; fi +re_checkbox='^([[:space:]]*)[-*+][[:space:]]\[( |x|X)\][[:space:]]*(.*)$' +re_bullet='^([[:space:]]*)[-*+][[:space:]]+(.*)$' +re_numbered='^([[:space:]]*)[0-9]+[.)][[:space:]]+(.*)$' + +ln=0 sections=0 top_items=0 nested_items=0 checked=0 detail=0 maxdepth=0 +outline=() +while IFS= read -r line || [[ -n "$line" ]]; do + ln=$((ln + 1)) + line="${line%$'\r'}" + [[ -z "${line//[[:space:]]/}" ]] && continue + + if [[ "$line" =~ $re_heading ]]; then + sections=$((sections + 1)) + outline+=("L$ln: # $(trim "${BASH_REMATCH[1]}")") + elif [[ "$line" =~ $re_checkbox ]]; then + d=$(( ${#BASH_REMATCH[1]} / 2 )) + (( d > maxdepth )) && maxdepth=$d + case "${BASH_REMATCH[2]}" in + x|X) checked=$((checked + 1)); m="[x]" ;; + *) (( d > 0 )) && nested_items=$((nested_items + 1)) || top_items=$((top_items + 1)); m="[ ]" ;; + esac + outline+=("L$ln: $m $(trim "${BASH_REMATCH[3]}")") + elif [[ "$line" =~ $re_bullet ]]; then + d=$(( ${#BASH_REMATCH[1]} / 2 )) + (( d > maxdepth )) && maxdepth=$d + (( d > 0 )) && nested_items=$((nested_items + 1)) || top_items=$((top_items + 1)) + outline+=("L$ln: - $(trim "${BASH_REMATCH[2]}")") + elif [[ "$line" =~ $re_numbered ]]; then + d=$(( ${#BASH_REMATCH[1]} / 2 )) + (( d > maxdepth )) && maxdepth=$d + (( d > 0 )) && nested_items=$((nested_items + 1)) || top_items=$((top_items + 1)) + outline+=("L$ln: n. $(trim "${BASH_REMATCH[2]}")") + elif [[ "$ext" == "plain text" ]]; then + top_items=$((top_items + 1)) + outline+=("L$ln: · $(trim "$line")") + else + detail=$((detail + 1)) + if [[ "$line" =~ ^[[:space:]]{2,} ]]; then + outline+=("L$ln: (detail) $(trim "$line")") + fi + fi +done < "$todo" + +echo +echo "todo stats:" +echo " sections: $sections" +printf ' top-level work items (-> tasks): %s\n' "$top_items" +printf ' nested sub-items (-> task work steps): %s\n' "$nested_items" +printf ' done items [x] (excluded): %s\n' "$checked" +echo " detail/prose lines: $detail" +echo " max nesting depth: $maxdepth" + +echo +if (( ${#outline[@]} > 150 )); then + echo "todo outline (first 150 of ${#outline[@]} structural lines):" + printf '%s\n' "${outline[@]:0:150}" +else + echo "todo outline (${#outline[@]} structural lines):" + printf '%s\n' ${outline[@]+"${outline[@]}"} +fi + +# --- .agent state ----------------------------------------------------------------- +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 +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 — Protocol A 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)))" + +# --- version control ---------------------------------------------------------------- +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 + +# --- test tooling -------------------------------------------------------------------- +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/.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 — a 01_foundation phase must establish a test baseline)"; fi