Sahand159 downloadsCollects tagged task lines from across your vault into a sidebar inbox, sorts them with the GTD method, and flags projects that have stalled.
An Obsidian plugin that automatically collects Markdown tasks (- [ ]) from
anywhere in your vault into a sidebar inbox and walks them through a friendly
GTD workflow. Optional #task discovery remains available in settings.
An open loop is anything you've committed to that isn't closed yet. TaskLoops finds them, helps you file them, and tells you when a project has quietly stalled.
Only two deliberate actions write to Markdown:
[ ] to [x].
Reopening is intentionally done in the source note.- [ ] … line to the capture note. That note
defaults to TaskLoops Inbox.md and is created on first use.Beyond those, nothing. Filing, dates, priorities, contexts, delegation, manual order, and project links never rewrite the note. TaskLoops does not add handled markers, change task text, touch frontmatter, move lines, or delete anything. If you never capture or complete a task, it never writes to Markdown.
Workflow state — bucket, context, delegate, date, priority, order, and project
links — lives in the plugin's own data.json. Completion stays visible and
portable in the Markdown checkbox itself.
One row of controls at the top of the panel:
A sidebar is narrow for a board, so the columns scroll horizontally. For the full width, run Open in a main tab from the command palette — it is the same view over the same data, just with room.
Standard GTD, one question at a time:
You can back out of any step, and re-file anything later from the ⋯ menu.
The + Task button in the panel header opens a capture row. Type, press Enter, and a checkbox is appended to your capture note and appears in the Inbox unsorted — the row stays open so a burst of thoughts costs one keystroke each. Escape closes it. There is also a Capture a task to the inbox command you can bind to a hotkey.
The first time you capture anything, you are asked which folder captures should
live in, and TaskLoops Inbox.md is created there. Dismissing that prompt is a
valid answer — it puts the note at the vault root. You are only asked once; the
capture row shows the destination underneath it, and clicking it moves the
capture note somewhere else at any time.
Use Project in the header to create an outcome in one step. The projects list stays a list of outcomes, not a place to keep steps. The plugin tracks one thing about a project: which actions belong to it, so it can tell you when a project has nothing open against it. A project showing no next action has stalled, which is the single failure mode the weekly review exists to catch. Those projects sort to the top and put a dot on the Projects tab.
Actions get attached to a project three ways:
⋯ → Part of project….Expanding a project shows its actions one level deep. There is no nesting beyond that, and a project can't belong to another project — choosing Standalone pins a task as unattached even if it sits under a project in the outline.
A project whose only children are unclarified inbox items still counts as stalled. That is deliberate: an uncaptured thought is not something you can act on.
Task text is edited in its source note. Click the note name under a card to jump straight to the line. The sidebar intentionally never rewrites task wording.
The context, waiting-on and date chips under each task are clickable: one click
changes any of them. Empty ones appear on hover as + context and + date, and
on touch they live on the ⋯ menu instead.
Changing an attribute never re-files a task. A context, a date and a person
are orthogonal to the filing decision — a delegated task can have a context, a
next action can have a date — so setting one leaves the task exactly where it
is. Buckets change only through the clarify flow, the ⋯ menu, or a drag, all
of which are explicit. Attributes also survive a re-file; returning a task to
the Inbox is the one reset, since that means it is unclarified again.
Each task has a priority slot: P1 high, P2 medium, P3 low, or none.
Click the chip, or use ⋯ → Priority. P1 and P2 also colour the card's left
edge, so urgency reads without reading the chip, and priority tints calendar
count badges.
Priority is a sort key, applied after any manual order you set and before how recently a task was filed. Tasks without one sort last, so nothing reshuffles until you start using it.
Everything files by dragging, in every layout:
Dropping onto Scheduled asks for a date and Waiting asks for a name, because those buckets are meaningless without them; every other target files immediately.
Cards show a drag grip on hover and every valid destination gets a clear active
state. Every drag action is also available from the ⋯ menu, which is the
accessible path on touch devices and for keyboard-driven workflows.
Three scales, switched with the Day / Week / Month buttons. The arrows step back and forward by whichever scale you are on, and Today jumps back immediately.
Every day shown is a drop target, at every scale, so rescheduling is a drag. Each day also has a + to create a task already dated to it. Undated tasks are listed at the bottom so there is always something to drag onto the calendar.
Every task row has a date chip. Click it to set, change or clear the date —
there are Today / Tomorrow / In a week shortcuts plus a picker. The same is on
the ⋯ menu. Setting a date records the date and nothing more — it does not
move the task to Scheduled. Use the clarify flow, the ⋯ menu or a drag onto
the Scheduled tab for that, all of which ask for the date too.
Checkbox and optional tag positions come from Obsidian's own metadata cache,
not a text search, so fenced code blocks, inline code and YAML frontmatter are
excluded automatically. #task/work and other child tags count when tag
discovery is enabled. List markers, checkbox state, blockquotes, headings and
block ids are stripped from the display text but left untouched in the note.
A task is identified by a hash of its note path, its cleaned text, and its index among identical lines in that file. Two consequences worth knowing:
Before changing a checkbox, the plugin re-locates the line by its exact prior text. If the line moved and the text is still unique, it follows it; if it cannot identify the line unambiguously, it writes nothing and tells you.
Renaming or moving a note carries its filed tasks along with it.
The panel sizes itself against its own width, not the window's, using a CSS container query — a desktop sidebar is often narrower than a phone screen, so viewport media queries would tune the wrong thing. Below about 290px the tab labels drop and the rail becomes icons and counts.
Touch devices get larger targets and lose nothing to hover: the ⋯ button is
always visible rather than fading in, and the hover-only "add a date" chip is
hidden outright, since its actions live on the ⋯ menu anyway. Verified with no
horizontal overflow down to a 260px panel.
From Obsidian — open Settings → Community plugins → Browse, search for TaskLoops, install and enable. This is the easiest route and gets you updates automatically. Plugin page
Requires Obsidian 1.7.2 or later, on desktop and mobile.
From a release — download main.js, manifest.json and styles.css from
the releases page into
<vault>/.obsidian/plugins/taskloops/, then enable it under
Settings → Community plugins.
From source — clone this repo, then:
npm install
OBSIDIAN_VAULT="/path/to/your/vault" npm run deploy
To avoid repeating the path, write it once into a .vault-path file, which is
gitignored:
echo "/path/to/your/vault" > .vault-path
npm run deploy
deploy builds and copies only the three files Obsidian loads. It never touches
data.json, so your sorting survives a redeploy.
npm install
npm run dev # watch build
npm run build # typecheck + minified build
npm test # 100 tests over the scanner
npm run deploy # build, then copy into a vault
Source layout:
src/
├── main.ts plugin lifecycle, state, reconciliation, filing actions
├── scanner.ts checkbox/tag discovery, identity, safe completion writes
├── view.ts sidebar: tabs, lists, cards, projects, drag and drop
├── wizard.ts the GTD clarify flow
├── modals.ts folder, project, task and date pickers
├── settings.ts settings pane
├── types.ts buckets, stored shapes, defaults
├── dates.ts calendar-day helpers
└── text.ts string helpers
scripts/ build tooling: deploy, version bump
test/ node test suite over scanner.ts
main.js is built to the repository root, because that is where Obsidian loads
it from and where the release workflow picks it up. styles.css and
manifest.json stay at the root for the same reason — they ship verbatim rather
than being compiled.
The tests cover the risky parts: which lines count as tasks, discovery modes, identity, indentation-based parents, capture formatting, and the guarantee that completion changes only the checkbox state character.
Obsidian resolves a release by its git tag, which must be the bare version
number with no v prefix, and expects main.js, manifest.json and
styles.css attached individually as assets.
npm version patch
git push && git push --tags
npm version bumps package.json, then version-bump.mjs writes the same
version into manifest.json and adds a row to versions.json. The .npmrc
sets tag-version-prefix="", which is what stops npm creating a v0.1.1 style
tag — Obsidian would not resolve that.
Pushing the tag runs .github/workflows/release.yml, which installs, tests,
builds, verifies the tag matches manifest.json, attests build provenance, and
opens a draft release with main.js, manifest.json and styles.css
attached individually. Review it and publish when ready.
versions.json maps each plugin version to the minimum Obsidian version it
needs, so older Obsidian installs resolve to a release they can actually run.
If TaskLoops is useful to you, you can buy me a coffee. Entirely optional — the plugin is free and always will be.
MIT © Sahand Poursadeghi Khiavi