diff --git a/AGENTS.md b/AGENTS.md index b434c2b..0be70be 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,73 +1,118 @@ -# PyAutoAgent — Agent Guidance +# PyAutoBrain — Agent Guidance This file is for AI coding agents (Claude Code, Codex, Cursor, etc.) and humans -discovering this repository. It is the canonical description of the -Build / Pulse / Agent split; PyAutoBuild and PyAutoPulse point back here. +discovering this repository. It is the canonical description of PyAutoBrain — the +**reasoning layer** of the PyAuto organism — and of the Brain / Heart / Build +boundary; PyAutoBuild and PyAutoHeart point back here. + +> Renamed from **PyAutoAgent** to **PyAutoBrain**. The CLI is now `pyauto-brain`; +> `pyauto-agent` remains as a back-compat shim. See "Renamed from PyAutoAgent". ## What this repo is -PyAutoAgent is the **orchestration brain** of the PyAuto release ecosystem. It -hosts named *agents* — each a documented role plus a deterministic entrypoint -script — that read tasks (from PyAutoPrompt and the developer), dispatch work, -and drive the release/health machinery at the right points. +PyAutoBrain is the **reasoning layer** of the PyAuto ecosystem as it grows into a +software organism. It figures out *how* work should be done and coordinates the +organs that do it. It hosts specialist **reasoning agents** — each a documented +role plus a deterministic entrypoint script — that read intent (from PyAutoMind +and the developer), reason about it, and drive the health/execution machinery at +the right points. + +PyAutoBrain owns **no state, no health checks, and no execution mechanics**. It +only *reasons* and *delegates*: it asks PyAutoHeart whether the organism is +healthy, decides whether and how to proceed, and tells PyAutoBuild to execute +when it should. + +## The organism + +The PyAuto ecosystem is structured as a software organism. Each repo is an +organ with one job: + +| Organ | Repo | Role | +|-------|------|------| +| **Mind** | PyAutoMind | Decides *what* should be done — intent, goals, priorities, future work. | +| **Brain** | **PyAutoBrain** (this repo) | Figures out *how* — reasoning, planning, decomposition, orchestration, agent coordination, decision-making. | +| **Hands** | PyAutoBuild | Builds and releases the software — packaging, tagging, notebooks, PyPI. (May later be renamed PyAutoHands.) | +| **Heart** | PyAutoHeart | Determines whether the organism is healthy — the authoritative readiness verdict. | +| **Memory** | PyAutoMemory | Long-term scientific, software and project knowledge. | -PyAutoAgent owns **no state, no checks, and no release mechanics**. It only -*decides* and *delegates*: it asks PyAutoPulse whether things are healthy, and -tells PyAutoBuild to execute when they are. +The clean boundary, in one line each: -## The boundary (one description, mirrored in all three repos) +- **Mind** → decides what should be done. +- **Brain** → figures out how. +- **Hands** → build and release the software. +- **Heart** → determines whether the organism is healthy. -- **PyAutoPulse — the health authority.** All health/readiness logic lives here: +PyAutoBrain does not build or release software (that belongs to the Hands / +PyAutoBuild) and does not measure health (that belongs to the Heart / +PyAutoHeart). The Brain determines *how* work should be done; the Hands build +and release it; the Heart says whether it is safe to. + +## The boundary (one description, mirrored across the organs) + +- **PyAutoHeart — the health authority.** All health/readiness logic lives here: version drift, install-path, URL hygiene, CI/worktree/timing monitoring. - `pyauto-pulse readiness` is the **authoritative** green/yellow/red verdict — - the single "is it safe to release?" gate. Pulse is an observer: it reads and + `pyauto-heart readiness` is the **authoritative** green/yellow/red verdict — + the single "is it safe to release?" gate. Heart is an observer: it reads and emits verdicts; it never writes into other repos and never triggers Build. -- **PyAutoBuild — the executor.** Packaging, tagging, notebook generation, and - PyPI publication via `release.yml`. Build runs **no** readiness checks of its - own and never re-derives a gate decision; it just executes. -- **PyAutoAgent — the brain.** Hosts the agents that connect the two. It owns no - checks and no release steps; it gates on Pulse and delegates execution to - Build. +- **PyAutoBuild — the executor (Hands).** Packaging, tagging, notebook + generation, and PyPI publication via `release.yml`. Build runs **no** readiness + checks of its own and never re-derives a gate decision; it just executes. +- **PyAutoBrain — the reasoning layer.** Hosts the specialist agents that connect + the organs. It owns no checks and no execution steps; it reasons over Heart's + verdict and delegates execution to Build. ## The call chain (always this order) ``` -Agent → Pulse (gate) → Build (execute) +Brain → Heart (gate) → Build (execute) ``` -The agent asks `pyauto-pulse readiness --json`; only on a **green** verdict does -it trigger Build's release. Pulse never triggers Build; Build never re-derives a -gate decision the agent already made. +The Brain asks `pyauto-heart readiness --json`, reasons over the result, and only +on a **green** verdict triggers Build's release. Heart never triggers Build; +Build never re-derives a decision the Brain already made. -## Agents +## Specialist reasoning agents Each agent is a directory under `agents//` with: -- `AGENTS.md` — what the agent is responsible for and how to run it. +- `AGENTS.md` — what the agent reasons about and how to run it. - a deterministic entrypoint script (`*.sh` / `*.py`) — the part CI and humans invoke identically, so behaviour isn't re-derived from prose each time. Current agents: -- **`agents/release/`** — gates on `pyauto-pulse readiness`, and on green +- **`agents/release/`** — reasons over `pyauto-heart readiness`, and on green triggers the PyAutoBuild release executor (`autobuild pre_build` → `release.yml`). -- **`agents/health/`** — drives the PyAutoPulse monitoring/readiness surface. +- **`agents/health/`** — reasons over the PyAutoHeart monitoring/readiness surface. -More agents are expected over time (e.g. several health agents each consuming a -different part of Pulse); add them as new `agents//` directories. +More specialist agents are expected over time (e.g. a Feature agent that reasons +over PyAutoMind tasks, a Build agent that coordinates execution, several health +agents each reasoning over a different part of Heart); add them as new +`agents//` directories. ## Running ```bash -bin/pyauto-agent help # list agents -bin/pyauto-agent release # readiness gate, then release on green -bin/pyauto-agent health # one health tick + readiness verdict +bin/pyauto-brain help # list agents +bin/pyauto-brain release # reason about readiness, then release on green +bin/pyauto-brain health # one health tick + readiness verdict ``` -Like the other PyAuto repos, PyAutoAgent runs from its checkout (no pip install); -it resolves the sibling `pyauto-pulse` and `autobuild` binaries from PATH or the +Like the other PyAuto repos, PyAutoBrain runs from its checkout (no pip install); +it resolves the sibling `pyauto-heart` and `autobuild` binaries from PATH or the `~/Code/PyAutoLabs/` checkouts. +## Renamed from PyAutoAgent + +This repository was previously **PyAutoAgent**. The rename to **PyAutoBrain** +reflects the organism model above: it is the reasoning layer, not just a host of +"agents". Backwards compatibility is preserved where practical: + +- `bin/pyauto-brain` is the canonical CLI; `bin/pyauto-agent` is a thin shim that + forwards to it. +- The sibling health authority moved from `pyauto-pulse`/PyAutoPulse to + `pyauto-heart`/PyAutoHeart (PyAutoHeart keeps a `pyauto-pulse` shim). + ## Never rewrite history NEVER perform these operations on any repo with a remote: diff --git a/CLAUDE.md b/CLAUDE.md index 4aff9b4..0ff49a6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,9 @@ -# PyAutoAgent — Claude guidance +# PyAutoBrain — Claude guidance Read [`AGENTS.md`](AGENTS.md) in this directory. It is the shared source of truth -for the Build / Pulse / Agent split and for how the agents in this repo work. +for PyAutoBrain — the reasoning layer of the PyAuto organism — for the +Brain / Heart / Build boundary, and for how the specialist reasoning agents in +this repo work. + +> This repository was renamed from **PyAutoAgent** to **PyAutoBrain**. The CLI is +> now `pyauto-brain` (with `pyauto-agent` kept as a back-compat shim). diff --git a/README.md b/README.md index f5e6f9e..3267640 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,60 @@ -# PyAutoAgent +# PyAutoBrain -The orchestration brain of the PyAuto release ecosystem. PyAutoAgent hosts named -**agents** that gate on [PyAutoPulse](https://github.com/PyAutoLabs/PyAutoPulse) +The **reasoning layer** of the PyAuto organism. PyAutoBrain figures out *how* +work should be done: it hosts specialist **reasoning agents** that reason over +the health verdict from [PyAutoHeart](https://github.com/PyAutoLabs/PyAutoHeart) and delegate execution to [PyAutoBuild](https://github.com/PyAutoLabs/PyAutoBuild). -It owns no state, no checks, and no release mechanics — it only decides and -delegates. +It owns no state, no health checks, and no release mechanics — it only reasons +and delegates. + +> Renamed from **PyAutoAgent**. The canonical CLI is now `pyauto-brain`; +> `pyauto-agent` remains as a back-compat shim. + +## The organism + +The PyAuto ecosystem is a software organism; each repo is an organ with one job: + +| Organ | Repo | Role | +|-------|------|------| +| **Mind** | PyAutoMind | Decides *what* should be done — intent, goals, priorities. | +| **Brain** | **PyAutoBrain** | Figures out *how* — reasoning, planning, orchestration. | +| **Hands** | PyAutoBuild | Builds and releases the software — packaging, tagging, notebooks, PyPI. | +| **Heart** | PyAutoHeart | Determines whether the organism is healthy. | +| **Memory** | PyAutoMemory | Long-term scientific / software / project knowledge. | + +In one line each: **Mind** decides what, **Brain** figures out how, **Hands** +build and release the software, **Heart** says whether it is healthy. ## The split | Repo | Role | |------|------| -| **PyAutoPulse** | Health authority. `pyauto-pulse readiness` is the authoritative green/yellow/red release gate. Observer-only. | -| **PyAutoBuild** | Executor. Packaging, tagging, notebooks, PyPI via `release.yml`. No checks. | -| **PyAutoAgent** | Brain. Gates on Pulse, delegates execution to Build. | +| **PyAutoHeart** | Health authority. `pyauto-heart readiness` is the authoritative green/yellow/red release gate. Observer-only. | +| **PyAutoBuild** | Executor (Hands). Packaging, tagging, notebooks, PyPI via `release.yml`. No checks. | +| **PyAutoBrain** | Reasoning layer. Reasons over Heart's verdict, delegates execution to Build. | Call chain, always in this order: ``` -Agent → Pulse (gate) → Build (execute) +Brain → Heart (gate) → Build (execute) ``` -## Agents +## Specialist reasoning agents -- **`agents/release/`** — `pyauto-pulse readiness` gate → on green, run the - PyAutoBuild release executor. -- **`agents/health/`** — drive the PyAutoPulse monitoring / readiness surface. +- **`agents/release/`** — reasons over `pyauto-heart readiness` → on green, runs + the PyAutoBuild release executor. +- **`agents/health/`** — reasons over the PyAutoHeart monitoring / readiness surface. ## Usage ```bash -bin/pyauto-agent help # list agents -bin/pyauto-agent release # gate, then release on green -bin/pyauto-agent health # one health tick + readiness verdict +bin/pyauto-brain help # list agents +bin/pyauto-brain release # reason about readiness, then release on green +bin/pyauto-brain health # one health tick + readiness verdict ``` -PyAutoAgent runs from its checkout (no pip install), resolving the sibling -`pyauto-pulse` and `autobuild` binaries from PATH or `~/Code/PyAutoLabs/`. +PyAutoBrain runs from its checkout (no pip install), resolving the sibling +`pyauto-heart` and `autobuild` binaries from PATH or `~/Code/PyAutoLabs/`. See [`AGENTS.md`](AGENTS.md) for the full description. diff --git a/agents/_common.sh b/agents/_common.sh index 57e3a36..e9f0c70 100755 --- a/agents/_common.sh +++ b/agents/_common.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash -# agents/_common.sh — shared helpers for PyAutoAgent agents. +# agents/_common.sh — shared helpers for PyAutoBrain's specialist agents. # -# Resolves the sibling PyAuto CLIs (pyauto-pulse, autobuild) the same way the -# autobuild shim resolves pyauto-pulse: prefer PATH, fall back to the sibling +# Resolves the sibling PyAuto organ CLIs (pyauto-heart, autobuild) the same way +# the autobuild shim resolves them: prefer PATH, fall back to the sibling # checkout under ~/Code/PyAutoLabs/. Nothing here is pip-installed. PYAUTO_ROOT="${PYAUTO_ROOT:-$HOME/Code/PyAutoLabs}" @@ -19,23 +19,26 @@ _resolve_bin() { printf '%s' "$fallback" return 0 fi - echo "pyauto-agent: '$name' not found on PATH or at $fallback" >&2 + echo "pyauto-brain: '$name' not found on PATH or at $fallback" >&2 echo " Clone the sibling repo under $PYAUTO_ROOT and add its bin/ to PATH." >&2 return 127 } -resolve_pulse() { - _resolve_bin pyauto-pulse "$PYAUTO_ROOT/PyAutoPulse/bin/pyauto-pulse" +# resolve_heart — locate the PyAutoHeart CLI (the health authority of the +# organism). The former name was `pyauto-pulse`; PyAutoHeart keeps that as a +# back-compat shim, but the canonical command is `pyauto-heart`. +resolve_heart() { + _resolve_bin pyauto-heart "$PYAUTO_ROOT/PyAutoHeart/bin/pyauto-heart" } resolve_autobuild() { _resolve_bin autobuild "$PYAUTO_ROOT/PyAutoBuild/bin/autobuild" } -# readiness_verdict — run `pyauto-pulse readiness --json` and echo the verdict -# string (green/yellow/red). Returns non-zero if Pulse can't be resolved/run. +# readiness_verdict — run `pyauto-heart readiness --json` and echo the verdict +# string (green/yellow/red). Returns non-zero if Heart can't be resolved/run. readiness_verdict() { - local pulse - pulse="$(resolve_pulse)" || return $? - "$pulse" readiness --json | python3 -c 'import json,sys; print(json.load(sys.stdin).get("verdict","unknown"))' + local heart + heart="$(resolve_heart)" || return $? + "$heart" readiness --json | python3 -c 'import json,sys; print(json.load(sys.stdin).get("verdict","unknown"))' } diff --git a/agents/health/AGENTS.md b/agents/health/AGENTS.md index 2c79add..7688c6a 100644 --- a/agents/health/AGENTS.md +++ b/agents/health/AGENTS.md @@ -1,32 +1,33 @@ # Health agent -Drives the PyAutoPulse monitoring / readiness surface. It is a thin, named -driver of `pyauto-pulse` — never a second implementation of any check (all -health logic lives in Pulse). +A specialist **PyAutoBrain** reasoning agent. It reasons over the PyAutoHeart +monitoring / readiness surface — a thin, named driver of `pyauto-heart`, never a +second implementation of any check (all health logic lives in Heart). The Brain +reasons about health; PyAutoHeart measures it. ## Responsibility -- Default: run one refresh cycle (`pyauto-pulse tick`) and print the - authoritative readiness verdict (`pyauto-pulse readiness`). -- Any subcommand is forwarded verbatim to `pyauto-pulse` (`status`, `watch`, +- Default: run one refresh cycle (`pyauto-heart tick`) and print the + authoritative readiness verdict (`pyauto-heart readiness`). +- Any subcommand is forwarded verbatim to `pyauto-heart` (`status`, `watch`, `logs`, `fix `, ...). ## Run ```bash -bin/pyauto-agent health # one tick + readiness verdict -bin/pyauto-agent health status # forward to: pyauto-pulse status -bin/pyauto-agent health watch 300 # forward to: pyauto-pulse watch 300 +bin/pyauto-brain health # one tick + readiness verdict +bin/pyauto-brain health status # forward to: pyauto-heart status +bin/pyauto-brain health watch 300 # forward to: pyauto-heart watch 300 ``` ## Future -Several health agents may eventually each own a different slice of Pulse (CI -status, worktree drift, script timing, version skew). They would be added as +Several health agents may eventually each reason over a different slice of Heart +(CI status, worktree drift, script timing, version skew). They would be added as sibling `agents//` directories, each forwarding to the relevant -`pyauto-pulse` checks. For now this single agent covers the whole surface. +`pyauto-heart` checks. For now this single agent covers the whole surface. ## What this agent must never do -- Implement or duplicate any health check (that is Pulse's job). +- Implement or duplicate any health check (that is Heart's job). - Trigger a release or write into other repos. diff --git a/agents/health/health.sh b/agents/health/health.sh index f00f89f..df65521 100755 --- a/agents/health/health.sh +++ b/agents/health/health.sh @@ -1,33 +1,34 @@ #!/usr/bin/env bash -# agents/health/health.sh — the health agent. +# agents/health/health.sh — the health agent (a PyAutoBrain reasoning agent). # -# Drives the PyAutoPulse monitoring surface. By default it runs one refresh -# cycle and prints the authoritative readiness verdict. Any subcommand is -# forwarded straight to `pyauto-pulse`, so this agent is a thin, named driver -# of Pulse rather than a second implementation of any check. +# Reasons over the PyAutoHeart monitoring surface. By default it runs one +# refresh cycle and prints the authoritative readiness verdict. Any subcommand +# is forwarded straight to `pyauto-heart`, so this agent is a thin, named driver +# of Heart rather than a second implementation of any check. The Brain reasons +# about health; PyAutoHeart measures it. # # Usage: # health.sh # one tick, then print readiness -# health.sh status # forward: pyauto-pulse status -# health.sh watch [secs] # forward: pyauto-pulse watch (continuous) -# health.sh ... # forward verbatim to pyauto-pulse +# health.sh status # forward: pyauto-heart status +# health.sh watch [secs] # forward: pyauto-heart watch (continuous) +# health.sh ... # forward verbatim to pyauto-heart # -# Future: several health agents may each consume a different slice of Pulse +# Future: several health agents may each reason over a different slice of Heart # (CI, worktree drift, timing, ...). For now this single agent covers the whole -# surface via pyauto-pulse. +# surface via pyauto-heart. set -uo pipefail HERE="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" source "$HERE/../_common.sh" -pulse="$(resolve_pulse)" || exit $? +heart="$(resolve_heart)" || exit $? if [[ $# -eq 0 ]]; then - echo "== health agent: refreshing PyAutoPulse state ==" - "$pulse" tick + echo "== health agent: refreshing PyAutoHeart state ==" + "$heart" tick echo - exec "$pulse" readiness + exec "$heart" readiness fi -exec "$pulse" "$@" +exec "$heart" "$@" diff --git a/agents/release/AGENTS.md b/agents/release/AGENTS.md index 0803700..0aa3201 100644 --- a/agents/release/AGENTS.md +++ b/agents/release/AGENTS.md @@ -1,31 +1,33 @@ # Release agent -Drives a PyAuto release through the canonical chain: +A specialist **PyAutoBrain** reasoning agent. It decides whether and when a +release should happen, then drives it through the canonical chain: ``` -Agent → Pulse (gate) → Build (execute) +Brain → Heart (gate) → Build (execute) ``` ## Responsibility -1. Refresh and read PyAutoPulse's authoritative readiness verdict - (`pyauto-pulse readiness --json`). -2. **Block** unless the verdict is green: +1. Refresh and read PyAutoHeart's authoritative readiness verdict + (`pyauto-heart readiness --json`). +2. **Reason over it** — block unless the verdict is green: - **RED** → a real release blocker; refuse (exit 3). - **YELLOW** → caution; refuse unless `--force` (exit 2). - **GREEN** → proceed. 3. On green, delegate to the PyAutoBuild executor — `autobuild pre_build`, which prepares the workspaces and dispatches `release.yml`. -The agent holds **no gate logic and no release mechanics of its own**. The -decision is Pulse's; the execution is Build's. +The agent holds **no health logic and no release mechanics of its own**. The +health decision is Heart's; the execution is Build's. The Brain only reasons +about the verdict and decides to proceed. ## Run ```bash -bin/pyauto-agent release # gate, then release on green -bin/pyauto-agent release --force # also proceed on yellow (cautions ack'd) -bin/pyauto-agent release -- 2 # forward `2` (minor_version) to pre_build +bin/pyauto-brain release # reason about readiness, release on green +bin/pyauto-brain release --force # also proceed on yellow (cautions ack'd) +bin/pyauto-brain release -- 2 # forward `2` (minor_version) to pre_build ``` Exit codes: `0` released/delegated · `2` yellow (use --force) · `3` red blocked @@ -33,6 +35,6 @@ Exit codes: `0` released/delegated · `2` yellow (use --force) · `3` red blocke ## What this agent must never do -- Re-derive or second-guess the readiness verdict (that is Pulse's job). +- Re-derive or second-guess the readiness verdict (that is Heart's job). - Run any packaging/tagging/publish step itself (that is Build's job). -- Write into PyAutoPulse or PyAutoBuild repos. +- Write into PyAutoHeart or PyAutoBuild repos. diff --git a/agents/release/release.sh b/agents/release/release.sh index 6015d51..f07fe55 100755 --- a/agents/release/release.sh +++ b/agents/release/release.sh @@ -1,16 +1,18 @@ #!/usr/bin/env bash -# agents/release/release.sh — the release agent. +# agents/release/release.sh — the release agent (a PyAutoBrain reasoning agent). # -# Call chain: Agent -> Pulse (gate) -> Build (execute). +# Call chain: Brain -> Heart (gate) -> Build (execute). # -# 1. Ask PyAutoPulse for the authoritative readiness verdict. -# 2. Block unless it is green (red = a real blocker; yellow = caution). +# 1. Ask PyAutoHeart for the authoritative readiness verdict. +# 2. Reason over it: block unless it is green (red = a real blocker; +# yellow = caution). # 3. On green, delegate to the PyAutoBuild executor (`autobuild pre_build`, # which prepares the workspaces and dispatches release.yml). # -# This agent contains NO gate logic and NO release mechanics of its own — it -# reads Pulse's verdict and delegates to Build. Everything it decides is decided -# by Pulse; everything it executes is executed by Build. +# This agent contains NO health logic and NO release mechanics of its own — it +# reasons over Heart's verdict and delegates execution to Build. The health +# decision is Heart's; the work is Build's. The Brain only decides whether and +# when to proceed. # # Usage: # release.sh [--force] [-- ] @@ -33,17 +35,17 @@ while [[ $# -gt 0 ]]; do esac done -echo "== release agent: gating on pyauto-pulse readiness ==" +echo "== release agent: reasoning over pyauto-heart readiness ==" -pulse="$(resolve_pulse)" || exit $? +heart="$(resolve_heart)" || exit $? # Refresh the verdict from a fresh tick so the gate is not stale, then read it. -"$pulse" tick >/dev/null 2>&1 || echo " (warning: tick failed; using last cached state)" >&2 +"$heart" tick >/dev/null 2>&1 || echo " (warning: tick failed; using last cached state)" >&2 verdict="$(readiness_verdict)" || { echo "release agent: could not obtain readiness verdict" >&2; exit 1; } # Show the human-readable readiness block too. -"$pulse" readiness || true +"$heart" readiness || true case "$verdict" in green) diff --git a/bin/pyauto-agent b/bin/pyauto-agent index 558ed0a..15da1dd 100755 --- a/bin/pyauto-agent +++ b/bin/pyauto-agent @@ -1,76 +1,8 @@ #!/usr/bin/env bash -# bin/pyauto-agent — PyAutoAgent CLI dispatcher. +# bin/pyauto-agent — backwards-compatibility shim. # -# Subcommand pattern mirrors PyAutoBuild/bin/autobuild and -# PyAutoPulse/bin/pyauto-pulse so the three CLIs feel the same. Each subcommand -# is a named agent under agents//. -# -# pyauto-agent release [args] gate on readiness, then release on green -# pyauto-agent health [args] drive the Pulse monitoring/readiness surface -# pyauto-agent help [name] list agents or show one agent's docs - -set -u - +# PyAutoAgent has been renamed to PyAutoBrain (the reasoning layer of the +# PyAuto organism). The canonical CLI is now `pyauto-brain`; this shim forwards +# every invocation to it so existing callers keep working. _self="$(readlink -f "${BASH_SOURCE[0]}")" -AGENT_HOME="$(cd "$(dirname "$_self")/.." && pwd)" -AGENTS_DIR="$AGENT_HOME/agents" - -declare -A AGENT_SCRIPT=( - [release]="$AGENTS_DIR/release/release.sh" - [health]="$AGENTS_DIR/health/health.sh" -) -declare -A AGENT_DESC=( - [release]="Gate on pyauto-pulse readiness, then run the Build release on green" - [health]="Drive the PyAutoPulse monitoring / readiness surface" -) -AGENT_ORDER=(release health) - -cmd_help() { - if [[ $# -gt 0 ]]; then - local name="$1" - local doc="$AGENTS_DIR/$name/AGENTS.md" - if [[ -f "$doc" ]]; then - cat "$doc" - return 0 - fi - echo "pyauto-agent: unknown agent '$name'" >&2 - return 2 - fi - cat < Pulse (gate) -> Build (execute) - -Usage: pyauto-agent [args...] - pyauto-agent help [agent] - -Agents: -EOF - local name - for name in "${AGENT_ORDER[@]}"; do - printf ' %-10s %s\n' "$name" "${AGENT_DESC[$name]}" - done - echo - echo "Run 'pyauto-agent help ' for that agent's full docs." -} - -main() { - local sub="${1:-help}" - case "$sub" in - ""|-h|--help|help) - shift 2>/dev/null || true - cmd_help "$@" - return $? - ;; - esac - shift - local script="${AGENT_SCRIPT[$sub]:-}" - if [[ -z "$script" || ! -f "$script" ]]; then - echo "pyauto-agent: unknown agent '$sub'" >&2 - cmd_help >&2 - return 2 - fi - exec bash "$script" "$@" -} - -main "$@" +exec "$(dirname "$_self")/pyauto-brain" "$@" diff --git a/bin/pyauto-brain b/bin/pyauto-brain new file mode 100755 index 0000000..b4fd6a9 --- /dev/null +++ b/bin/pyauto-brain @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# bin/pyauto-brain — PyAutoBrain CLI dispatcher. +# +# PyAutoBrain is the reasoning layer of the PyAuto organism: it figures out HOW +# work should be done and coordinates the organs that do it. The subcommand +# pattern mirrors PyAutoBuild/bin/autobuild and PyAutoHeart/bin/pyauto-heart so +# the CLIs feel the same. Each subcommand is a specialist reasoning agent under +# agents//. +# +# pyauto-brain release [args] reason about readiness, then release on green +# pyauto-brain health [args] reason over the PyAutoHeart health surface +# pyauto-brain help [name] list agents or show one agent's docs +# +# Back-compat: the former name `pyauto-agent` is kept as a thin shim that execs +# this dispatcher. + +set -u + +_self="$(readlink -f "${BASH_SOURCE[0]}")" +BRAIN_HOME="$(cd "$(dirname "$_self")/.." && pwd)" +AGENTS_DIR="$BRAIN_HOME/agents" + +declare -A AGENT_SCRIPT=( + [release]="$AGENTS_DIR/release/release.sh" + [health]="$AGENTS_DIR/health/health.sh" +) +declare -A AGENT_DESC=( + [release]="Reason about pyauto-heart readiness, then run the Build release on green" + [health]="Reason over the PyAutoHeart monitoring / readiness surface" +) +AGENT_ORDER=(release health) + +cmd_help() { + if [[ $# -gt 0 ]]; then + local name="$1" + local doc="$AGENTS_DIR/$name/AGENTS.md" + if [[ -f "$doc" ]]; then + cat "$doc" + return 0 + fi + echo "pyauto-brain: unknown agent '$name'" >&2 + return 2 + fi + cat < Heart (gate) -> Build (execute) + +Usage: pyauto-brain [args...] + pyauto-brain help [agent] + +Specialist reasoning agents: +EOF + local name + for name in "${AGENT_ORDER[@]}"; do + printf ' %-10s %s\n' "$name" "${AGENT_DESC[$name]}" + done + echo + echo "Run 'pyauto-brain help ' for that agent's full docs." +} + +main() { + local sub="${1:-help}" + case "$sub" in + ""|-h|--help|help) + shift 2>/dev/null || true + cmd_help "$@" + return $? + ;; + esac + shift + local script="${AGENT_SCRIPT[$sub]:-}" + if [[ -z "$script" || ! -f "$script" ]]; then + echo "pyauto-brain: unknown agent '$sub'" >&2 + cmd_help >&2 + return 2 + fi + exec bash "$script" "$@" +} + +main "$@"