devtree

command module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2026 License: MIT Imports: 2 Imported by: 0

README ΒΆ

English Β· Русский Β· δΈ­ζ–‡ Β· Deutsch Β· FranΓ§ais

CI Release Container Go Reference

Your plan is a file β€” .devtree/tree.yaml β€” that sits next to the code. It is versioned with the code, reviewed in the pull request, and merged line by line. From it, devtree draws a Mermaid diagram into TREE.md or straight into your README.md, and a picture of its own into .svg β€” as a tree, or as a board. GitHub and GitLab render both natively. No browser extension, no image to regenerate by hand, nothing to host.


Why keep the plan in the repository

A roadmap in a tracker drifts away from the branch it describes. A roadmap in a wiki is read once and never again. A roadmap in the repository is a file people already have habits for.


How it works


What it looks like

The Mermaid backend, which GitHub draws right here in the page:

flowchart TD
    classDef todo fill:#f6f8fa,stroke:#8c959f,color:#1f2328
    classDef in_progress fill:#fff8c5,stroke:#bf8700,color:#1f2328
    classDef blocked fill:#ffebe9,stroke:#cf222e,color:#1f2328
    classDef done fill:#dafbe1,stroke:#1a7f37,color:#1f2328
    classDef dropped fill:#eaeef2,stroke:#8c959f,color:#6e7781,stroke-dasharray:4 3
    n_mvp["◐ MVP<br/><i>1/6</i>"]:::in_progress
    n_mvp --> n_authentication
    n_authentication["◐ Authentication<br/><i>0/2 Β· feat/auth Β· #35;12 Β· @ann</i>"]:::in_progress
    n_authentication --> n_oauth_providers
    n_oauth_providers["◐ OAuth providers<br/><i>feat/oauth</i>"]:::in_progress
    n_authentication --> n_password_reset
    n_password_reset["β›” Password reset"]:::blocked
    n_mvp --> n_payments
    n_payments["◐ Payments<br/><i>1/2</i>"]:::in_progress
    n_payments --> n_stripe
    n_stripe["βœ” Stripe"]:::done
    n_payments --> n_apple_pay
    n_apple_pay["☐ Apple Pay<br/><i>#35;51</i>"]:::todo
    n_public_api_v2["☐ Public API v2<br/><i>0/1</i>"]:::todo
    n_public_api_v2 --> n_openapi_schema
    n_openapi_schema["☐ OpenAPI schema"]:::todo

The same plan in your terminal:

Payment Gateway

◐ MVP  (mvp)  [1/6]
β”œβ”€ ◐ Authentication  (authentication)  feat/auth  #12  @ann  [0/2]
β”‚  β”œβ”€ ◐ OAuth providers  (oauth-providers)  feat/oauth
β”‚  └─ β›” Password reset  (password-reset)
└─ ◐ Payments  (payments)  [1/2]
   β”œβ”€ βœ” Stripe  (stripe)
   └─ ☐ Apple Pay  (apple-pay)  #51
☐ Public API v2  (public-api-v2)  [0/1]
└─ ☐ OpenAPI schema  (openapi-schema)

β–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘  1/9

And devtree's own drawing β€” further down, rendering this repository's plan.


Install

brew install SergeyLubivui-dev/tap/devtree     # macOS and Linux
go install github.com/SergeyLubivui-dev/devtree@latest

Prebuilt binaries for Linux, macOS, and Windows are attached to every release, with checksums. Or run it without installing anything:

docker run --rm -v "$PWD:/work" ghcr.io/sergeylubivui-dev/devtree render

Building from source needs Go 1.22 and nothing else. Details, and a shell alias that makes the container disappear into the background: docs/container.md.


Quickstart

cd your-project

devtree init --project "Payment Gateway" --repo https://github.com/acme/pay --hook --action

