- Scala 91.5%
- Shell 7.9%
- CSS 0.6%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
Release / release (push) Successful in 19m35s
Bump version to 0.22.1 |
||
| .claude | ||
| .git-hooks | ||
| .github | ||
| .iw | ||
| commands | ||
| core | ||
| dashboard | ||
| docs | ||
| project-management | ||
| schemas | ||
| scripts | ||
| test | ||
| webapp-knowledge | ||
| .gitignore | ||
| .mill-version | ||
| .scalafix.conf | ||
| .scalafmt.conf | ||
| build.mill | ||
| CLAUDE.md | ||
| CONTRIBUTING.md | ||
| iw | ||
| iw-bootstrap | ||
| iw-run | ||
| llms.txt | ||
| mill | ||
| README.md | ||
| RELEASE.md | ||
| VERSION | ||
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:
- Read the version from
.iw/config.conf(defaults to "latest") - Download the specified version to
~/.local/share/iw/versions/<version>/ - Pre-compile dependencies for offline use
- 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 thepackageManagerfield inpackage.json) - Yarn 4 — installed automatically by Corepack; no separate install needed
- Mill 1.1.5 — the committed
./millwrapper downloads it on first use WEBAWESOME_NPM_TOKEN— set in env before runningyarn install; needed to fetch licensed UI components
Commands
An orientation list, by name and one clause each — not the flags or exit
codes, which live in one place: run iw --list for the whole roster, grouped,
and iw <cmd> --help for one command's full surface.
| Command | Description |
|---|---|
iw init |
Interactive setup, creates .iw/config.conf |
iw doctor |
Validate environment (tokens, tmux, etc.) |
iw start |
Create sibling worktree + tmux session |
iw open |
Open tmux session (defaults to current branch) |
iw rm |
Close workspace, remove worktree, keep branch |
iw issue |
Fetch issue details from configured tracker |
iw projects |
List all registered projects |
iw worktrees |
List worktrees for current project |
iw status |
Show detailed worktree status |
iw review-state |
Locate, validate, write and update the review-state card (kept in the common git dir, not in the tree) |
iw service |
Declare the services of a worktree (add, list, rm) for the dashboard to link to |
iw port-slot |
Claim or list the port slot of a worktree, a number unique among the worktrees of the repository (claim, list) |
iw ./test |
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"
When standard input is not a terminal (an agent shell, CI, cron), start and open do all their work, do not attach, print the attach command, and exit 0 (5 if a --prompt was not delivered).
For an unattended caller that launches its own agent process separately, start --no-agent creates the worktree and workspace and runs the setup hooks (direnv allow, a search-index build), but skips the session-action hook that would launch an agent, and does not attach or switch to the session. The setup commands are sent to the pane and can still be running when start returns.
# Create the worktree and session without launching an agent
iw start 203 --no-agent
--no-agent cannot be combined with --prompt.
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 the card format, the schema contract and common workflows, see
docs/review-state-cards.md; for the flags and
exit codes themselves, see iw review-state --help or the generated
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 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.
Removing a worktree
iw rm closes the workspace, runs the cleanup hooks, removes the directory and
keeps the branch. It names every uncommitted file before it asks, and refuses
rather than ask when it has no terminal to ask in.
See docs/commands/rm.md for the flags and the exit codes a caller reads it by, and docs/worktree-removal.md for what it refuses beyond those and how the dashboard's Remove control uses it.
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). iw start --no-agent skips that session-action hook — the setup hooks still run.
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_TOKENenvironment variable must be setENABLE_LIVE_API_TESTS=1must 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