legume is a minimialist, distributed, code comments-based issue tracker
Fixes issue with string literal escapes in strings
Removes filters t,T,s -- this is grep's or jq's job
Add 'bug' to the keyword list


browse log
browse .tar.gz




Legume is a distributed issue tracker, and can be used along side other, more formal, issue trackers as an intra-developer tracking mechanism. I also tend to use it, rather than a web-based ticket tracker, on small, single-file scripts.

Legume leverages the developer tradition of using code comments to make notes about things that need to be addressed. Notes of this sort retain locality; they are reminders for code browsers, and contain meta-data about where to start looking when addressing an issue. They integrate naturally with whatever VCS the developer is using, and make it easy for a developer to remember to remove the comment when the topic is addressed. They require no extra external database.

Legume is developer oriented. Submit issues with the end-user Legume tracker.


The biggest selling feature of legume is that issues are embedded in code. This has a number of benefits:

  • Issues can be place-marks for where developers think things are happening.
  • Commenting code with TODO and FIXME comments is a decades-old traditional developer habit
  • Integrated with version control at an intuitive level, reducing cognitive load. No external integration with VCS necessary.
  • No external DB for tickets. TODOs and FIXMEs are still relevant even if you stop using legume. Which also means...
  • legume is not strictly necessary for working with issues. It's a convenience tool. This means you don't have to build your workflow around legume, and nobody in your team needs to install legume. You can find and grep your way through issues.

Legume will also address other, non-developer, use cases by recognizing a todo.txt file in the directory of execution. Legume will assume every line is a separate TODO. This allows easy integration with todo.txt, but also provides a place for sticking issues that either aren't linked to source, or come from a source other than a developer (e.g., web app). todo.txt syntax is also supported in comments.

  • (A) priorities
  • 2017-03-20 "created" dates
  • due:2017-03-20 "tagged" dates
  • *@contexts_
  • +projects
  • key:value tags
  • Multi-line issues (new issue, blank line, or non-comment breaks issue)

The list of programming languages that legume should understand is here.


The trade-offs, which may or may not be considered limitations, include:

  • No unique IDs, which can make referencing tickets difficult or cumbersome
  • Very simple tickets. No attachments, no cross-references or dependencies, no robust commenting; history tracking limited to what's available in the VCS.
  • Efficiency. In a pure state, collating tickets requires walking directory trees and parsing source files. This is addressed by caching, but caches introduce opportunities for sync errors; it's a necessary wart on an otherwise elegant and simple solution.


There's the usual -h/--help, but if you installed with a package manager there should also be a more detailed man page (man legume). If you installed from source, you can generate the man page yourself with leg --create-manpage -- pipe this to leg.1 and copy it to /usr/local/man/man1, or do the hardcore thing and nroff it (legume --create-manpage | nroff -man | less).

Comments must always start with a keyword, TODO,FIXME,BUG, orXXX`. These key words are case sensitive. After that, todo.txt formats are allowed. Example:

    // TODO 2017-03-20 (A) @con_example +proj_example due:2018-03-22 key:value Priorities, contexts, projects, created & due dates, and key/value pairs

Use the built-in help for options. One workflow is:

$ leg       # to list all todo/fixmes in a project
$ leg -d 5  # to list the details of item # 5
$ leg 5     # Same as above: for convenience, the `-d` may be ommitted

Filtering changes indexing. Because there is no canonical item numbering, any filters that produce a list must be also used when using -d (details). For example,

$ leg -P test       # to ignore all files in the test/ directory
$ leg -P test 3     # For convenience, the `-d` may be ommitted

Legume assumes that if the data source is a pipe (STDIN), that it's seeing a diff. In this mode, Legume counts any add (+) tickets as new, and any removed (-) tickets as closed, and reports them this way. For example, on the Legume repository itself:

➜  legume hg diff -r 4:98 | leg  -
  1 REQ    NEW Include the time stamp in the report; removed use old version, add use new
  2 REQ    NEW filter on priority, category, and project; use meta-tags and -t
  3 BUG    NEW catch string lit escapes
  4 REQ CLOSED Support for STDIN
  5 REQ CLOSED Config file
  6 REQ CLOSED Support for unified diff
  7 REQ CLOSED Add test cases.
  8 REQ CLOSED Add test cases.
  9 REQ CLOSED Add test cases.
 10 REQ    NEW Add unit tests for Alias [component:ui]
 11 REQ    NEW Implement & add unit tests for Keywords [component:ui]
 12 REQ CLOSED Add test cases.
 13 REQ CLOSED Add test cases.
 14 REQ CLOSED Add test cases.
 15 REQ    NEW Parsing diffs seems to be broken
 16 REQ CLOSED Add test cases.
 17 INF    NEW refactor parsing to "consume-to-end"

Obvious limitations result from how diff reports information; a changed line is reported as a combination delete + add, which looks to Legume like a close + open. In practice and over short spans, it works pretty well.

Note Legume's executable is called leg. However, there's an older project called peg (a C parser generator) that also has a leg command; to avoid coflicts in distributions, I've named the executable legume in package managers. In this document I use leg.


legume build status Binaries for OSX, Linux, and Windows are here, and are signed with the GPG key 5E0D7ABD6668FDD1 (available from hkp://hkps.pool.sks-keyservers.net).

If you want to build it yourself, you can either go install the package, or clone it and compile it manually.

   go install ser1.net/legume/cmd/leg@v1.3.3

Note "latest" doesn't work at the moment. This is an older project, and the combination of Mercurial bookmarks and tags confuses go install. Use a version number.


A super-simple vim script is available; to install, copy the contents of legume.vim into your vimrc. It binds <leader>lg to execute legume with the -f vim option and open the quickfix buffer.



The kakfile can be put in your $XDG_CONFIG_HOME/kak/autoload directory, and exposes some make-like functions & functionality. Kakoune's CWD should be the project directory.

  • leg opens a buffer list of the issues
  • leg-next-todo jumps to the next issue
  • leg-previous-todo jumps to the previous

If you want, map these to shortcuts as you would for make, for example:

declare-user-mode leg
map global leg n ": leg-next-todo<ret>" -docstring "go to next issue"
map global leg p ": leg-previous-todo<ret>" -docstring "go to previous issue"
hook global BufCreate \*leg\* %{
    map global user g ": enter-user-mode leg<ret>" -docstring "legume issues"
hook global BufClose \*leg\* %{
    unmap global user g 

#Performance samples

Legume has already satisfied the basic performance requirement; small projects have sub-second parse times. A future version may implement caching to bring larger projects to this benchmark.

Project with 108 files, 14k lines, 15 todos:

leg .  0.04s user 0.03s system 81% cpu 0.090 total

Project with 992 files, 564k lines, 244 todos:

leg .  1.34s user 0.07s system 95% cpu 1.464 total

The Linux 5.7 kernel source tree, 64,309 files, 28,136,537 lines, 9,697 todos:

leg .  49.60s user 0.96s system 99% cpu 50.713 total


Legume is intended for a narrow problem space. Large projects, such as the Linux kernel (and certainly the boundary is much lower than that) are certainly better served by a "real" issue tracking system. For large code bases, any tool that parses the entire code base on every invocation will be too slow, even if a tool like legume didn't lack most of the features of a sophisticated isuse tracking system.

Testing on languages other than Go has been limited. Testing on Windows is non-existent.

The diff feature absolutely has a number of limitations; diffs containing multi-line TODOs where a line other than the first changed will be missed; similarly, lines where only the first line changed will report partial descriptions. There's simply a limit to how much meta-information can be interpreted from a diff.

At the time of this writing, despite the version number this legume is young and I'm the only user I know of. There will be bugs. Sorry about that.

#Prior Art

Legume was inspired by lentil and follows the philosophy of todo.txt. Legume has goals beyond merely duplicating lentil:

  • Accept diffs & STDIN. This allows some ability to track history by using the output of a VCS. E.g., hg diff -r <histnode> \| leg would produce a list of all issues added and resolved, using diff markup (+/-) to indicate opened/closed
  • Improve performance.
  • Support todo.txt syntax (priorities, dates, etc)

Below are some other distributed ticketing systems, many of which I've tried. If legume isn't your cup of tea, maybe one of the ones listed below will be more suitable for your workflow.

  • lentil, discussed below.
  • Artemis. Separate bug DB (maildir); integrated with VCS (Mercurial, beta git support). Supports more traditional ticketing features, such as attachments and comments. Only slightly younger than BugsEverywhere, the first commit was in 2007.
  • b. Separate bug DB. Tightly coupled with Mercurial -- implemented as an HG extension. Supports more traditional ticketing features, such as assigning tickets to people.
  • BugsEverywhere. Separate bug DB. Lots of features, including a web interface, email ticket communication, supporting multiple VSes, and most traditional ticketing features. The grand-daddy of distributed ticketing systems; first commit was 2005!
  • git-bug, tightly coupled to git.
  • The one with my favorite name, ScmBug
  • Fossil, which is a kitchen-sink project; the VCS & bug tracker are very tightly coupled, but it does have a distributed ticketing system, so it makes the list.
  • SD, which has integration with git and darcs, and keeps bugs in a SQLite database.
  • ditz, which I used quite a bit way back when I was using Darcs (a predecessor to both git & Mercurial).
  • ticgit, now unmaintained.
  • ditrack; the web site appears to have reverted to the domain name provider.

And numerous discussions and blogs:

Most of these keep bug data in a separate database that's version tracked alongside the code. One of my requirements is to leverage code comments, and Lentil is the only tracker I found that does that. Some trackers keep bugs in a database that are one or more text files, which is OK because the VCS will be able to diff them efficiently; any tracker that keeps bugs in a binary DB (like SQLite) that requires checking into the VCS is, IMO, a non-starter.

Lentil is written in Haskell, and although I do like Haskell, I wanted something lower-impact to compile than Haskell. The core Haskell stack on Arch Linux, including common libraries, is 183 packages and 1.9GB. This is a heavy lift for small systems.

Additionally, I'm less fluent these days in Haskell than Go, and one of the issues I have with Lentil is performance. I'm not advanced enough with Haskell to be able to easily performance tune somebody else's code, and getting better at performance tuning Haskell was not my main objective for this project; I just wanted better speed and some additional functionality, and to get to the functionality I'd have to have solved the performance issues first... it was simply easier to implement a new tool.

// vim: set ft=pandoc