Skip to content

kortex-hub/kortex-cli

Repository files navigation

kortex-cli

codecov Documentation

kortex-cli is a command-line interface for launching and managing AI agents in isolated, reproducible workspaces. It creates runtime-based environments (containers, VMs, or other backends) where agents run with your project source code mounted, automatically configured and ready to use — no manual onboarding or setup required.

The architecture is built around pluggable runtimes. The first supported runtime is Podman, which creates container-based workspaces using a custom Fedora image. Additional runtimes (e.g., MicroVM, Kubernetes) can be added to support other execution environments.

Supported Agents

  • Claude Code - Anthropic's official CLI for Claude
  • Goose - AI agent for development tasks
  • Cursor - AI-powered code editor agent

Key Features

  • Isolated workspaces per project, each running in its own runtime instance
  • Pluggable runtime system — Podman is the default, with support for adding other runtimes
  • Automatic agent configuration (onboarding flags, trusted directories) on workspace creation
  • Multi-level configuration: workspace, global, project-specific, and agent-specific settings
  • Inject environment variables and mount directories into workspaces at multiple scopes
  • Connect to MCP servers and integrate with various LLM providers (including Vertex AI)
  • Consistent CLI interface across different agent types and runtimes

Getting Started

Prerequisites

  • Go 1.26+
  • Make

Build

make build

This creates the kortex-cli binary in the current directory.

Run

# Display help and available commands
./kortex-cli --help

# Execute a specific command
./kortex-cli <command> [flags]

Install

To install the binary to your GOPATH/bin for system-wide access:

make install

Run Tests

# Run all tests
make test

# Run tests with coverage report
make test-coverage

Glossary

Agent

An AI assistant that can perform tasks autonomously. In kortex-cli, agents are the different AI tools (Claude Code, Goose, Cursor) that can be launched and configured.

LLM (Large Language Model)

The underlying AI model that powers the agents. Examples include Claude (by Anthropic), GPT (by OpenAI), and other language models.

MCP (Model Context Protocol)

A standardized protocol for connecting AI agents to external data sources and tools. MCP servers provide agents with additional capabilities like database access, API integrations, or file system operations.

Runtime

The environment where workspaces run. kortex-cli supports multiple runtimes (e.g., Podman containers), allowing workspaces to be hosted on different backends depending on your needs.

Skills

Pre-configured capabilities or specialized functions that can be enabled for an agent. Skills extend what an agent can do, such as code review, testing, or specific domain knowledge.

Workspace

A registered directory containing your project source code and its configuration. Each workspace is tracked by kortex-cli with a unique ID and a human-readable name. Workspaces can be accessed using either their ID or name in all commands (start, stop, remove, terminal).

Scenarios

Claude with a Model from Vertex AI

This scenario demonstrates how to configure Claude Code to use a model hosted on Google Cloud Vertex AI instead of the default Anthropic API. This is useful when you need to use Claude through your Google Cloud organization's billing or compliance setup.

Prerequisites:

  • A Google Cloud project with the Vertex AI API enabled and Claude models available
  • Google Cloud credentials configured on your host machine (via gcloud auth application-default login)

Step 1: Configure Claude agent settings

Create or edit ~/.kortex-cli/config/agents.json to add the required environment variables and mount your Google Cloud credentials into the workspace:

{
  "claude": {
    "environment": [
      {
        "name": "CLAUDE_CODE_USE_VERTEX",
        "value": "1"
      },
      {
        "name": "ANTHROPIC_VERTEX_PROJECT_ID",
        "value": "my-gcp-project-id"
      },
      {
        "name": "CLOUD_ML_REGION",
        "value": "my-region"
      }
    ],
    "mounts": [
      {"host": "$HOME/.config/gcloud", "target": "$HOME/.config/gcloud", "ro": true}
    ]
  }
}

Fields:

  • CLAUDE_CODE_USE_VERTEX - Set to 1 to instruct Claude Code to use Vertex AI instead of the Anthropic API
  • ANTHROPIC_VERTEX_PROJECT_ID - Your Google Cloud project ID where Vertex AI is configured
  • CLOUD_ML_REGION - The region where Claude is available on Vertex AI (e.g., us-east5)
  • $HOME/.config/gcloud mounted read-only - Provides the workspace access to your application default credentials

Step 2: Register and start the workspace

# Register a workspace with the Podman runtime and Claude agent
kortex-cli init /path/to/project --runtime podman --agent claude

# Start the workspace (using name or ID)
kortex-cli start my-project

# Connect to the workspace — Claude Code will use Vertex AI automatically
kortex-cli terminal my-project

When Claude Code starts, it detects ANTHROPIC_VERTEX_PROJECT_ID and CLOUD_ML_REGION and routes all requests to Vertex AI using the mounted application default credentials.

Sharing local Claude settings (optional)

To reuse your host Claude Code settings (preferences, custom instructions, etc.) inside the workspace, add ~/.claude and ~/.claude.json to the mounts:

{
  "claude": {
    "environment": [
      {
        "name": "CLAUDE_CODE_USE_VERTEX",
        "value": "1"
      },
      {
        "name": "ANTHROPIC_VERTEX_PROJECT_ID",
        "value": "my-gcp-project-id"
      },
      {
        "name": "CLOUD_ML_REGION",
        "value": "my-region"
      }
    ],
    "mounts": [
      {"host": "$HOME/.config/gcloud", "target": "$HOME/.config/gcloud", "ro": true},
      {"host": "$HOME/.claude", "target": "$HOME/.claude"},
      {"host": "$HOME/.claude.json", "target": "$HOME/.claude.json"}
    ]
  }
}

~/.claude contains your Claude Code configuration directory (skills, settings) and ~/.claude.json stores your account and preferences. These are mounted read-write so that changes made inside the workspace (e.g., updated preferences) are persisted back to your host.

Notes:

  • Run gcloud auth application-default login on your host machine before starting the workspace to ensure valid credentials are available
  • The $HOME/.config/gcloud mount is read-only to prevent the workspace from modifying your host credentials
  • No ANTHROPIC_API_KEY is needed when using Vertex AI — credentials are provided via the mounted gcloud configuration
  • To pin a specific Claude model, add a ANTHROPIC_MODEL environment variable (e.g., "claude-opus-4-5")

Starting Claude with Default Settings

This scenario demonstrates how to pre-configure Claude Code's settings so that when it starts inside a workspace, it skips the interactive onboarding flow and uses your preferred defaults. kortex-cli automatically handles the onboarding flags, and you can optionally customize other settings like theme preferences.

Automatic Onboarding Skip

When you register a workspace with the Claude agent, kortex-cli automatically:

  • Sets hasCompletedOnboarding: true to skip the first-run wizard
  • Sets hasTrustDialogAccepted: true for the workspace sources directory (the exact path is determined by the runtime)

This happens automatically for every Claude workspace — no manual configuration required.

Optional: Customize Theme and Other Settings

If you want to customize Claude's theme or other preferences, create default settings:

Step 1: Create the agent settings directory

mkdir -p ~/.kortex-cli/config/claude

Step 2: Write the default Claude settings file

cat > ~/.kortex-cli/config/claude/.claude.json << 'EOF'
{
  "theme": "dark-daltonized"
}
EOF

Fields:

  • theme - The UI theme for Claude Code (e.g., "dark", "light", "dark-daltonized")

You don't need to set hasCompletedOnboarding or hasTrustDialogAccepted — kortex-cli adds these automatically when creating the workspace.

Step 3: Register and start the workspace

# Register a workspace — the settings file is embedded in the container image
kortex-cli init /path/to/project --runtime podman --agent claude

# Start the workspace (using name or ID)
kortex-cli start my-project

# Connect — Claude Code starts directly without onboarding
kortex-cli terminal my-project

When init runs, kortex-cli:

  1. Reads all files from ~/.kortex-cli/config/claude/ (e.g., your theme preferences)
  2. Automatically adds hasCompletedOnboarding: true and marks the workspace sources directory as trusted (the path is determined by the runtime)
  3. Copies the final merged settings into the container image at /home/agent/.claude.json

Claude Code finds this file on startup and skips onboarding.

Notes:

  • Onboarding is skipped automatically — even if you don't create any settings files, kortex-cli ensures Claude starts without prompts
  • The settings are baked into the container image at init time, not mounted at runtime — changes to the files on the host require re-registering the workspace to take effect
  • Any file placed under ~/.kortex-cli/config/claude/ is copied into the container home directory, preserving the directory structure (e.g., ~/.kortex-cli/config/claude/.some-tool/config becomes /home/agent/.some-tool/config inside the container)
  • This approach keeps your workspace self-contained — other developers using the same project are not affected, and your local ~/.claude directory is not exposed inside the container
  • To apply changes to the settings, remove and re-register the workspace: kortex-cli remove <workspace-id> then kortex-cli init again

Using Goose Agent with a Model from Vertex AI

This scenario demonstrates how to configure the Goose agent in a kortex-cli workspace using Vertex AI as the backend, covering credential injection, sharing your local gcloud configuration, and pre-configuring the default model.

