0.6.1 — assign tasks in Markdown, and roll them up for GitHub
Assign work with a plain checkbox line, then roll every task in a repo into one file you can read on GitHub.
Why Care?
Plans and specs turn into to-do lists, and those lists need owners. This release gives you one plain line that reads correctly in Obsidian and on GitHub, plus a script that gathers a repo’s open tasks into a single file, each linking to the line it came from.
- [ ] @mpstaton @tanujsk Review [[path/to/Page-Name.md]] #Docs ~3d 2026-11-15
Nothing changes for existing sites. The plugin is opt-in, and the line works as ordinary Markdown without it.
What’s New?
Added
lfm-task-metadata: reads assignees, tags, estimates and due dates from task list items, enabled with thetaskMetadataoption.aggregateTasks,groupTasks,tasksToMarkdown: roll many documents’ tasks into one list, group them, and write Markdown. They never touch the filesystem.lfm-tasks: a script that runs all of that over a git repo’s tracked Markdown files.lfm-tasks --install-hook: a local git hook that regeneratesTASKS.mdwhen a commit touchescontext-v/.
Changed since 0.6.0
None of these affect runtime behavior:
context-v/is now an Open Knowledge Format bundle. The 12 loose root docs were filed into folders, every doc declares its type, and each folder has an index.- Source comments and one README line now point at those new paths.
- The splash no longer renders those index files as pages. They’re navigation for agents, and rendering them listed the ledes of docs marked
publish: false.
See changelog/2026-10-29_01.md and changelog/2026-10-29_02.md for the sweeps themselves.
We checked this rather than assuming it. Comparing the 29 files published as 0.6.0 on JSR against the repo, ignoring import paths that JSR rewrites at publish, they match commit 66790f6 exactly. Since then, only three of them differ, and only in the comments and README line above.
Reading a task
| Token | Rule |
|---|---|
@handle | Directly after the checkbox. Anywhere else, or in backticks, it stays text. |
#Tag | Anywhere after whitespace, with at least one letter, so #123 stays text. |
~3d | At the end of the title, with a unit. |
2026-11-15 | The last thing on the line, and a real date. 📅 2026-11-15 also works. |
const tree = await parseMarkdown(body, {
taskMetadata: { collaborators: ['mpstaton', 'tanujsk'] },
});
tree.data.tasks;
// [{ text, markdown, checked, section, line, assignees, tags, estimate, due }]
Set collaborators if you display assignees. Without it, a task that starts with a bare @page reads as assigned to page. A title that ends in a date, like Send the report for 2026-11-15, reads as due that day.
Aggregating a repo
lfm-tasks -o TASKS.md
lfm-tasks context-v --group-by section --exclude context-v/extra
## @mpstaton
- [ ] @mpstaton Review [Page Name](specs/Page-Name.md) #Docs 2026-11-15 ([plans/Launch.md:5](plans/Launch.md?plain=1#L5))
- Tracked files only, since that’s what GitHub can link to. Submodules are skipped, and
changelog/directories are skipped unless you pass--include-changelog. - Page references become links.
[[path/to/Page-Name.md]]stays in your source for Obsidian. One that doesn’t settle on a single tracked file keeps its display text and gets no link. - No timestamp, so the file only changes when the tasks do.
Keeping TASKS.md current
lfm-tasks --install-hook
When a commit touches a context-v/ Markdown file, the hook regenerates TASKS.md from the staged content and adds it to that commit, so it goes up with the same push. Nothing runs on GitHub.
It never blocks a commit, and it refuses to overwrite an existing pre-commit or post-commit hook or to install when core.hooksPath is set. Each clone installs its own.
Upgrading
pnpm add jsr:@lossless-group/lfm@^0.6.1
Purely additive. To use tasks, pass taskMetadata: true or taskMetadata: { collaborators }. Strip frontmatter before parsing, as everywhere in this pipeline, or the last frontmatter line becomes the section of any task above the first real heading.
Known gaps
lfm-tasksisn’t a command for JSR installs. It’s declared inpackage.json, which JSR doesn’t read. Installing from GitHub Packages provides the command. From a repo that installs through JSR, runnode path/to/lfm/dist/cli/lfm-tasks.jsfrom a built clone.- Not yet checked on GitHub or in Obsidian. That covers the
?plain=1#L5source links and Obsidian’stask-todo:@mpstatonsearch. The tests exercise the parser, the script and real commits through the hook, not those two products. - Unresolved page references are silent. They lose their link without a warning.
- Sub-tasks come out flat in the aggregated file.
- GitHub Packages can’t match JSR exactly. 0.2.0, 0.2.2 and 0.2.3 are JSR-only, because their published source doesn’t match a commit in this repo. GitHub Packages also has a 0.1.0 that JSR doesn’t.
See also
changelog/2026-11-01_01.md: how this came together, including the decisions we reversed and a hook bug the tests caughtcontext-v/specs/Maintain-Project-Task-Management-w-LFM.md: the design record