devtree add "Authentication"  -p mvp -b feat/auth -i 12 -o ann -s wip
devtree add "OAuth providers" -p authentication -b feat/oauth
devtree add "Password reset"  -p authentication -s blocked -n "waiting on SMTP"

devtree ls                                   # the tree, in your terminal
devtree done oauth-providers
git add . && git commit -m "feat: oauth"     # the hook refreshes the diagram for you

IDs are derived from titles β€” "OAuth providers" becomes oauth-providers, and non-Latin titles are transliterated so the ID stays typeable. Pass --id when you want to choose one yourself.

A longer walk through the same ground: docs/getting-started.md.


Everyday recipes

Short, real things people do with a plan. Copy one and change the words.

Start a feature, finish a feature. The task and the branch are named together, so anyone reading the diagram knows where the code is:

devtree add "Search filters" -p mvp -b feat/search -i 214 -o you -s wip
git switch -c feat/search
# ...write the code...
devtree done search-filters
git commit -am "feat: search filters"        # the hook refreshes the diagram

Break something big into something doable. Parents count their children automatically, so the milestone reports progress you never have to update by hand:

devtree add "Billing" -s wip
devtree add "Invoices" -p billing
devtree add "Refunds"  -p billing
devtree add "Dunning"  -p billing -s blocked -n "needs the payments API"
devtree ls
◐ Billing  (billing)  [0/3]
β”œβ”€ ☐ Invoices  (invoices)
β”œβ”€ ☐ Refunds  (refunds)
└─ β›” Dunning  (dunning)

Pick up a ticket. Give it the issue number and it becomes a link in the table under the diagram:

devtree add "Fix timezone drift" -i 512 -o ann -s wip --tags bug

Park what you cannot finish. A blocked task with no note is the one thing check complains about, because "blocked" without a reason is a task nobody can pick up:

devtree set password-reset -s blocked -n "waiting on the SMTP contract"
devtree board -s blocked

Monday morning, in three commands:

devtree board          # what is in flight, what is stuck, what is waiting
devtree ls -s blocked  # only the branches that need a decision
devtree check          # anything marked done that still has open work under it?

Two people, two branches, one plan. Both add tasks, both commit, and the merge is boring β€” the node list is flat and .gitattributes says merge=union. If two branches happen to pick the same ID, devtree check fails on the spot with the duplicate named.


The board

The tree says how the work is organized. The board says what state it is in this morning β€” same file, different question:

devtree board
Payment Gateway

☐ not started · 1
  Apple Pay        Payments  #51

◐ in progress Β· 1
  OAuth providers  Authentication  !31

β›” blocked Β· 1
  Password reset   Authentication  β€” waiting on SMTP

βœ” done Β· 1
  Stripe           Payments  !44

β–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘  1/7

Only leaves appear β€” a milestone is a container, not a card β€” and each task carries its milestone as a breadcrumb. Name an output board.svg and the same thing is drawn as columns:

More: docs/board.md.


Finished work

A plan that keeps every task ever completed stops being a plan and becomes a log. Two commands handle that from opposite ends:

devtree archive          # what is finished and could move
devtree archive --all    # move it into .devtree/archive.yaml
devtree restore v1       # bring a branch of it back

devtree sync             # tasks whose branch git has already merged
devtree sync --apply     # mark them done

Nothing moves until you say so, and a node qualifies only when its whole subtree is done or dropped, so live work can never leave with the milestone above it. sync proposes rather than acts, because git knows which branches were merged but not which of them were merged finished.

More: docs/finished-work.md.


Commands