Authenticating with Vertex AI

Goose can use Google Cloud Vertex AI as its backend. Authentication relies on Application Default Credentials (ADC) provided by the gcloud CLI. Mount your local ~/.config/gcloud directory to make your host credentials available inside the workspace, and set the GCP_PROJECT_ID, GCP_LOCATION, and GOOSE_PROVIDER environment variables to tell Goose which project and region to use.

Create or edit ~/.kortex-cli/config/agents.json:

{
  "goose": {
    "environment": [
      {
        "name": "GOOSE_PROVIDER",
        "value": "gcp_vertex_ai"
      },
      {
        "name": "GCP_PROJECT_ID",
        "value": "my-gcp-project"
      },
      {
        "name": "GCP_LOCATION",
        "value": "my-region"
      }
    ],
    "mounts": [
      {"host": "$HOME/.config/gcloud", "target": "$HOME/.config/gcloud", "ro": true}
    ]
  }
}

The ~/.config/gcloud directory contains your Application Default Credentials and active account configuration. It is mounted read-only so that credentials are available inside the workspace while the host configuration remains unmodified.

Then register and start the workspace:

# Register a workspace with the Podman runtime and Goose agent
kortex-cli init /path/to/project --runtime podman --agent goose

# Start the workspace
kortex-cli start my-project

# Connect — Goose starts with Vertex AI configured
kortex-cli terminal my-project

Sharing Local Goose Settings

To reuse your host Goose settings (model preferences, provider configuration, etc.) inside the workspace, mount the ~/.config/goose directory.

Edit ~/.kortex-cli/config/agents.json to add the mount alongside the Vertex AI configuration:

{
  "goose": {
    "environment": [
      {
        "name": "GOOSE_PROVIDER",
        "value": "gcp_vertex_ai"
      },
      {
        "name": "GCP_PROJECT_ID",
        "value": "my-gcp-project"
      },
      {
        "name": "GCP_LOCATION",
        "value": "my-region"
      }
    ],
    "mounts": [
      {"host": "$HOME/.config/gcloud", "target": "$HOME/.config/gcloud", "ro": true},
      {"host": "$HOME/.config/goose", "target": "$HOME/.config/goose"}
    ]
  }
}

The ~/.config/goose directory contains your Goose configuration (settings, model preferences, etc.). It is mounted read-write so that changes made inside the workspace are persisted back to your host.

Using Default Settings

If you want to pre-configure Goose with default settings without exposing your local ~/.config/goose directory inside the container, create default settings files that are baked into the container image at workspace registration time. This is an alternative to mounting your local Goose settings — use one approach or the other, not both.

Automatic Onboarding Skip

When you register a workspace with the Goose agent, kortex-cli automatically sets GOOSE_TELEMETRY_ENABLED to false in the Goose config file if it is not already defined, so Goose skips its telemetry prompt on first launch.

Step 1: Create the agent settings directory

mkdir -p ~/.kortex-cli/config/goose/.config/goose

Step 2: Write the default Goose settings file

As an example, you can configure the model and enable telemetry:

cat > ~/.kortex-cli/config/goose/.config/goose/config.yaml << 'EOF'
GOOSE_MODEL: "claude-sonnet-4-6"
GOOSE_TELEMETRY_ENABLED: true
EOF

Fields:

  • GOOSE_MODEL - The model identifier Goose uses for its AI interactions
  • GOOSE_TELEMETRY_ENABLED - Whether Goose sends usage telemetry; set to true to opt in, or omit to have kortex-cli default it to false

Step 3: Register and start the workspace

# Register a workspace — the settings file is embedded in the container image
kortex-cli init /path/to/project --runtime podman --agent goose

# Start the workspace
kortex-cli start my-project

# Connect — Goose starts with the configured provider and model
kortex-cli terminal my-project

When init runs, kortex-cli:

  1. Reads all files from ~/.kortex-cli/config/goose/ (e.g., your provider and model settings)
  2. Automatically sets GOOSE_TELEMETRY_ENABLED: false in .config/goose/config.yaml if the key is not already defined
  3. Copies the final settings into the container image at /home/agent/.config/goose/config.yaml

Goose finds this file on startup and uses the pre-configured settings without prompting.

Notes:

  • Telemetry is disabled automatically — even if you don't create any settings files, kortex-cli ensures Goose starts without the telemetry prompt
  • If you prefer to enable telemetry, set GOOSE_TELEMETRY_ENABLED: true in ~/.kortex-cli/config/goose/.config/goose/config.yaml
  • The settings are baked into the container image at init time, not mounted at runtime — changes to the files on the host require re-registering the workspace to take effect
  • Any file placed under ~/.kortex-cli/config/goose/ is copied into the container home directory, preserving the directory structure (e.g., ~/.kortex-cli/config/goose/.config/goose/config.yaml becomes /home/agent/.config/goose/config.yaml inside the container)
  • This approach keeps your workspace self-contained — other developers using the same project are not affected, and your local ~/.config/goose directory is not exposed inside the container
  • To apply changes to the settings, remove and re-register the workspace: kortex-cli remove <workspace-id> then kortex-cli init again

Using Cursor CLI Agent

This scenario demonstrates how to configure the Cursor agent in a kortex-cli workspace, covering API key injection, sharing your local Cursor settings, and pre-configuring the default model.

Defining the Cursor API Key via a Secret

Cursor requires a CURSOR_API_KEY environment variable to authenticate with the Cursor service. Rather than embedding the key as plain text, use the secret mechanism to keep credentials out of your configuration files.

Step 1: Create the secret

For the Podman runtime, create the secret once on your host machine using podman secret create:

echo "$CURSOR_API_KEY" | podman secret create cursor-api-key -

Step 2: Reference the secret in agent configuration

Create or edit ~/.kortex-cli/config/agents.json to inject the secret as an environment variable for the cursor agent:

{
  "cursor": {
    "environment": [
      {
        "name": "CURSOR_API_KEY",
        "secret": "cursor-api-key"
      }
    ]
  }
}

Step 3: Register and start the workspace

# Register a workspace with the Podman runtime and Cursor agent
kortex-cli init /path/to/project --runtime podman --agent cursor

# Start the workspace
kortex-cli start my-project

# Connect — Cursor starts with the API key available
kortex-cli terminal my-project

The secret name (cursor-api-key) must match the secret field value in your configuration. At workspace creation time, kortex-cli passes the secret to Podman, which injects it as the CURSOR_API_KEY environment variable inside the container.

Sharing Local Cursor Settings

To reuse your host Cursor settings (preferences, keybindings, extensions configuration, etc.) inside the workspace, mount the ~/.cursor directory.

Edit ~/.kortex-cli/config/agents.json to add the mount:

{
  "cursor": {
    "environment": [
      {
        "name": "CURSOR_API_KEY",
        "secret": "cursor-api-key"
      }
    ],
    "mounts": [
      {"host": "$HOME/.cursor", "target": "$HOME/.cursor"}
    ]
  }
}

The ~/.cursor directory contains your Cursor configuration (settings, model preferences, etc.). It is mounted read-write so that changes made inside the workspace are persisted back to your host.

Using Default Settings

If you want to pre-configure Cursor with default settings without exposing your local ~/.cursor directory inside the container, create default settings files that are baked into the container image at workspace registration time. This is an alternative to mounting your local Cursor settings — use one approach or the other, not both.

Automatic Onboarding Skip

When you register a workspace with the Cursor agent, kortex-cli automatically creates a .workspace-trusted file in the Cursor projects directory for the workspace sources path, so Cursor skips its workspace trust dialog on first launch.

Step 1: Configure the agent environment

Create or edit ~/.kortex-cli/config/agents.json to inject the API key. No mount is needed since settings are baked in:

{
  "cursor": {
    "environment": [
      {
        "name": "CURSOR_API_KEY",
        "secret": "cursor-api-key"
      }
    ]
  }
}

Step 2: Create the agent settings directory

mkdir -p ~/.kortex-cli/config/cursor/.cursor

Step 3: Write the default Cursor settings file

As an example, you can configure a default model:

cat > ~/.kortex-cli/config/cursor/.cursor/cli-config.json << 'EOF'
{
  "model": {
    "modelId": "claude-4.5-opus-high-thinking",
    "displayModelId": "claude-4.5-opus-high-thinking",
    "displayName": "Opus 4.5 Thinking",
    "displayNameShort": "Opus 4.5 Thinking",
    "aliases": [
      "opus",
      "opus-4.5",
      "opus-4-5"
    ],
    "maxMode": false
  },
  "hasChangedDefaultModel": true
}
EOF

Fields:

  • model.modelId - The model identifier used internally by Cursor
  • model.displayName / model.displayNameShort - Human-readable model names shown in the UI
  • model.aliases - Shorthand names that can be used to reference the model
  • model.maxMode - Whether to enable max mode for this model
  • hasChangedDefaultModel - Tells Cursor that the model selection is intentional and should not prompt the user to choose a model

Step 4: Register and start the workspace

# Register a workspace — the settings file is embedded in the container image
kortex-cli init /path/to/project --runtime podman --agent cursor

