Project-local CLI for git worktree and issue tracker management
  • Scala 91.5%
  • Shell 7.8%
  • CSS 0.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-22 07:54:49 +00:00
.claude/skills/build-iw-cli-skills Remove AI-specific commands from iw-cli core (#324) 2026-04-17 16:24:51 +02:00
.git-hooks IW-106: Restructure source code location - separate from .iw directory (#330) 2026-04-02 23:21:06 +02:00
.github fix(ci): let the contract label actually trigger the contract job 2026-07-30 15:19:46 +02:00
.iw IW config update to forgejo 2026-06-26 23:35:41 +02:00
commands refactor(IW-422): bind the doctor check at the composition root 2026-08-21 15:44:04 +02:00
core Merge pull request 'Support herdr as a terminal workspace manager beside tmux' (#427) from IW-422 into main 2026-08-22 07:54:49 +00:00
dashboard feat(gates): a verb that asks a gate point what it still owes 2026-08-14 22:20:32 +02:00
docs workflow: triage lessons for IW-422 (entries 5-6) 2026-08-21 15:44:58 +02:00
project-management chore(IW-422): update review-state to all_complete 2026-08-21 23:00:04 +02:00
research fix(doctor): Remove timeout from runInteractive 2026-02-13 12:06:15 +01:00
schemas feat(ci-gate): gate work that has no phase, and keep the budget across resumes 2026-08-13 10:34:00 +02:00
scripts fix(IW-399): address Phase 3 code-review findings 2026-07-24 21:44:59 +02:00
test test: assert JSON on stdout alone, not on merged output 2026-08-21 13:53:34 +02:00
.gitignore feat(ci-gate): normalise CI job logs into greppable evidence 2026-08-11 22:53:47 +02:00
.mill-version IW-345: Mill module for dashboard with Vite + Tailwind + Web Awesome (#370) 2026-04-27 11:30:26 +02:00
.scalafix.conf feat(quality): Add CI workflow, git hooks, scalafix config, and CONTRIBUTING.md 2026-02-13 14:08:43 +01:00
.scalafmt.conf IW-345: Mill module for dashboard with Vite + Tailwind + Web Awesome (#370) 2026-04-27 11:30:26 +02:00
build.mill test: Rebalance suite — unit + contract + smoke + scoverage (+ 0.6.0 bump) (#377) 2026-06-11 09:53:09 +02:00
CLAUDE.md Workspace port + tmux backend 2026-08-20 17:18:02 +02:00
CONTRIBUTING.md IW-106: Restructure source code location - separate from .iw directory (#330) 2026-04-02 23:21:06 +02:00
iw IW-106: Restructure source code location - separate from .iw directory (#330) 2026-04-02 23:21:06 +02:00
iw-bootstrap Phase 1: Consumption side — docs + bootstrap repoint (#403) 2026-07-11 10:52:06 +00:00
iw-run feat(gates): a verb that asks a gate point what it still owes 2026-08-14 22:20:32 +02:00
llms.txt IW-106: Restructure source code location - separate from .iw directory (#330) 2026-04-02 23:21:06 +02:00
mill IW-345: Mill module for dashboard with Vite + Tailwind + Web Awesome (#370) 2026-04-27 11:30:26 +02:00
README.md feat(hooks): tell session hooks which kind of worktree they are in 2026-07-27 15:49:21 +02:00
RELEASE.md fix(IW-399): address Phase 3 code-review findings 2026-07-24 21:44:59 +02:00
VERSION Workspace port + tmux backend 2026-08-20 17:18:02 +02:00

iw-cli

Project-local CLI tool for managing git worktrees and integrating with issue trackers.

Overview

iw is a project-local tool (similar to mill) that:

  • Manages git worktrees as sibling directories for isolated development
  • Integrates with issue trackers (Linear, YouTrack, GitHub, GitLab)
  • Provides tmux session management per worktree
  • Supports project-specific extensibility via scala-cli scripts

Installation

Quick Start

Download and run the bootstrap script in your project root:

curl -L https://code.iterative.works/iterative-works/iw-cli/releases/download/vlatest/iw-bootstrap -o iw
chmod +x iw
./iw init

The bootstrap script will:

  1. Read the version from .iw/config.conf (defaults to "latest")
  2. Download the specified version to ~/.local/share/iw/versions/<version>/
  3. Pre-compile dependencies for offline use
  4. Execute the requested command

Version Pinning

Pin a specific version in your project's .iw/config.conf:

version = "0.1.0"

Or use the latest release:

version = "latest"

If not specified, defaults to "latest".

Requirements

  • scala-cli: Install from scala-cli.virtuslab.org
  • git: For worktree management
  • tmux (optional): For session management
  • curl: For downloading releases

Dashboard contributor requirements

Additional prerequisites for working on the dashboard module:

  • Node 20+ with corepack enable (drives Yarn 4 via the packageManager field in package.json)
  • Yarn 4 — installed automatically by Corepack; no separate install needed
  • Mill 1.1.5 — the committed ./mill wrapper downloads it on first use
  • WEBAWESOME_NPM_TOKEN — set in env before running yarn install; needed to fetch licensed UI components

Commands

Command Description
iw init Interactive setup, creates .iw/config.conf
iw doctor Validate environment (tokens, tmux, etc.)
iw start [--prompt <text>] (<issue-id> | --effort <slug>) Create sibling worktree + tmux session
iw open [--prompt <text>] [issue-id|effort-slug] Open tmux session (defaults to current branch)
iw rm <issue-id|effort-slug> Kill session, remove worktree, delete branch
iw issue [issue-id] Fetch issue details from configured tracker
iw projects [--json] List all registered projects
iw worktrees [--all] [--json] List worktrees for current project
iw status [issue-id|effort-slug] [--json] Show detailed worktree status
iw review-state Manage review-state.json files (validate, write, update)
iw ./test [unit|compile|e2e] Run tests (project-specific command)

Agent integration

The --prompt flag on start and open enables agent-driven development. When provided, iw launches a Claude Code session in the tmux pane with the given prompt and exits immediately (detached mode).

# Start work on an issue with an agent
iw start 203 --prompt "Analyze this issue and create analysis.md"

# Send new instructions to an existing session
iw open 203 --prompt "Continue with implementation phase 2"

The projects, worktrees, and status commands support --json for machine-readable output, enabling agents to discover and monitor worktrees programmatically.

# List all projects
iw projects --json

# List worktrees for current project
iw worktrees --json

# Get detailed status for a worktree
iw status IW-203 --json

For detailed documentation on review-state commands, see docs/commands/review-state.md.

Effort worktrees

A worktree is keyed either on a tracker issue or on an effort — a human-named branch with no tracker issue, hosting several slices that ship as one PR. The effort must already be framed (project-management/efforts/<slug>/ present) before the worktree is created:

iw start --effort mail-capture

This creates branch mail-capture and worktree <project>-mail-capture. iw open, iw rm and iw status accept the slug wherever they accept an issue ID.

See docs/effort-worktrees.md for the slug rules, the on-disk layout, and what the dashboard shows for an effort worktree.

Plugins

AI-specific workflow commands (e.g. implement, batch-implement, transcript-analyze) are not part of iw-cli core. They are distributed as plugin commands — the reference distribution is kanon, which packages Claude Code integrations as hooks and commands.

Plugins are discovered automatically under $XDG_DATA_HOME/iw/plugins/ (defaults to ~/.local/share/iw/plugins/) or via the IW_PLUGIN_DIRS environment variable. A plugin's commands are invoked with the <plugin>/<command> syntax:

iw kanon/batch-implement IW-123
iw kanon/implement IW-123 --phase 2

Plugins may also contribute hooks that extend the behavior of core commands (e.g. kanon's claude-session.hook-start.scala adds an agent session launch to iw start).

A hook may declare the version it was written against with a // REQUIRES: iw-cli >= X.Y.Z header. Hooks the installed version cannot satisfy are skipped with a warning naming the hook and both versions, and the command runs without them — a hook decorates a command rather than being the thing you asked to run, so one stale plugin hook never blocks it. Commands carrying the same header are stricter: an unsatisfied requirement stops the run.

Session hooks receive a SessionContext carrying key (the identity the worktree is addressed under) and kind (Issue or Effort). Read kind before interpreting key: only an issue key resolves against the tracker — an effort worktree's key is its slug. See docs/effort-worktrees.md.

Setting IW_PLUGINS_DISABLED=1 suppresses discovery from every source, so the run only sees the commands and hooks shipped with the installation and the project. Use it to reproduce iw-cli behavior without installed plugins.

GitHub Integration

Repository Auto-Detection

When using iw init --tracker=github, the repository is automatically detected from your git remote URL:

# Automatically detects "iterative-works/iw-cli" from remote URL
iw init --tracker=github

Supported URL formats:

  • HTTPS: https://github.com/owner/repo.git
  • HTTPS without .git: https://github.com/owner/repo
  • HTTPS with username: https://username@github.com/owner/repo.git
  • HTTPS with trailing slash: https://github.com/owner/repo/
  • SSH: git@github.com:owner/repo.git
  • SSH without .git: git@github.com:owner/repo

Multiple remotes: When your repository has multiple remotes (e.g., origin and upstream), the origin remote is always used for auto-detection.

Manual input: If auto-detection fails (non-GitHub remote or no remote configured), you'll be prompted to enter the repository manually in owner/repo format.

Authentication

GitHub integration uses the gh CLI for authentication:

# Authenticate once
gh auth login

# Then use iw normally
iw init --tracker=github
iw issue

No API tokens needed - iw uses your existing gh authentication.

GitLab Integration

Repository Auto-Detection

When using iw init --tracker=gitlab, the repository is automatically detected from your git remote URL:

# Automatically detects "owner/project" from remote URL
iw init --tracker=gitlab --team-prefix=PROJ

Supported URL formats:

  • HTTPS: https://gitlab.com/owner/project.git
  • HTTPS without .git: https://gitlab.com/owner/project
  • HTTPS with trailing slash: https://gitlab.com/owner/project/
  • SSH: git@gitlab.com:owner/project.git
  • SSH without .git: git@gitlab.com:owner/project
  • Self-hosted: https://gitlab.company.com/owner/project.git
  • Nested groups: https://gitlab.com/company/team/project.git

Multiple remotes: When your repository has multiple remotes (e.g., origin and upstream), the origin remote is always used for auto-detection.

Manual input: If auto-detection fails (non-GitLab remote or no remote configured), you'll be prompted to enter the repository manually in owner/project or group/subgroup/project format.

Authentication

GitLab integration uses the glab CLI for authentication:

# Install glab CLI (macOS)
brew install glab

# Install glab CLI (Linux/Windows)
# See https://gitlab.com/gitlab-org/cli#installation

# Authenticate once
glab auth login

# Then use iw normally
iw init --tracker=gitlab --team-prefix=PROJ
iw issue

No API tokens needed - iw uses your existing glab authentication.

Self-Hosted GitLab

For self-hosted GitLab instances, specify the base URL during initialization:

iw init --tracker=gitlab --team-prefix=PROJ --base-url=https://gitlab.company.com

Or add it manually to .iw/config.conf:

tracker {
  type = gitlab
  repository = "team/project"
  teamPrefix = "PROJ"
  baseUrl = "https://gitlab.company.com"  # Optional, defaults to gitlab.com
}

Nested Groups

GitLab supports nested group structures like company/team/project. These are fully supported:

# Auto-detection works with nested groups
git remote add origin https://gitlab.com/company/team/project.git
iw init --tracker=gitlab --team-prefix=PROJ

Configuration:

tracker {
  type = gitlab
  repository = "company/team/project"
  teamPrefix = "PROJ"
}

Worktree Layout

Worktrees are created as sibling directories:

~/projects/
├── myproject/              # main worktree (master/main)
├── myproject-IW-123/       # worktree for issue IW-123
├── myproject-IW-456/       # worktree for issue IW-456

Development

Setup

Clone the repository and use the local iw script directly:

git clone https://code.iterative.works/iterative-works/iw-cli.git
cd iw-cli
./iw --list

The local iw script runs commands from commands/ without downloading anything - ideal for development.

Testing in Other Projects

To test your local iw-cli changes in other projects, set IW_HOME:

# In any project using iw-bootstrap
IW_HOME=/path/to/iw-cli ./iw --list

# Or export for the session
export IW_HOME=/path/to/iw-cli
./iw start ISSUE-123

This bypasses version download and uses your local iw-run directly.

Testing

The project has three types of tests:

Unit Tests (Scala/munit)

Run Scala unit tests with:

./iw ./test unit

Or directly with scala-cli:

scala-cli test core/

E2E Tests (BATS)

End-to-end tests verify the CLI behavior. By default, tests that would create real Linear issues are skipped.

Run E2E tests (without live API calls):

./iw ./test e2e

Or directly with BATS:

bats test/

Live API Tests

Some E2E tests can create real Linear issues for comprehensive testing. These are disabled by default to prevent issue accumulation.

To enable live API tests:

ENABLE_LIVE_API_TESTS=1 ./iw ./test e2e

Requirements:

  • LINEAR_API_TOKEN environment variable must be set
  • ENABLE_LIVE_API_TESTS=1 must be explicitly set

Warning: Live API tests will create real issues in Linear with [TEST] prefix. These should be cleaned up periodically.

Run All Tests

./iw ./test

Or manually:

scala-cli test core/ && bats test/

Project Structure

iw-cli/
├── iw                    # Development launcher (runs locally)
├── iw-bootstrap          # Distribution bootstrap (downloads releases)
├── iw-run                # Distribution launcher (in release tarball)
├── VERSION               # Current version
├── commands/             # Shared command implementations (*.scala)
├── core/                 # Shared library code
│   ├── project.scala     # Build configuration (deps, Scala version)
│   └── test/             # Unit tests
├── test/                 # E2E tests (*.bats)
├── scripts/              # Build/release scripts
├── .iw/
│   ├── config.conf       # Project config (tracker, etc.)
│   └── commands/         # Project-specific commands (test.scala)
└── RELEASE.md            # Release process documentation

Creating a Release

See RELEASE.md for the full release process. Quick version:

scripts/package-release.sh 0.1.0
# Creates release/iw-cli-0.1.0.tar.gz

License

MIT