Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ iloom

#### Links to key sections

[How It Works](#how-it-works-the-multi-agent-workflow) • [Installation](#quick-start) • [Configuration](#configuration) • [Advanced Features](#advanced-features) • [Swarm Mode](#swarm-mode-epic-orchestration) • [Telemetry](#telemetry) • [Limitations](#system-requirements--limitations) • [Contributing](#contributing-to-iloom)
[How It Works](#how-it-works-the-multi-agent-workflow) • [Installation](#quick-start) • [Configuration](#configuration) • [Advanced Features](#advanced-features) • [Monorepo](#monorepo-support) • [Swarm Mode](#swarm-mode-epic-orchestration) • [Telemetry](#telemetry) • [Limitations](#system-requirements--limitations) • [Contributing](#contributing-to-iloom)

## How can your team trust the code your AI wrote, when you don't?

Expand Down Expand Up @@ -688,6 +688,34 @@ Then use `il dev-server`, `il open`, or `il run` as normal.

Docker Compose multi-service stacks are not yet supported — see [#332](https://github.com/iloom-ai/iloom-cli/issues/332) for the roadmap. For full configuration options and known limitations, see the [Complete Command Reference](docs/iloom-commands.md#docker-dev-server-mode).

### Monorepo Support

iloom detects monorepo workspace configurations during `il init` and scopes commands to only the packages relevant to your current task.

**Auto-detection:** iloom recognizes `pnpm-workspace.yaml` and `package.json` `workspaces` field, adding `"monorepo"` to your project capabilities automatically.

**How it works:**

1. **Package detection agent** runs before implementation (or after swarm completion in swarm mode) to determine which packages are affected by the current issue
2. **Scoped validation** — `il test`, `il lint`, `il compile`, and `il build` run only against the declared packages, using the correct filter syntax for your package manager (pnpm `--filter`, yarn `workspace`, npm `--workspace`)
3. **Scoped dev server** — `il dev-server` watches for the detection agent's decision and launches from the correct package subdirectory

**Quick setup:**

```bash
# Auto-detected during init
il init

# Or add manually to .iloom/package.iloom.json
{
"capabilities": ["monorepo", "web"]
}
```

Non-monorepo projects are completely unaffected — all commands behave identically to before.

**-> [Complete Monorepo Guide](docs/monorepo-guide.md)** — Full details on detection, package scoping, dev server behavior, and troubleshooting.

### Epic Planning and Decomposition

The `il plan` command launches an interactive Architect session that helps you break down complex features into manageable child issues.
Expand Down Expand Up @@ -820,7 +848,9 @@ This is an early-stage product.

* ✅ **Node.js CLI Tools:** Full support with isolated binary generation.

* ✅ **Multi-Language Projects:** Python, Rust, Ruby, Go, and other languages via `.iloom/package.iloom.json`.
* ✅ **Multi-Language Projects:** Python, Rust, Ruby, Go, and other languages via `.iloom/package.iloom.json`.

* ✅ **Monorepo Projects:** Automatic detection and scoped commands for pnpm, yarn, and npm workspaces. See [Monorepo Support](#monorepo-support) below.

See all [known limitations](https://github.com/iloom-ai/iloom-cli/issues?q=is:issue+is:open+label:known-limitation) on GitHub. If you're feeling left out - you're absolutely right! The best way to complain about something is to fix it. So...

Expand Down
72 changes: 69 additions & 3 deletions docs/iloom-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -800,8 +800,19 @@ il dev-server [identifier] [options]

1. Resolves the target loom
2. Loads environment variables from `.env` files
3. Executes dev script from `package.json` or `.iloom/package.iloom.json`
4. Runs in foreground (useful for debugging and manual testing)
3. Detects project capabilities (web, monorepo, CLI)
4. In monorepo mode: waits for `packagesToRun` to be set in loom metadata, then launches from the package subdirectory
5. Executes dev script from `package.json` or `.iloom/package.iloom.json`
6. Runs in foreground (useful for debugging and manual testing)

**Monorepo Support:**

When both `web` and `monorepo` capabilities are detected, `il dev-server` waits for the package detection agent to set `packagesToRun` in the loom metadata before launching. This ensures the dev server starts from the correct package subdirectory.

- If `packagesToRun` is already set (detection agent ran before `il dev-server`), the server starts immediately.
- If `packagesToRun` is empty, `il dev-server` watches the metadata file (with 15-second polling fallback) and launches once the agent populates it.
- Only the first package in `packagesToRun` is used (multi-package dev servers are not supported in v1).
- Times out after 60 seconds with an actionable error message if the agent never runs.

**Examples:**

Expand Down Expand Up @@ -972,6 +983,10 @@ il build
| Ruby | `bundle install` | `.iloom/package.iloom.json` |
| Go | `go build ./...` | `.iloom/package.iloom.json` |

**Monorepo Support:**

When `packagesToValidate` is set in the loom metadata, `il build` scopes execution to those specific packages using the package manager's workspace filter flag (`--filter` for pnpm, `--workspace` for npm, `--include` for yarn). Packages that do not define a `build` script are gracefully skipped. When `packagesToValidate` is empty or not set, `il build` runs at the project root as normal.

**Notes:**
- Works with any language/framework via `.iloom/package.iloom.json`
- Environment variables are automatically loaded before execution
Expand Down Expand Up @@ -1031,6 +1046,10 @@ il lint feat/my-feature
| Ruby | `rubocop` | `.iloom/package.iloom.json` |
| Go | `golangci-lint run` | `.iloom/package.iloom.json` |

**Monorepo Support:**

When `packagesToValidate` is set in the loom metadata, `il lint` scopes execution to those specific packages using the package manager's workspace filter flag (`--filter` for pnpm, `--workspace` for npm, `--include` for yarn). Packages that do not define a `lint` script are gracefully skipped. When `packagesToValidate` is empty or not set, `il lint` runs at the project root as normal.

**Notes:**
- Works with any linter via `.iloom/package.iloom.json`
- Environment variables are automatically loaded before execution
Expand Down Expand Up @@ -1090,6 +1109,10 @@ il test feat/my-feature
| Ruby | `bundle exec rspec` | `.iloom/package.iloom.json` |
| Go | `go test ./...` | `.iloom/package.iloom.json` |

**Monorepo Support:**

When `packagesToValidate` is set in the loom metadata, `il test` scopes execution to those specific packages using the package manager's workspace filter flag (`--filter` for pnpm, `--workspace` for npm, `--include` for yarn). Packages that do not define a `test` script are gracefully skipped. When `packagesToValidate` is empty or not set, `il test` runs at the project root as normal.

**Notes:**
- Works with any test framework via `.iloom/package.iloom.json`
- Environment variables are automatically loaded before execution
Expand Down Expand Up @@ -1156,6 +1179,10 @@ il typecheck feat/my-feature
| Rust | `cargo check` | `.iloom/package.iloom.json` |
| Go | `go build ./...` (no-op compile) | `.iloom/package.iloom.json` |

**Monorepo Support:**

When `packagesToValidate` is set in the loom metadata, `il compile` scopes execution to those specific packages using the package manager's workspace filter flag (`--filter` for pnpm, `--workspace` for npm, `--include` for yarn). Packages that do not define a `compile` or `typecheck` script are gracefully skipped. When `packagesToValidate` is empty or not set, `il compile` runs at the project root as normal.

**Notes:**
- Works with any compiler/type checker via `.iloom/package.iloom.json`
- Useful for catching type errors without running full test suite
Expand Down Expand Up @@ -2162,10 +2189,49 @@ il init "configure neon database with project ID abc-123"
- IDE preference (VS Code, Cursor, Windsurf, etc.)
- Merge behavior (local, pr, draft-pr)
- Permission modes
- Project type (web app, CLI tool, etc.)
- Project type (web app, CLI tool, monorepo, etc.)
- Base port for development servers
- Environment variable names

**Project Capabilities:**

iloom detects and persists project capabilities in `.iloom/package.iloom.json` under the `capabilities` field. Valid values are:

| Capability | Description | Auto-detected from |
|------------|-------------|-------------------|
| `"web"` | Web application with a dev server | React, Next.js, Vite, and other web framework dependencies |
| `"cli"` | Command-line tool with a `bin` entry | `bin` field in `package.json` |
| `"monorepo"` | Monorepo with multiple workspace packages | `pnpm-workspace.yaml` file OR `workspaces` field in `package.json` |

Capabilities can also be set manually in `.iloom/package.iloom.json`:
```json
{
"capabilities": ["monorepo"]
}
```

The `monorepo` capability is auto-detected during `il init` when either of these workspace configuration files are present:
- `pnpm-workspace.yaml` — used by pnpm workspaces
- `workspaces` field in `package.json` — used by yarn and npm workspaces

**Monorepo Package Detection:**

When the `"monorepo"` capability is set, iloom integrates a dedicated `iloom-monorepo-package-detector` agent into the workflow to determine which packages to run and validate.

*How it works:*
- The detection agent explores the workspace structure, reads the issue context, and calls MCP tools to set `packagesToRun` and `packagesToValidate` on the loom metadata
- In **non-swarm mode**: the detection agent runs before the implementation agent begins
- In **swarm mode**: the detection agent runs after the swarm completes (called by the orchestrator), not by individual child agents

*Package metadata reminder:*
When the `"monorepo"` capability is set, a UserPromptSubmit hook injects a reminder into each agent session: if the agent touches packages not already listed in `packagesToValidate`, it should call `mcp__recap__set_packages_to_validate` to update the list.

*MCP tools used:*
- `set_package_to_run` — declare which package to run (dev server, etc.)
- `set_packages_to_validate` — declare which packages need test/lint/build validation

When the project does not have the `"monorepo"` capability, no detection agent runs and no monorepo instructions appear in prompts.

**Jira Advanced Settings:**

The following Jira settings can be configured in `.iloom/settings.json` under `issueManagement.jira`:
Expand Down
142 changes: 142 additions & 0 deletions docs/monorepo-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Monorepo Support in iloom

This guide explains how iloom detects and works with monorepo projects, how package detection works, and how validation and dev server commands are scoped to specific packages.

## What is Monorepo Support?

Monorepo support means iloom is aware that your repository contains multiple packages in subdirectories and can scope its commands — validation, testing, linting, and the dev server — to only the packages relevant to a given loom's changes. This keeps CI-style commands fast and avoids running tests for packages that were not touched.

## How Monorepo Detection Works

iloom detects the `monorepo` capability automatically during `il init` by looking for workspace configuration files in the repository root:

- **`pnpm-workspace.yaml`** — used by pnpm workspaces
- **`workspaces` field in `package.json`** — used by yarn and npm workspaces

When either is present, iloom adds `"monorepo"` to the `capabilities` array in `.iloom/package.iloom.json`:

```json
{
"capabilities": ["monorepo", "web"]
}
```

You can also add `"monorepo"` manually to `.iloom/package.iloom.json` if auto-detection does not apply to your project layout.

## The Package Detection Agent

When the `monorepo` capability is set, iloom integrates a dedicated `iloom-monorepo-package-detector` agent into the workflow.

**What it does:**

1. The agent explores the loom's workspace, examines the issue context, and determines which packages were touched or need to run.
2. It calls MCP tools to write two arrays to the loom metadata file (`~/.config/iloom-ai/looms/<slug>.json`):
- `packagesToRun` — packages whose dev server should be started (relative paths from repo root, e.g., `./apps/web`)
- `packagesToValidate` — packages that `il test`, `il lint`, `il compile`, and `il build` should be scoped to (relative paths from repo root, e.g., `./packages/api`, `./apps/web`)

**When it runs:**

- **Non-swarm mode**: the detection agent runs before the implementation agent begins.
- **Swarm mode**: the detection agent runs after the swarm completes, called by the orchestrator. Individual child agents do not invoke it directly.

**UserPromptSubmit hook reminder:**

When `monorepo` capability is set, a `UserPromptSubmit` hook injects a reminder into each agent session: if the agent touches packages not already listed in `packagesToValidate`, it should call `mcp__recap__set_packages_to_validate` to update the list.

## How `packagesToRun` and `packagesToValidate` Work

Both fields are arrays of relative paths from the repository root. They live in the loom metadata file at `~/.config/iloom-ai/looms/<slug>.json`.

| Field | Used by | Purpose |
|-------|---------|---------|
| `packagesToRun` | `il dev-server` | Which package's dev server to start |
| `packagesToValidate` | `il test`, `il lint`, `il compile`, `il build` | Which packages to scope validation to |

These fields are set by the package detection agent. They can also be set manually via MCP tools (`mcp__recap__set_package_to_run`, `mcp__recap__set_packages_to_validate`) if you need to override the agent's decision.

When a field is empty or not set, the corresponding command runs at the project root, covering the entire repository — the same behavior as for non-monorepo looms.

## How Commands Are Scoped

When `packagesToValidate` contains one or more packages, `il test`, `il lint`, `il compile`, and `il build` pass those packages to the package manager's workspace filter mechanism:

| Package Manager | Filter syntax |
|-----------------|--------------|
| **pnpm** | `pnpm --filter ./pkg1 --filter ./pkg2 run <script> --if-present` |
| **npm** | `npm run <script> --workspace=./pkg1 --workspace=./pkg2 --if-present` |
| **yarn (berry)** | `yarn workspaces foreach --include ./pkg1 --include ./pkg2 run <script>` |

Packages that do not define the target script are gracefully skipped (`--if-present` / `--if-present` equivalent).

When `packagesToValidate` is empty (no packages detected, or a non-monorepo loom), the commands run at the project root without any filter, giving you normal full-repo behavior.

## Dev Server Metadata Watching

When both `web` and `monorepo` capabilities are detected, `il dev-server` needs to know which package's dev server to launch. It reads `packagesToRun` from the loom metadata:

1. If `packagesToRun` is already set when `il dev-server` starts, the server launches immediately from that package's subdirectory.
2. If `packagesToRun` is empty, `il dev-server` watches the metadata file (with a 15-second polling fallback) and launches once the package detection agent populates the field.
3. Only the first package in `packagesToRun` is used — multi-package dev servers are not supported in v1.
4. `il dev-server` times out after 60 seconds with an actionable error if the agent never runs.

For non-monorepo looms (or when only the `web` capability is set), `il dev-server` starts from the workspace root immediately, unchanged from pre-monorepo behavior.

## Non-Monorepo Projects Are Unaffected

Projects without the `monorepo` capability behave identically to before. The `packagesToValidate` and `packagesToRun` fields default to empty arrays, and all commands run at the project root without any workspace filters.

## Example: Full Loom Lifecycle for a Monorepo

```
1. il start 123
└── il init previously detected monorepo capability via pnpm-workspace.yaml

2. Package detection agent runs (before implementation agent in non-swarm mode)
└── Sets packagesToRun: ["./apps/web"]
└── Sets packagesToValidate: ["./apps/web", "./packages/ui"]

3. Implementation agent works on the issue

4. il test
└── Reads packagesToValidate from loom metadata
└── Runs: pnpm --filter ./apps/web --filter ./packages/ui run test --if-present

5. il lint
└── Runs: pnpm --filter ./apps/web --filter ./packages/ui run lint --if-present

6. il compile
└── Runs: pnpm --filter ./apps/web --filter ./packages/ui run compile --if-present

7. il dev-server
└── Reads packagesToRun from loom metadata
└── Starts dev server in ./apps/web/

8. il finish 123
```

## Manual Override

If the detection agent sets the wrong packages, you can override them before running validation:

```bash
# Via the MCP tool (in an agent session)
mcp__recap__set_packages_to_validate(["./apps/web", "./packages/shared"])

# Or by editing the metadata file directly
# ~/.config/iloom-ai/looms/<slug>.json
```

## Troubleshooting

**Detection agent never ran / metadata is empty:**
- Confirm `"monorepo"` is in `.iloom/package.iloom.json` `capabilities`
- In swarm mode, confirm the orchestrator called the detection agent after swarm completion
- In non-swarm mode, confirm the pre-agent hook is configured

**Dev server times out waiting for `packagesToRun`:**
- Check whether the detection agent completed without error
- As a workaround, set `packagesToRun` manually via `mcp__recap__set_package_to_run`

**Commands run against the entire monorepo instead of scoped packages:**
- `packagesToValidate` is empty — either the detection agent has not run yet, or no packages were detected
- You can set packages manually or re-run the detection agent
11 changes: 9 additions & 2 deletions src/commands/build.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { BuildCommand } from './build.js'
import { GitWorktreeManager } from '../lib/GitWorktreeManager.js'
import { MetadataManager } from '../lib/MetadataManager.js'
import type { GitWorktree } from '../types/worktree.js'
import * as packageJson from '../utils/package-json.js'
import * as packageManager from '../utils/package-manager.js'
Expand All @@ -12,6 +13,7 @@ vi.mock('../utils/IdentifierParser.js', () => ({
parseForPatternDetection: vi.fn(),
})),
}))
vi.mock('../lib/MetadataManager.js')

// Mock package utilities
vi.mock('../utils/package-json.js', () => ({
Expand Down Expand Up @@ -47,6 +49,10 @@ describe('BuildCommand', () => {
beforeEach(() => {
mockGitWorktreeManager = new GitWorktreeManager()
command = new BuildCommand(mockGitWorktreeManager)
// Set up MetadataManager mock to return no packagesToValidate by default
vi.mocked(MetadataManager).mockImplementation(() => ({
readMetadata: vi.fn().mockResolvedValue(null),
} as unknown as MetadataManager))
})

describe('identifier parsing', () => {
Expand Down Expand Up @@ -138,7 +144,7 @@ describe('BuildCommand', () => {

await command.execute({})

expect(packageManager.runScript).toHaveBeenCalledWith('build', mockWorktree.path, [])
expect(packageManager.runScript).toHaveBeenCalledWith('build', mockWorktree.path, [], { packages: [] })
})

it('should pass worktree path to runScript()', async () => {
Expand All @@ -152,7 +158,8 @@ describe('BuildCommand', () => {
expect(packageManager.runScript).toHaveBeenCalledWith(
'build',
mockWorktree.path,
[]
[],
{ packages: [] }
)
})
})
Expand Down
Loading
Loading