# Start the workspace
kortex-cli start my-project

# Connect — Cursor starts with the configured model
kortex-cli terminal my-project

When init runs, kortex-cli:

  1. Reads all files from ~/.kortex-cli/config/cursor/ (e.g., your model settings)
  2. Automatically creates the workspace trust file so Cursor skips its trust dialog
  3. Copies the final settings into the container image at /home/agent/.cursor/cli-config.json

Cursor finds this file on startup and uses the pre-configured model without prompting.

Notes:

  • The settings are baked into the container image at init time, not mounted at runtime — changes to the files on the host require re-registering the workspace to take effect
  • Any file placed under ~/.kortex-cli/config/cursor/ is copied into the container home directory, preserving the directory structure (e.g., ~/.kortex-cli/config/cursor/.cursor/cli-config.json becomes /home/agent/.cursor/cli-config.json inside the container)
  • To apply changes to the settings, remove and re-register the workspace: kortex-cli remove <workspace-id> then kortex-cli init again
  • This approach keeps your workspace self-contained — other developers using the same project are not affected, and your local ~/.cursor directory is not exposed inside the container
  • Do not combine this approach with the ~/.cursor mount from the previous section — the mounted directory would override the baked-in defaults at runtime

Sharing a GitHub Token

This scenario demonstrates how to make a GitHub token available inside workspaces using the multi-level configuration system — either globally for all projects or scoped to a specific project.

For all projects

Edit ~/.kortex-cli/config/projects.json and add the token and your git configuration under the global "" key:

{
  "": {
    "environment": [
      {
        "name": "GH_TOKEN",
        "secret": "github-token"
      }
    ],
    "mounts": [
      {"host": "$HOME/.gitconfig", "target": "$HOME/.gitconfig", "ro": true}
    ]
  }
}

The GH_TOKEN variable is automatically picked up by the gh CLI and other GitHub-aware tools running inside the workspace. The $HOME/.gitconfig mount makes your git identity (name, email, aliases, etc.) available to git commands run by the agent.

For a specific project

Use the project identifier as the key instead. The identifier is the git remote URL (without .git) as detected by kortex-cli during init:

{
  "https://github.com/my-org/my-repo/": {
    "environment": [
      {
        "name": "GH_TOKEN",
        "secret": "github-token"
      }
    ]
  }
}

This injects the token only when working on workspaces that belong to https://github.com/my-org/my-repo/, leaving other projects unaffected.

Both at once

You can combine global and project-specific entries in the same file. The project-specific value takes precedence over the global one if both define the same variable:

{
  "": {
    "environment": [
      {
        "name": "GH_TOKEN",
        "secret": "github-token-default"
      }
    ]
  },
  "https://github.com/my-org/my-private-repo/": {
    "environment": [
      {
        "name": "GH_TOKEN",
        "secret": "github-token-private"
      }
    ]
  }
}

Creating the secret

How secrets are created depends on the runtime being used. The secret field value is the name under which the secret is registered with that runtime.

For the Podman runtime, create the secret once on your host machine using podman secret create before registering the workspace:

# Create the secret from an environment variable
echo "$GITHUB_TOKEN" | podman secret create github-token -

# Or create it from a file
podman secret create github-token /path/to/token-file

The secret name (github-token here) must match the secret field value in your configuration. At workspace creation time, kortex-cli passes --secret github-token,type=env,target=GH_TOKEN to Podman, which injects the secret value as the GH_TOKEN environment variable inside the container.

Podman secrets are stored locally on the host and never written to the container image.

Notes:

  • The secret field references a secret by name rather than embedding the token value directly, keeping credentials out of the configuration file
  • The project identifier used as the key must match what kortex-cli detected during init — run kortex-cli list -o json to see the project field for each registered workspace
  • Configuration changes in projects.json take effect the next time you run kortex-cli init for that workspace; already-registered workspaces need to be removed and re-registered

Working with Git Worktrees

This scenario demonstrates how to run multiple agents in parallel, each working on a different branch of the same repository. Git worktrees allow each branch to live in its own directory, so each agent gets its own isolated workspace.

Step 1: Clone the repository

git clone https://github.com/my-org/my-repo.git /path/to/my-project/main

Step 2: Create a worktree for each feature branch

cd /path/to/my-project/main

git worktree add ../feature-a feature-a
git worktree add ../feature-b feature-b

This results in the following layout:

/path/to/my-project/
├── main/       ← main branch (original clone)
├── feature-a/  ← feature-a branch (worktree)
└── feature-b/  ← feature-b branch (worktree)

Step 3: Configure the main branch mount in your local project config

If you want the agents to have access to the main branch (e.g., to compare changes), add the mount in ~/.kortex-cli/config/projects.json under the project identifier. This keeps the configuration on your machine only — not all developers of the project may use worktrees, so it does not belong in the repository's .kortex/workspace.json.

{
  "https://github.com/my-org/my-repo/": {
    "mounts": [
      {"host": "$SOURCES/../main", "target": "$SOURCES/../main"}
    ]
  }
}

$SOURCES expands to the workspace sources directory (e.g., /path/to/my-project/feature-a), so $SOURCES/../main resolves to /path/to/my-project/main on both the host and inside the container.

Step 4: Register a workspace for each worktree

kortex-cli init /path/to/my-project/feature-a --runtime podman --agent claude
kortex-cli init /path/to/my-project/feature-b --runtime podman --agent claude

Step 5: Start and connect to each workspace independently

# Start both workspaces (using names or IDs)
kortex-cli start feature-a
kortex-cli start feature-b

# Connect to each agent in separate terminals
kortex-cli terminal feature-a
kortex-cli terminal feature-b

Each agent runs independently in its own container, operating on its own branch without interfering with the other.

Notes:

  • Each worktree shares the same .git directory, so agents can run git commands that are branch-aware
  • Workspaces for different worktrees of the same repository share the same project identifier (derived from the git remote URL), so the mount defined in projects.json automatically applies to all of them

Managing Workspaces from a UI or Programmatically

This scenario demonstrates how to manage workspaces programmatically using JSON output, which is ideal for UIs, scripts, or automation tools. All commands support the --output json (or -o json) flag for machine-readable output.

Step 1: Check existing workspaces

$ kortex-cli workspace list -o json
{
  "items": []
}

Exit code: 0 (success, but no workspaces registered)

Step 2: Register a new workspace

$ kortex-cli init /path/to/project --runtime podman --agent claude -o json
{
  "id": "2c5f16046476be368fcada501ac6cdc6bbd34ea80eb9ceb635530c0af64681ea"
}

Exit code: 0 (success)

Step 3: Register with verbose output to get full details

$ kortex-cli init /path/to/another-project --runtime podman --agent claude -o json -v
{
  "id": "f6e5d4c3b2a1098765432109876543210987654321098765432109876543210a",
  "name": "another-project",
  "agent": "claude",
  "project": "/absolute/path/to/another-project",
  "state": "stopped",
  "paths": {
    "source": "/absolute/path/to/another-project",
    "configuration": "/absolute/path/to/another-project/.kortex"
  }
}

Exit code: 0 (success)

Step 3a: Register and start immediately with auto-start flag

$ kortex-cli init /path/to/third-project --runtime podman --agent claude -o json --start
{
  "id": "3c4d5e6f7a8b9098765432109876543210987654321098765432109876543210b"
}

Exit code: 0 (success, workspace is running)

Step 4: List all workspaces

$ kortex-cli workspace list -o json
{
  "items": [
    {
      "id": "2c5f16046476be368fcada501ac6cdc6bbd34ea80eb9ceb635530c0af64681ea",
      "name": "project",
      "agent": "claude",
      "project": "/absolute/path/to/project",
      "state": "running",
      "paths": {
        "source": "/absolute/path/to/project",
        "configuration": "/absolute/path/to/project/.kortex"
      }
    },
    {
      "id": "f6e5d4c3b2a1098765432109876543210987654321098765432109876543210a",
      "name": "another-project",
      "agent": "claude",
      "project": "/absolute/path/to/another-project",
      "state": "stopped",
      "paths": {
        "source": "/absolute/path/to/another-project",
        "configuration": "/absolute/path/to/another-project/.kortex"
      }
    }
  ]
}

Exit code: 0 (success)

Step 5: Start a workspace

$ kortex-cli workspace start 2c5f16046476be368fcada501ac6cdc6bbd34ea80eb9ceb635530c0af64681ea -o json
{
  "id": "2c5f16046476be368fcada501ac6cdc6bbd34ea80eb9ceb635530c0af64681ea"
}

Exit code: 0 (success)

Step 6: Stop a workspace

$ kortex-cli workspace stop 2c5f16046476be368fcada501ac6cdc6bbd34ea80eb9ceb635530c0af64681ea -o json
{
  "id": "2c5f16046476be368fcada501ac6cdc6bbd34ea80eb9ceb635530c0af64681ea"
}

Exit code: 0 (success)

Step 7: Remove a workspace

$ kortex-cli workspace remove 2c5f16046476be368fcada501ac6cdc6bbd34ea80eb9ceb635530c0af64681ea -o json
{
  "id": "2c5f16046476be368fcada501ac6cdc6bbd34ea80eb9ceb635530c0af64681ea"
}

