pinto
Clarity, Simplicity and Humanity.
With or without AI.
pinto is a lightweight, local-first Scrum backlog and Kanban board for the terminal. It keeps Product Backlog Items (PBIs), Sprints, and board state in readable text files so that every change is easy to inspect with Git.
Principles
- Fast and simple. Minimal dependencies and a small vocabulary.
- Focused on Scrum. Product Backlog, Sprints, and Kanban—not project-suite features such as billing, CRM, or Gantt charts.
- Plain text and Git-friendly. Board data is Markdown with TOML frontmatter.
- Local first. No server, database service, or account is required.
Installation
Install Rust 1.89 or newer (the minimum supported Rust version; Rust 2024 edition itself requires Rust 1.85 or newer), then build from source:
The latest published release is 0.1.1. Install it from crates.io with:
The installed binary remains pinto.
Agent skill
The repository includes a pinto-workflow Agent Skill for backlog management, CLI dogfooding, and
local-first Scrum workflows. See docs/skills.md for reproducible installation and
usage with the official skills CLI.
Documentation
The user and contributor guide is built with mdBook. Install the managed tools and build it with:
Run mdbook serve to preview the site locally. The source is in
docs/book/src, and the generated site is written to target/book/. The
repository's design, JSON, migration, and dogfooding reference documents remain available under
docs/.
Quick start
# Create a board in the current directory.
# Add a Product Backlog Item (PBI).
# Inspect and update it.
# Render the board.
Example board output:
todo (1)
T-2 Design the board layout
in-progress (1)
T-1 Implement the parser
review (0)
(empty)
done (0)
(empty)
Commands
| Command | Description |
|---|---|
pinto init |
Initialize .pinto/ in the current directory. It is idempotent. |
pinto add <title> |
Add a PBI. Use --points, --label, --sprint, --body, --edit, or --template as needed. |
pinto list |
List PBIs; filter with --status <status>... (or repeat --status), --sprint <id>, --label <label>... (OR; use --all-labels for AND), or --search/-F (use --regex/-R for regular expressions). Use --roots-only to omit PBIs with a parent. --long/-l shows ID, TITLE, STATUS, POINTS, ASSIGNEE, CREATED, and UPDATED; add --label or --sprint without a value to include that column. |
pinto show <id>... |
Show one or more PBIs in input order; --plain keeps raw Markdown and --json always returns an array. |
pinto edit <id> |
Update PBI fields, or open $VISUAL/$EDITOR when no field is supplied. |
pinto rm <id>... |
Archive (default) or permanently delete one or more PBIs. |
pinto move <id> <status> |
Move a PBI to a workflow column. |
pinto reorder <id> |
Reorder a PBI with --before, --after, --top, or --bottom. |
pinto board |
Display the board (PBI by column). Filter by --status <status>... (or repeat --status) for multiple columns, Sprint, or --label <label>... (OR; use --all-labels for AND). Use --roots-only to omit PBIs with a parent; --long/-l uses the same detail columns as pinto list --long, with --label and --sprint available as column selectors. |
pinto kanban |
Open the interactive terminal board. By default, [tui].hidden_columns is omitted; use --column <status>... (or repeat --column) to override the configured display columns. |
pinto dep add/rm |
Add or remove item dependencies. |
pinto link add/rm/scan |
Associate Git commits with PBIs, or scan commit messages for item IDs. |
pinto dod |
View, set, or clear the shared Definition of Done. |
pinto sprint |
Create, edit, delete, start, close, list, assign, and report on Sprints (burndown, velocity, capacity). |
pinto cycletime / pinto ct |
Report cycle and lead-time metrics. |
pinto rebalance |
Reassign oversized ranks while preserving item order. Use --dry-run to preview changes. |
pinto migrate --to <backend> |
Move a board between the file, Git, and optional SQLite storage backends. |
pinto automate --plan <JSON/PATH/-> |
Validate and execute a structured plan from inline JSON, a file, or standard input; add --dry-run or --json for safe previews and machine-readable results. |
pinto shell |
Start the interactive shell with history, editing, and completion. |
pinto completion <shell> |
Print a completion script for a supported shell. |
Run pinto --help or pinto <command> --help for the complete interface.
Command examples
Run these commands from a directory initialized with pinto init. Replace
example IDs, labels, and dates with values from your board.
Backlog and board
kanban opens the interactive terminal board. Its footer keeps the five primary
operations visible: cursor movement, expand, details, normal quit, and help. Press
? to open the secondary-operation help window; q opens a quit
confirmation and e edits the selected item with your editor. The help window is non-modal:
commands remain available while it is displayed and close it once accepted; press ? to close it
without running another command.
To hide workflow columns from the default Kanban display, add their exact names under [tui].
The workflow order remains the order in columns, and kanban --column overrides this setting
for one invocation:
[]
= ["backlog"]
For example, with columns = ["backlog", "ready", "in-progress", "review", "done"], the
default display contains ready, in-progress, review, and done. Unknown hidden columns are
reported as configuration errors. --column ready in-progress displays only those columns, while
still retaining the full workflow for Kanban movement operations.
Human-readable timestamps use the operating system's local timezone by default. Set
[display].timezone in .pinto/config.toml to local, UTC, or a fixed offset such as
+09:00 or -05:00:
[]
= true
= "+09:00"
This setting applies to timestamp columns in list --long and board --long, Sprint
periods in sprint list, PBI details from show, and the Kanban details popup. JSON output
always keeps timestamps in UTC RFC3339 form for stable machine-readable data. Unsupported
timezone values are rejected with the accepted formats in the error message.
Parent PBI points are opt-in. Set [points].aggregate_children to true to derive a parent
PBI's displayed points from its active descendant leaves:
[]
= true
The parent's stored points are ignored while it has children, and nested parent values are counted
only once through their leaves. PBIs in done_column do not contribute. An active descendant leaf
without points makes the affected parent uncomputed (-); this avoids presenting an incomplete
sum as a complete estimate. The default is false, so existing boards keep their stored points.
The configuration schema is strict. Unknown keys (for example, a misspelled
[display].timezome) are rejected with their table and field path instead of
silently using a default. Workflow columns must be non-blank and unique, and
done_column, hidden columns, and WIP limit keys must name configured columns.
The project name must not be blank, and the project key uses the same
ASCII-letter grammar as PBI IDs.
Dependencies, Git links, and Definition of Done
Sprints and reports
Close a completed sprint with pinto sprint close S-1. The burndown,
velocity, and cycletime reports can also emit JSON with --json where
available, making them easy to use in scripts.
Storage, automation, and shell integration
The Git backend creates one commit for each complete write operation (a
multi-file migration is one operation). It isolates pre-existing Git changes
with a temporary index, so they remain available for the user's own commit.
If it needs to initialize a Git repository for the first write, pinto prints a
warning first. automate
accepts only a validated argv-style JSON plan; it does not execute shell code. Use a file path or
- for standard input when a plan contains long or multiline bodies. --dry-run validates and
executes the plan in an isolated copy of the board, reporting planned changes without modifying
the real board. The source board is locked while that snapshot is taken. For a normal repository
or a linked worktree, pinto copies only .pinto and creates a temporary owner-private Git
repository when the source project has Git metadata; it never recursively copies the source .git
object store. The temporary workspace is removed after both successful and failed previews. --json
reports each command as valid, succeeded, failed, or skipped,
including created and updated item IDs and recovery-relevant errors. An add command can combine
--template default with --body without opening an editor.
TUI demo
Start the interactive board with pinto kanban (or pinto k) in a terminal:
┌ todo ─────────────────┬ in-progress ──────────┬ review ────────────────┐
│ T-2 Design board │ T-1 Implement parser │ (empty) │
│ │ │ │
├────────────────────────┴────────────────────────┴────────────────────────┤
│ h/l,j/k cursor Space expand │
│ v details q quit ?: help │
└───────────────────────────────────────────────────────────────────────────┘
This ASCII preview shows the layout without requiring image assets or a
particular terminal theme. The live TUI adapts columns to the terminal width,
displays the selected item's details, and keeps secondary key bindings in the
? help window.
FAQ
Where is my board stored?
Everything is local to .pinto/ beneath the directory where you run init.
PBI files are readable Markdown with TOML frontmatter, so ordinary Git tools
can review their history.
Can I customize the workflow?
Yes. Edit .pinto/config.toml to set columns and done_column; subsequent
list, move, board, and TUI commands use the new workflow immediately.
Kanban key assignments live under [tui.key_bindings]. Each operation accepts
an ordered array; every entry is active and the first entry is shown in the
fixed footer or the ? help window:
[]
= ["Ctrl+d", "v"]
= ["Cmd+q", "Esc"]
= ["?"]
= ["h", "Left"]
= ["a"]
= ["d"]
= ["D"]
= ["p"]
Key names include Esc, Enter, Space, Left/Right/Up/Down, and
single characters. Modifiers are written as Ctrl+, Alt+, Shift+, Cmd+,
Meta+, or Hyper+; Cmd maps to the terminal's platform-specific
Command/Super modifier. For printable keys, write the resulting character
directly (A or <, not Shift+a or Shift+,), including after another
modifier (Ctrl+A, not Ctrl+Shift+a). Shift remains available for named keys
such as Shift+Left. Other keys use their key names.
The footer
shows the first configured key for the five primary operations plus help; every
other accepted operation key appears in the ? help window. The default ? key
opens help, so the default
regular-expression search key is Ctrl+?. Missing operations retain the built-in
defaults, while invalid names report the operation and the supported syntax.
In Kanban, a opens a two-step form for a new PBI (title, then body). d adds a
dependency, D removes one, and p sets or clears the selected PBI's parent;
in each relation form, move the cursor to another card and press Enter, or type an ID directly.
Press Enter on the selected source with an empty parent field to clear its parent.
Esc cancels without changing the board. These operations reuse the CLI validation
and persistence services, so invalid IDs, missing items, parent cycles, and dependency
cycle warnings use the same rules as the corresponding CLI commands.
How do I get command-specific help?
Run pinto <command> --help, for example pinto sprint --help or
pinto migrate --help. This is the authoritative reference for every option.
Does pinto need a server or account?
No. It is local-first and works with the filesystem and optional local Git repository already on your machine.
Board files
pinto init creates .pinto/config.toml and stores each PBI as a separate
Markdown file. The default workflow is todo → in-progress → review → done.
Customize its column names and order in config.toml; pinto board and
pinto move immediately use the updated workflow. The configured done_column
is displayed newest-first by completion time.
The same configuration file contains [tui] settings, including the complete
default [tui.key_bindings] table written by pinto init.
pinto rm archives one or more PBIs to .pinto/archive/ by default. Use
--force only when permanent deletion is intended.
Sprints and reports
Create a Sprint with pinto sprint new, then assign PBIs with pinto sprint add. A goal-less planned Sprint can be repaired with pinto sprint edit
before starting it. pinto sprint remove removes the Sprint and releases its
PBIs without deleting them. Assign PBIs only to planned or active Sprints;
existing assignments remain visible after close and can be removed with
pinto sprint unassign. A Sprint progresses from planned to active to closed. Capacity,
velocity, burndown, and cycle-time reports are local calculations over this
stored board data; they require no external service.
Templates and editor support
Reusable item and Sprint bodies live in these plain-text paths:
.pinto/templates/item/<name>.md
.pinto/templates/sprint/<name>.md
Apply them through --template. pinto add --edit opens an empty temporary
file (or a template) in $VISUAL, falling back to $EDITOR; its saved contents
become the item body.
Automation and JSON
Most read commands accept --json for machine-readable output. pinto automate --plan accepts a JSON argv plan from inline input, a file, or standard
input and runs each command through the same validation, service, and storage
paths as the normal CLI. It neither stores API keys nor requires a particular AI
provider.
Development
The project uses mise for tool versions and common tasks:
See CONTRIBUTING.md for the TDD workflow and docs/DESIGN.md for architecture decisions. Stability and dependency trade-offs are recorded in docs/stability.md and docs/dependencies.md.
License
Distributed under the MIT License.