# agentic-app-template

A guuey agentic app: a code-mode agent, a custom MCP server you can copy to
add your own tools, and a `ggui` config for generative UI — all runnable
locally with one command, and deployable to guuey with one more.

## What got scaffolded

```
.
├── guuey.json          # the deploy contract: agent framework/model, system prompt,
│                        #   mcpServers (name → colocated source + devPort), ggui config
├── package.json         # agent deps + the pnpm workspace root (workspaces: mcps/*, web)
├── src/worker.ts        # your agent code (code-mode worker); build emits ./guuey.worker.js
├── prompts/system.md    # system prompt, referenced from guuey.json#agent.systemPrompt.file
├── mcps/todo/           # the "copy-me" custom MCP server — @modelcontextprotocol/sdk,
│                         #   Streamable HTTP, in-memory todo list, port :6782
├── ggui/                # ggui.json + blueprints/ — generative-UI config.
│                         #   `ggui serve` runs against this locally; `guuey deploy` pushes
│                         #   generation config + blueprints (the deployed RENDER THEME is
│                         #   platform data, not this dir — ggui.json#theme is local-preview
│                         #   only, kept in step with guuey.app.json by `pnpm bootstrap`).
├── web/                 # the product frontend (Vite + React on @guuey/chat):
│                         #   landing (widget) · login (guest + BYO-OIDC seam) · home
│                         #   (status + distribution guide) · the chat surface
├── guuey.app.json       # frontend/brand config — written by `pnpm bootstrap`
│                         #   (schema: guuey.app.schema.json; public-safe by design)
├── theme.json           # the chat theme document (@guuey/chat tokens) — web/ renders it
│                         #   locally and `pnpm bootstrap -- --link` pushes it to the hosted
│                         #   app; `mode` + accent follow guuey.app.json, the rest is yours
├── AGENTS.md            # coding-agent steering; `pnpm bootstrap` maintains the
│                         #   managed block with this project's real facts
├── scripts/dev.mjs      # `pnpm dev` orchestrator — boots the whole local stack
├── scripts/bootstrap.mjs # `pnpm bootstrap` — configure, then `-- --link` to bind
├── .env.example          # ANTHROPIC_API_KEY / OPENAI_API_KEY for local dev
└── .mcp.json             # Claude Code convenience wiring (mcp.ggui.ai/dev)
```

The project root **is** the agent package — `guuey deploy` packs this directory
directly as the deploy tarball, so `guuey.json` and `src/worker.ts` live at the top
level rather than nested under a `servers/` or `apps/` folder.

## Quick start

```bash
pnpm install
pnpm bootstrap               # brand, theme, copy → guuey.app.json + theme.json + AGENTS.md (no account needed;
                             #   every web page is gated on this, and production builds fail without it)
cp .env.example .env.local   # done automatically on scaffold if .env.local is absent
# set ANTHROPIC_API_KEY (or OPENAI_API_KEY, for the openai-agents-sdk template) in .env.local
pnpm dev
```

When the agent is deployed (`npx guuey login && npx guuey deploy`), bind it into the
frontend: `pnpm bootstrap -- --link` — that records the app id, endpoint,
widget origin and portal link in `guuey.app.json` and pushes the brand accent
and `theme.json` (the chat theme) to the platform. `pnpm status` shows the live
app state; `pnpm bootstrap -- --check`
prints machine-readable configuration state.

`pnpm dev` (`scripts/dev.mjs`) boots four processes with prefixed, interleaved
logs. Ctrl-C tears all of them down together.

| process      | port  | what                                                                                                                                       |
| ------------ | ----- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `worker`     | —     | `tsup --watch` — rebuilds `guuey.worker.js` on every save                                                                                  |
| `guuey dev`  | :6790 | local router: spawns your worker per turn, streams SSE; auto-spawns every `colocated` MCP entry — `mcps/todo` lands on its `devPort` :6782 |
| `ggui serve` | :6781 | local generative-UI server, over `ggui/`                                                                                                   |
| `web`        | :6890 | the product frontend (landing/login/home/chat on @guuey/chat)                                                                              |

The todo MCP still answers on :6782 — it's just a supervised child of
`guuey dev` now (copy `mcps/todo/` to add your own server; new `colocated`
entries in `guuey.json` are auto-spawned the same way).