Exit code: 0 (success)

Step 8: Verify removal

$ kortex-cli workspace list -o json
{
  "items": [
    {
      "id": "f6e5d4c3b2a1098765432109876543210987654321098765432109876543210a",
      "name": "another-project",
      "agent": "claude",
      "project": "/absolute/path/to/another-project",
      "state": "stopped",
      "paths": {
        "source": "/absolute/path/to/another-project",
        "configuration": "/absolute/path/to/another-project/.kortex"
      }
    }
  ]
}

Exit code: 0 (success)

Error Handling

All errors are returned in JSON format when using --output json, with the error written to stdout (not stderr) and a non-zero exit code.

Error: Non-existent directory

$ kortex-cli init /tmp/no-exist --runtime podman --agent claude -o json
{
  "error": "sources directory does not exist: /tmp/no-exist"
}

Exit code: 1 (error)

Error: Workspace not found

$ kortex-cli workspace remove unknown-id -o json
{
  "error": "workspace not found: unknown-id"
}

Exit code: 1 (error)

Best Practices for Programmatic Usage

  1. Always check the exit code to determine success (0) or failure (non-zero)
  2. Parse stdout for JSON output in both success and error cases
  3. Use verbose mode with init (-v) when you need full workspace details immediately after creation
  4. Handle both success and error JSON structures in your code:
    • Success responses have specific fields (e.g., id, items, name, paths)
    • Error responses always have an error field

Example script pattern:

#!/bin/bash

# Register a workspace
output=$(kortex-cli init /path/to/project --runtime podman --agent claude -o json)
exit_code=$?

if [ $exit_code -eq 0 ]; then
    workspace_id=$(echo "$output" | jq -r '.id')
    echo "Workspace created: $workspace_id"
else
    error_msg=$(echo "$output" | jq -r '.error')
    echo "Error: $error_msg"
    exit 1
fi

Environment Variables

kortex-cli supports environment variables for configuring default behavior.

KORTEX_CLI_DEFAULT_RUNTIME

Sets the default runtime to use when registering a workspace with the init command.

Usage:

export KORTEX_CLI_DEFAULT_RUNTIME=fake
kortex-cli init /path/to/project --agent claude

Priority:

The runtime is determined in the following order (highest to lowest priority):

  1. --runtime flag (if specified)
  2. KORTEX_CLI_DEFAULT_RUNTIME environment variable (if set)
  3. Error if neither is set (runtime is required)

Example:

# Set the default runtime for the current shell session
export KORTEX_CLI_DEFAULT_RUNTIME=fake

# Register a workspace using the environment variable
kortex-cli init /path/to/project --agent claude

# Override the environment variable with the flag
kortex-cli init /path/to/another-project --agent claude --runtime podman

Notes:

  • The runtime parameter is mandatory when registering workspaces
  • If neither the flag nor the environment variable is set, the init command will fail with an error
  • Supported runtime types depend on the available runtime implementations
  • Setting this environment variable is useful for automation scripts or when you consistently use the same runtime

KORTEX_CLI_DEFAULT_AGENT

Sets the default agent to use when registering a workspace with the init command.

Usage:

export KORTEX_CLI_DEFAULT_AGENT=claude
kortex-cli init /path/to/project --runtime podman

Priority:

The agent is determined in the following order (highest to lowest priority):

  1. --agent flag (if specified)
  2. KORTEX_CLI_DEFAULT_AGENT environment variable (if set)
  3. Error if neither is set (agent is required)

Example:

# Set the default agent for the current shell session
export KORTEX_CLI_DEFAULT_AGENT=claude

# Register a workspace using the environment variable
kortex-cli init /path/to/project --runtime podman

# Override the environment variable with the flag
kortex-cli init /path/to/another-project --runtime podman --agent goose

Notes:

  • The agent parameter is mandatory when registering workspaces
  • If neither the flag nor the environment variable is set, the init command will fail with an error
  • Supported agent types depend on the available agent configurations in the runtime
  • Agent names must contain only alphanumeric characters or underscores (e.g., claude, goose, my_agent)
  • Setting this environment variable is useful for automation scripts or when you consistently use the same agent

KORTEX_CLI_STORAGE

Sets the default storage directory where kortex-cli stores its data files.

Usage:

export KORTEX_CLI_STORAGE=/custom/path/to/storage
kortex-cli init /path/to/project --runtime podman --agent claude

Priority:

The storage directory is determined in the following order (highest to lowest priority):

  1. --storage flag (if specified)
  2. KORTEX_CLI_STORAGE environment variable (if set)
  3. Default: $HOME/.kortex-cli

Example:

# Set a custom storage directory
export KORTEX_CLI_STORAGE=/var/lib/kortex

# All commands will use this storage directory
kortex-cli init /path/to/project --runtime podman --agent claude
kortex-cli list

# Override the environment variable with the flag
kortex-cli list --storage /tmp/kortex-storage

KORTEX_CLI_INIT_AUTO_START

Automatically starts a workspace after registration when using the init command.

Usage:

export KORTEX_CLI_INIT_AUTO_START=1
kortex-cli init /path/to/project --runtime podman --agent claude

Priority:

The auto-start behavior is determined in the following order (highest to lowest priority):

  1. --start flag (if specified)
  2. KORTEX_CLI_INIT_AUTO_START environment variable (if set to a truthy value)
  3. Default: workspace is not started automatically

Supported Values:

The environment variable accepts the following truthy values (case-insensitive):

  • 1
  • true, True, TRUE
  • yes, Yes, YES

Any other value (including 0, false, no, or empty string) will not trigger auto-start.

Example:

# Set auto-start for the current shell session
export KORTEX_CLI_INIT_AUTO_START=1

# Register and start a workspace automatically
kortex-cli init /path/to/project --runtime podman --agent claude
# Workspace is now running

# Override the environment variable with the flag
export KORTEX_CLI_INIT_AUTO_START=0
kortex-cli init /path/to/another-project --runtime podman --agent claude --start
# Workspace is started despite env var being 0

Notes:

  • Auto-starting combines the init and start commands into a single operation
  • Useful for automation scripts where you want workspaces ready to use immediately
  • If the workspace fails to start, the registration still succeeds, but an error is returned
  • The --start flag always takes precedence over the environment variable

Podman Runtime

The Podman runtime provides a container-based development environment for workspaces. It creates an isolated environment with all necessary tools pre-installed and configured.

Container Image

Base Image: registry.fedoraproject.org/fedora:latest

The Podman runtime builds a custom container image based on Fedora Linux, providing a stable and up-to-date foundation for development work.

Installed Packages

The runtime includes a comprehensive development toolchain:

  • Core Utilities:

    • which - Command location utility
    • procps-ng - Process management utilities
    • wget2 - Advanced file downloader
  • Development Tools:

    • @development-tools - Complete development toolchain (gcc, make, etc.)
    • jq - JSON processor
    • gh - GitHub CLI
  • Language Support:

    • golang - Go programming language
    • golangci-lint - Go linter
    • python3 - Python 3 interpreter
    • python3-pip - Python package manager

User and Permissions

The container runs as a non-root user named agent with the following configuration:

  • User: agent
  • UID/GID: Matches the host user's UID and GID for seamless file permissions
  • Home Directory: /home/agent

Sudo Permissions:

The agent user has limited sudo access with no password required (NOPASSWD) for:

  • Package Management:

    • /usr/bin/dnf - Install, update, and manage packages
  • Process Management:

    • /bin/nice - Run programs with modified scheduling priority
    • /bin/kill, /usr/bin/kill - Send signals to processes
    • /usr/bin/killall - Kill processes by name

All other sudo commands are explicitly denied for security.

AI Agents

The Podman runtime includes default configurations for the following AI agents:

Claude Code - Installed using the official installation script from claude.ai/install.sh:

  • Full Claude Code CLI capabilities
  • Integrated development assistance
  • Access to Claude's latest features

Goose - Installed using the official installer from github.com/block/goose:

  • AI-powered development agent
  • Task automation and code assistance
  • Configurable development workflows

The agent runs within the container environment and has access to the mounted workspace sources and dependencies.

Working Directory

The container's working directory is set to /workspace/sources, which is where your project source code is mounted. This ensures that the agent and all tools operate within your project context.

Example Usage

# Register a workspace with the Podman runtime
kortex-cli init /path/to/project --runtime podman --agent claude

User Experience:

When you register a workspace with the Podman runtime, you'll see progress feedback for each operation:

⠋ Creating temporary build directory
✓ Temporary build directory created
⠋ Generating Containerfile
✓ Containerfile generated
⠋ Building container image: kortex-cli-myproject
✓ Container image built
⠋ Creating container: myproject
✓ Container created

The init command will:

  1. Create a temporary build directory - with progress spinner
  2. Generate a Containerfile with the configuration above - with progress spinner
  3. Build a custom image (tagged as kortex-cli-<workspace-name>) - with progress spinner
  4. Create a container with your source code mounted - with progress spinner

