# AstroAgent — the standalone agent framework A generic **host** that runs **pluggable agents**, cloneable into any SeedProject site, where each project **enables the agents it needs** via config. The host knows nothing site-specific; identity comes from config, brand from the per-site `brand` skill. ## Module layout (`agents/console/`) | File | Role | |---|---| | `host.mjs` | The generic runtime — **site-agnostic**. Shared primitives + route registry + scheduler + core routes (`/ping`, `/auth`, `/logout`). | | `site.mjs` | The **only** place identity is read. Derives `name / url / host / model / agentUser / home / role / qaEmail / qaRoutes / agents` from `site.config.json` + `astroagent.config.json`. | | `server.mjs` | Bootstrap: load host → register the **enabled** agents (with their settings) → `host.listen()`. | | `agents/.mjs` | One agent each — `in-page-console`, `project-builder`, `web-designer`, `qa`. Site-agnostic; identity via `site.*`, brand via `brand/BRAND.md` + the `brand` skill. | ## Agent module contract Each `agents/.mjs` exports a `manifest` and a `register`: ```js export const manifest = { name: "qa", title: "QA Agent", class: "runtime", description: "…", triggers: [ { type:"endpoint", method:"POST", path:"/qa/run" }, { type:"schedule", settingKey:"heartbeatMin" } ], tools: "Read Skill", skills: ["qa"], tables: ["cja_qa_runs","cja_qa_findings"], // migrations this agent needs cli: ["qa-start","qa-finish","qa-routes","qa-autofix"], // api/cli/*.php bridges it needs }; export function register(host, settings = {}) { host.route("POST", "/qa/run", (req, res) => { /* uses host.* */ }); if ((settings.heartbeatMin ?? 60) > 0) host.everyMinutes(settings.heartbeatMin, fn); } ``` `register` receives the host + this agent's per-project `settings`. It wires routes/triggers and uses **only** `host.*` primitives — never globals — so an agent is a self-contained unit. ## Host API (`host.*`) - `route(method, path, handler)` — register an HTTP route (`method:"ANY"` matches any verb). - `runClaudeJson({prompt, tools, model})` → `{ok, result}` — one-shot Claude. - `runClaudeStream({prompt, tools, model, resume, onMessage, …})` — streaming Claude (SSE). - `build(extraEnv)`, `git(args)`, `phpCli(args)` — build / git / the DB bridge. - `everyMinutes(min, fn, {bootDelayMs})` — scheduled trigger (heartbeat). - `newJob/emit/jobs` — SSE job model. `readBody/json` — HTTP helpers. - `state.busy` — the shared single-flight lock (mutable across agent modules). - `gitScope`, `REPO`, `APP`, `PREVIEW_DIR`, `DEFAULT_MODEL`, `AGENT_TOOLS` — framework constants. - Extension points agents attach, e.g. `host.drainTasks` (web-designer) which QA autofix calls. ## The roster — assign agents to a project `astroagent.config.json`: ```json "agents": { "enabled": ["in-page-console", "project-builder", "web-designer", "qa"], "settings": { "qa": { "heartbeatMin": 60, "autofix": true } } } ``` `server.mjs` loads only `enabled` agents. Disable one → its routes 404 and its triggers stop. A restaurant site might enable `["web-designer","qa","menu-updater"]`; a law firm `["web-designer","qa","seo","intake-triage"]` — same framework, different roster. ## Clone + assign 1. Clone the base → `scripts/new-site.sh` (or edit `site.config.json` + `node scripts/configure.mjs`) — stamps identity (name, url, agentUser, timezone…). `site.mjs` picks these up automatically. 2. Set `astroagent.config.json → agents.enabled` to the roster this project needs; tune `settings`. 3. Provision each enabled agent's dependencies — its `tables` (migrations), its `cli` (`api/cli/*.php`), its `skills` (`.claude/skills/*` or MCP). The manifest lists them; **`node agents/catalog.mjs` → `agents/agents.json`, diff it against a clone to find gaps.** 4. Start the console service → the host loads exactly the enabled agents. ## Add a new agent Drop `agents/console/agents/.mjs` exporting `manifest` + `register`, add its name to `agents.enabled`, provide its migrations/CLI/skills, run `node agents/catalog.mjs`, restart. ## What stays site-specific (never in the core) `site.config.json` + `astroagent.config.json` (identity + roster), the `brand` skill + `brand/BRAND.md` (visual rules), the `cja_*` content, and the built site. The registry (`agents/agents.json`, generated) unifies pipeline (`agents/scripts/*`) and runtime (console) agents so one manifest diff covers a whole clone.