Curvilinear
unlistedby Seth Davis
A Linear-flavored issue tracker for your vault. Issues are plain notes; boards are just views.
Curvilinear
Track issues and projects as plain Markdown notes, on a board that works the way Linear does. Issues are notes with frontmatter; the board is just a view over them.
Why keep issues in your vault
Issues live next to your thinking. This is the whole idea. An issue can be linked from a meeting note, embedded in a spec, backlinked from research, and show up in your graph. When your tracker and your notes are separate tools, you spend real effort keeping them in step. Here there is one set of files.
The data is yours, permanently. Markdown in a folder. Readable in twenty years, diffable in git, greppable from a terminal. There is no export step because it is already exported.
It works with no account and no network. On a plane, behind a firewall, and after any company changes its pricing or its mind.
It is free and unlimited. No seats, no issue caps, no workspace admin.
You can change it. Want a client field, or grouping by retainer? Parse it in
vault-index.ts, render it on the card, add it to the grouping list. That is a small
afternoon rather than a feature request into someone else's backlog.
It inherits the rest of Obsidian. Search, graph view, templates, Canvas, Bases, Dataview, and backlinks all work on your issues without anyone building integrations for them.
Where a hosted tracker is the better tool
Curvilinear is single-player by design. There is no real-time collaboration, no notification inbox, no comment threads, and no PR status sync. Obsidian Sync or git gives you one person across several devices; two people editing the same board produce merge conflicts rather than merges.
If a teammate or a client needs to see the board, reach for a hosted tracker. Use this for the work that lives in your head and your vault. See Known limits for the full picture.
Status
v0.1.0. Board and list views, projects, drag-and-drop, bulk edit, labels, due dates, archive, keyboard navigation.
Layout
Everything lives under one folder, and the folder tree is the data model:
Curvilinear/
Projects/
Rivet UI v2/
ABOUT.md <- the project note
ENG-8 Token contrast audit.md
ENG-9 Slider primitive.md
Auth Hardening/
ABOUT.md
ENG-1 Fix login redirect.md
Inbox/
ENG-5 Virtualize long columns.md <- no project yet
Archive/
Projects/Rivet UI v2/
ENG-3 Old idea.md <- archived, off the board
The folder an issue sits in is its project. Nothing else decides membership,
which means you can reassign an issue by dragging it between folders in Obsidian's
own file explorer, and archive a project by moving one folder out. Issues that
belong to no project live in Inbox/.
A project is any folder directly inside Projects/ containing a note with
type: project. New projects get an ABOUT.md, which sorts above the issues
beside it. The filename is not load-bearing: the plugin finds the project note by
its type: project frontmatter, so rename it to README.md or anything else and
nothing breaks. The project's name comes from the folder, so renaming the folder
renames the project.
Issue format
An issue is any note under the Curvilinear folder with type: issue in its frontmatter.
---
type: issue
id: ENG-142
status: in-progress # backlog | todo | in-progress | done | canceled
priority: 1 # 0 none, 1 urgent, 2 high, 3 medium, 4 low
assignee: seth
project: "[[Curvilinear/Projects/Rivet UI v2/ABOUT|Rivet UI v2]]" # derived
estimate: 3
due: 2026-09-01
labels:
- design
- tech-debt
created: 2026-08-15
sort: 0.5
---
The note body is the issue description. Write whatever you want here.
The project field is written and kept in sync purely so project notes collect
backlinks and Bases queries work. Editing it does nothing. Move the note to
change its project.
The filename is ENG-142 Fix login redirect.md, so wikilinks read naturally and the
title comes from the filename with the identifier stripped off.
sort is a fractional index for manual ordering within a column. Dropping a card
writes the midpoint between its new neighbours, so a reorder rewrites one file
instead of the whole column.
Project format
---
type: project
status: active # planned | active | paused | completed | canceled
lead: seth
target: 2026-09-30
created: 2026-08-15
sort: 0
---
The note body is the project brief.
The project's name is its folder name. Add name: to display something different.
Projects are one-to-many with issues: a project has many issues, an issue belongs to at most one project. Use the board's project filter to scope to a single project, or No project to find unfiled work in the Inbox.
Usage
| Action | How |
|---|---|
| Open the board | Ribbon icon, or Curvilinear: Open board |
| New issue | Curvilinear: New issue, or c on the board |
| New project | Curvilinear: New project, or the board button |
| Scope to a project | Project dropdown in the board header |
| Move / reorder | Drag a card |
| Navigate | j k h l or arrow keys |
| Open an issue | Enter, or double-click |
| Switch board / list | Toggle in the header |
| Set priority | 0–4 on the selected card |
| Reassign project | p on the selected card, or drag the note between folders |
| Copy git branch name | b, giving eng-21-magic-link-auth-flow |
| Archive | e, or the bulk bar |
| Select a range | Shift-click within a column or group |
| Add or remove one | Cmd-click |
| Clear selection | Esc |
Creating an issue while the board is scoped to a project files it into that project. Reassigning moves the note on disk, so inbound links follow it.
Select more than one issue and a bulk bar appears: set status, priority, or project for all of them at once, or archive the lot. Keyboard actions apply to the whole selection too, so selecting six issues and pressing 1 makes all six urgent.
List view is the one to use past a screenful of work. Group by status, project, assignee, priority, or label, and sort by manual order, due date, or priority.
Assign a hotkey to New issue in Obsidian's hotkey settings for a global c.
Settings
- Curvilinear folder: the umbrella, default
Curvilinear. Notes outside it are ignored. Changing it does not move existing notes. - Team prefix: used for generated identifiers, e.g.
ENGinENG-142. - Default status: applied to new issues.
- Columns: hide statuses you don't want on the board.
Development
bun install
bun run dev # esbuild watch, writes main.js
bun run typecheck
bun run build # production bundle
To test in a vault, symlink the repo into <vault>/.obsidian/plugins/curvilinear
and enable it under Community plugins. With the obsidian CLI installed, reload
without restarting the app:
obsidian plugin:reload id=curvilinear
UI preview
Obsidian can't be driven by a headless browser, so dev/preview.html mounts the
board components against an in-memory fake for visual and interaction checks:
bun run preview # http://localhost:4399/dev/preview.html
Append ?theme=dark for the dark palette.
Due dates, labels, and archive
due: 2026-09-01 puts a date on the card. Anything past today gets a red left edge
and red text; today's is orange.
labels is a list property. Chip colour is derived from the label text, so the same
label is the same colour everywhere without configuring a palette:
labels:
- bug
- ui
Archiving moves a note to Curvilinear/Archive/, keeping the sub-path it had. It
leaves the index entirely but stays in the vault, and moving it back restores it to
the project it came from. Archiving more than one issue asks first.
Known limits
- Single user. Obsidian Sync or git gives you async across your own devices; there is no real-time collaboration.
- Repeatedly halving the same gap in one column will eventually exhaust float precision in
sort. A rebalance pass is the fix and is not written yet. - Long columns are not virtualized. Expect this to get slow somewhere in the low thousands of issues per column.
- No sub-issues, cycles, or comments yet.
- An issue belongs to at most one project. Projects do not nest, and an issue in a sub-folder of a project folder counts as unfiled.
- Ordering is global per status, not per project view. Reordering inside a project filter also moves the issue relative to hidden issues in that column.
- There is no projects overview view yet; projects are reached through the board filter or their own notes.
- Changing the Curvilinear folder setting does not relocate existing notes.
- Grouping by cycle is not offered because there is no cycle field yet.
- Due dates are compared as plain ISO strings against the day the view mounted, so a board left open past midnight keeps yesterday's idea of "overdue" until reopened.
License
MIT
For plugin developers
Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.