After registration, you can start the workspace:

# Start the workspace
kortex-cli start <workspace-id>

Note: When using --output json, all progress spinners are hidden to avoid polluting the JSON output.

Customizing Podman Runtime Configuration

The Podman runtime is fully configurable through JSON files. When you first use the Podman runtime, default configuration files are automatically created in your storage directory.

Configuration Location:

$HOME/.kortex-cli/runtimes/podman/config/
├── image.json    # Base image configuration
├── claude.json   # Claude agent configuration
└── goose.json    # Goose agent configuration

Or if using a custom storage directory:

<storage-dir>/runtimes/podman/config/

Base Image Configuration (image.json)

Controls the container's base image, packages, and sudo permissions.

Structure:

{
  "version": "latest",
  "packages": [
    "which",
    "procps-ng",
    "wget2",
    "@development-tools",
    "jq",
    "gh",
    "golang",
    "golangci-lint",
    "python3",
    "python3-pip"
  ],
  "sudo": [
    "/usr/bin/dnf",
    "/bin/nice",
    "/bin/kill",
    "/usr/bin/kill",
    "/usr/bin/killall"
  ],
  "run_commands": []
}

Fields:

  • version (required) - Fedora version tag

    • Examples: "latest", "40", "41"
    • The base registry registry.fedoraproject.org/fedora is hardcoded and cannot be changed
  • packages (optional) - DNF packages to install

    • Array of package names
    • Can include package groups with @ prefix (e.g., "@development-tools")
    • Empty array is valid if no packages needed
  • sudo (optional) - Binaries the agent user can run with sudo

    • Must be absolute paths (e.g., "/usr/bin/dnf")
    • Creates a single ALLOWED command alias in sudoers
    • Empty array disables all sudo access
  • run_commands (optional) - Custom shell commands to run during image build

    • Executed as RUN instructions in the Containerfile
    • Run before agent-specific commands
    • Useful for additional setup steps

Agent Configuration

Controls agent-specific packages and installation steps. The Podman runtime provides default configurations for Claude Code (claude.json) and Goose (goose.json).

Structure (claude.json):

{
  "packages": [],
  "run_commands": [
    "curl -fsSL --proto-redir '-all,https' --tlsv1.3 https://claude.ai/install.sh | bash",
    "mkdir -p /home/agent/.config"
  ],
  "terminal_command": [
    "claude"
  ]
}

Structure (goose.json):

{
  "packages": [],
  "run_commands": [
    "cd /tmp && curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh | CONFIGURE=false bash"
  ],
  "terminal_command": [
    "goose"
  ]
}

Fields:

  • packages (optional) - Additional packages specific to this agent

    • Merged with packages from image.json
    • Useful for agent-specific dependencies
  • run_commands (optional) - Commands to set up the agent

    • Executed after image configuration commands
    • Typically used for agent installation
  • terminal_command (required) - Command to launch the agent

    • Must have at least one element
    • Can include flags: ["claude", "--verbose"]

Applying Configuration Changes

Configuration changes take effect when you register a new workspace with init. The Containerfile is generated and the image is built during workspace registration, using the configuration files that exist at that time.

To apply new configuration:

  1. Edit the configuration files:

    # Edit base image configuration
    nano ~/.kortex-cli/runtimes/podman/config/image.json
    
    # Edit agent configuration (use the agent you want)
    nano ~/.kortex-cli/runtimes/podman/config/claude.json
    # or
    nano ~/.kortex-cli/runtimes/podman/config/goose.json
  2. Register a new workspace (this creates the Containerfile and builds the image):

    # Using Claude agent
    kortex-cli init /path/to/project --runtime podman --agent claude
    # or using Goose agent
    kortex-cli init /path/to/project --runtime podman --agent goose
  3. Start the workspace:

    kortex-cli start <workspace-id>

Notes:

  • The first init command using Podman creates default config files automatically
  • Config files are never overwritten once created - your customizations are preserved
  • The Containerfile and image are built during init, not start
  • Each workspace's image is built once using the configuration at registration time
  • To rebuild a workspace with new config, remove and re-register it
  • Validation errors in config files will cause workspace registration to fail with a descriptive message
  • The generated Containerfile is automatically copied to /home/agent/Containerfile inside the container for reference

Workspace Configuration

Each workspace can optionally include a configuration file that customizes the environment and mount behavior for that specific workspace. The configuration is stored in a workspace.json file within the workspace's configuration directory (typically .kortex in the sources directory).

Configuration File Location

By default, workspace configuration is stored at:

<sources-directory>/.kortex/workspace.json

The configuration directory (containing workspace.json) can be customized using the --workspace-configuration flag when registering a workspace with init. The flag accepts a directory path, not the file path itself.

Configuration Structure

The workspace.json file uses a nested JSON structure:

{
  "environment": [
    {
      "name": "DEBUG",
      "value": "true"
    },
    {
      "name": "API_KEY",
      "secret": "github-token"
    }
  ],
  "mounts": [
    {"host": "$SOURCES/../main", "target": "$SOURCES/../main"},
    {"host": "$HOME/.ssh", "target": "$HOME/.ssh"},
    {"host": "/absolute/path/to/data", "target": "/workspace/data"}
  ]
}

Environment Variables

Define environment variables that will be set in the workspace runtime environment.

Structure:

{
  "environment": [
    {
      "name": "VAR_NAME",
      "value": "hardcoded-value"
    },
    {
      "name": "SECRET_VAR",
      "secret": "secret-reference"
    }
  ]
}

Fields:

  • name (required) - Environment variable name
    • Must be a valid Unix environment variable name
    • Must start with a letter or underscore
    • Can contain letters, digits, and underscores
  • value (optional) - Hardcoded value for the variable
    • Mutually exclusive with secret
    • Empty strings are allowed
  • secret (optional) - Reference to a secret containing the value
    • Mutually exclusive with value
    • Cannot be empty

Validation Rules:

  • Variable name cannot be empty
  • Exactly one of value or secret must be defined
  • Variable names must follow Unix conventions (e.g., DEBUG, API_KEY, MY_VAR_123)
  • Invalid names include those starting with digits (1INVALID) or containing special characters (INVALID-NAME, INVALID@NAME)

Mount Paths

Configure additional directories to mount in the workspace runtime.

Structure:

{
  "mounts": [
    {"host": "$SOURCES/../main", "target": "$SOURCES/../main"},
    {"host": "$HOME/.gitconfig", "target": "$HOME/.gitconfig"},
    {"host": "/absolute/path/to/data", "target": "/workspace/data", "ro": true}
  ]
}

Fields:

  • host (required) - Path on the host filesystem to mount
  • target (required) - Path inside the container where the host path is mounted
  • ro (optional) - Mount as read-only (default: false)

Path Variables:

Both host and target support the following variables:

  • $SOURCES - Expands to the workspace sources directory on the host, or /workspace/sources in the container
  • $HOME - Expands to the user's home directory on the host, or /home/agent in the container

Paths can also be absolute (e.g., /absolute/path).

Validation Rules:

  • host and target cannot be empty
  • Each path must be absolute or start with $SOURCES or $HOME
  • $SOURCES-based container targets must not escape above /workspace
  • $HOME-based container targets must not escape above /home/agent

Configuration Validation

When you register a workspace with kortex-cli init, the configuration is automatically validated. If workspace.json exists and contains invalid data, the registration will fail with a descriptive error message.

Example - Invalid configuration (both value and secret set):

$ kortex-cli init /path/to/project --runtime podman --agent claude
Error: workspace configuration validation failed: invalid workspace configuration:
environment variable "API_KEY" (index 0) has both value and secret set

Example - Invalid configuration (missing host in mount):

$ kortex-cli init /path/to/project --runtime podman --agent claude
Error: workspace configuration validation failed: invalid workspace configuration:
mount at index 0 is missing host

Configuration Examples

Basic environment variables:

{
  "environment": [
    {
      "name": "NODE_ENV",
      "value": "development"
    },
    {
      "name": "DEBUG",
      "value": "true"
    }
  ]
}

Using secrets:

{
  "environment": [
    {
      "name": "API_TOKEN",
      "secret": "github-api-token"
    }
  ]
}

git worktree:

{
  "mounts": [
    {"host": "$SOURCES/../main", "target": "$SOURCES/../main"}
  ]
}

Sharing user configurations:

{
  "mounts": [
    {"host": "$HOME/.claude", "target": "$HOME/.claude"},
    {"host": "$HOME/.gitconfig", "target": "$HOME/.gitconfig"},
    {"host": "$HOME/.kube/config", "target": "$HOME/.kube/config", "ro": true}
  ]
}

Complete configuration:

{
  "environment": [
    {
      "name": "NODE_ENV",
      "value": "development"
    },
    {
      "name": "DATABASE_URL",
      "secret": "local-db-url"
    }
  ],
  "mounts": [
    {"host": "$SOURCES/../main", "target": "$SOURCES/../main"},
    {"host": "$HOME/.claude", "target": "$HOME/.claude"},
    {"host": "$HOME/.gitconfig", "target": "$HOME/.gitconfig"}
  ]
}

