# Flue quickstart

> Scaffold a Flue project, run `flue dev`, invoke a workflow with `flue run`, and connect to an agent instance with `flue connect`.

- Repository: withastro/flue-with-vercel-eve
- GitHub: https://github.com/withastro/flue
- Human docs: https://www.grok-wiki.com/public/docs/withastro-flue-with-vercel-eve-f4b79875fff6
- Complete Markdown: https://www.grok-wiki.com/public/docs/withastro-flue-with-vercel-eve-f4b79875fff6/llms-full.txt

## Source Files

- `withastro-flue:packages/cli/bin/flue.ts`
- `withastro-flue:examples/hello-world/flue.config.ts`
- `withastro-flue:examples/hello-world/src/workflows/hello.ts`
- `withastro-flue:examples/hello-world/src/app.ts`
- `withastro-flue:packages/cli/src/lib/dev.ts`
- `withastro-flue:packages/cli/src/lib/config.ts`

---

---
title: "Flue quickstart"
description: "Scaffold a Flue project, run `flue dev`, invoke a workflow with `flue run`, and connect to an agent instance with `flue connect`."
---

The Flue CLI (`@flue/cli`) discovers agents and workflows from a project's source root, builds a Node or Cloudflare artifact, and exposes four day-one commands: `flue init` scaffolds `flue.config.ts`, `flue dev` runs a watch-mode HTTP server on port **3583** by default, `flue run <workflow>` performs a one-shot Node workflow invocation, and `flue connect <agent> <instance-id>` opens an interactive stdin prompt loop against a built agent.

## Prerequisites

| Requirement | Detail |
| --- | --- |
| Node.js | `>=22.19.0` (`@flue/cli` engines constraint) |
| Packages | `@flue/runtime` (runtime) and `@flue/cli` (CLI, dev dependency) |
| `target` | Required on the CLI (`--target node` or `--target cloudflare`) or in `flue.config.ts` — no default |
| Model access | A provider API key in `.env` or shell env, or a local OpenAI-compatible server registered in `app.ts` |
| Source layout | One of `.flue/`, `src/`, or project root — first existing directory wins |

<Note>
Provider and model registration is a runtime concern in `app.ts` via `registerProvider(...)`, not in `flue.config.ts`. The config file only sets build-shaped knobs: `target`, `root`, and `output`.
</Note>

## Scaffold a project

<Steps>
<Step title="Install packages">

<Tabs>
<Tab title="npm">

```bash
mkdir my-flue-app && cd my-flue-app
npm init -y
npm install @flue/runtime
npm install --save-dev @flue/cli
```

</Tab>
<Tab title="pnpm">

```bash
mkdir my-flue-app && cd my-flue-app
pnpm init
pnpm add @flue/runtime
pnpm add -D @flue/cli
```

</Tab>
</Tabs>

</Step>

<Step title="Initialize config">

`flue init` writes a starter `flue.config.ts` with the chosen `target`. Re-run with `--force` to overwrite an existing config.

```bash
npx flue init --target node
```

Generated file:

```ts title="flue.config.ts"
import { defineConfig } from '@flue/cli/config';

export default defineConfig({
	target: 'node',
});
```

</Step>

<Step title="Add environment variables">

When present, `<project>/.env` loads before config resolution. Shell-exported values override file values.

```bash
echo 'ANTHROPIC_API_KEY="your-api-key"' > .env
```

Add `.env` to `.gitignore`. Do not commit provider credentials.

</Step>

<Step title="Create source modules">

Flue resolves the source root as `<root>/.flue` when that directory exists, otherwise `<root>/src`, otherwise `<root>`. Module names derive from filenames under `agents/` and `workflows/`.

:::files
my-flue-app/
├── flue.config.ts
├── .env
├── package.json
└── src/
    ├── app.ts              # optional Hono composition + registerProvider(...)
    ├── agents/
    │   └── assistant.ts
    └── workflows/
        └── hello.ts