> **If a server's deps carry build scripts** (the classic: `tsx` → esbuild's
> platform-binary fetch), pnpm ≥11 refuses to run them on a fresh install —
> `ERR_PNPM_IGNORED_BUILDS` — and the server never boots. Approve the
> specific packages declaratively in the workspace-root `package.json` (or
> the server's own, for a standalone install):
> `"pnpm": { "onlyBuiltDependencies": ["esbuild"] }`. This scaffold ships
> the approval already; add to the list when a new dep needs its build step.
> Don't disable the gate wholesale — it's pnpm's supply-chain protection.

Open http://localhost:6890 to chat with your agent locally.

## Local dev vs. deployed — what's different

`guuey dev` is the open, local equivalent of the pod router that runs your
worker in production. It mirrors the real spawn/stream contract closely — the
same normalizer, the same SSE shape — but it deliberately cuts three corners:
two that only matter once real users and real money are involved, and one
you should read twice:

- **No sandboxing — read this one twice.** Locally your worker runs as a
  plain child process: no `bwrap`/gVisor isolation on any platform, it
  inherits your **full shell environment** (every variable, including
  credentials unrelated to this agent), and when filesystem layers are
  bound the host grants the agent shell and file tools **without
  prompting** (the production jail those tools assume does not exist
  locally). Net: `guuey dev` runs an LLM-driven agent with unprompted
  shell access as you, with your env. That's a deliberate dev-trust
  posture, not an accident — treat the agent like any script you'd run
  from the internet: run `guuey dev` in a throwaway shell/container with
  a minimal environment if your agent (or any MCP server it talks to)
  handles untrusted input. In production every invocation runs inside a
  gVisor-isolated pod with only its declared environment.
- **Permissive auth.** `ggui serve --dev-allow-all` accepts any bearer
  locally. The deployed ggui instance enforces real auth.
- **No metering or history.** Local runs aren't billed and aren't persisted.
  Deployed conversations are metered and their history is written to
  DynamoDB so users can resume a thread.

None of this changes your code — `guuey.json` is the same file in both
worlds; only how it's resolved differs. Locally, `guuey dev` auto-spawns each
`colocated` entry on its `devPort` and points the agent at
`localhost:<devPort>`; deployed, the same server code runs as a supervised
child inside your agent's pod, reached over the pod's loopback with a
per-request federated identity. (`hosted` entries deploy to the platform's
federated URLs; locally they need a `devPort` pointing at a server you run
yourself, or they're skipped with a warning.)

## Deploying

```bash
npx guuey login    # device-flow auth; stores a token in ~/.guuey/auth.json
npx guuey deploy   # ships everything
```

`guuey deploy` runs four legs, in order, so a hard failure aborts before
anything user-visible changes:

1. **MCP leg** — deploys each `hosted` entry under `guuey.json#agent.mcpServers`
   as its own hosted MCP server (build → deploy → registry), then writes the
   resulting server id back into `guuey.json`. This scaffold has none — the
   todo MCP is `colocated`, so this leg is a no-op here.
2. **ggui asset leg** — pushes `ggui/` (generation config, blueprints) to your
   app's guuey-managed ggui instance.
3. **Agent leg** — builds `src/worker.ts` into `guuey.worker.js`, packs the
   project root (including `mcps/`), and deploys it as a gVisor-isolated,
   scale-to-zero pod. Each `colocated` entry (e.g. `mcps/todo`) is built into
   the worker image and auto-spawned as a supervised, sandboxed child at pod
   boot — no separate server, registry row, or write-back.
4. **Output** — prints your agent's endpoint URL and a Portal deep link.

Re-running `guuey deploy` converges: unchanged pieces are skipped or reused,
nothing is duplicated.

## Filesystem + memory

Every deployed invoke gets three bound directories — `$GUUEY_HOME_DIR`
(durable, per-user), `$GUUEY_APP_DIR` (read-only, shared), and cwd (session
scratch) — plain `node:fs`, no wrapper API required. Signed-in users get
durable cross-session memory for free (a platform-owned prompt tells the
model to read/write `$GUUEY_HOME_DIR/memories/MEMORY.md`); guests never get
durable storage, by design. **This recall is `claude-agent-sdk`-only today**
— openai-agents-sdk and google-adk agents get the three bound directories
but not the automatic memory-file recall; all-framework support arrives
with guuey's own memory MCP (a platform tool every framework can call over
its existing MCP channel). Framework-native memory backends (e.g. ADK's
Vertex MemoryBank) are unsupported on Guuey — they'd store user data
outside guuey's deletion boundary. Full contract, code examples, and
rollout status: the "Your agent's filesystem" section of the guuey
monorepo's `docs/quickstart.md`, or
[`@guuey/fs`](https://www.npmjs.com/package/@guuey/fs)'s own README — an
optional, three-helper sugar layer over the same paths.

## Need help?

The guuey community is on Discord — https://guuey.com/discord. Bugs in the scaffold or the SDKs: https://github.com/withguuey/guuey-sdks/issues.

## How people talk to your agent

- **guuey Portal** — a Telegram-like agent App Store and universal chat
  client. Once deployed, your agent is reachable from Portal with **zero
  frontend code** — most builders never need to touch `web/` at all.
- **`web/`** — ships anyway, because the local dev loop needs a chat surface,
  and it's a worked example of the bring-your-own-frontend path if you want
  to embed your agent somewhere Portal doesn't reach.
- **Future: guuey widgets** — an embeddable web-chat widget for dropping
  your agent into an existing site is planned as a separate feature; `web/`
  is designed to make that graduation straightforward when it lands.