Notes

  • Configuration is optional - workspaces can be registered without a workspace.json file
  • The configuration file is validated only when it exists
  • Validation errors are caught early during workspace registration (init command)
  • All validation rules are enforced to prevent runtime errors
  • The configuration model is imported from the github.com/kortex-hub/kortex-cli-api/workspace-configuration/go package for consistency across tools

Multi-Level Configuration

kortex-cli supports configuration at multiple levels, allowing you to customize workspace settings for different contexts. Configurations are automatically merged with proper precedence, making it easy to share common settings while still allowing project and agent-specific customization.

Configuration Levels

1. Workspace Configuration (.kortex/workspace.json)

  • Stored in your project repository
  • Shared with all developers
  • Used by all agents
  • Committed to version control

2. Global Project Configuration (~/.kortex-cli/config/projects.json with "" key)

  • User-specific settings applied to all projects
  • Stored on your local machine (not committed to git)
  • Perfect for common settings like .gitconfig, SSH keys, or global environment variables
  • Never shared with other developers

3. Project-Specific Configuration (~/.kortex-cli/config/projects.json)

  • User-specific settings for a specific project
  • Stored on your local machine (not committed to git)
  • Overrides global settings for this project
  • Identified by project ID (git repository URL or directory path)

4. Agent-Specific Configuration (~/.kortex-cli/config/agents.json)

  • User-specific settings for a specific agent (Claude, Goose, etc.)
  • Stored on your local machine (not committed to git)
  • Overrides all other configurations
  • Perfect for agent-specific environment variables or tools

Configuration Precedence

When registering a workspace, configurations are merged in this order (later configs override earlier ones):

  1. Workspace (.kortex/workspace.json) - Base configuration from repository
  2. Global (projects.json "" key) - Your global settings for all projects
  3. Project (projects.json specific project) - Your settings for this project
  4. Agent (agents.json specific agent) - Your settings for this agent

Example: If DEBUG is defined in workspace config as false, in project config as true, and in agent config as verbose, the final value will be verbose (from agent config).

Storage Location

User-specific configurations are stored in the kortex-cli storage directory:

  • Default location: ~/.kortex-cli/config/
  • Custom location: Set via --storage flag or KORTEX_CLI_STORAGE environment variable

The storage directory contains:

  • config/agents.json - Agent-specific environment variables and mounts
  • config/projects.json - Project-specific and global environment variables and mounts
  • config/<agent>/ - Agent default settings files (e.g., config/claude/.claude.json)

Agent Configuration File

Location: ~/.kortex-cli/config/agents.json

Format:

{
  "claude": {
    "environment": [
      {
        "name": "DEBUG",
        "value": "true"
      }
    ],
    "mounts": [
      {"host": "$HOME/.claude-config", "target": "$HOME/.claude-config"}
    ]
  },
  "goose": {
    "environment": [
      {
        "name": "GOOSE_MODE",
        "value": "verbose"
      }
    ]
  }
}

Each key is an agent name (e.g., claude, goose). The value uses the same structure as workspace.json.

Project Configuration File

Location: ~/.kortex-cli/config/projects.json

Format:

{
  "": {
    "mounts": [
      {"host": "$HOME/.gitconfig", "target": "$HOME/.gitconfig"},
      {"host": "$HOME/.ssh", "target": "$HOME/.ssh"}
    ]
  },
  "github.com/kortex-hub/kortex-cli": {
    "environment": [
      {
        "name": "PROJECT_VAR",
        "value": "project-value"
      }
    ],
    "mounts": [
      {"host": "$SOURCES/../kortex-common", "target": "$SOURCES/../kortex-common"}
    ]
  },
  "/home/user/my/project": {
    "environment": [
      {
        "name": "LOCAL_DEV",
        "value": "true"
      }
    ]
  }
}

Special Keys:

  • Empty string "" - Global configuration applied to all projects
  • Git repository URL - Configuration for all workspaces in that repository (e.g., github.com/user/repo)
  • Directory path - Configuration for a specific directory (takes precedence over repository URL)

Use Cases

Global Settings for All Projects:

{
  "": {
    "mounts": [
      {"host": "$HOME/.gitconfig", "target": "$HOME/.gitconfig"},
      {"host": "$HOME/.ssh", "target": "$HOME/.ssh"},
      {"host": "$HOME/.gnupg", "target": "$HOME/.gnupg"}
    ]
  }
}

This mounts your git config and SSH keys in every workspace you create.

Project-Specific API Keys:

{
  "github.com/company/project": {
    "environment": [
      {
        "name": "API_KEY",
        "secret": "project-api-key"
      }
    ]
  }
}

This adds an API key only for workspaces in the company project.

Agent-Specific Debug Mode:

{
  "claude": {
    "environment": [
      {
        "name": "DEBUG",
        "value": "true"
      }
    ]
  }
}

This enables debug mode only when using the Claude agent.

Using Multi-Level Configuration

Register workspace with agent-specific config:

kortex-cli init --runtime podman --agent claude

Register workspace with custom project:

kortex-cli init --runtime podman --project my-custom-project --agent goose

Note: The --agent flag is required (or set KORTEX_CLI_DEFAULT_AGENT environment variable) when registering a workspace.

Merging Behavior

Environment Variables:

  • Variables are merged by name
  • Later configurations override earlier ones
  • Example: If workspace sets DEBUG=false and agent sets DEBUG=true, the final value is DEBUG=true

Mount Paths:

  • Mounts are deduplicated by host+target pair (duplicates removed)
  • Order is preserved (first occurrence wins)
  • Example: If workspace has mounts for .gitconfig and .ssh, and global adds .ssh and .kube, the result contains .gitconfig, .ssh, and .kube

Configuration Files Don't Exist?

All multi-level configurations are optional:

  • If agents.json doesn't exist, agent-specific configuration is skipped
  • If projects.json doesn't exist, project and global configurations are skipped
  • If workspace.json doesn't exist, only user-specific configurations are used

The system works without any configuration files and merges only the ones that exist.

Example: Complete Multi-Level Setup

Workspace config (.kortex/workspace.json - committed to git):

{
  "environment": [
    {"name": "NODE_ENV", "value": "development"}
  ]
}

Global config (~/.kortex-cli/config/projects.json - your machine only):

{
  "": {
    "mounts": [
      {"host": "$HOME/.gitconfig", "target": "$HOME/.gitconfig"},
      {"host": "$HOME/.ssh", "target": "$HOME/.ssh"}
    ]
  }
}

Project config (~/.kortex-cli/config/projects.json - your machine only):

{
  "github.com/kortex-hub/kortex-cli": {
    "environment": [
      {"name": "DEBUG", "value": "true"}
    ]
  }
}

Agent config (~/.kortex-cli/config/agents.json - your machine only):

{
  "claude": {
    "environment": [
      {"name": "CLAUDE_VERBOSE", "value": "true"}
    ]
  }
}

Result when running kortex-cli init --runtime podman --agent claude:

  • Environment: NODE_ENV=development, DEBUG=true, CLAUDE_VERBOSE=true
  • Mounts: $HOME/.gitconfig, $HOME/.ssh

Commands

init - Register a New Workspace

Registers a new workspace with kortex-cli, making it available for agent launch and configuration.

Usage

kortex-cli init [sources-directory] [flags]

Arguments

  • sources-directory - Path to the directory containing your project source files (optional, defaults to current directory .)

Flags

  • --runtime, -r <type> - Runtime to use for the workspace (required if KORTEX_CLI_DEFAULT_RUNTIME is not set)
  • --agent, -a <name> - Agent to use for the workspace (required if KORTEX_CLI_DEFAULT_AGENT is not set)
  • --workspace-configuration <path> - Directory for workspace configuration files (default: <sources-directory>/.kortex)
  • --name, -n <name> - Human-readable name for the workspace (default: generated from sources directory)
  • --project, -p <identifier> - Custom project identifier to override auto-detection (default: auto-detected from git repository or source directory)
  • --start - Start the workspace after registration (can also be set via KORTEX_CLI_INIT_AUTO_START environment variable)
  • --verbose, -v - Show detailed output including all workspace information
  • --output, -o <format> - Output format (supported: json)
  • --show-logs - Show stdout and stderr from runtime commands (cannot be combined with --output json)
  • --storage <path> - Storage directory for kortex-cli data (default: $HOME/.kortex-cli)

Examples

Register the current directory:

kortex-cli init --runtime podman --agent claude

Output: a1b2c3d4e5f6... (workspace ID)

Register a specific directory:

kortex-cli init /path/to/myproject --runtime podman --agent claude

Register with a custom name:

kortex-cli init /path/to/myproject --runtime podman --agent claude --name "my-awesome-project"

Register with a custom project identifier:

kortex-cli init /path/to/myproject --runtime podman --agent claude --project "my project"

Register with custom configuration location:

kortex-cli init /path/to/myproject --runtime podman --agent claude --workspace-configuration /path/to/config

Register and start immediately:

kortex-cli init /path/to/myproject --runtime podman --agent claude --start

