3514d57b00eeb2788d7015011c94f4e0c21dd732
- #<number> navigates to a full detail view (rendered title + body, meta badges, parent link, children list, edit/delete/sub buttons) - Clicking entry row main area navigates to detail instead of opening edit - Edit dialog remains for editing, delete redirects back to list Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
task-inventory
Personal task management. Organise tasks, thoughts, and ideas as a hierarchy. Designed to grow into project management over time.
Features
- Hierarchical tasks with unlimited nesting — create subtasks, reparent tasks, or promote subtasks to roots
- Tree view (roots only, expandable) and flat view (all tasks)
- Filters by status, priority, and tag; free-text search across title and body
- Markdown rendering via a local Gitea instance — preview tab in the editor, rendered inline in the task list
- Sequential integer IDs
- Flat NDJSON key-value store — single file, no database required
Setup
npm install
cp config.yaml.example config.yaml
# edit config.yaml — add your Gitea URL and token
node server.mjs
Server starts on http://localhost:3025 by default.
Configuration
config.yaml (not committed):
gitea:
url: https://gitea.example.com
token: your_token_here # needs read:misc scope minimum
If Gitea is not configured, task titles and bodies are shown as plain text.
Environment overrides:
| Variable | Default |
|---|---|
PORT |
3025 |
BIND_ADDRESS |
localhost |
Data model
Tasks are stored in data/tasks.ndjson. Sequential IDs are tracked in data/ids.ndjson.
Task {
id integer sequential, auto-assigned
title string markdown
body string markdown, optional
status open | deferred | done | cancelled
priority high | normal | low
tags string[]
parent_id integer | null null = root task
created_at ms timestamp
updated_at ms timestamp
}
API
All responses: { ok: true, ...data } or { ok: false, error: string }.
GET /api/tasks
POST /api/tasks body: { title, body?, status?, priority?, tags?, parent_id? }
GET /api/tasks/:id
PUT /api/tasks/:id body: any subset of task fields
DELETE /api/tasks/:id blocked if task has subtasks
POST /api/render-markdown body: { text, mode? } → { html }
Running as a system service
deployment/task-inventory.service targets /srv/task-inventory with a dedicated task-inventory user.
# Create service user
sudo useradd -r task-inventory
# Deploy files
sudo cp -r . /srv/task-inventory
sudo chown -R task-inventory:task-inventory /srv/task-inventory
# Install dependencies as service user
sudo -u task-inventory npm ci --prefix /srv/task-inventory
# Copy and configure config.yaml
sudo cp config.yaml.example /srv/task-inventory/config.yaml
sudo chown task-inventory:task-inventory /srv/task-inventory/config.yaml
# edit /srv/task-inventory/config.yaml
# Enable and start
sudo systemctl enable --now "$(realpath deployment/task-inventory.service)"
Packaging (Arch, Debian, etc.) is not set up yet.
File map
server.mjs Entry point — Express 5, all routes
lib/
config.mjs Loads config.yaml (ENOENT-safe)
ids.mjs Sequential integer ID generator per namespace
kv-store.mjs Flat NDJSON key-value store (auto-load, debounced flush)
storage.mjs Task CRUD wrappers
public/
app.mjs SPA — state, rendering, dialogs
index.html Shell
style.css All styles
templates.html HTML templates (injected at init)
gitea-markup.css Vendored Gitea markup + chroma CSS for markdown rendering
lib/
api.mjs fetch wrappers for all API endpoints
dom.mjs qs(), clone(), set_text(), show(), hide()
data/ Created at runtime, not committed
tasks.ndjson All task records
ids.ndjson Sequence counters
Description
Languages
JavaScript
76%
CSS
17.6%
HTML
5.4%
Makefile
1%