# AGENTS.md / CLAUDE.md snippet

Instructions that tell a coding agent how commands run in Shark. Two forms, one source:

- **Claude Code skill** (full version, loads itself when the agent works in a Space or you mention Shark):
  `shark skills install` copies it to `~/.claude/skills/shark`.
- **Snippet** for `AGENTS.md` (Codex, OpenCode, Gemini CLI, Cursor and most agents) or `CLAUDE.md`: the block below.
  `shark skills install --agents-md AGENTS.md --space myproject` writes it into the file between
  `<!-- shark:begin -->` and `<!-- shark:end -->` markers (idempotent; `--dry-run` shows what it would do), and
  `shark skills snippet --space myproject` prints it.

The canonical text lives in the Shark repository under `skills/shark/` (`SKILL.md`, `reference/`,
`AGENTS-snippet.md`); this page copies the snippet verbatim and a test fails if the two differ. Replace `myproject`
with your Space's name (`shark create --name myproject`). It assumes the `shark` CLI is installed and logged in where
the agent runs ([quickstart.md](quickstart.md)), or that the agent runs inside the Space ([shark-code.md](shark-code.md)).

````markdown
## Shark: how commands run here

Builds, tests, installs, scripts and servers run in the Shark Space `myproject`. If `SHARK_SPACE` or `SHARK_SP` is set,
your shell already runs there (`shark code`): run commands normally. Otherwise run each one as
`shark run myproject -- '<bash command line>'` (quote the whole line; `--cwd DIR` for a subdirectory).

- Check, don't assume: `echo "$SHARK_CONTEXT"` or `shark context --json` says where you run (`SHARK_MODE`
  devbox|shim|api), what works here (background processes, exposed processes, browser, egress) and what is the
  user's to do (credit, keys, hosts). Lines starting `shark:` on stderr are Shark's: follow their next step.
- Each command runs in a fresh Linux microVM. Files persist between commands; `cd` and `export` do not. Put variables
  inline (`FOO=1 npm test`) and keep the cwd inside the Space. Python: system pip is refused; use a venv in the Space
  (`python3 -m venv .venv && .venv/bin/pip install …`, then `.venv/bin/python`).
- Processes started with `&`: where `can.background_after_command` is true (hosted `shark code`) they keep running
  as a job `bg-N` in their own VM, reachable at `127.0.0.1:<port>` from later commands but invisible to `ps`/`pkill`:
  `shark ps`, `shark logs bg-N`, `shark kill bg-N`. Bound them and wait in the same command:
  `(timeout 1800 npm run dev > dev.log 2>&1 &); for i in $(seq 120); do curl -sf localhost:3000 >/dev/null && break; sleep 0.5; done`.
  Elsewhere they die with the command: start and probe in one command (`npm run dev & sleep 5; curl -sf localhost:3000; kill $!`).
- Postgres/Redis: commit `.shark/services.toml` (`[postgres]`, `version = "17"`); commands get `DATABASE_URL`. A
  server the user opens in their browser: `shark expose myproject 3000 -- npm run dev` and `shark open myproject 3000`
  (a one-time 60 s login link; private by default; `422 unsupported` on a node without previews). Previews are billed
  while awake even with auto-sleep: tell the user, and `shark expose myproject --rm 3000` when nobody is looking at it.
- Long jobs: `shark run --detach myproject -- '<cmd>'` prints an op id; `shark logs myproject <op-id>` follows it.
  Sync API calls stop at 900 s, streamed/async ones at 2 h.
- Parallel attempts: `shark fork myproject -n 3` (O(1) copies; not secrets, processes or database data); delete the
  forks you do not keep (`shark rm <id>`). Inside one command, use the VM's cores (`make -j8`, `pytest -n 8`).
- Headless browser: only inside one command (launch, screenshot to a file, close). `shark screenshot
  http://127.0.0.1:3000/ out.png` uses the image's browser where it has one (`$CHROME_PATH`; Playwright 1.56 finds it
  with no install). No browser stays open between commands; no desktop/computer use.
- Reads are free: read files with your file tools or `shark cp myproject:path -`, not `cat` in a VM. Nothing is billed
  while you think; do not poll with `sleep` in a VM; remove exposed servers and forks you no longer need.
- Exit 137 is usually out of memory (4 GiB VM): lower parallelism or split the job. `shark: conflict` → run it again;
  `shark: infra_error` or exit 125 → run the same command again. `402` → stop and tell the user (credits);
  `429 lease_cap_reached` and `503 out_of_capacity` → the command did not start: wait `Retry-After` and retry;
  `422 unsupported` → not on this node, do not retry.
- Never print, paste or commit the Shark API key or any secret, and never put one in the Space. Third-party tokens are
  Space secrets with `expose: inject`: commands see a placeholder (`shk_ph_…`), which is expected; do not override
  the CA bundle variables or disable TLS checks. Blocked host → tell the user; they change the Space's egress policy.
- Never quote Shark prices, limits or numbers from memory. Check: `shark whoami`, `GET /v1/quotas`,
  `GET /v1/capabilities`. Docs for agents: https://sharkmagic.si/docs/llms.txt
````

## Variant: SDK-based agent

If your agent calls Shark through the SDK instead of the CLI, keep the same rules and replace the command lines with:

````markdown
- Run commands with `space.run(cmd, op_id=<tool call id>)`; pass the tool call id so a replayed call never runs twice.
- A non-zero `exit` is a normal result. `shark_status` other than `ok` is Shark's: `conflict` → re-run with a new
  op_id; `infra_error` → retry with the same op_id; `timeout` → raise `timeout` or use `space.stream`.
````

## Why each line is there

| line | reason |
|---|---|
| quote the whole command | `shark run` joins its arguments into one bash line; an unquoted `&&` or `>` would run on the local machine |
| `SHARK_SPACE` / `SHARK_SP` | inside `shark code` (or the shell shim) the shell is already routed to Shark; prefixing `shark run` there would fail or double-route ([agents.md](agents.md)) |
| files persist; `&` is a job in its own VM, or dies | each command is a new microVM; kept jobs are a node setting, on in hosted `shark code` ([concepts.md](concepts.md#lease-one-command-one-microvm), [background processes](concepts.md#background-processes)) |
| venv | the image's system Python refuses `pip install`; a venv in the Space persists for later commands and forks ([concepts.md](concepts.md#lease-one-command-one-microvm)) |
| services.toml / expose | the homes for long-running things, each in its own VM that sleeps ([concepts.md](concepts.md#services-that-sleep), [exposed processes](concepts.md#exposed-processes-and-preview-urls)) |
| detach, 900 s / 2 h | sync and streamed limits ([limits.md](limits.md#commands)) |
| fork | O(1) on files, made for parallel attempts ([concepts.md](concepts.md#fork)) |
| browser in one command | nothing survives the command; browsers in the hosted image are not verified ([integrations.md](integrations.md#heavy-jobs-one-command-does-the-whole-job)) |
| reads are free | file reads are served from the committed tree without a VM ([billing.md](billing.md#what-is-metered)) |
| 137 / conflict / 125 / 402 / 429 / 503 / 422 | [errors.md](errors.md) and `shark_status` ([concepts.md](concepts.md#lease-one-command-one-microvm)) |
| keys, placeholders, CA bundle | an agent that sees a key can leak it; `inject` keeps third-party values out of the VM and needs the command's CA bundle ([security.md](security.md)) |
| never quote numbers from memory | limits and prices change; the API is the source ([limits.md](limits.md)) |