# CLI

The `june` command runs the workflow or chat a file default exports, and `june doctor` checks your setup. Bun imports `.tsx` and `.ts` files directly, so there's no build step, and a file's models, providers and stores are whatever its code builds: there are no provider or model flags.

| Need                              | Command                                       |
| --------------------------------- | --------------------------------------------- |
| Run a workflow                    | [`june run review.tsx`](#june-run)            |
| Open an interactive conversation  | [`june chat chat.ts`](#june-chat)             |
| Ask once, or script a chat        | [`june ask chat.ts "…"`](#june-ask)           |
| Check Bun and provider keys       | [`june doctor`](#june-doctor)                 |

The CLI comes with the package as `node_modules/.bin/june` and requires [Bun](https://bun.sh) 1.2 or later. Run it as `bunx june` if it isn't on your `PATH`; [Install](https://june.dev/docs/workflows.md#install) covers setup.

## `june run`

```sh
june run review.tsx
```

Renders the component the file default exports and runs it to completion. The [Quickstart](https://june.dev/docs/workflows.md#quickstart) walks through a first run, and [Running workflows](https://june.dev/docs/workflows/running.md) covers what a run does. With options:

```sh
june run triage.tsx --input '{"repo":"johno/june","issues":[12,15]}'
june run triage.tsx --input-file ./triage.json
june run review.tsx --json > review.json
june run review.tsx --timeout 60000
```

- **The file** must default export a workflow component. `june: cannot find <file>` if it doesn't exist, and `june: <file> must default export a workflow component` if its default export isn't a function.
- **Progress** goes to stderr for each `<Step>`, `<FineTune>` and `<Evaluate>` as it starts (`▸ id`), calls a tool (`↳ tool {input}`), streams text, and finishes (`✓ id`) or fails (`✗ id: message`). A `useStep` or `<Write>` prints no progress, only its result.
- **Results** go to stdout once every step has finished: each step's result under `── id`, in the order the steps finished. A failed run prints no results, only the error on stderr.
- **Tools.** The CLI doesn't add tools to a workflow: it runs the tools the workflow's own `<Agent>` declares, including [`<Bash />`](https://june.dev/docs/workflows/tools.md).
- **Approvals.** When a step asks to run a tool that needs approval, `june run` asks on stderr, `? Allow <tool> {input} (step <id>)? [y/N]`, and reads the answer from stdin. `y` or `yes` approves; anything else, or the end of stdin, denies.

### Options

- **`--input <json>`** passes a JSON object to the component as its props, as `run(<Workflow {...input} />)` would. It must be an object: `june: --input must be a JSON object of the workflow's props` for anything else, and `june: --input is not valid JSON` when it doesn't parse. June never prints the input itself, even in errors; input your workflow puts in a prompt or a result shows up wherever those do, including `--json`'s `tree`.
- **`--input-file <path>`** reads the props from a JSON file, relative to the current directory. Use one of `--input` and `--input-file`, not both. Without either, the props are `{}`.
- **`--json`** prints the whole result to stdout as one JSON object once the run succeeds (a failed run prints nothing to stdout): `steps` (each result, by step id), `usage` (the tokens the run used, when steps reported any) and `tree` (the run's trace). Progress stays on stderr, so stdout parses. Use it for anything that reads the output: the plain text format may change, the JSON follows the [compatibility](https://june.dev/docs/reference/compatibility.md) rules.
- **`--timeout <ms>`** aborts the run after that many milliseconds, a positive number. A run that times out fails with `june: The operation timed out`.

### Exit status

- `0` when every step finished.
- `1` when a step failed, the run timed out, or the file or options were wrong. The message is on stderr, after `june: `.
- `130` when you pressed Ctrl-C. The first Ctrl-C aborts the run; a second exits at once.

## `june chat`

```sh
june chat chat.ts
```

An interactive chat in your terminal with the chat a file default exports, such as one made with `createChat()` from `june/chat`. [Chat](https://june.dev/docs/chat.md) covers what a chat is and how sessions work.

```ts
import { openai } from "june/sdk/openai";
import { createChat, fileStore } from "june/chat";

export default createChat({
  model: openai("gpt-5-mini"),
  instructions: "You are helping build June.",
  store: fileStore(".june/chat"),
});
```

```text
$ june chat chat.ts
session 4f9c…e1
/new starts a session, /session prints its id, /feedback up|down rates the last reply, /quit exits

> What is June?
June is a declarative runtime for AI workflows…

> Say that in five words.
Declarative AI workflows, in React.

>
```

A chat in the terminal's normal scrollback, not a full-screen app. Replies stream to stdout; the `> ` prompt, the session id and status go to stderr. A blank line separates turns.

- **`--session <id>`** reopens a saved session: `june chat chat.ts --session <id>`. It needs a persistent store, such as `fileStore(".june/chat")`. With the default `memoryStore()`, a session ends with its process, and `--session` fails with `no chat session`.
- **Commands.** `/new` starts a new session, `/session` prints the current id, `/feedback up` or `/feedback down` rates the last reply, and `/quit` or Ctrl-D exits.
- **Ctrl-C** while a reply streams cancels that turn (it's recorded as canceled and left out of later context) and returns to the prompt. Ctrl-C at the prompt exits with status 130.
- **Errors.** A failed turn prints its error and returns to the prompt.
- **Approvals.** When a workflow chat's step asks to run a tool that needs approval, it asks on stderr, like `june run`: `y` approves, anything else denies.

[Sessions and stores](https://june.dev/docs/chat/sessions.md) covers what a session stores and how it reopens.

## `june ask`

```sh
june ask chat.ts "Explain this package"
```

One turn with the same chat, for scripts. The prompt is the remaining arguments, or stdin when there are none:

```sh
june ask chat.ts --session <id> "And its tests?"
git diff | june ask chat.ts
june ask chat.ts --json "Explain this package" > reply.json
june ask chat.ts -- "-h means help?"
```
 The reply streams to stdout, followed by a newline, and `session <id>` goes to stderr, so `--session` can continue the conversation later. With `--json`, the id is the object's `sessionId` instead.

- **`--session <id>`** continues a saved session.
- **`--json`** prints exactly one JSON object to stdout once the turn completes, and nothing else.
- **`--`** ends the options, so a prompt can start with `-`.
- **Approvals** are asked on stderr and answered on stdin, so a prompt piped on stdin leaves nothing to answer with and the call is denied.

```json
{
  "sessionId": "4f9c…e1",
  "turnId": "b2d1…07",
  "text": "June is a declarative runtime for AI workflows…",
  "metadata": {
    "responder": { "kind": "model" },
    "model": { "provider": "openai.responses", "modelId": "gpt-5-mini" },
    "usage": { "inputTokens": 24, "outputTokens": 31, "totalTokens": 55 },
    "finishReason": "stop"
  }
}
```

`metadata` is the turn's [provenance](https://june.dev/docs/chat/responders.md#provenance). A failed turn exits 1 with the error on stderr, and Ctrl-C cancels it and exits 130.

## `june doctor`

```sh
june doctor
june doctor review.tsx
```

Checks that Bun is 1.2 or later and which provider API keys are set, without calling a model.

- **With a file**, it finds the providers the file and its local imports use: each `june/sdk/<provider>` or `@ai-sdk/<provider>` import, and a gateway model string such as `"anthropic/claude-sonnet-5-5"`. It reports the key each needs and exits 1 if one is missing.
- **Without a file**, it lists every key it knows and exits 1 only if none is set.

| Provider            | Key                            |
| ------------------- | ------------------------------ |
| `@ai-sdk/openai`    | `OPENAI_API_KEY`               |
| `@ai-sdk/anthropic` | `ANTHROPIC_API_KEY`            |
| `@ai-sdk/google`    | `GOOGLE_GENERATIVE_AI_API_KEY` |
| `@ai-sdk/groq`      | `GROQ_API_KEY`                 |
| `@ai-sdk/mistral`   | `MISTRAL_API_KEY`              |
| `@ai-sdk/xai`       | `XAI_API_KEY`                  |
| AI Gateway          | `AI_GATEWAY_API_KEY`           |

It doesn't check that a key is valid, or that the model a file names exists. Those show up when you run.

## Help and version

- **`--help`**, `-h`, `june help` or `june` alone prints the usage, options and examples.
- **`--version`**, `-v` prints the installed version.
- An unknown command exits 1 with `june: unknown command <name>, run 'june --help' for usage`.
