Metadata-Version: 2.4
Name: kanboard-cli
Version: 1.2.4
Summary: Python SDK and CLI for the Kanboard JSON-RPC API
Project-URL: Homepage, https://github.com/geekmuse/kanboard-cli
Project-URL: Repository, https://github.com/geekmuse/kanboard-cli
Project-URL: Changelog, https://github.com/geekmuse/kanboard-cli/blob/main/CHANGELOG.md
Project-URL: Documentation, https://github.com/geekmuse/kanboard-cli#readme
Author-email: Brad Campbell <oss-projects@bradleyscampbell.net>
License: MIT License
        
        Copyright (c) 2024 kanboard-cli contributors
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: cli,kanboard,project-management,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: click>=8.1
Requires-Dist: httpx>=0.27
Requires-Dist: rich>=13.0
Requires-Dist: tomli-w>=1.0
Provides-Extra: dev
Requires-Dist: coverage>=7.0; extra == 'dev'
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: security
Requires-Dist: bandit>=1.7; extra == 'security'
Requires-Dist: hypothesis>=6.100; extra == 'security'
Requires-Dist: pip-audit>=2.7; extra == 'security'
Description-Content-Type: text/markdown

# kanboard-cli

> A Python SDK and CLI for the [Kanboard](https://kanboard.org/) JSON-RPC API — complete coverage of all 158 API methods, a plugin system for custom workflows, and a first-class developer experience.

[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
<!-- [![PyPI version](https://img.shields.io/pypi/v/kanboard-cli.svg)](https://pypi.org/project/kanboard-cli/) -->

**Documentation:**
[CLI Reference](docs/cli-reference.md) •
[SDK Guide](docs/sdk-guide.md) •
[Configuration](docs/configuration.md) •
[Workflows](docs/workflows.md) •
[Contributing](CONTRIBUTING.md)

---

## Overview

**kanboard-cli** provides two complementary interfaces for [Kanboard](https://kanboard.org/):

- **`kanboard` (SDK)** — An importable Python library (`import kanboard`) with typed models, structured exceptions, and resource-based access to the full Kanboard API.
- **`kanboard` (CLI)** — A Click-based command-line tool with rich table output, JSON/CSV/quiet modes, named configuration profiles, and a plugin system for user-defined workflows.

Both are distributed as a single `kanboard-cli` package installable from PyPI.

## Key Features

- **Complete API coverage** — All 158 Kanboard JSON-RPC methods across 24 resource categories
- **Typed Python SDK** — Dataclass models, structured exceptions, and IDE-friendly type hints
- **Powerful CLI** — Subcommand groups for every resource, four output formats, shell completions
- **Multiple output formats** — `table` (rich/colored), `json`, `csv`, `quiet` (IDs only)
- **Named profiles** — Manage multiple Kanboard instances (production, dev, staging) via TOML config
- **Layered configuration** — Config file → environment variables → CLI flags
- **Workflow plugins** — Drop `.py` files into `~/.config/kanboard/workflows/` to add custom CLI commands
- **Context manager support** — `KanboardClient` works with `with` statements for clean resource management
- **Cross-project orchestration** — Portfolio management, cross-project milestones, dependency analysis, and critical-path computation with dual-backend support (local JSON file or server-side plugin API)

## Cross-Project Orchestration

kanboard-cli ships a **dual-backend orchestration layer** that treats multiple Kanboard projects as a unified portfolio, with two interchangeable persistence backends.

### What it enables

| Capability | Description |
|---|---|
| **Portfolio management** | Group multiple projects into a named portfolio; stored locally or via the plugin |
| **Cross-project milestones** | Define milestones that span tasks from multiple projects; track percent-complete and at-risk status |
| **Dependency analysis** | Discover cross-project `blocks`/`is blocked by` relationships; detect blocked and blocking tasks |
| **Critical path** | Compute the longest dependency chain (topological sort) across all portfolio tasks |
| **Metadata sync** | Push portfolio and milestone membership into Kanboard project/task metadata using the `kanboard_cli:` key prefix |
| **Backend migration** | Move data between local and remote backends with `portfolio migrate` |

### Backends

kanboard-cli supports two interchangeable backends for portfolio and milestone storage:

| | **Local (default)** | **Remote (plugin)** |
|---|---|---|
| **Flag** | `--portfolio-backend local` | `--portfolio-backend remote` |
| **Storage** | `~/.config/kanboard/portfolios.json` on your machine | Server-side database tables in Kanboard (via plugin) |
| **Plugin required** | No — works with any Kanboard instance | Yes — [kanboard-plugin-portfolio-management](https://github.com/geekmuse/kanboard-plugin-portfolio-management) |
| **Shared with other users** | No — data is machine-local only | Yes — all users with server access see the same portfolios |
| **Offline operation** | Yes — reads/writes local file | No — requires a live Kanboard connection |
| **Performance** | N+1 API calls for task aggregation | Single server-side query (faster for large portfolios) |
| **Kanboard web UI integration** | No | Yes — portfolio dashboards, Gantt timeline, dependency graphs |
| **Referential integrity** | None — stale task/project IDs possible | Enforced by server-side foreign keys |

**When to use Local:** solo use, no plugin, offline-capable, quick setup — portfolio definitions live only on your machine and are invisible to other Kanboard users.

**When to use Remote:** team environments, shared visibility, large portfolios (performance), or when you want Kanboard's web UI to show portfolio dashboards and dependency graphs.

The backend is selected via `--portfolio-backend` flag, `KANBOARD_PORTFOLIO_BACKEND` env var, or `portfolio_backend` in your TOML profile (see [Configuration — Portfolio Backend Selection](docs/configuration.md#portfolio-backend-selection)). Switching backends at any time is safe — use `portfolio migrate` to copy data across.

### Quick example

```bash
# Create a portfolio grouping two projects
kanboard portfolio create "Platform Launch" --description "Q3 release"
kanboard portfolio add-project "Platform Launch" 1
kanboard portfolio add-project "Platform Launch" 2

# Create a milestone spanning both projects
kanboard milestone create "Platform Launch" "Beta Release" --target-date 2026-06-30
kanboard milestone add-task "Platform Launch" "Beta Release" 42
kanboard milestone add-task "Platform Launch" "Beta Release" 99 --critical

# Check progress and risks
kanboard milestone progress "Platform Launch"
kanboard portfolio show "Platform Launch"

# Visualise cross-project dependencies
kanboard portfolio dependencies "Platform Launch"
kanboard portfolio blocked "Platform Launch"
kanboard portfolio critical-path "Platform Launch"
```

### SDK example

```python
from kanboard import KanboardClient
from kanboard.orchestration import PortfolioManager, DependencyAnalyzer, LocalPortfolioStore

with KanboardClient(url=URL, token=TOKEN) as kb:
    store = LocalPortfolioStore()
    manager = PortfolioManager(kb, store)
    analyzer = DependencyAnalyzer(kb)

    # Aggregate tasks across all portfolio projects
    tasks = manager.get_portfolio_tasks("Platform Launch")

    # Find blocked cross-project tasks
    edges = analyzer.get_dependency_edges(tasks, cross_project_only=True)
    blocked = analyzer.get_blocked_tasks(tasks)

    # Compute critical path
    critical = analyzer.get_critical_path(tasks)
```

### Backend migration

```bash
# Check current backend config and data counts
kanboard portfolio migrate status

# Preview migration without writing
kanboard portfolio migrate local-to-remote --all --dry-run

# Migrate all local portfolios to the remote plugin
kanboard portfolio migrate local-to-remote --all

# Pull all remote portfolios to local
kanboard portfolio migrate remote-to-local --all

# Compare local and remote state
kanboard portfolio migrate diff --all
```

### SDK backend selection

```python
from kanboard import KanboardClient, create_backend
from kanboard.orchestration import PortfolioManager

with KanboardClient(url=URL, token=TOKEN) as kb:
    # Local backend (default — no plugin required)
    local_backend = create_backend("local")
    manager = PortfolioManager(kb, local_backend)

    # Remote backend (plugin required)
    remote_backend = create_backend("remote", client=kb)
    remote_manager = PortfolioManager(kb, remote_backend)

    # Both managers expose the same API
    tasks = manager.get_portfolio_tasks("Platform Launch")
    tasks = remote_manager.get_portfolio_tasks("Platform Launch")
```

### Server-side visualization (optional)

For in-browser Kanboard UI features — including interactive dependency graphs, multi-project Gantt timelines, portfolio dashboards, and board-level blocking indicators — see the companion **[Kanboard Portfolio plugin](https://github.com/geekmuse/kanboard-plugin-portfolio-management)**.

The CLI's `portfolio` and `milestone` commands work independently of the plugin (using the local JSON backend by default), but the plugin provides both the visual layer within Kanboard's web interface and the `remote` backend for the CLI and SDK.

See **[docs/sdk-guide.md#cross-project-orchestration](docs/sdk-guide.md#cross-project-orchestration)** and **[docs/cli-reference.md#portfolio](docs/cli-reference.md#portfolio)** for full reference documentation.

## Prerequisites

- **Python 3.11+** (required for `tomllib`, modern typing features)
- **A running Kanboard instance** with API access enabled
- **API token** from Kanboard Settings → API

## Installation

### From PyPI

```bash
pip install kanboard-cli
```

### From source (development)

```bash
git clone https://github.com/geekmuse/kanboard-cli.git
cd kanboard-cli
pip install -e ".[dev]"
```

## Quick Start

### 1. Configure your connection

Create `~/.config/kanboard/config.toml`:

```toml
[profiles.default]
url = "https://kanboard.example.com/jsonrpc.php"
token = "your-api-token-here"

[settings]
default_profile = "default"
output_format = "table"
```

Or use environment variables:

```bash
export KANBOARD_URL="https://kanboard.example.com/jsonrpc.php"
export KANBOARD_TOKEN="your-api-token-here"
```

### 2. Use the CLI

```bash
# List all projects
kanboard project list

# Create a task
kanboard task create 1 "Fix login bug" --color red --priority 3

# Get task details as JSON
kanboard task get 42 --output json

# Search tasks
kanboard task search 1 "status:open assignee:me"

# View the board
kanboard board show 1

# List overdue tasks across all projects
kanboard task overdue
```

### 3. Use the SDK

```python
from kanboard import KanboardClient

with KanboardClient(url="https://kanboard.example.com/jsonrpc.php",
                     token="your-api-token") as kb:
    # Create a project
    project_id = kb.projects.create_project(name="My Project")

    # Create a task
    task_id = kb.tasks.create_task(
        title="Implement feature X",
        project_id=project_id,
        color_id="green",
        priority=2,
    )

    # Get all active tasks
    tasks = kb.tasks.get_all_tasks(project_id, status_id=1)
    for task in tasks:
        print(f"#{task.id} [{task.color_id}] {task.title}")

    # Work with columns, swimlanes, comments, tags, and more
    columns = kb.columns.get_columns(project_id)
    kb.comments.create_comment(task_id=task_id, user_id=1, content="Started work")
    kb.tags.set_task_tags(project_id, task_id, ["backend", "urgent"])
```

For the full SDK guide including all resource examples, exception handling, and batch usage, see **[docs/sdk-guide.md](docs/sdk-guide.md)**.

## Configuration

### Config File

Location: `~/.config/kanboard/config.toml`

```toml
[profiles.default]
url = "https://kanboard.example.com/jsonrpc.php"
token = "your-api-token-here"
portfolio_backend = "local"        # or "remote" if plugin is installed

[profiles.dev]
url = "http://localhost:8080/jsonrpc.php"
token = "dev-token"

[settings]
default_profile = "default"
output_format = "table"            # table | json | csv | quiet
```

### Environment Variables

| Variable                       | Overrides                           |
|--------------------------------|-------------------------------------|
| `KANBOARD_URL`                 | `profiles.<active>.url`             |
| `KANBOARD_TOKEN`               | `profiles.<active>.token`           |
| `KANBOARD_PROFILE`             | `settings.default_profile`          |
| `KANBOARD_OUTPUT_FORMAT`       | `profiles.<active>.output_format`   |
| `KANBOARD_AUTH_MODE`           | `profiles.<active>.auth_mode`       |
| `KANBOARD_USERNAME`            | `profiles.<active>.username`        |
| `KANBOARD_PASSWORD`            | `profiles.<active>.password`        |
| `KANBOARD_PORTFOLIO_BACKEND`   | `profiles.<active>.portfolio_backend` |

### CLI Flags

| Flag                        | Purpose                                              |
|-----------------------------|------------------------------------------------------|
| `--url URL`                 | Kanboard JSON-RPC endpoint                           |
| `--token TOKEN`             | API token                                            |
| `--profile NAME`            | Config profile to use                                |
| `--output FORMAT`           | Output format: `table`, `json`, `csv`, `quiet`       |
| `--auth-mode MODE`          | Auth mode: `app` (API token) or `user` (username/password) |
| `--portfolio-backend BACKEND` | Portfolio storage: `local` (JSON file) or `remote` (plugin API) |
| `--verbose`                 | Enable debug logging                                 |

**Resolution order:** config file → environment variables → CLI flags (highest priority)

## CLI Reference

Every Kanboard resource has a corresponding command group:

| Command Group        | Description                              |
|----------------------|------------------------------------------|
| `kanboard task`      | Tasks: list, get, create, update, close, open, remove, search, move |
| `kanboard project`   | Projects: list, get, create, update, remove, enable, disable, activity |
| `kanboard board`     | Board view for a project                 |
| `kanboard column`    | Column management                        |
| `kanboard swimlane`  | Swimlane management                      |
| `kanboard comment`   | Task comments                            |
| `kanboard category`  | Project categories                       |
| `kanboard tag`       | Tags (global and per-project)            |
| `kanboard subtask`   | Subtask management                       |
| `kanboard timer`     | Subtask time tracking                    |
| `kanboard user`      | User administration                      |
| `kanboard me`        | Current user dashboard and activity      |
| `kanboard link`      | Link type definitions                    |
| `kanboard task-link` | Internal task-to-task links              |
| `kanboard external-link` | External task links                  |
| `kanboard group`     | Group management and membership          |
| `kanboard action`    | Automatic action configuration           |
| `kanboard project-file` | Project file attachments              |
| `kanboard task-file` | Task file attachments                    |
| `kanboard project-meta` | Project key-value metadata            |
| `kanboard task-meta` | Task key-value metadata                  |
| `kanboard project-access` | Project permissions (users/groups)  |
| `kanboard app`       | Application info (version, colors, roles)|
| `kanboard config`    | Configuration management                 |
| `kanboard workflow`  | List discovered workflow plugins         |
| `kanboard completion`| Shell completion (bash/zsh/fish)         |
| `kanboard portfolio` | Portfolio management + dependency analysis (cross-project orchestration) |
| `kanboard milestone` | Cross-project milestone tracking         |

Use `kanboard <command> --help` for detailed usage of any command.

For the full command reference, see **[docs/cli-reference.md](docs/cli-reference.md)**.

## Output Formats

All list/get commands support four output formats via `--output` / `-o`:

```bash
# Rich colored table (default)
kanboard task list 1

# JSON (for scripting and piping)
kanboard task list 1 -o json

# CSV (for spreadsheets and data tools)
kanboard task list 1 -o csv

# Quiet / ID-only (for shell pipelines)
kanboard task list 1 -o quiet | xargs -I{} kanboard task close {}
```

## Workflows (Plugin System)

kanboard-cli supports user-defined workflow plugins for custom automation.

### Creating a workflow

1. Create a Python file in `~/.config/kanboard/workflows/`:

```python
# ~/.config/kanboard/workflows/my_workflow.py
import click
from kanboard_cli.workflows.base import BaseWorkflow

class MyWorkflow(BaseWorkflow):
    @property
    def name(self) -> str:
        return "my-workflow"

    @property
    def description(self) -> str:
        return "My custom automation workflow"

    def register_commands(self, cli: click.Group) -> None:
        @cli.command("my-workflow")
        @click.pass_context
        def run(ctx):
            """Run my custom workflow."""
            client = ctx.obj["client"]
            # Use the full SDK here
            tasks = client.tasks.get_all_tasks(project_id=1, status_id=1)
            click.echo(f"Found {len(tasks)} active tasks")
```

2. Optionally add config in `~/.config/kanboard/config.toml`:

```toml
[workflows.my-workflow]
target_project = 1
```

3. The workflow is automatically discovered on next CLI invocation:

```bash
kanboard workflow list       # See discovered workflows
kanboard my-workflow         # Run your workflow
```

See `docs/plan/05-milestone-3-extended.md` (Tasks 40–41) for full architecture details.

## Project Structure

```
kanboard-cli/
├── src/
│   ├── kanboard/                   # SDK package (import kanboard)
│   │   ├── __init__.py             # Public API surface
│   │   ├── client.py               # JSON-RPC transport layer
│   │   ├── config.py               # Layered configuration resolution (incl. portfolio_backend)
│   │   ├── exceptions.py           # Typed exception hierarchy
│   │   ├── models.py               # Dataclass response models + plugin models
│   │   ├── resources/              # 26 modules, one per API category
│   │   │   ├── portfolios.py       # PortfoliosResource — plugin API (18 methods)
│   │   │   ├── milestones.py       # MilestonesResource — plugin API (10 methods)
│   │   │   └── [24 core modules]
│   │   └── orchestration/          # Cross-project orchestration (opt-in)
│   │       ├── __init__.py         # Exports: PortfolioManager, DependencyAnalyzer, LocalPortfolioStore,
│   │       │                       #          PortfolioBackend, RemotePortfolioBackend, create_backend
│   │       ├── portfolio.py        # PortfolioManager — multi-project aggregation
│   │       ├── dependencies.py     # DependencyAnalyzer — graph traversal, critical path
│   │       ├── store.py            # LocalPortfolioStore — JSON persistence
│   │       └── backend.py          # PortfolioBackend Protocol, RemotePortfolioBackend, create_backend()
│   └── kanboard_cli/               # CLI package
│       ├── main.py                 # Click app root, global options (incl. --portfolio-backend)
│       ├── formatters.py           # Table / JSON / CSV / quiet renderers
│       ├── renderers.py            # ASCII dependency graph, progress bar renderers
│       ├── workflow_loader.py      # Plugin discovery and loading
│       ├── commands/               # One module per CLI command group
│       │   ├── portfolio.py        # Portfolio CRUD + dependency analysis + migrate subgroup
│       │   └── milestone.py        # Cross-project milestone management
│       └── workflows/
│           └── base.py             # BaseWorkflow ABC
├── tests/
│   ├── unit/                       # Mocked httpx tests
│   │   ├── resources/              # One test file per resource module
│   │   └── orchestration/          # Orchestration unit tests (incl. test_backend.py)
│   ├── integration/                # Docker-based lifecycle tests (incl. test_plugin_backend.py)
│   ├── security/                   # Security fuzzing (API fuzz + Hypothesis property tests)
│   └── cli/                        # CliRunner output tests
├── docs/
│   ├── plan/                       # Architecture and build plan
│   ├── design/                     # Design documents and research
│   └── tasks/                      # Per-task implementation notes
├── docker/
│   └── Dockerfile.kanboard-plugin-test   # Kanboard image with portfolio plugin pre-installed
├── docker-compose.test.yml
├── pyproject.toml
├── Makefile
├── LICENSE
└── CHANGELOG.md
```

## Development

### Setup

```bash
git clone https://github.com/geekmuse/kanboard-cli.git
cd kanboard-cli
pip install -e ".[dev]"
```

### Common Commands

```bash
make install          # Install in editable mode with dev dependencies
make lint             # Run ruff linter
make test             # Run unit and CLI tests
make test-integration # Run integration tests (requires Docker)
make coverage         # Run tests with coverage report
```

### Testing

- **Unit tests** — Mocked `httpx` via `pytest-httpx`; covers all SDK resource methods and exception paths
- **CLI tests** — Click `CliRunner`-based; verifies output across all four formats
- **Integration tests** — Docker-based (`docker-compose.test.yml`) with a live Kanboard instance; full CRUD lifecycle for every resource
- **Security fuzzing** — See [Security Testing](#security-testing) below

### Security Testing

kanboard-cli includes a two-layer security fuzzing framework:

```bash
# Install security dependencies
pip install -e ".[security]"

# Run Python-native security tests only (no Docker needed)
./scripts/security-fuzz.sh --no-docker

# Run the full suite including API fuzzing (requires Docker)
./scripts/security-fuzz.sh

# Run API fuzzing only
./scripts/security-fuzz.sh --api-only
```

**Layer 1 — JSON-RPC API Fuzzing** (`tests/security/test_jsonrpc_fuzz.py`):  
Sends malicious payloads to a live Dockerized Kanboard instance and asserts safe handling — SQL injection, XSS, path traversal, command injection, type confusion, boundary values, null bytes, unicode edge cases, protocol abuse, and auth boundary testing.  All 31 plugin API methods are also fuzzed via `api-schema.json`.

**Layer 2 — Python-native Fuzz Testing** (`tests/security/test_sdk_fuzz.py`):  
Uses [Hypothesis](https://hypothesis.readthedocs.io/) property-based testing (200 examples per test) to verify the SDK client, model deserialization, config resolution, CLI input handling, and response parsing all survive adversarial inputs.

**Static analysis:**  
[Bandit](https://bandit.readthedocs.io/) for Python security issues and [pip-audit](https://pypi.org/project/pip-audit/) for dependency vulnerabilities.

The full suite runs nightly via GitHub Actions (`.github/workflows/security-fuzz.yml`) and on every release.

### Dependencies

| Package       | Purpose                                   |
|---------------|-------------------------------------------|
| `httpx`       | HTTP client (JSON-RPC transport)          |
| `click`       | CLI framework                             |
| `rich`        | Colored table output                      |
| `tomli-w`     | TOML config writing                       |
| `pytest`      | Test framework (dev)                      |
| `pytest-httpx`| HTTP mocking for tests (dev)              |
| `ruff`        | Linter and formatter (dev)                |
| `coverage`    | Code coverage reporting (dev)             |
| `bandit`      | Python static security analysis (security)|
| `pip-audit`   | Dependency vulnerability scanning (security)|
| `hypothesis`  | Property-based fuzz testing (security)    |

## Exception Hierarchy

The SDK provides structured exceptions for programmatic error handling:

```
KanboardError (base)
├── KanboardConfigError          # Missing/invalid configuration
├── KanboardConnectionError      # Network/connection failures
├── KanboardAuthError            # HTTP 401/403, invalid credentials
├── KanboardAPIError             # JSON-RPC error responses
│   ├── KanboardNotFoundError    # Resource not found (null response)
│   └── KanboardValidationError  # Invalid parameters
└── KanboardResponseError        # Malformed/unparseable responses
```

## License

[MIT](LICENSE)