Command What it does
init [--project N] [--repo URL] [--outputs F] [--hook] [--action] [--empty] Creates .devtree/tree.yaml, .gitattributes, and the first diagram
add "Title" [-p ID] [-s STATUS] [-b BRANCH] [-i N] [--pr N] [-o WHO] [--tags a,b] [-n NOTE] [--id ID] Adds a task
set ID [--title T] [-s ...] [-p ...] [...] Changes fields; only the flags you pass are touched
done ID [ID...] Marks tasks done
mv ID PARENT|root Re-parents a task
rm ID [--cascade] Deletes a task; without --cascade its children move up to its parent
ls [-s STATUS] Prints the tree in the terminal
board [-s STATUS] Prints the work grouped by status
open ID [--issue|--pr|--branch] [--print] Opens what a task points at
archive [ID...] [--all] [--list] Moves finished branches of the plan into the archive
restore ID [ID...] Brings archived work back
sync [--apply] Closes tasks whose branch git has already merged
history [--limit N] Reads past versions of the plan out of git and shows how far along it was
render [--file F] [--quiet] Regenerates every output
check [--strict] Validates the plan β€” for CI and hooks
install hook|action|gitlab|all Installs the pre-commit hook, the GitHub Action, or the GitLab job
outputs Prints the files the diagram is written to

Flags may come before or after the title, so both of these do the same thing:

devtree add "Authentication" -p mvp -s wip
devtree add -p mvp -s wip "Authentication"

Narrowing the view

ls and board take the same four filters, and they combine:

devtree ls -s blocked              # one status
devtree board -o ann               # one person's work
devtree ls --tag billing,payments  # any of these tags
devtree board --root mvp           # one branch of the plan

render takes --root too, so a milestone can have a picture of its own:

devtree render --root mvp --file docs/mvp.svg

And when you want the thing itself rather than the plan:

devtree open authentication          # its pull request, issue, or branch
devtree open authentication --print  # print the URL instead of opening it

Statuses

The canonical spelling is what lands in the file, whichever shorthand you type.


Where the diagram goes

The plan lists its own output files, and the name decides the drawing:

File name What gets written
TREE.md, README.md the Mermaid block, between <!-- devtree:begin --> and <!-- devtree:end --> markers
docs/tree.svg devtree's own drawing of the tree, light palette
docs/tree-dark.svg the same, dark palette
docs/board.svg the board
docs/plan.html a page you can click: links, filters, tooltips
outputs: "README.md, docs/tree.svg, docs/tree-dark.svg, docs/board.svg, docs/plan.html"

Everything outside the markers is left exactly as you wrote it, and re-rendering an unchanged plan rewrites nothing at all. Point a <picture> at a light and dark pair and GitHub switches it with the reader's theme.

Three things in the drawing move, and each one is information: a dash travels down the edges leading into work in progress, a glyph turns while a task is in flight and breathes while it is blocked, and progress bars grow in once. Readers who ask their system for less motion β€” and anyone printing β€” get a still picture.

File naming rules, palettes, the animation vocabulary, and the content security policy all of it has to survive: docs/svg-output.md.

The one thing a diagram cannot do

Neither backend can carry a link: GitHub's Mermaid ignores click, and an SVG served as an image is sandboxed. So there is a third one. Name an output .html and devtree writes a page where every task that points somewhere is a link to its pull request, issue, or branch β€” with a tree view, a board view, filters by status, owner and tag, and notes as tooltips.

It is one self-contained file with no script, no fonts and no images: the whole interface is CSS built on :has(). Open it from a checkout, drop it on a shared drive, or point GitHub Pages at it. This repository's own is docs/plan.html β€” see docs/html-export.md.


Automation

devtree install hook      # validate and re-render before every commit
devtree install action    # fail a pull request whose diagram is out of date

The hook keeps your own commits honest; the Action keeps everyone else's honest without requiring them to install anything. A teammate without devtree is never blocked β€” the hook notices the binary is missing and steps aside. On GitLab, devtree install gitlab writes a job that runs the published container, so the pipeline needs no toolchain at all. Line by line: docs/automation.md.

Where you have been

The plan has been in the repository all along, so its own past is already recorded. Nothing had to be tracked to read it back:

devtree history
Payment Gateway β€” 3 revision(s) of the plan

