iloom supports projects in any programming language, not just Node.js. Whether you're working with Python, Rust, Ruby, Go, or any other language, iloom can provide isolated development environments for your feature branches.
The key to multi-language support is the .iloom/package.iloom.json configuration file, which defines your project's capabilities and scripts. This file works alongside (or instead of) package.json and allows iloom to manage non-Node.js projects effectively.
While iloom has native support for Node.js projects, it also supports:
- Python (pip/venv, Poetry, PDM)
- Rust (Cargo)
- Ruby (Bundler, Rails)
- Go (go modules)
- Java/Kotlin (Gradle, Maven)
- PHP (Composer)
- Any language with command-line build tools
The easiest way to configure iloom for your project is to use the interactive setup:
il initThe framework detector agent will:
- Analyze your project structure and detect your programming language
- Identify common build tools, test frameworks, and dev servers
- Generate a
.iloom/package.iloom.jsonconfiguration file - Prompt you to review and customize the detected configuration
This interactive flow ensures your configuration is tailored to your project without requiring manual file creation.
If you prefer to configure iloom manually or need to customize an existing configuration, create or edit .iloom/package.iloom.json in your project root.
your-project/
├── .iloom/
│ └── package.iloom.json
├── src/
└── ...
{
"name": "my-project",
"capabilities": ["cli", "web"],
"scripts": {
"install": "cargo fetch",
"build": "cargo build --release",
"test": "cargo test",
"dev": "cargo run --bin my-project",
"lint": "cargo clippy",
"typecheck": "cargo check"
}
}The project name. Used for display purposes.
An array defining what type of project this is:
"cli"- Command-line application that produces executable binaries- Enables CLI isolation (unique binary paths per loom)
- Ensures each workspace can have its own built binaries
"web"- Web application with a development server- Enables automatic port assignment (3000 + issue number)
- Prevents port conflicts when running multiple dev servers
You can specify both capabilities if your project is both a CLI and web app.
Shell commands for common development tasks. All scripts are optional - if not defined, that step is skipped.
| Script | Purpose | When Used |
|---|---|---|
install |
Install dependencies | il start (loom creation), il finish (post-merge) |
build |
Compile/build project | il build, il finish (CLI projects, post-merge) |
test |
Run test suite | il test, il finish validation |
dev |
Start dev server | il dev-server |
lint |
Run linter | il lint, il finish validation |
typecheck |
Type checking | il typecheck, il finish validation |
compile |
Alternative to typecheck | il compile, il finish validation (preferred over typecheck if both exist) |
{
"name": "my-python-app",
"capabilities": ["web"],
"scripts": {
"install": "python -m pip install -e .",
"build": "python -m pip install -e .",
"test": "pytest tests/",
"dev": "python -m uvicorn app.main:app --reload --port $PORT",
"lint": "ruff check .",
"typecheck": "mypy src/"
}
}{
"name": "my-poetry-app",
"capabilities": ["web"],
"scripts": {
"install": "poetry install",
"build": "poetry install",
"test": "poetry run pytest",
"dev": "poetry run python -m uvicorn app.main:app --reload --port $PORT",
"lint": "poetry run ruff check .",
"typecheck": "poetry run mypy src/"
}
}{
"name": "my-rust-app",
"capabilities": ["cli", "web"],
"scripts": {
"install": "cargo fetch",
"build": "cargo build --release",
"test": "cargo test",
"dev": "cargo run --bin server -- --port $PORT",
"lint": "cargo clippy -- -D warnings",
"typecheck": "cargo check"
}
}{
"name": "my-rails-app",
"capabilities": ["web"],
"scripts": {
"install": "bundle install",
"build": "bundle exec rails assets:precompile",
"test": "bundle exec rspec",
"dev": "bundle exec rails server -p $PORT",
"lint": "bundle exec rubocop"
}
}{
"name": "my-ruby-app",
"capabilities": ["cli"],
"scripts": {
"install": "bundle install",
"test": "bundle exec rspec",
"lint": "bundle exec rubocop",
"typecheck": "bundle exec steep check"
}
}{
"name": "my-go-app",
"capabilities": ["cli", "web"],
"scripts": {
"install": "go mod download",
"build": "go build -o ./bin/myapp ./cmd/myapp",
"test": "go test ./...",
"dev": "go run ./cmd/server -port $PORT",
"lint": "golangci-lint run",
"typecheck": "go vet ./..."
}
}{
"name": "my-java-app",
"capabilities": ["web"],
"scripts": {
"install": "./gradlew dependencies",
"build": "./gradlew build",
"test": "./gradlew test",
"dev": "./gradlew bootRun --args='--server.port=$PORT'",
"lint": "./gradlew checkstyleMain"
}
}{
"name": "my-php-app",
"capabilities": ["web"],
"scripts": {
"install": "composer install",
"test": "vendor/bin/phpunit",
"dev": "php -S localhost:$PORT -t public",
"lint": "vendor/bin/phpcs"
}
}Understanding how iloom executes scripts helps you write effective configurations:
Scripts defined in package.iloom.json are executed directly as shell commands:
sh -c "<command>"This means:
- You have full shell capabilities (pipes, environment variables, etc.)
- Scripts run in your project's root directory
- The
$PORTenvironment variable is available for web apps - You must include the full command (e.g.,
poetry run pytest, not justpytest)
If package.json exists, scripts are executed via your package manager:
pnpm run <script> # or npm/yarn depending on your lock fileWhen both package.iloom.json and package.json exist:
package.iloom.jsonscripts take precedence- If a script is not defined in
package.iloom.json, iloom falls back topackage.json - This allows gradual migration or hybrid configurations
iloom automatically provides:
$PORT- Unique port for web apps (3000 + issue number)- All variables from your
.envfile (modified for the current loom)
Once configured, use these iloom commands to work with your project:
Runs the build script from your configuration:
il buildThis is equivalent to executing the build command directly, but provides a consistent interface across all projects.
Runs the test script:
il testRuns the lint script:
il lintRuns the dev script for web applications:
il dev-serverThis command automatically sets the $PORT environment variable based on the current loom's issue number.
Declare "cli" capability when your project produces executable binaries:
{
"capabilities": ["cli"]
}This tells iloom that:
- Your project has build artifacts (binaries, executables)
- Each loom should have isolated binary paths
- Build outputs should not conflict between looms
Use cases:
- Command-line tools
- Binary applications
- Anything that compiles to an executable
Declare "web" capability when your project runs a development server:
{
"capabilities": ["web"]
}This tells iloom that:
- Your project needs a unique port per loom
- Port conflicts should be prevented
- The
$PORTvariable should be provided - It should start a web server when you start a new loom
Use cases:
- Web applications
- API servers
- Any application that listens on a network port
Some projects are both CLI and web applications:
{
"capabilities": ["cli", "web"]
}Example: A Rust web framework that:
- Compiles to a binary (
cli) - Runs a web server (
web)
iloom's environment variable isolation works by managing .env files. Features like database branching and loom-specific environment variables require your framework to read configuration from .env files.
The following secret/config storage mechanisms are not managed by iloom. If your framework uses these, database branching and per-loom environment isolation will not work:
- Rails Encrypted Credentials (
config/credentials.yml.enc) - ASP.NET User Secrets (stored in user profile)
- SOPS (Secrets OPerationS) encrypted files
- Kubernetes Secrets / Docker Secrets
- Cloud Provider Secrets (AWS Secrets Manager, GCP Secret Manager, Azure Key Vault)
To use iloom's isolation features with these frameworks, you must configure them to read from .env files instead.
To enable database branching with Rails, configure it to read from environment variables:
# config/database.yml
development:
password: <%= ENV['DATABASE_PASSWORD'] %>
# .env
DATABASE_PASSWORD=secret123iloom will automatically create loom-specific copies of .env with isolated database URLs.
Problem: Running il build fails with "No build script defined in package.iloom.json"
Solution: Add a build script to your .iloom/package.iloom.json:
{
"scripts": {
"build": "your-build-command"
}
}Or run il init to auto-detect your build command.
Problem: Script fails with "command not found" error
Possible causes:
- Command requires a package manager prefix (Poetry, Bundler, etc.)
- Command is not in PATH
- Virtual environment is not activated
Solutions:
- Use full command path:
poetry run pytestnotpytest - Activate environment in script:
source venv/bin/activate && pytest - Use absolute paths:
/usr/local/bin/mycommand
Problem: Dev server fails to start because port is in use
Solutions:
- Ensure your
devscript uses$PORTvariable - Declare
"web"capability inpackage.iloom.json - Check if another loom is using the same issue number
Problem: Scripts are using the wrong database or environment variables
Possible causes:
- Script is not reading from
.env - Framework has its own config system
- Variables are hardcoded
Solutions:
- Configure your framework to read from environment variables
- Use standard
.envfiles instead of framework-specific config - See "Secret Storage Limitations" above
Problem: Building in one loom affects another loom
Solutions:
- Configure build output to use relative paths. Since looms use git worktrees, each loom already has its own directory. Build artifacts are naturally isolated as long as they're relative to the project root (e.g.,
./target,./build,./dist) - If you're building a CLI tool, make sure that the capabilities array includes
cli.
Problem: il init doesn't detect your framework correctly
Solution:
Option 1:
Tell the init agent how you want the scripts to be configured!
Option 2:
Manually create .iloom/package.iloom.json using the examples above as a template. The auto-detection is a convenience feature, but manual configuration is fully supported.
Problem: Commands work when run directly but fail through il build, il test, etc.
Possible causes:
- Different working directory
- Missing environment variables
- Shell environment differences
Solutions:
- Scripts run from project root, use relative paths
- Check if environment variables are defined in
.env - Use explicit paths and avoid shell aliases
- Use the
--debugflag to see exactly what commands are being executed:Note: Debug output can be verbose, but it will show the exact shell command and arguments being passed.il --debug build
For more information, see the main README.md or run il --help.