| name | kan |
| description | Manage kanban boards using the Kan CLI. Use when working with tasks, cards, boards, columns, or project tracking via the kan command. |
Kan CLI
Kan is a file-based kanban board CLI. All data lives in .kan/ as plain files.
Full documentation is available at amterp.dev/kan/docs.
Getting Help
Every command supports --help for detailed usage:
kan --help
kan add --help
kan column --help
Board Setup Wizard
When a user asks to create a new board (kan board create) or initialize a new Kan project (kan init), run this interactive setup process rather than creating a board with defaults. This applies to both new projects and additional boards.
Before writing any config, consult the Kan documentation to double-check available options and TOML structure.
Step 1: Understand the Project and User
Before suggesting anything, learn about what the user is building:
- What kind of project is this? (software, personal, team, open source, etc.)
- What workflow are they trying to track?
- Is this their first board or are they adding to an existing setup?
This context lets you make relevant, project-specific suggestions in later steps.
Also gauge whether the user is already familiar with Kan. If it's unclear, ask. If they're new, explain concepts (like wanted fields, card display roles, enum-set types) as they come up during the wizard. If they're experienced, keep it snappy.
Step 2: Columns
Ask the user what columns they want. Offer these templates as inspiration - they are not rigid. The user can mix, match, rename, add, or remove columns freely.
Simple - Good default for most projects:
| Column | Description | Limit |
|---|
| backlog | Planned work not yet started | |
| next | Ready to be picked up next | 5 |
| in-progress | Currently being worked on | 5 |
| done | Completed work | |
Prioritized Backlog - Splits the backlog for triage:
| Column | Description | Limit |
|---|
| backlog-lo | Low priority planned work | |
| backlog-hi | High priority planned work | 10 |
| next | Ready to be picked up next | 5 |
| in-progress | Currently being worked on | 5 |
| done | Completed work | |
With Ideas - Adds a staging area for uncommitted thoughts:
| Column | Description | Limit |
|---|
| uncommitted | Ideas and thoughts not yet committed to | |
| backlog | Planned work not yet started | |
| next | Ready to be picked up next | 5 |
| in-progress | Currently being worked on | 5 |
| done | Completed work | |
Full - Prioritized backlog with ideas column:
| Column | Description | Limit |
|---|
| uncommitted | Ideas and thoughts not yet committed to | |
| backlog-lo | Low priority planned work | |
| backlog-hi | High priority planned work | 10 |
| next | Ready to be picked up next | 5 |
| in-progress | Currently being worked on | 5 |
| done | Completed work | |
Important: Every column should have a description. Descriptions serve as self-documentation and help guide AI agents using the board. Suggest descriptions if the user doesn't provide them.
Column Limits: Columns can have an optional limit that caps how many cards they hold. When a column is full, adding or moving cards into it is refused. This is a core kanban practice for controlling flow. Suggest limits for active workflow columns (like next and in-progress) - leave unbounded columns (like backlog and done) without limits. The defaults in the templates above are good starting points; adjust based on preference.
The first column in the list becomes the default column for new cards.
Step 3: Custom Fields
Walk the user through what fields they want on their cards. For each field, discuss:
- What type? (
string, enum, enum-set, free-set, date, boolean)
- What are the options/values? (for
enum and enum-set types)
- Descriptions for the field itself and each of its options
- Should this field be wanted? (If the user is new, explain: wanted fields generate a warning when a card is created without them, encouraging consistent metadata across cards)
Type Field (Strongly Recommended)
Unless the user explicitly doesn't want one, recommend a type field (enum, wanted). This categorizes what kind of work a card represents. Suggest these default values:
| Value | Color | Description |
|---|
| bug | #dc2626 | A defect in existing functionality |
| enhancement | #2563eb | An improvement to existing functionality |
| feature | #16a34a | New functionality to be added |
| chore | #4b5563 | Maintenance, refactoring, or housekeeping |
The user can customize these - add, remove, rename, or change descriptions to fit their project.
Other Fields
The type field alone is often enough, especially for new boards. Lean toward simplicity. But if the user asks about other fields, or if their project clearly calls for one based on what you learned in Step 1, here are some examples to get their thinking going:
- labels (
enum-set) - Flexible tagging. Example values: ai-hi (well-suited for autonomous AI work - low complexity, little judgment needed), ai-lo (likely suitable for AI but less certain). Cards without an AI label are implicitly not suitable for autonomous AI work.
- effort (
enum) - T-shirt sizing for rough estimation: xs, s, m, l, xl
- area (
enum-set) - What part of the project a card touches, e.g. backend, frontend, infra, docs
Don't actively suggest these - just mention that additional fields are possible and offer examples if the user is interested.
Step 4: Card Display
Configure how fields appear on cards in the web UI and CLI output. Apply sensible defaults without overwhelming the user with implementation details:
- If the user has a
type field, set it as the type_indicator (colored badge on each card).
- Any
enum-set or free-set fields (like labels) should go in badges (chip-style labels on cards).
- Other fields like dates or effort can go in
metadata (small text below the card).
- If the user has an enum field they want to use for visual emphasis (like priority or urgency), it can be assigned to
tint to wash the card's background with that option's color.
- To make the board open already sorted by a field (instead of manual order), set
default_sort = "<field>" under [card_display], plus default_sort_desc = true for descending. This affects the web board view on load; anyone can still re-sort or switch to Manual order for their own session, and it never changes saved card positions. Only set it if the user asks for a default ordering - don't apply it proactively.
For new users, just set these defaults and briefly mention that their type will show as a colored badge on cards and any tags will show as labels. Don't expose config key names like type_indicator or badges unless the user asks for details.
Step 5: Pattern Hooks (Optional)
Pattern hooks automate actions when cards are created with titles matching a pattern. This is entirely optional - offer it, but don't push it.
If the user set up a type field, suggest the type shortcut hook: when creating a card in the web UI with !bug anywhere in the title (e.g. !bug Fix login crash or Fix login crash !bug), the hook automatically strips the !bug and sets the type field to bug. It also supports aliases like !feat for feature and !enh for enhancement.
For newcomers, keep the explanation focused on what it does rather than how: "This is a convenience shortcut. Instead of setting the type field manually after creating a card, you can just include !bug, !feat, !chore, etc. anywhere in your title and it gets set for you automatically."
If the user wants this hook:
-
Detect Rad: Silently run rad -v. If Rad is installed, ask whether they'd prefer the hook written in Rad or Bash. If not installed, silently default to Bash.
-
Create the hook script at .kan/hooks/type-shortcut.rad (or .sh if writing Bash). Here's the Rad version - translate to Bash if needed:
#!/usr/bin/env rad
---
Pattern hook for setting card type from title shortcuts like !bug, !feat, !chore.
Receives card_id and board_name as arguments from Kan pattern hooks.
---
args:
card_id str
board_name str
type_aliases = {
"feat": "feature",
"enh": "enhancement",
}
code, stdout = quiet $`kan show {card_id} -b {board_name} --json`
if code != 0:
exit(1)
card_data = parse_json(stdout)
title = card_data["card"]["title"]
if not matches(title, "![a-zA-Z]+", partial=true):
exit(0)
match_result = replace(title, "(?i).*!([a-z]+).*", "$1")
type_keyword = lower(match_result)
card_type = type_keyword
if type_keyword in type_aliases:
card_type = type_aliases[type_keyword]
new_title = replace(title, "(?i)\\s*![a-z]+\\s*", " ")
new_title = trim(new_title)
new_title = replace(new_title, "\\s+", " ")
quiet $`kan edit {card_id} -b {board_name} -t "{new_title}" -f type={card_type}`
print("Set type to '{card_type}'")
-
Make executable: chmod +x .kan/hooks/type-shortcut.rad (or .sh)
-
Add the hook to the board config:
[[pattern_hooks]]
name = "type-shortcut"
pattern_title = "![a-zA-Z]+"
command = ".kan/hooks/type-shortcut.rad"
Step 6: Execute and Verify
-
Create the board:
- New project:
kan init -c <columns> -n <board-name> -p <project-name>
- Additional board:
kan board create <board-name>, then edit the TOML to set up columns
-
Edit the board config at .kan/boards/<name>/config.toml to add:
- Column descriptions (and column definitions if using
kan board create)
- Custom fields with types, options, descriptions, colors, and wanted flags
- Card display configuration
-
Verify the result: kan board describe
Reference TOML format for custom fields and card display:
[custom_fields.type]
type = "enum"
wanted = true
description = "The category of work this card represents"
[[custom_fields.type.options]]
value = "bug"
color = "#dc2626"
description = "A defect in existing functionality"
[[custom_fields.type.options]]
value = "enhancement"
color = "#2563eb"
description = "An improvement to existing functionality"
[custom_fields.labels]
type = "enum-set"
description = "Flexible tags for categorization"
[[custom_fields.labels.options]]
value = "ai-hi"
description = "Well-suited for autonomous AI work"
[[custom_fields.labels.options]]
value = "ai-lo"
description = "Likely suitable for AI but less certain"
[card_display]
type_indicator = "type"
badges = ["labels"]
Column descriptions and limits are added to the [[columns]] entries:
[[columns]]
name = "backlog"
color = "#6b7280"
description = "Planned work not yet started"
[[columns]]
name = "in-progress"
color = "#f59e0b"
description = "Currently being worked on"
limit = 5
Git Worktree Support
When you run kan commands inside a git worktree, Kan automatically uses the board from the main worktree. This means all worktrees share the same kanban board by default - you don't need to initialize or manage separate boards per worktree.
If you run kan init inside a worktree, Kan warns that this will create a separate, independent board. If you confirm, the worktree gets its own .kan/ directory and worktree_independent = true is set in its project config.
Initialize
kan init
kan init -l .kanboard
kan init -c todo,doing,done
kan init -n myboard
kan init -p myproject
kan init -c a,b,c -n project
| Flag | Description |
|---|
-l, --location | Custom location for .kan directory |
-c, --columns | Comma-separated column names (default: backlog,next,in-progress,done) |
-n, --name | Board name (default: main) |
-p, --project-name | Project name for favicon and page title (default: git repo or directory name) |
Adding Cards
kan add "Fix login bug"
kan add "Fix login bug" -c backlog
kan add "Feature" -b features -c todo
kan add "Title" "Description here" -c backlog
kan add "Subtask" -p 12
kan add "Task" -f priority=high -f type=bug
kan add "Task" -f component=core -f component=cli
kan add "Urgent" -c backlog --position 0
kan add "Follow-up" --after fix
kan add "Buy milk" -g
| Flag | Description |
|---|
-b, --board | Target board |
-c, --column | Target column |
-p, --parent | Parent card ID or alias |
--position | Insert at index (0 = top, -1 = end, negatives count from end) |
--before | Insert before this card (ID or alias) |
--after | Insert after this card (ID or alias) |
-f, --field | Custom field (key=value, repeatable; set fields also accept comma-separated values) |
--strict | Error if wanted fields are missing (default: warn) |
-g, --global | Target the designated global board (see Global Board) |
--position/--before/--after are mutually exclusive; default is end of column. Without -c, the card is placed in the anchor card's column. Prefer --before/--after for non-boundary spots (kan list shows no indices to count against).
Listing Cards
kan list
kan list -c done
kan list -b myboard
kan list --sort priority
kan list --sort priority --descending
--sort <field> orders cards within each column by a custom field instead of by manual position. For enum/enum-set fields the order follows the option order in the board config (not alphabetical); cards with no value are listed last. Add --descending (-d) to sort high to low. It's a view sort - saved card positions are unchanged.
Showing Card Details
kan show 12
kan show fix
kan show fix -b myboard
Card identifiers accept partial substring matches against a card's alias or ID
(case-insensitive, min 3 chars). A single match resolves; multiple matches
produce a disambiguation error listing up to 5 candidates. Exact ID or alias
always wins over fuzzy. This applies to show, history, edit, delete, and
comment add.
kan show also displays how long the card has been in its current column
(e.g. Column: review (3 days)), tracked natively by Kan rather than inferred
from VCS commits.
Card History
kan history 12
kan history fix -b myboard
kan history fix --json
Kan records each column transition (with its event time) on the card, so you
can see the full journey and how long the card spent in each column -
independent of how often you commit. Title/description edit history is left to
your VCS (git log), which already tracks content changes well.
Editing Cards
kan edit 12
kan edit fix -t "New title"
kan edit fix -c done
kan edit fix -c done --position 0
kan edit fix --before deploy
kan edit fix -d "New description"
kan edit fix -f priority=low
| Flag | Description |
|---|
-b, --board | Board name |
-t, --title | Set card title |
-d, --description | Set card description |
-c, --column | Move card to column |
-p, --parent | Set parent card |
--position | Move to index in column (0 = top, -1 = end, negatives count from end) |
--before | Move before this card (ID or alias) |
--after | Move after this card (ID or alias) |
-a, --alias | Set explicit alias |
-f, --field | Set custom field (key=value, repeatable; set fields also accept comma-separated values) |
--strict | Error if wanted fields are missing (default: warn) |
--position/--before/--after are mutually exclusive. They reorder within the current column or place precisely when moving columns. Without -c, the card is placed in the anchor card's column. Prefer --before/--after for non-boundary spots.
Deleting Cards
kan delete 12
kan delete fix-login
kan delete 12 --force
kan delete 12 -b myboard
| Flag | Description |
|---|
-b, --board | Board name |
-f, --force | Skip confirmation (required in non-interactive mode) |
Board Management
kan board create features
kan board list
kan board delete features
kan board delete features -f
kan board describe
kan board describe --json
Column Management
kan column add review
kan column add review --color "#9333ea"
kan column add review --position 2
kan column add review --description "Cards under review"
kan column add review --limit 5
kan column delete review
kan column rename review code-review
kan column edit review --color "#ec4899"
kan column edit review --description "Updated purpose"
kan column edit review --limit 3
kan column edit review --limit 0
kan column list
kan column move review --position 1
kan column move review --after backlog
Comments
kan comment add fix-login "Found the issue"
kan comment add fix-login
kan comment edit c_9kL2x "Updated text"
kan comment delete c_9kL2x
| Flag | Description |
|---|
-b, --board | Board name |
Committing Kan Files
kan commit
kan commit -m "update board"
| Flag | Description |
|---|
-m, --message | Commit message (default: "chore: update kan files") |
Only kan data files are committed, leaving any other staged changes untouched. Requires being inside a git repository.
Global Board
Designate one board as the global board so you can act on it from any working directory with -g/--global - useful for an "inbox" board captured to from anywhere.
kan global set
kan global set inbox
kan global show
kan global unset
kan add -g "Buy milk"
kan list -g
kan edit -g buy-milk -c done
-g works on add, list, show, history, edit, delete, and comment add/edit/delete. It targets the global board's project with the designated board as default; an explicit -b overrides it (kan add -g -b other "..."). There is no implicit fallback - -g must be explicit, and bare commands outside a project still error rather than capturing to the global board.
Web Interface
kan serve
kan serve -p 8080
kan serve --no-open
Migration
kan migrate
kan migrate --dry-run
kan migrate --all
kan migrate --all --dry-run
| Flag | Description |
|---|
--dry-run | Show what would be changed without modifying files |
--all | Migrate all projects registered in global config |
Health Checks
kan doctor
kan doctor --fix
kan doctor --dry-run
kan doctor -b main
kan doctor --json
Exit codes: 0 = no errors (warnings OK), 1 = errors found
Fixable issues: orphaned cards, missing card references, duplicate IDs, invalid default column, invalid parent refs.
Shell Completion
kan completion bash
kan completion zsh
eval "$(kan completion zsh)"
eval "$(kan completion bash)"
Completion supports commands, flags, board names, card IDs/aliases, and column names.
Global Flags
| Flag | Description |
|---|
-I, --non-interactive | Fail instead of prompting for input |
--json | Output results as JSON (supported by: show, list, add, edit, board list, column list, comment add, doctor) |
Board Configuration
Board configuration is stored in .kan/boards/<boardname>/config.toml. Key features:
Pattern Hooks
Run commands when cards are created with matching titles:
[[pattern_hooks]]
name = "jira-sync"
pattern_title = "^[A-Z]+-\\d+$"
command = ".kan/hooks/jira-sync.sh"
timeout = 60
Hooks receive <card_id> <board_name> as arguments and run after card creation. The command must be a path to an executable (not a shell command with arguments). Use ~ for home directory.
Link Rules
Auto-link patterns in card descriptions:
[[link_rules]]
name = "jira"
pattern = "([A-Z]+-\\d+)"
url = "https://jira.example.com/browse/{1}"
JSON Output
Use --json for programmatic access to Kan data:
kan show fix-login --json | jq .card.title
kan list --json | jq '.cards | length'
kan add "New task" --json | jq .card.id
kan board list --json | jq .boards
kan column list --json | jq '.columns[].name'
kan comment add fix-login "Note" --json | jq .comment.id
Tips
- Cards are identified by flexible IDs: numeric ID, alias, or partial match
- Use
-I for scripting to ensure commands fail rather than prompt
- Use
--json for programmatic access to card data
Documentation
Full documentation is available at amterp.dev/kan/docs. Consult the docs when setting up boards or editing config files to verify available options and TOML structure.