2026-08-14  β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘  0/2
2026-08-15  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘  1/3  +1 done  +1 planned
2026-08-16  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘  2/5  +1 done  +2 planned

2 of 5 done, up 2 since 2026-08-14
☐ 2 not started   ◐ 1 in progress   βœ” 2 done

Documentation

This page is the tour. docs/ is the detail underneath it.

Getting started A first plan, from an empty repository to a diagram in the README
The file format Every field, every rule, and what the parser refuses
The board Working by status instead of by structure
Finished work Archiving, restoring, closing tasks from merged branches
Automation The pre-commit hook and the GitHub Action, line by line
SVG output File naming, palettes, the animation vocabulary, the CSP it survives
The container Running devtree without installing anything
Architecture The layering, the rules that hold it, and how it is tested

Limitations

  • The storage format is a strict subset of YAML β€” a flat list of scalar fields. Anchors, multi-line block scalars, and nested mappings are rejected, with the line number.
  • Nothing rendered into a README is clickable: GitHub's Mermaid ignores click directives, and an SVG served as an image is sandboxed. Links live in the collapsed table under the Mermaid block.
  • Text in the SVG output is measured by estimate rather than by font metrics β€” shipping real metrics would mean shipping a font. Cards are sized a few pixels generously to compensate.
  • Very wide trees (hundreds of nodes) render slowly in the browser. Split them across several output files with --outputs.

devtree's own plan

Both of these come from the same .devtree/tree.yaml in this repository, regenerated on every push. First the SVG backend β€” GitHub swaps the file when you switch themes:

devtree's own development tree

And the same plan as a Mermaid block, injected straight into this README:

🌳 devtree

β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘ 22 / 24 tasks done

flowchart TD
    classDef todo fill:#f6f8fa,stroke:#8c959f,color:#1f2328
    classDef in_progress fill:#fff8c5,stroke:#bf8700,color:#1f2328
    classDef blocked fill:#ffebe9,stroke:#cf222e,color:#1f2328
    classDef done fill:#dafbe1,stroke:#1a7f37,color:#1f2328
    classDef dropped fill:#eaeef2,stroke:#8c959f,color:#6e7781,stroke-dasharray:4 3
    n_v0_1["βœ” v0.1 - first release<br/><i>6/6</i>"]:::done
    n_v0_1 --> n_domain
    n_domain["βœ” Domain model and validation"]:::done
    n_v0_1 --> n_format
    n_format["βœ” Strict YAML subset parser"]:::done
    n_v0_1 --> n_diagram
    n_diagram["βœ” Mermaid renderer"]:::done
    n_v0_1 --> n_cli
    n_cli["βœ” Command line"]:::done
    n_v0_1 --> n_automation
    n_automation["βœ” Pre-commit hook and GitHub Action"]:::done
    n_v0_1 --> n_tests
    n_tests["βœ” Test suite"]:::done
    n_v0_2["◐ v0.2 - sharper day-to-day use<br/><i>11/11</i>"]:::in_progress
    n_v0_2 --> n_filters
    n_filters["βœ” Filter ls by owner and tag"]:::done
    n_v0_2 --> n_focus
    n_focus["βœ” Render one subtree with --root"]:::done
    n_v0_2 --> n_open
    n_open["βœ” devtree open ID - jump to the issue or PR"]:::done
    n_v0_2 --> n_history
    n_history["βœ” Progress history from git log"]:::done
    n_v0_2 --> n_svg_output
    n_svg_output["βœ” Native SVG output with embedded icons"]:::done
    n_v0_2 --> n_html_export
    n_html_export["βœ” Interactive HTML export"]:::done
    n_v0_2 --> n_animation
    n_animation["βœ” Animation in the SVG output"]:::done
    n_v0_2 --> n_board
    n_board["βœ” Board layout, in the terminal and in SVG"]:::done
    n_v0_2 --> n_archive
    n_archive["βœ” Archive and restore finished work"]:::done
    n_v0_2 --> n_sync
    n_sync["βœ” Close tasks from merged branches"]:::done
    n_v0_2 --> n_docs_layout
    n_docs_layout["βœ” Documentation folder and a tidy root"]:::done
    n_distribution["◐ Distribution<br/><i>4/4</i>"]:::in_progress
    n_distribution --> n_binaries
    n_binaries["βœ” Prebuilt binaries on every tag"]:::done
    n_distribution --> n_homebrew
    n_homebrew["βœ” Homebrew tap"]:::done
    n_distribution --> n_gitlab
    n_gitlab["βœ” GitLab CI template"]:::done
    n_distribution --> n_container
    n_container["βœ” Container image on GHCR"]:::done

