> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ascii.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Setup & Scripts

> Run code inside a Box: setup scripts, pre-commit hooks, and driving setup from your own program.

Four ways to run code in a Box, from the one that needs no code of your own to the one you drive entirely from your backend. They stack, so most setups use more than one.

|                                                          | Runs                                      | Best for                                           |
| -------------------------------------------------------- | ----------------------------------------- | -------------------------------------------------- |
| [Repository setup script](#repository-setup-scripts)     | Once at start, in one repository's folder | `npm install`, migrations, anything per repository |
| [Pre-commit hooks](#pre-commit-hooks)                    | Before each commit in that repository     | Linting, tests, blocking bad commits               |
| [`--setup-file`](#per-box-setup-file)                    | Once at start, per Box, in the background | A one-off script that differs between Boxes        |
| [Commands from your code](#driving-setup-from-your-code) | Whenever you call                         | Orchestration your backend controls                |

For anything heavy or slow, do not script it at all: bake it into a [template Box](/box/snapshots#template-boxes) so it is already installed when the Box starts.

## Repository setup scripts

Attached to a repository in [Dashboard > Environment](https://box.ascii.dev/box/dashboard?tab=environment). Runs once when the Box starts, inside that repository's folder, with your environment's variables and secret files already in place.

```bash theme={null}
#!/bin/bash
npm install
npm run db:migrate
```

Mark it **blocking** when the Box is not usable until it finishes. A blocking script delays the Box reaching ready; a non-blocking one runs alongside everything else.

## Pre-commit hooks

Also per repository, run before each commit made in that repository. Mark a hook blocking to fail the commit when the hook fails.

```bash theme={null}
#!/bin/bash
npm run lint
```

## Talking back to the Box

Setup scripts and hooks can both steer the agent running in the Box:

| Call                             | Effect                                             |
| -------------------------------- | -------------------------------------------------- |
| `queuePrompt "your instruction"` | Sends a message to the Box, as if you had typed it |
| `stopAgent`                      | Halts the agent                                    |

That turns a failing hook into an instruction rather than a dead end:

```bash theme={null}
#!/bin/bash
npm run lint || queuePrompt "lint failed, fix the errors then commit again"
```

## Per-Box setup file

When the work differs from Box to Box, pass a local script at create time. Up to 64KB, UTF-8.

```bash theme={null}
box new --setup-file ./setup.sh
```

It runs in the background once the Box is ready and never delays `ready`. Watch it with `box info`:

| Field         | Values                                 |
| ------------- | -------------------------------------- |
| `setupStatus` | `pending`, `running`, `done`, `failed` |
| `setupError`  | Why it failed                          |

## Driving setup from your code

Configure secrets in the environment first, then run whatever you need. The script reads normal environment variables and the secret files at their configured paths, so nothing sensitive passes through the command line.

<CodeGroup>
  ```bash CLI theme={null}
  box_id="$(box new --json | jq -r 'select(.event == "ready") | .id')"
  box exec "$box_id" --cwd my-repo "bash ./setup.sh"
  ```

  ```bash curl theme={null}
  curl -sS -X POST "$BOX_API_BASE/boxes/$BOX_ID/commands" \
    -H "Authorization: Bearer $BOX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"command":"bash -lc ./setup.sh","cwd":"my-repo","timeoutSeconds":60}'
  ```

  ```ts TypeScript theme={null}
  await box.command({
    boxId,
    commandRequest: {
      command: "bash -lc './setup.sh'",
      cwd: "my-repo",
      timeoutSeconds: 60,
    },
  });
  ```

  ```python Python theme={null}
  from ascii_box_sdk.models.command_request import CommandRequest

  box.command(
      box_id,
      CommandRequest(command="bash -lc './setup.sh'", cwd="my-repo", timeout_seconds=60),
  )
  ```
</CodeGroup>

Wait for the Box to reach `ready` first; earlier calls are refused with a retryable `box_starting`. Synchronous commands cap at 600 seconds, so anything longer should detach and be polled. See [Long-Running Tasks](/box/long-running-tasks).

To pipe a script that is not on the Box yet, use SSH instead. It streams stdin, stdout and stderr, so nothing has to be copied to a temporary path first:

```bash theme={null}
box ssh bx_f7k2q9hd -- bash -s < ./setup.sh
```

<Note>
  On Windows, run this from Node, Python, WSL, Git Bash, or `cmd.exe`. Native PowerShell pipelines can keep stdin open for native executables, which hangs the command.
</Note>

## Related

* [Environments](/box/environments)
* [Snapshots & Copies](/box/snapshots)
* [Long-Running Tasks](/box/long-running-tasks)
* [SSH Access](/box/ssh-access)
