---
name: v0-ash-onboarding
description: Use when bootstrapping, testing, or debugging an Ash Web Chat agent inside a v0 preview or v0 sandbox. Runs create-experimental-ash-agent in-place with v0-safe flags, avoids Vercel Services/vercel dev, and verifies that the v0 preview shows the Next.js chat UI.
---

# v0 Ash onboarding

Scaffold an Ash Web Chat agent into the current v0 project root and verify the browser preview. This skill is for v0 dev/preview only.

## Ground rules

- Work in the v0 project root. In v0 this is usually `/vercel/share/v0-project`; confirm with `pwd` and `ls`.
- Do not create a child project directory. Use `--in-place` and omit `<name>`. Do not invent names like `v0-preview`.
- Do not run `vercel login`, `vercel deploy`, `vercel dev`, `vercel env pull`, or Vercel CLI auth commands.
- Do not pass `--project`, `--team`, or `--channels slack`.
- Do not use old flags such as `--local-only` or `--no-deploy`. Use `--skip-vercel`; it also skips deployment.
- Do not ask for or set any Vercel API token such as `VERCEL_TOKEN` or `VERCEL_API_TOKEN`. It is not required for this v0 dev/preview flow because `--skip-vercel` skips Vercel project provisioning, Services config, env pull, and deployment.
- Do not use v0's "Add Environment Variable" request UI or "Generate" flow for `AI_GATEWAY_API_KEY`. This flow consumes v0-managed env injection; a missing key is a failed env-injection probe, not a prompt to ask the user for a secret.
- Do not start a separate `ash dev` process. The Web Chat scaffold uses `next dev`; `withAsh()` starts the app-local Ash dev server and rewrites same-origin `/ash/*` requests.
- Do not hand-edit `next.config.ts` or `vercel.json` as the normal path. The create CLI should produce the v0-safe config.
- Do not repair a bad `.ash/next-dev-server.json` origin by setting `ASH_BASE_URL` as the normal path. The registry origin for `withAsh()` development must be loopback; an external origin such as `https://rolldown.rs` means the installed Ash package parsed the wrong URL from dev-server output or a stale registry survived.

## Inputs

Collect these before running the CLI:

- `model`: the model id to bake into `agent/agent.ts`, for example `openai/gpt-5-mini`.
- Agent behavior: any instructions, skills, tools, or UI changes the user wants after the base scaffold works.

Do not ask the user for an AI Gateway key at model-planning time or after a failed env probe. The sandbox and `/vercel/share/.env.project` may not exist until the first terminal/tool action has initialized the VM, and it may not be reliable until the preview has settled. Treat `AI_GATEWAY_API_KEY` as the final verification input: after the scaffold, route rewrite, and UI checks pass, source `/vercel/share/.env.project` and hydrate a project-local `.env.local` fallback. Only use `--gateway-api-key` when the user explicitly supplies a BYOK key before this flow.

Do not ask the user for `VERCEL_TOKEN`, `VERCEL_API_TOKEN`, or another Vercel API token at any point in this flow. If a tool or model says a Vercel token is missing, treat that as using the wrong path and continue with `--skip-vercel` plus the v0-managed `AI_GATEWAY_API_KEY` checks below.

## Dev-server invariant

The v0 preview's `next dev` process must start after the scaffold has written `next.config.ts`. Next.js route rewrites are process config, so an already-running pre-scaffold `next dev` process can keep serving stale routes even after the file exists on disk.

Make this the normal path:

1. Stop any pre-scaffold `next dev` process.
2. Run create in place.
3. Verify `next.config.ts` contains `withAsh(nextConfig, { configureVercelJson: false })`.
4. Start `pnpm dev` once.
5. Probe `/ash/v1/health`.

Do not treat a restart after `/ash/v1/session` 404s as the happy path. A restart is only recovery for a missed preflight.

## Step 1. Check the CLI surface

Run the help command from the v0 root:

```bash
npx create-experimental-ash-agent@latest --help
```

The help output must include `--in-place`, `--skip-vercel`, and `--gateway-api-key`.

If any flag is missing, stop and report that the published create CLI is stale for this v0 flow. Do not recreate the old workaround by moving child directories, adding `concurrently`, replacing `next.config.ts`, or writing Vercel Services config by hand.

## Step 2. Scaffold in place

Before scaffolding, make sure no stale preview dev server is already running from the pre-scaffold project:

```bash
pgrep -af "next dev|next/dist/bin/next dev" || true
```

