# Organizing Flue Agents: .flue Directory Layout vs Root-Level Layout

> Discover Flue's agent organization options: master `.flue` directory layout versus root-level `agents` and `roles` folders. Optimize your project structure effortlessly.

- Repository: [Astro/flue](https://github.com/withastro/flue)
- Tags: architecture
- Published: 2026-05-11

---

**Flue enforces an exclusive choice between two project structures: if a `.flue/` directory exists, all agents and roles are loaded from it exclusively; otherwise, Flue reads from root-level `agents/` and `roles/` folders.**

When working with the [withastro/flue](https://github.com/withastro/flue) SDK, you must decide how to structure your AI agents and role definitions. The framework supports two mutually exclusive layouts, automatically detecting which convention your project follows through a deterministic resolution algorithm implemented in the build pipeline.

## Understanding the Two Layout Options

Flue discovers agents and roles from exactly one location, never both. The decision hinges on the presence of a `.flue/` directory at your project root.

### Root-Level Layout

In this convention, agent definitions live directly under the project root alongside your other source files.

```

my-project/
├─ agents/
│   └─ hello.ts
├─ roles/
│   └─ analyst.md
└─ flue.config.ts

```

Use this layout when you want Flue code co-located with your application source, or when your project follows a traditional structure without a dedicated configuration folder.

### .flue Directory Layout

This alternative nests all Flue-specific files inside a hidden `.flue/` directory, similar to the `.src/` or `.app/` patterns used by modern frameworks.

```

my-project/
├─ .flue/
│   ├─ agents/
│   │   └─ translate.ts
│   ├─ roles/
│   │   └─ analyst.md
│   └─ app.ts        # optional custom app entry

└─ flue.config.ts

```

**Critical rule:** The presence of `.flue/` wins unconditionally. Even if you maintain both `agents/` at root and `.flue/agents/`, Flue ignores the root-level folders entirely when `.flue/` exists.

## How Flue Resolves the Source Root

The core logic resides in [`packages/sdk/src/build.ts`](https://github.com/withastro/flue/blob/main/packages/sdk/src/build.ts), specifically within the `resolveSourceRoot()` helper function:

```typescript
// packages/sdk/src/build.ts
export function resolveSourceRoot(root: string): string {
  const dotFlue = path.join(root, '.flue');
  if (fs.existsSync(dotFlue)) return dotFlue;   // <-- .flue layout takes precedence
  return root;                                 // <-- otherwise use the root
}

```

*Source:* [[`build.ts`](https://github.com/withastro/flue/blob/main/build.ts) lines 94-98](https://github.com/withastro/flue/blob/main/packages/sdk/src/build.ts#L94-L98)

This function returns the effective source root, which then propagates through the entire build pipeline. During initialization, Flue calls this resolver once and uses the returned path for all subsequent discovery operations:

```typescript
const sourceRoot = resolveSourceRoot(root);
const roles   = discoverRoles(sourceRoot);
const agents  = discoverAgents(sourceRoot);

```

*Source:* [[`build.ts`](https://github.com/withastro/flue/blob/main/build.ts) lines 106-118](https://github.com/withastro/flue/blob/main/packages/sdk/src/build.ts#L106-L118)

Because `resolveSourceRoot()` executes at the entry point of every command (build, dev, or deploy), the layout choice remains consistent across the CLI, development server, and production builds.

## Why Two Layouts Exist

The dual-layout system provides specific architectural benefits:

* **Flexibility for existing codebases** – Projects with established `src/` directories or complex monorepo structures can adopt Flue without reorganizing their entire file tree. Root-level layout keeps agents accessible alongside existing business logic.
* **Clean separation of concerns** – The `.flue/` layout creates a dedicated namespace for AI configuration, preventing accidental collisions with existing folders named `agents/` or `roles/`. This isolation mirrors patterns from Next.js, Astro, and other modern frameworks.
* **Deterministic build behavior** – By enforcing an exclusive-or rule (`.flue` wins if present), Flue eliminates ambiguous lookups. The build pipeline never merges files from both locations, ensuring predictable outputs.

## Project Structure Examples

### Root-Level Layout Example

The `examples/hello-world/` directory in the Flue repository demonstrates this approach:

```

examples/hello-world/
├─ agents/
│   └─ greeter.ts
├─ roles/
│   └─ friendly-assistant.md
└─ flue.config.ts

```

Flue reads agents from `./agents/` and roles from `./roles/` because no `.flue/` folder exists.

### .flue Layout Example

The same repository contains hidden `.flue/` trees demonstrating the alternative:

```

examples/hello-world/
├─ .flue/
│   ├─ agents/
│   │   └─ translator.ts
│   └─ roles/
│       └─ code-reviewer.md
└─ flue.config.ts

```

When the `.flue/` directory is present, Flue reads exclusively from `.flue/agents/` and `.flue/roles/`, ignoring any root-level counterparts.

## Practical Implementation

### Detecting the Source Root Programmatically

To query which layout your project uses at runtime:

```typescript
import { resolveSourceRoot } from '@flue/sdk/build';

const projectRoot = process.cwd();      // e.g. "/my-project"
const srcRoot = resolveSourceRoot(projectRoot);

console.log('Flue source root:', srcRoot);
// → prints "/my-project/.flue" if that folder exists,
//   otherwise "/my-project"

```

*Source:* [[`build.ts`](https://github.com/withastro/flue/blob/main/build.ts) lines 94-98](https://github.com/withastro/flue/blob/main/packages/sdk/src/build.ts#L94-L98)

### Discovering Agents Dynamically

Access agent metadata programmatically using the resolved source root:

```typescript
import { discoverAgents, resolveSourceRoot } from '@flue/sdk/build';

const srcRoot = resolveSourceRoot(process.cwd());
const agents = discoverAgents(srcRoot);

// `agents` is an array of { name, filePath, triggers }
agents.forEach(a => console.log(a.name, a.filePath));

```

*Source:* [[`build.ts`](https://github.com/withastro/flue/blob/main/build.ts) lines 106-118](https://github.com/withastro/flue/blob/main/packages/sdk/src/build.ts#L106-L118)

### Scaffolding Connectors with Layout Awareness

When generating new connectors, respect the same layout rule used by the CLI:

```typescript
import * as path from 'path';
import * as fs from 'fs';

const root = process.cwd();
const connectorPath = fs.existsSync(path.join(root, '.flue'))
  ? path.join(root, '.flue', 'connectors', 'daytona.ts')
  : path.join(root, 'connectors', 'daytona.ts');

fs.writeFileSync(connectorPath, `/* Daytona sandbox implementation */`);

```

*Guidance:* See the "File location" section of the [sandbox connector documentation](https://github.com/withastro/flue/blob/main/connectors/sandbox.md#file-location).

## Summary

- **Exclusive layouts** – Flue supports either root-level `agents/`/`roles/` or `.flue/agents/`/`.flue/roles/`, never both simultaneously.
- **Automatic detection** – The `resolveSourceRoot()` function in [`packages/sdk/src/build.ts`](https://github.com/withastro/flue/blob/main/packages/sdk/src/build.ts) checks for `.flue/` existence and returns that path if found; otherwise, it returns the project root.
- **Propagation** – All discovery routines (`discoverAgents()`, `discoverRoles()`, `discoverAppEntry()`) derive paths from the single `sourceRoot` value, ensuring consistency across the build pipeline.
- **Migration path** – You can switch layouts by creating or removing the `.flue/` directory, but you must move all Flue-specific files (agents, roles, optional [`app.ts`](https://github.com/withastro/flue/blob/main/app.ts), connectors) to match the new root.

## Frequently Asked Questions

### What happens if I have both `./agents/` and `./.flue/agents/`?

Flue ignores the root-level `./agents/` directory entirely. The presence of `.flue/` triggers unconditional precedence, meaning all discovery operations read exclusively from `./.flue/`. You cannot merge or combine files from both locations.

### Can I configure Flue to use a custom directory name instead of `.flue/`?

No. The directory name is hardcoded in `resolveSourceRoot()` as `.flue` (lines 94-98 of [`packages/sdk/src/build.ts`](https://github.com/withastro/flue/blob/main/packages/sdk/src/build.ts)). The framework does not expose configuration options to rename this directory or customize the layout detection logic.

### Does the layout choice affect my build output directory?

No. The `dist/` output directory (or your configured build target) remains independent of your source layout. Only the source resolution changes—`resolveSourceRoot()` determines where Flue reads agents from, but the compilation target is calculated separately based on your [`flue.config.ts`](https://github.com/withastro/flue/blob/main/flue.config.ts) settings.

### Where should I place my custom [`app.ts`](https://github.com/withastro/flue/blob/main/app.ts) entry point?

Place [`app.ts`](https://github.com/withastro/flue/blob/main/app.ts), `app.mts`, [`app.js`](https://github.com/withastro/flue/blob/main/app.js), or `app.mjs` inside the active source root. For root-level layouts, put it at [`./app.ts`](https://github.com/withastro/flue/blob/main/./app.ts); for `.flue` layouts, place it at [`./.flue/app.ts`](https://github.com/withastro/flue/blob/main/./.flue/app.ts). The `discoverAppEntry()` function scans the same `sourceRoot` returned by `resolveSourceRoot()` when locating your custom application entry.