pinto-cli 0.1.1

A lightweight, local-first, Git-friendly Scrum backlog and Kanban board for the CLI and TUI
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# 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:

```bash
git clone https://github.com/moriturus/pinto
cd pinto
cargo install --path . --locked
```

The latest published release is `0.1.1`. Install it from crates.io with:

```bash
cargo install pinto-cli --version 0.1.1
```

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](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:

```bash
mise install
mise run book
```

Run `mdbook serve` to preview the site locally. The source is in
[`docs/book/src`](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/`](docs/).

## Quick start

```bash
# Create a board in the current directory.
pinto init

# Add a Product Backlog Item (PBI).
pinto add "Implement the parser" --points 3 --label backend

# Inspect and update it.
pinto list
pinto show T-1
pinto move T-1 in-progress

# Render the board.
pinto board
```

Example board output:

```text
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

```bash
pinto init
pinto add "Implement the parser" --points 3 --label backend
pinto list --status todo --long
pinto list --status todo in-progress --json
pinto list --label backend frontend                 # either label (OR)
pinto list --label backend frontend --all-labels    # both labels (AND)
pinto list --roots-only --status todo --json       # roots only, machine-readable
pinto show T-1
pinto move T-1 in-progress
pinto reorder T-1 --top
pinto edit T-1 --title "Implement the Markdown parser"
pinto rm T-1 T-2             # archive one or more PBIs by default
pinto rm T-1 T-2 --force     # permanently remove several PBIs
pinto board --sort rank
pinto board --roots-only --status todo --long
pinto kanban --column in-progress
pinto rebalance --dry-run
```

`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:

```toml
[tui]
hidden_columns = ["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`:

```toml
[display]
markdown = true
timezone = "+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:

```toml
[points]
aggregate_children = 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

```bash
pinto dep add T-2 T-1        # T-2 depends on T-1
pinto dep rm T-2 T-1
pinto link add T-1 abc1234
pinto link rm T-1 abc1234
pinto link scan
pinto dod                     # print the shared DoD
pinto dod set "Tests pass and documentation is updated"
pinto dod clear
```

### Sprints and reports

```bash
pinto sprint new S-1 "Sprint 1" --goal "Ship the parser" --start 2026-07-01 --end 2026-07-14
pinto sprint edit S-1 --goal "Ship the parser" --start 2026-07-01 --end 2026-07-14
pinto sprint start S-1
pinto sprint add S-1 T-1
pinto sprint add S-1 --status todo --limit 3  # assign the top 3 matching PBIs
pinto sprint add S-1 --status todo             # assign all matching PBIs
pinto sprint unassign S-1 T-1
pinto sprint remove S-1       # releases assigned PBIs without deleting them
pinto sprint list
pinto sprint capacity S-1 --daily-hours 8 --holidays 0 --deduction-factor 0.2
pinto sprint burndown S-1
pinto sprint velocity
pinto cycletime --sprint S-1
```

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

```bash
pinto migrate --to git
pinto automate --plan '{"commands":[["add","Draft release notes"]]}'
pinto automate --plan plan.json --dry-run --json
pinto automate --plan - --json < plan.json
pinto shell
pinto completion zsh > "${fpath[1]}/_pinto"
```

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:

```text
┌ 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:

```toml
[tui.key_bindings]
details = ["Ctrl+d", "v"]
quit = ["Cmd+q", "Esc"]
help = ["?"]
select_left = ["h", "Left"]
add = ["a"]
dependency_add = ["d"]
dependency_remove = ["D"]
parent = ["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:

```text
.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](https://mise.jdx.dev) for tool versions and common
tasks:

```bash
mise install
mise run test
mise run lint
mise run fmt
mise run check
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for the TDD workflow and
[docs/DESIGN.md](docs/DESIGN.md) for architecture decisions.
Stability and dependency trade-offs are recorded in
[docs/stability.md](docs/stability.md) and [docs/dependencies.md](docs/dependencies.md).

## License

Distributed under the [MIT License](LICENSE).