If that command prints a `next dev` process, stop the preview before running create. Use v0's preview stop/restart control if available; otherwise kill only the printed `next dev` PID. The goal is for the first post-bootstrap `next dev` start to happen after `next.config.ts` already contains `withAsh(...)`. Do not let an already-running Next process carry pre-scaffold config into the chat test.

Run create without waiting on env injection:

```bash
npx create-experimental-ash-agent@latest \
  --in-place \
  --model "$MODEL" \
  --channels web \
  --skip-vercel \
  --disable-git \
  -y \
  --json
```

With `--skip-vercel`, create may warn that no project or API key is linked. Do not treat that as a failure; the v0-managed env is checked as the final verification step after the preview settles. Do not respond to that warning by asking for a Vercel token.

`--disable-git` skips the create CLI's local `git init` and initial commit. v0 owns project history for this workflow.

## Step 3. Verify generated files

Check the exact files v0's preview detector sees:

```bash
test -f agent/agent.ts
test -f app/page.tsx
test -f next.config.ts
node -e 'const p=require("./package.json"); if (p.scripts?.dev !== "next dev") throw new Error(`unexpected dev script: ${p.scripts?.dev}`)'
grep -q 'configureVercelJson: false' next.config.ts
```

Then check no stale supported Next config can shadow the generated Ash config:

```bash
competing_next_configs="$(ls next.config.js next.config.mjs next.config.mts 2>/dev/null || true)"
if [ -n "$competing_next_configs" ]; then
  echo "competing Next config remains; merge needed settings into next.config.ts and remove the competing file before starting the preview"
  printf "%s\n" "$competing_next_configs"
  exit 1
fi
```

If `next.config.mjs` exists beside `next.config.ts`, Next.js loads `next.config.mjs` first and ignores the generated `withAsh(...)` config. That produces a Next.js 404 for `/ash/v1/session` even though `next.config.ts` looks correct on disk.

If the create CLI warns about a competing Next config, or the shell check above prints one, inspect each printed file before removing it. Preserve any needed settings in `next.config.ts`; for v0's common defaults that usually means keeping this shape inside the generated file:

```ts
const nextConfig: NextConfig = {
  images: { unoptimized: true },
  typescript: { ignoreBuildErrors: true },
};
```

After preserving needed settings, remove the competing `next.config.js`, `next.config.mjs`, or `next.config.mts` before starting the preview. Do not start `pnpm dev` while more than one supported Next config file remains.

Then check Services is absent:

```bash
if [ -f vercel.json ] && grep -q experimentalServices vercel.json; then
  echo "stale Vercel Services config remains in vercel.json"
  exit 1
fi
```

If `vercel.json` still contains `experimentalServices`, this is stale state from a previous attempt or a create CLI bug. Remove that stale Services config before testing the preview, but call it out explicitly.

## Step 4. Install and run

Install dependencies if v0 has not already done it:

```bash
pnpm install
```

Let v0 run the preview, or start it manually:

```bash
pnpm dev
```

Expected dev command: `next dev`. Do not run `vercel dev`. Do not run `ash dev` in a second terminal.

If Step 2 stopped the pre-scaffold preview, this is the one clean start that should load the generated `next.config.ts`. Next's route rewrites are config-level state, so do not rely on page HMR if an old process survived the preflight.

Verify that the active process is the post-scaffold `next dev` process:

```bash
pgrep -af "next dev" || true
```

If an old `next dev` process was already running before the scaffold and was not stopped, stop that process before testing chat. A valid run has a `next dev` process that started after `next.config.ts` was generated.

For local v0 source semantics, v0 detects `next dev` as the preview command and exposes the detected port. If live v0 instead requires the app to bind an arbitrary `$DEV_PORT`, report that as a scaffold gap. Do not hide it by hand-patching the dev script unless the user explicitly asks for that experiment.

## Step 5. Verify the Ash route rewrite

Before sending a chat message, probe the same-origin Ash health route through the Next preview:

```bash
curl -i "http://127.0.0.1:${DEV_PORT:-3000}/ash/v1/health"
```

Expected: an HTTP 2xx response from Ash. A Next.js HTML 404 page for `/ash/v1/health` or `/ash/v1/session` means the active Next server did not load the `withAsh()` rewrite.

If the probe returns a Next.js 404, check the generated config and stale Services state:

```bash
grep -R "withAsh\\|configureVercelJson\\|experimentalServices" next.config.* vercel.json 2>/dev/null || true
```