:::

**Agent module** — default export registers the agent; filename becomes the CLI name:

```ts title="src/agents/assistant.ts"
import { createAgent } from '@flue/runtime';

export default createAgent(() => ({
	model: 'anthropic/claude-sonnet-4-6',
	instructions: 'You are a concise, helpful assistant.',
}));
```

**Workflow module** — export `run()`; initialize the agent inside it:

```ts title="src/workflows/hello.ts"
import { createAgent, type FlueContext } from '@flue/runtime';
import * as v from 'valibot';

const agent = createAgent(() => ({ model: 'anthropic/claude-sonnet-4-6' }));

export async function run({ init, log }: FlueContext) {
	const harness = await init(agent);
	const session = await harness.session();
	const response = await session.prompt('What is 2 + 2? Return only the number.', {
		result: v.object({ answer: v.number() }),
	});
	log.info('solved arithmetic prompt', { answer: response.data.answer });
	return response.data;
}
```

The `hello` workflow is available as `flue run hello`. The `assistant` agent is available as `flue connect assistant local`.

</Step>
</Steps>

<Tip>
For AI-guided scaffolding, fetch `https://flueframework.com/start.md` and follow its agent-first layout rules. `flue init` prints this URL as the recommended next step.
</Tip>

## Run the dev server

`flue dev` performs an initial build, starts the target-specific server, watches the project root (150ms debounce), rebuilds on changes, and prints discovered agents, workflows, and channels.

```bash
npx flue dev --target node
```

<ParamField body="--port" type="number">
Dev server listen port. Default: **3583** (`DEFAULT_DEV_PORT`).
</ParamField>

<ParamField body="--root" type="string">
Project root. Default: current working directory.
</ParamField>

<ParamField body="--config" type="string">
Explicit `flue.config.*` path (relative to cwd). CLI flags override config file values.
</ParamField>

<ParamField body="--env" type="string">
Single alternate `.env`-format file. Without `--env`, commands load `<project>/.env` when present.
</ParamField>

Expected stderr output includes target, server URL, source root, and discovery lists:

```text
flue dev
  target    node
  server    http://localhost:3583
  source    src
  ...
agents
  assistant
workflows
  hello
...
connect with: flue connect assistant local
watching for changes; Ctrl+C to stop
```

While `flue dev` is running, workflows are also reachable over HTTP:

```bash
curl -X POST 'http://localhost:3583/workflows/hello?wait=result' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

<Warning>
`flue run` and `flue connect` are Node-only one-shot invokers. For Cloudflare targets, use `flue dev --target cloudflare` and invoke workflows via HTTP (`POST /workflows/:name`).
</Warning>

## Invoke a workflow with `flue run`

`flue run` builds silently, spawns a local Node child process (`FLUE_MODE=local`), invokes the named workflow once, streams agent events to stderr, prints the JSON return value to stdout, and exits.

```bash
npx flue run hello --target node
```

With a JSON payload (defaults to `{}` when omitted):

```bash
npx flue run hello --target node --payload '{"name": "World"}'
```

<ParamField body="workflow" type="string" required>
Positional workflow name matching a file in `workflows/` (basename without extension).
</ParamField>

<ParamField body="--payload" type="string">
JSON object passed to the workflow `run()` context. Must parse as valid JSON.
</ParamField>

<ParamField body="--target" type="string">
Build target. For `flue run`, only `node` is supported. Cloudflare configs exit with a curl-based workaround hint.
</ParamField>

<RequestExample>

```bash
npx flue run hello --target node
```

</RequestExample>

<ResponseExample>

```text
# stderr (branded header + streamed events)
flue run
  workflow  hello
  target    node
run  run_01H...

  solved arithmetic prompt

