---
description: Build, test, deploy, and operate on Zibby — workflows + hosted apps + tests
globs:
  - "**/.zibby/workflows/**"
  - "**/workflows/**"
  - ".zibby.config.mjs"
  - "**/agent.json"
  - "**/graph.mjs"
  - "test-specs/**"
  - ".zibby/memory/**"
  - "AGENTS.md"
alwaysApply: false
---

# Zibby — workflows + apps + tests

This project uses **Zibby**. Two production surfaces share `.zibby.config.mjs`:

1. **Workflows** — graphs of AI-agent steps in a sandboxed container.
2. **Apps** — long-running hosted SaaS instances (n8n, grafana, …).

Plus **Tests** — plain-language `.txt` specs converted to Playwright runs.

See `.claude/CLAUDE.md` for the long-form. `AGENTS.md` has the cross-IDE
recipes. Canonical docs: https://docs.zibby.app.

## Workflows — common dev loop

```
zibby agent new <name>        # scaffold (agents/<name>/ by default)
zibby agent run <name>        # one-shot LOCAL run (preferred for dev loop)
zibby agent validate <name>   # static check (graph topology, schemas)
zibby agent deploy <name>     # build + push to cloud → UUID
zibby agent trigger <uuid>    # remote run
zibby agent logs <uuid> -t    # tail live logs (background-run this in chat)
zibby agent list              # local + cloud
zibby agent delete <uuid>
```

File layout (agents/<name>/):
```
agent.json    # name, entryClass, triggers, defaultAgent
graph.mjs        # class extends WorkflowAgent, buildGraph() returns WorkflowGraph
nodes/*.mjs      # { name, outputSchema (Zod), prompt|execute }
state.js         # OPTIONAL — Zod schema for caller-provided inputs (-p)
package.json     # @zibby/core + zod
```

Each node's output goes to `state[nodeName]`. Read downstream as
`state.previousNode.field`. Initial input (`-p key=value`) is top-level
in state (`state.key`). Loops route back via `addConditionalEdges`;
counter state lives in the looping node's own outputSchema.

`run` and `trigger` share input flags: `-p key=value` (highest, repeatable),
`--input '<json>'`, `--input-file path.json` (lowest).

## Apps — common deploy + operate loop

```
zibby app templates                                 # browse catalog
zibby app deploy <appType> --project <id>           # catalog deploy (~2-3 min)
zibby app deploy --goal "<text>" --project <id>     # goal-mode (LLM bootstrap, 5-30 min)
zibby app list
zibby app status <instanceId>
zibby app logs <instanceId> [-t] [--service <name>]
zibby app upgrade <instanceId> --version vX.Y.Z     # agent-ops base image
zibby app set-auth <instanceId> --auth-type basic --auth-user admin --auth-password ...
zibby app set-auth <instanceId> --off               # remove auth sidecar
zibby app destroy <instanceId> --yes                # PERMANENT — wipes app data
```

**Catalog vs goal-mode** — catalog uses a baked task def, goal-mode runs
an autonomous `agent-ops` install loop. Use catalog when the app is in
`zibby app templates`; goal-mode for anything else.

Goal-mode flags: `--provider claude|codex`, `--model <id>`,
`--anthropic-token sk-ant-...` (per-deploy override), `--max-turns N`
(default 25, bump to 60-100 for heavy installs), `--timeout-min N`
(default 20, bump to 30-45 for heavy installs).

**Auth** — every app gets a public `*.apps.zibby.app` URL. Set
`--auth-type basic|token` at deploy time (or via `zibby app set-auth`
after). `none` is only safe if the app has its own login. Generate
credentials with `openssl rand -hex 16` (basic password) / `-hex 32`
(token). Never log credentials.

**Multi-service entries** — `wordpress` (wordpress + mysql), `mattermost`
(mattermost + postgres), `gas-town` (web + worker + scheduler). Use
`--service <name>` to tail one service; `app status` lists `services[]`.

## Composing agents

Need spans MULTIPLE marketplace agents → **compose**, don't rebuild:
a small wrapper workflow dispatches deployed agents as sub-workflows —
`graph.addNode('review', { workflow: '<deployed-slug>', input: (state) =>
({...}) })`; the child's final state lands at `state[nodeName]`, branch
on it via `addConditionalEdges`. Never modify marketplace template
source. Brick already deployed → ASK: reuse (shared config) vs a
dedicated named instance (isolated). Wrapper declares its own
`triggers.events`; the platform suppresses wrapped members'
subscriptions. See §10 of `.claude/CLAUDE.md`.

## Auth + login

```
zibby login                  # browser OAuth → ~/.zibby/session.json
zibby status                 # current auth + workspace + project + agent creds
```

Headless / CI: `export ZIBBY_API_KEY=zby_xxx` (PAT from https://zibby.dev/settings/api-keys). Env wins over session.

## Agent's job: decide workflow vs app

| User wants… | Use |
|---|---|
| Schedule / event-driven LLM automation | **Workflow** |
| Long-running hosted web service | **App** |
| Bootstrap an arbitrary OSS thing | **App goal-mode** |
| Sub-second interactive UI | Neither (Lambda / own backend) |

Always ask before:
- `zibby agent deploy` (cloud cost)
- `zibby app deploy` (per-minute billing starts immediately)
- `zibby app destroy` (irreversible, app data wiped)
- Any `--anthropic-token` paste (credentials in chat history leak)

## How to invoke the CLI

`zibby` on PATH (preferred) or `./.zibby/bin/zibby` as a fallback shim
that always exists in this project. Don't use `npx @zibby/cli` — not
always published.

## Canonical docs (always prefer over these notes)

- Workflows: https://docs.zibby.app/workflows
- Apps: https://docs.zibby.app/apps
- agent-ops daemon: https://docs.zibby.app/apps/agent-ops
- CLI: https://docs.zibby.app/cli
- MCP: https://docs.zibby.app/cli/mcp