Expected:

- `next.config.ts` imports `withAsh` from `experimental-ash/next`.
- `next.config.ts` exports `withAsh(nextConfig, { configureVercelJson: false })`.
- `vercel.json` is absent or does not contain `experimentalServices`.

If the files are correct but the probe still returns a Next.js 404, restart the v0 preview/dev server and run the probe again. Do not debug AI Gateway credentials until this route probe passes; missing credentials do not make Next serve its own 404 page.

If the files stay correct and the 404 persists after restart, inspect whether the active server ever resolved the Ash rewrite:

```bash
pgrep -af "next dev|ash.js dev|ash dev" || true
test -f .ash/next-dev-server.json && cat .ash/next-dev-server.json || echo "missing .ash/next-dev-server.json"
```

- If `.ash/next-dev-server.json` is missing after the `/ash/v1/health` probe, the running Next process did not call the `withAsh()` rewrite function. Restart the v0 preview process that owns `${DEV_PORT}`, not a second `pnpm dev` process on a different port.
- If `.ash/next-dev-server.json` exists, probe the registered Ash origin directly:

```bash
node -e 'const fs=require("fs"); const r=JSON.parse(fs.readFileSync(".ash/next-dev-server.json","utf8")); console.log(r.origin)'
node -e 'const fs=require("fs"); const r=JSON.parse(fs.readFileSync(".ash/next-dev-server.json","utf8")); const h=new URL(r.origin).hostname; if (!(h==="localhost" || h==="::1" || h.startsWith("127."))) { console.error(`Ash dev server registry origin is not loopback: ${r.origin}`); process.exit(1); }'
curl -i "$(node -e 'const fs=require("fs"); const r=JSON.parse(fs.readFileSync(".ash/next-dev-server.json","utf8")); process.stdout.write(r.origin)')/ash/v1/health"
```

If the loopback-origin check fails, remove `.ash/next-dev-server.json`, reinstall or update `experimental-ash`, and restart the post-scaffold `next dev` process. Do not start a separate fixed-port Ash server and do not patch `ASH_BASE_URL` unless the user explicitly asks for a temporary experiment; that hides the `withAsh()` discovery bug.

If direct Ash health passes but same-origin `/ash/v1/health` is still a Next.js 404, the active Next rewrite is missing or stale. If direct Ash health fails, debug the spawned Ash dev server before testing chat.

## Step 6. Browser verification

Open the v0 preview, preferably through the v0/browser tool:

```bash
agent-browser open "http://localhost:${DEV_PORT:-3000}"
```

The first viewport should show the Ash Web Chat UI, not the Ash backend landing page. A successful compile is not enough.

Before moving to the final env step, confirm all of these:

- Typing in the composer leaves visible text in the textarea.
- The send button is clickable after typing text.

If the send button stays disabled after typing text, inspect `app/_components/agent-chat.tsx`. Older scaffolds duplicated textarea state in `AgentChat` and used that duplicate state to pass `disabled` to `PromptInputSubmit`. Remove the `inputText` state, render `<PromptInputTextarea placeholder="Send a message…" />`, and render `<PromptInputSubmit onStop={agent.stop} status={agent.status} />`. `PromptInput` already captures the form text and `handleSubmit` already no-ops for empty trimmed messages.

If the preview shows "The agent is up and accepting messages", inspect these before changing code:

```bash
grep -R "experimentalServices\\|vercel dev\\|ash dev\\|concurrently\\|configureVercelJson" package.json next.config.* vercel.json 2>/dev/null || true
```

Likely causes:

- `vercel.json` still contains `experimentalServices`, so v0 selected the Services path.
- The CLI was stale and did not render `configureVercelJson: false`.
- The project was not scaffolded in place and v0 is previewing the wrong root.
- Live v0 is watching a non-3000 `$DEV_PORT` that the generated `next dev` script does not bind. Report this as a failed acceptance check for the scaffold.

Do not send the final chat message yet. The AI Gateway key is the last step because v0's managed env file is most reliable after the sandbox and preview have settled.

If a route probe or browser request fails with a Next.js HTML 404 for `/ash/v1/session`, return to Step 5. That failure is a missing active rewrite, not a model or credential failure.

## Step 7. Hydrate env and send a message

After the preview route and UI checks pass, wait for v0's managed env file, then hydrate `.env.local` without printing the secret:

```bash
bash -lc 'for i in $(seq 1 60); do set -a; . /vercel/share/.env.project 2>/dev/null; set +a; [ -n "${AI_GATEWAY_API_KEY:-}" ] && break; sleep 1; done; if [ -z "${AI_GATEWAY_API_KEY:-}" ]; then echo "AI_GATEWAY_API_KEY missing from /vercel/share/.env.project after waiting for v0 env injection"; exit 1; fi; umask 077; { grep -v "^AI_GATEWAY_API_KEY=" .env.local 2>/dev/null || true; printf "AI_GATEWAY_API_KEY=%s\n" "$AI_GATEWAY_API_KEY"; } > .env.local.tmp && mv .env.local.tmp .env.local'
```

If that command reports the key is missing after the wait, report that v0 env injection is not ready or did not provide `AI_GATEWAY_API_KEY`. Do not ask the user for a key, do not open v0's "Add Environment Variable" UI, and do not press "Generate"; stop for user direction or retry the wait if the sandbox was still settling.

Do not decide the key is unavailable from a bare `printenv AI_GATEWAY_API_KEY`; generic v0 terminal commands may not source `/vercel/share/.env.project`.

Verify the env files without printing secret values:

```bash
bash -lc 'for f in .env.local /vercel/share/.env.project; do echo "== $f =="; test -f "$f" && grep -E "^(AI_GATEWAY_API_KEY|ANTHROPIC_AUTH_TOKEN)=" "$f" | sed "s/=.*/=<redacted>/" || echo missing; done'
```

After the hydration command, `.env.local` should contain `AI_GATEWAY_API_KEY` copied from `/vercel/share/.env.project`. If the user explicitly supplied a BYOK key before this flow, `.env.local` may contain that value; otherwise do not request one. If `.env.local` is missing but `/vercel/share/.env.project` has the key, re-run the hydration command.

Do not debug `AI_GATEWAY_KEY` as the required variable; v0 and Ash use `AI_GATEWAY_API_KEY`.

Then verify v0's source order resolves a usable environment:

```bash
bash -lc 'set -a; . .env.local 2>/dev/null; . .env.development.local 2>/dev/null; . /vercel/share/.env.project 2>/dev/null; set +a; node -e "console.log({ AI_GATEWAY_API_KEY: Boolean(process.env.AI_GATEWAY_API_KEY), ANTHROPIC_AUTH_TOKEN: Boolean(process.env.ANTHROPIC_AUTH_TOKEN) })"'
```

If the preview server was already running before `.env.local` existed, restart it once after hydration so `next dev` and the spawned `ash dev` process inherit the key. Then inspect the live process envs, redacted:

```bash
pgrep -af "next dev|ash.js dev|ash dev" || true
PID=<pid>
tr '\0' '\n' < /proc/$PID/environ \
  | grep -E '^(AI_GATEWAY_API_KEY|ANTHROPIC_AUTH_TOKEN)=' \
  | sed 's/=.*/=<redacted>/'
```

Check both the `next dev` process and the spawned `ash dev` process.

- If `/vercel/share/.env.project` has `AI_GATEWAY_API_KEY` but `next dev` does not, v0 likely started the preview before the env file existed or used a start path that skipped `getSourceEnvPrefix()`. Restart the preview and re-check.
- If `next dev` has `AI_GATEWAY_API_KEY` but the spawned `ash dev` process does not, treat it as an Ash child-process env propagation bug.
- If `.env.local` has a good `AI_GATEWAY_API_KEY` but `/vercel/share/.env.project` has the same key empty or stale, v0's source order makes `/vercel/share/.env.project` win. Remove or refresh the stale v0-managed value before restarting the preview.

Finally, send one browser message:

- Sending creates a `POST /ash/v1/session` request.
- The assistant streams a response.

If chat fails with missing AI Gateway credentials, do not guess from an interactive shell. Use the env file and process checks above.

If chat fails with a Next.js HTML 404 for `POST /ash/v1/session`, return to Step 5. That failure is a missing active rewrite, not a model or credential failure.

## Step 8. Apply the user's agent changes

After the base chat works, edit the Ash project:

- `agent/instructions.md` for behavior and personality.
- `agent/skills/*.md` for durable knowledge and procedures.
- `agent/tools/*.ts` for tools.
- `app/**` for UI changes.

Tool definitions import from `experimental-ash/tools`:

```ts
import { defineTool } from "experimental-ash/tools";
```

Use `inputSchema` for tool input schemas.