Output: a1b2c3d4e5f6... (workspace ID, workspace is now running)

Register and start using environment variable:

export KORTEX_CLI_INIT_AUTO_START=1
kortex-cli init /path/to/myproject --runtime podman --agent claude

Output: a1b2c3d4e5f6... (workspace ID, workspace is now running)

View detailed output:

kortex-cli init --runtime podman --agent claude --verbose

Output:

Registered workspace:
  ID: a1b2c3d4e5f6...
  Name: myproject
  Project: /absolute/path/to/myproject
  Agent: claude
  Sources directory: /absolute/path/to/myproject
  Configuration directory: /absolute/path/to/myproject/.kortex
  State: stopped

JSON output (default - ID only):

kortex-cli init /path/to/myproject --runtime podman --agent claude --output json

Output:

{
  "id": "a1b2c3d4e5f6..."
}

JSON output with verbose flag (full workspace details):

kortex-cli init /path/to/myproject --runtime podman --agent claude --output json --verbose

Output:

{
  "id": "a1b2c3d4e5f6...",
  "name": "myproject",
  "agent": "claude",
  "project": "/absolute/path/to/myproject",
  "state": "stopped",
  "paths": {
    "source": "/absolute/path/to/myproject",
    "configuration": "/absolute/path/to/myproject/.kortex"
  }
}

JSON output with short flags:

kortex-cli init -r fake -a claude -o json -v

Show runtime command output (e.g., image build logs):

kortex-cli init --runtime podman --agent claude --show-logs

Workspace Naming

  • If --name is not provided, the name is automatically generated from the last component of the sources directory path
  • If a workspace with the same name already exists, kortex-cli automatically appends an increment (-2, -3, etc.) to ensure uniqueness

Examples:

# First workspace in /home/user/project
kortex-cli init /home/user/project --runtime podman --agent claude
# Name: "project"

# Second workspace with the same directory name
kortex-cli init /home/user/another-location/project --runtime podman --agent claude --name "project"
# Name: "project-2"

# Third workspace with the same name
kortex-cli init /tmp/project --runtime podman --agent claude --name "project"
# Name: "project-3"

Project Detection

When registering a workspace, kortex-cli automatically detects and stores a project identifier. This allows grouping workspaces that belong to the same project, even across different branches, forks, or subdirectories.

The project is determined using the following rules:

1. Git repository with remote URL

The project is the repository remote URL (without .git suffix) plus the workspace's relative path within the repository:

  • At repository root: https://github.com/user/repo/
  • In subdirectory: https://github.com/user/repo/sub/path

Remote priority:

  1. upstream remote is checked first (useful for forks)
  2. origin remote is used if upstream doesn't exist
  3. If neither exists, falls back to local repository path (see below)

Example - Fork with upstream:

# Repository setup:
# upstream: https://github.com/kortex-hub/kortex-cli.git
# origin:   https://github.com/myuser/kortex-cli.git (fork)

# Workspace at repository root
kortex-cli init /home/user/kortex-cli --runtime podman --agent claude
# Project: https://github.com/kortex-hub/kortex-cli/

# Workspace in subdirectory
kortex-cli init /home/user/kortex-cli/pkg/git --runtime podman --agent claude
# Project: https://github.com/kortex-hub/kortex-cli/pkg/git

This ensures all forks and branches of the same upstream repository are grouped together.

2. Git repository without remote

The project is the repository root directory path plus the workspace's relative path:

  • At repository root: /home/user/my-local-repo
  • In subdirectory: /home/user/my-local-repo/sub/path

Example - Local repository:

# Workspace at repository root
kortex-cli init /home/user/local-repo --runtime podman --agent claude
# Project: /home/user/local-repo

# Workspace in subdirectory
kortex-cli init /home/user/local-repo/pkg/utils --runtime podman --agent claude
# Project: /home/user/local-repo/pkg/utils

3. Non-git directory

The project is the workspace source directory path:

Example - Regular directory:

kortex-cli init /tmp/workspace --runtime podman --agent claude
# Project: /tmp/workspace

Benefits:

  • Cross-branch grouping: Workspaces in different git worktrees or branches of the same repository share the same project
  • Fork grouping: Forks reference the upstream repository, grouping all contributors working on the same project
  • Subdirectory support: Monorepo subdirectories are tracked with their full path for precise identification
  • Custom override: Use --project flag to manually group workspaces under a custom identifier (e.g., "client-project")
  • Future filtering: The project field enables filtering and grouping commands (e.g., list all workspaces for a specific project)

Notes

  • Runtime is required: You must specify a runtime using either the --runtime flag or the KORTEX_CLI_DEFAULT_RUNTIME environment variable
  • Agent is required: You must specify an agent using either the --agent flag or the KORTEX_CLI_DEFAULT_AGENT environment variable
  • Project auto-detection: The project identifier is automatically detected from git repository information or source directory path. Use --project flag to override with a custom identifier
  • Auto-start: Use the --start flag or set KORTEX_CLI_INIT_AUTO_START=1 to automatically start the workspace after registration, combining init and start into a single operation
  • All directory paths are converted to absolute paths for consistency
  • The workspace ID is a unique identifier generated automatically
  • Workspaces can be listed using the workspace list command
  • The default configuration directory (.kortex) is created inside the sources directory unless specified otherwise
  • JSON output format is useful for scripting and automation
  • Without --verbose, JSON output returns only the workspace ID
  • With --verbose, JSON output includes full workspace details (ID, name, agent, paths)
  • Use --show-logs to display the full stdout and stderr from runtime commands (e.g., podman build output during image creation)
  • JSON error handling: When --output json is used, errors are written to stdout (not stderr) in JSON format, and the CLI exits with code 1. Always check the exit code to determine success/failure

workspace list - List All Registered Workspaces

Lists all workspaces that have been registered with kortex-cli. Also available as the shorter alias list.

Usage

kortex-cli workspace list [flags]
kortex-cli list [flags]

Flags

  • --output, -o <format> - Output format (supported: json)
  • --storage <path> - Storage directory for kortex-cli data (default: $HOME/.kortex-cli)

Examples

List all workspaces (human-readable format):

kortex-cli workspace list

Output:

ID: a1b2c3d4e5f6...
  Name: myproject
  Project: /absolute/path/to/myproject
  Agent: claude
  Sources: /absolute/path/to/myproject
  Configuration: /absolute/path/to/myproject/.kortex
  State: running

ID: f6e5d4c3b2a1...
  Name: another-project
  Project: /absolute/path/to/another-project
  Agent: goose
  Sources: /absolute/path/to/another-project
  Configuration: /absolute/path/to/another-project/.kortex
  State: stopped

Use the short alias:

kortex-cli list

List workspaces in JSON format:

kortex-cli workspace list --output json

Output:

{
  "items": [
    {
      "id": "a1b2c3d4e5f6...",
      "name": "myproject",
      "agent": "claude",
      "project": "/absolute/path/to/myproject",
      "state": "running",
      "paths": {
        "source": "/absolute/path/to/myproject",
        "configuration": "/absolute/path/to/myproject/.kortex"
      }
    },
    {
      "id": "f6e5d4c3b2a1...",
      "name": "another-project",
      "agent": "goose",
      "project": "/absolute/path/to/another-project",
      "state": "stopped",
      "paths": {
        "source": "/absolute/path/to/another-project",
        "configuration": "/absolute/path/to/another-project/.kortex"
      }
    }
  ]
}

List with short flag:

kortex-cli list -o json

Notes

  • When no workspaces are registered, the command displays "No workspaces registered"
  • The JSON output format is useful for scripting and automation
  • All paths are displayed as absolute paths for consistency
  • JSON error handling: When --output json is used, errors are written to stdout (not stderr) in JSON format, and the CLI exits with code 1. Always check the exit code to determine success/failure

workspace start - Start a Workspace

Starts a registered workspace by its name or ID. Also available as the shorter alias start.

Usage

kortex-cli workspace start NAME|ID [flags]
kortex-cli start NAME|ID [flags]

Arguments

  • NAME|ID - The workspace name or unique identifier (required)

Flags

  • --output, -o <format> - Output format (supported: json)
  • --show-logs - Show stdout and stderr from runtime commands (cannot be combined with --output json)
  • --storage <path> - Storage directory for kortex-cli data (default: $HOME/.kortex-cli)

Examples

Start a workspace by ID:

kortex-cli workspace start a1b2c3d4e5f6...

Output: a1b2c3d4e5f6... (ID of started workspace)

Start a workspace by name:

kortex-cli workspace start my-project

Output: a1b2c3d4e5f6... (ID of started workspace)

Use the short alias:

kortex-cli start my-project

View workspace names and IDs before starting:

# First, list all workspaces to find the name or ID
kortex-cli list

# Then start the desired workspace (using either name or ID)
kortex-cli start my-project

JSON output:

kortex-cli workspace start a1b2c3d4e5f6... --output json

Output:

{
  "id": "a1b2c3d4e5f6..."
}

JSON output with short flag:

kortex-cli start a1b2c3d4e5f6... -o json

Show runtime command output:

kortex-cli workspace start a1b2c3d4e5f6... --show-logs

Error Handling

Workspace not found (text format):

kortex-cli start invalid-id

Output:

Error: workspace not found: invalid-id
Use 'workspace list' to see available workspaces

Workspace not found (JSON format):

kortex-cli start invalid-id --output json

Output:

{
  "error": "workspace not found: invalid-id"
}

Notes

  • You can specify the workspace using either its name or ID (both can be obtained using the workspace list or list command)
  • The command always outputs the workspace ID, even when started by name
  • Starting a workspace launches its associated runtime instance
  • The workspace runtime state is updated to reflect that it's running
  • JSON output format is useful for scripting and automation
  • When using --output json, errors are also returned in JSON format for consistent parsing
  • JSON error handling: When --output json is used, errors are written to stdout (not stderr) in JSON format, and the CLI exits with code 1. Always check the exit code to determine success/failure

workspace stop - Stop a Workspace

Stops a running workspace by its name or ID. Also available as the shorter alias stop.

Usage

kortex-cli workspace stop NAME|ID [flags]
kortex-cli stop NAME|ID [flags]

Arguments

  • NAME|ID - The workspace name or unique identifier (required)

Flags

  • --output, -o <format> - Output format (supported: json)
  • --show-logs - Show stdout and stderr from runtime commands (cannot be combined with --output json)
  • --storage <path> - Storage directory for kortex-cli data (default: $HOME/.kortex-cli)

Examples

Stop a workspace by ID:

kortex-cli workspace stop a1b2c3d4e5f6...

Output: a1b2c3d4e5f6... (ID of stopped workspace)

Stop a workspace by name:

kortex-cli workspace stop my-project

Output: a1b2c3d4e5f6... (ID of stopped workspace)

Use the short alias:

kortex-cli stop my-project

View workspace names and IDs before stopping:

# First, list all workspaces to find the name or ID
kortex-cli list

# Then stop the desired workspace (using either name or ID)
kortex-cli stop my-project

JSON output:

kortex-cli workspace stop a1b2c3d4e5f6... --output json

Output:

{
  "id": "a1b2c3d4e5f6..."
}

JSON output with short flag:

kortex-cli stop a1b2c3d4e5f6... -o json

Show runtime command output:

kortex-cli workspace stop a1b2c3d4e5f6... --show-logs

Error Handling

Workspace not found (text format):

kortex-cli stop invalid-id

Output:

Error: workspace not found: invalid-id
Use 'workspace list' to see available workspaces

Workspace not found (JSON format):

kortex-cli stop invalid-id --output json

Output:

{
  "error": "workspace not found: invalid-id"
}

Notes

  • You can specify the workspace using either its name or ID (both can be obtained using the workspace list or list command)
  • The command always outputs the workspace ID, even when stopped by name
  • Stopping a workspace stops its associated runtime instance
  • The workspace runtime state is updated to reflect that it's stopped
  • JSON output format is useful for scripting and automation
  • When using --output json, errors are also returned in JSON format for consistent parsing
  • JSON error handling: When --output json is used, errors are written to stdout (not stderr) in JSON format, and the CLI exits with code 1. Always check the exit code to determine success/failure

workspace terminal - Connect to a Running Workspace

Connects to a running workspace with an interactive terminal session. Also available as the shorter alias terminal.

Usage

kortex-cli workspace terminal NAME|ID [COMMAND...] [flags]
kortex-cli terminal NAME|ID [COMMAND...] [flags]

Arguments

  • NAME|ID - The workspace name or unique identifier (required)
  • COMMAND... - Optional command to execute instead of the default agent command

Flags

  • --storage <path> - Storage directory for kortex-cli data (default: $HOME/.kortex-cli)

Examples

Connect using the default agent command (by ID):

kortex-cli workspace terminal a1b2c3d4e5f6...

Connect using the default agent command (by name):

kortex-cli workspace terminal my-project

This starts an interactive session with the default agent (typically Claude Code) inside the running workspace container.

Use the short alias:

kortex-cli terminal my-project

Run a bash shell:

kortex-cli terminal my-project bash

Run a command with flags (use -- to prevent kortex-cli from parsing them):

kortex-cli terminal a1b2c3d4e5f6... -- bash -c 'echo hello'

The -- separator tells kortex-cli to stop parsing flags and pass everything after it directly to the container. This is useful when your command includes flags that would otherwise be interpreted by kortex-cli.

List workspaces and connect to a running one:

# First, list all workspaces to find the ID
kortex-cli list

# Start a workspace if it's not running
kortex-cli start a1b2c3d4e5f6...

# Then connect with a terminal
kortex-cli terminal a1b2c3d4e5f6...

Error Handling

Workspace not found:

kortex-cli terminal invalid-id

Output:

Error: workspace not found: invalid-id
Use 'workspace list' to see available workspaces

Workspace not running:

kortex-cli terminal a1b2c3d4e5f6...

Output:

Error: instance is not running (current state: created)

In this case, you need to start the workspace first:

kortex-cli start a1b2c3d4e5f6...
kortex-cli terminal a1b2c3d4e5f6...

Notes

  • The workspace must be in a running state before you can connect to it. Use workspace start to start a workspace first
  • You can specify the workspace using either its name or ID (both can be obtained using the workspace list or list command)
  • By default (when no command is provided), the runtime uses the terminal_command from the agent's configuration file
    • For example, if the workspace was created with --agent claude, it will use the command defined in claude.json (typically ["claude"])
    • This ensures you connect directly to the configured agent
  • You can override the default by providing a custom command (e.g., bash, python, or any executable available in the container)
  • Use the -- separator when your command includes flags to prevent kortex-cli from trying to parse them
  • The terminal session is fully interactive with stdin/stdout/stderr connected to your terminal
  • The command execution happens inside the workspace's container/runtime environment
  • JSON output is not supported for this command as it's inherently interactive
  • Runtime support: The terminal command requires the runtime to implement the Terminal interface. The Podman runtime supports this using podman exec -it

workspace remove - Remove a Workspace

Removes a registered workspace by its name or ID. Also available as the shorter alias remove.

Usage

kortex-cli workspace remove NAME|ID [flags]
kortex-cli remove NAME|ID [flags]

Arguments

  • NAME|ID - The workspace name or unique identifier (required)

Flags

  • --force, -f - Stop the workspace if it is running before removing it
  • --output, -o <format> - Output format (supported: json)
  • --show-logs - Show stdout and stderr from runtime commands (cannot be combined with --output json)
  • --storage <path> - Storage directory for kortex-cli data (default: $HOME/.kortex-cli)

Examples

Remove a workspace by ID:

kortex-cli workspace remove a1b2c3d4e5f6...

Output: a1b2c3d4e5f6... (ID of removed workspace)

Remove a workspace by name:

kortex-cli workspace remove my-project

Output: a1b2c3d4e5f6... (ID of removed workspace)

Use the short alias:

kortex-cli remove my-project

View workspace names and IDs before removing:

# First, list all workspaces to find the name or ID
kortex-cli list

# Then remove the desired workspace (using either name or ID)
kortex-cli remove my-project

Remove a running workspace (stops it first):

kortex-cli workspace remove a1b2c3d4e5f6... --force

Output: a1b2c3d4e5f6... (ID of removed workspace)

JSON output:

kortex-cli workspace remove a1b2c3d4e5f6... --output json

Output:

{
  "id": "a1b2c3d4e5f6..."
}

JSON output with short flag:

kortex-cli remove a1b2c3d4e5f6... -o json

Show runtime command output:

kortex-cli workspace remove a1b2c3d4e5f6... --show-logs

Error Handling

Workspace not found (text format):

kortex-cli remove invalid-id

Output:

Error: workspace not found: invalid-id
Use 'workspace list' to see available workspaces

Workspace not found (JSON format):

kortex-cli remove invalid-id --output json

Output:

{
  "error": "workspace not found: invalid-id"
}

Removing a running workspace without --force:

Attempting to remove a running workspace without --force will fail because the runtime refuses to remove a running instance. Stop the workspace first, or use --force:

# Stop first, then remove
kortex-cli stop a1b2c3d4e5f6...
kortex-cli remove a1b2c3d4e5f6...

# Or remove in one step
kortex-cli remove a1b2c3d4e5f6... --force

Notes

  • You can specify the workspace using either its name or ID (both can be obtained using the workspace list or list command)
  • The command always outputs the workspace ID, even when removed by name
  • Removing a workspace only unregisters it from kortex-cli; it does not delete any files from the sources or configuration directories
  • If the workspace name or ID is not found, the command will fail with a helpful error message
  • Use --force to automatically stop a running workspace before removing it; without this flag, removing a running workspace will fail
  • Tab completion for this command suggests only non-running workspaces by default; when --force is specified, all workspaces are suggested
  • JSON output format is useful for scripting and automation
  • When using --output json, errors are also returned in JSON format for consistent parsing
  • JSON error handling: When --output json is used, errors are written to stdout (not stderr) in JSON format, and the CLI exits with code 1. Always check the exit code to determine success/failure

Packages

 
 
 

Contributors

Languages