# stdout (workflow return value)
{
  "answer": 4
}
```

</ResponseExample>

The stderr `run` line carries the generated run ID. Inspect that run while a dev server is active:

```bash
npx flue logs run_01H... --server http://127.0.0.1:3583
```

## Connect to an agent with `flue connect`

`flue connect` builds the project, starts a local agent process, waits for readiness (60s timeout), then reads prompts from stdin one line at a time until EOF (`Ctrl+D`).

```bash
npx flue connect assistant local --target node
```

<ParamField body="agent" type="string" required>
Agent module name (filename under `agents/` without extension).
</ParamField>

<ParamField body="instance-id" type="string" required>
Opaque instance identifier for this connection (for example `local`, `thread-1`, `user-42`).
</ParamField>

<Warning>
`flue connect` does not accept `--payload`. Enter prompts interactively after connecting.
</Warning>

<RequestExample>

```bash
npx flue connect assistant local --target node
```

</RequestExample>

<ResponseExample>

```text
flue connect assistant/local starting...
connected
enter one prompt per line; Ctrl+D to exit
What can you help with?
{
  "text": "I can answer questions, draft content, and summarize information."
}
```

</ResponseExample>

Each submitted line sends a `prompt` IPC message; agent events (text deltas, tool calls, thinking, errors) render on stderr. Structured results print as JSON on stdout.

## Command comparison

| Command | Mode | Primary use | Exit behavior |
| --- | --- | --- | --- |
| `flue dev` | Long-running watch server | Local iteration, HTTP API, channel webhooks | Runs until `Ctrl+C` (exit 130/143) |
| `flue run <workflow>` | One-shot Node subprocess | CI scripts, ad-hoc workflow execution | Exits after workflow completes |
| `flue connect <agent> <id>` | Interactive Node subprocess | Manual agent testing, REPL-style prompts | Exits on `Ctrl+D` or child crash |

```text
  flue.config.ts + src/{agents,workflows}/
           │
           ▼
      flue build (implicit)
           │
     ┌─────┴─────┬─────────────┐
     ▼           ▼             ▼
 flue dev    flue run    flue connect
 (HTTP :3583) (workflow)  (agent stdin)
```

## Troubleshooting

<AccordionGroup>
<Accordion title="Missing required `target`">

```
[flue] Missing required `target`. Set it via `--target <node|cloudflare>`
or in `flue.config.ts` as `target: "node"` (or `"cloudflare"`).
```

Pass `--target` on every command or set `target` in `flue.config.ts`.
</Accordion>

<Accordion title="Config already exists during `flue init`">

`flue init` refuses to overwrite unless `--force` is passed. Discovery checks all `flue.config.*` basenames, not only `.ts`.
</Accordion>

<Accordion title="Invalid `--payload` JSON">

`flue run` validates `--payload` at parse time. Quote the JSON for the shell:

```bash
npx flue run hello --target node --payload '{"key": "value"}'
```
</Accordion>

<Accordion title="Cloudflare `flue run` / `flue connect` rejected">

Both commands require a Node.js runtime. Start `flue dev --target cloudflare` and call `POST /workflows/:name` instead.
</Accordion>

<Accordion title="Dev server port in use">

Override the default port:

```bash
npx flue dev --target node --port 8787
```
</Accordion>
</AccordionGroup>

## Next

<CardGroup>
<Card title="Installation" href="/installation">
Prerequisites, package managers, and first-install commands.
</Card>
<Card title="Flue project layout" href="/flue-project-layout">
Source-root resolution, module discovery, and `app.ts` composition.
</Card>
<Card title="Flue workflows" href="/flue-workflows">
Author workflow modules, HTTP invocation, and `flue logs` inspection.
</Card>
<Card title="Build Flue agents" href="/build-flue-agents">
`createAgent`, profiles, tools, skills, sandboxes, and providers.
</Card>
<Card title="Flue CLI reference" href="/flue-cli-reference">
Full command flags, defaults, and exit behavior.
</Card>
<Card title="Flue examples" href="/flue-examples">
Copy-pasteable hello-world and integration fixtures.
</Card>
</CardGroup>