☐ not started Β· ◐ in progress Β· β›” blocked Β· βœ” done Β· βœ– dropped

Contributing

Issues and pull requests are welcome. CONTRIBUTING.md covers the checks CI runs, which package new code belongs in, and the two rules the layering depends on.

License

MIT Β© SergeyLubivui-dev

The vector glyphs in internal/icons are vendored from Reicon, also MIT β€” see NOTICE.

Documentation ΒΆ

Overview ΒΆ

Command devtree keeps a development plan inside the repository and renders it as a Mermaid diagram that GitHub and GitLab draw natively.

The plan lives in .devtree/tree.yaml: it is versioned with the code, reviewed in the pull request, and merged line by line. See README.md for the full tour, or run `devtree help`.

This file is deliberately thin. Everything it could do belongs in a package that can be tested without a process: the tree itself in internal/tree, the file format in internal/store, the output in internal/render, and the command line in internal/cli.

Directories ΒΆ

Path Synopsis
internal
art command
Command art draws the diagrams the README uses.
Command art draws the diagrams the README uses.
browser
Package browser opens a URL in whatever the operating system considers the default browser.
Package browser opens a URL in whatever the operating system considers the default browser.
cli
Package cli is the outermost layer: it parses arguments, calls the domain and storage packages, and owns every line printed to the terminal.
Package cli is the outermost layer: it parses arguments, calls the domain and storage packages, and owns every line printed to the terminal.
draw
Package draw holds the primitives every SVG devtree writes is built from: measuring text, escaping it, wrapping it, and stamping a glyph.
Package draw holds the primitives every SVG devtree writes is built from: measuring text, escaping it, wrapping it, and stamping a glyph.
icons
Package icons holds the vector glyphs devtree draws into SVG output.
Package icons holds the vector glyphs devtree draws into SVG output.
render
Package render turns a plan into text: a Markdown block for the repository, a Mermaid diagram GitHub draws natively, and an ASCII tree for the terminal.
Package render turns a plan into text: a Markdown block for the repository, a Mermaid diagram GitHub draws natively, and an ASCII tree for the terminal.
render/html
Package html exports a plan as a page you can click.
Package html exports a plan as a page you can click.
render/svg
Package svg draws a plan as a standalone SVG file.
Package svg draws a plan as a standalone SVG file.
scaffold
Package scaffold writes the files that make a plan keep itself up to date: a pre-commit hook, a GitHub Actions workflow, and a .gitattributes rule.
Package scaffold writes the files that make a plan keep itself up to date: a pre-commit hook, a GitHub Actions workflow, and a .gitattributes rule.
store
Package store reads and writes .devtree/tree.yaml.
Package store reads and writes .devtree/tree.yaml.
tree
Package tree is the domain layer: the nodes of a development plan, the parent/child relationships between them, and the rules that keep those relationships a tree rather than a tangle.
Package tree is the domain layer: the nodes of a development plan, the parent/child relationships between them, and the rules that keep those relationships a tree rather than a tangle.
vcs
Package vcs asks git what it already knows.
Package vcs asks git what it already knows.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL