Update README to reflect current state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 23:18:33 +00:00
parent 5e3e30b64c
commit 1d25c5e1c4

View File

@@ -1,15 +1,18 @@
# task-inventory
Personal task management. Organise tasks, thoughts, and ideas as a hierarchy. Designed to grow into project management over time.
Personal inventory for tasks, ideas, notes, and anything else. Entries are organised in a hierarchy, with configurable entry types. 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
- **Configurable entry types** — default types are task, idea, note; create your own
- **Hierarchical entries** — unlimited nesting, any type can be a child of any other
- **Tree view** (roots only, expandable — children always shown unfiltered) and **flat view**
- **Filters** by status, priority, and tag; free-text search across title and body; clickable tag chips
- **Entry detail view** — full rendered markdown, metadata, children list; URL-addressable (`#4`)
- **URL routing** — `#all`, `#type/<id>`, `#<entry-id>`, `#manage-types`; back/forward works
- **Markdown rendering** via a local Gitea instance — edit/preview tabs in the editor
- **Sequential integer IDs** — global across all entry types
- **Flat NDJSON key-value store** — single file, no database required
## Setup
@@ -32,7 +35,7 @@ gitea:
token: your_token_here # needs read:misc scope minimum
```
If Gitea is not configured, task titles and bodies are shown as plain text.
If Gitea is not configured, titles and bodies are shown as plain text.
Environment overrides:
@@ -43,73 +46,84 @@ Environment overrides:
## Data model
Tasks are stored in `data/tasks.ndjson`. Sequential IDs are tracked in `data/ids.ndjson`.
All data is stored in `data/tasks.ndjson` as a flat NDJSON key-value store.
```
Task {
id integer sequential, auto-assigned
Entry {
id integer sequential, global across all types
type string entry type id (e.g. "task", "idea", "note")
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
parent_id integer | null null = root entry
created_at ms timestamp
updated_at ms timestamp
}
Entry_Type {
id string lowercase alphanumeric/underscore, e.g. "bug_report"
title string display name
description string
created_at ms timestamp
updated_at ms timestamp
}
```
On first run, three default entry types are seeded: task, idea, note. Existing data from older flat-key format (`task:1`, `task:2`, …) is migrated automatically.
## 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
GET /api/entry-types
POST /api/entry-types body: { id, title, description? }
PUT /api/entry-types/:id body: { title?, description? }
DELETE /api/entry-types/:id blocked if type still has entries
POST /api/render-markdown body: { text, mode? } → { html }
GET /api/entries ?type=<id> to filter by type
POST /api/entries body: { type, title, body?, status?, priority?, tags?, parent_id? }
GET /api/entries/:id
PUT /api/entries/:id body: any subset of entry fields
DELETE /api/entries/:id blocked if entry has children
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.
## Deployment
```bash
# Create service user
# First time: create service user and config
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 mkdir -p /srv/task-inventory
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
# Deploy (and on every update)
make deploy DEST=/srv/task-inventory
# Enable service (first time only)
sudo systemctl enable --now "$(realpath deployment/task-inventory.service)"
```
Packaging (Arch, Debian, etc.) is not set up yet.
`make deploy` runs rsync (excluding `data/`, `config.yaml`, `node_modules/`), fixes ownership, and runs `npm ci` as the service user. `make sync` does the rsync+chown only, skipping `npm ci`.
To bind to all interfaces instead of localhost, uncomment `Environment=BIND_ADDRESS=0.0.0.0` in `deployment/task-inventory.service`.
## File map
```
server.mjs Entry point — Express 5, all routes
Makefile deploy / sync targets
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
storage.mjs Entry types + entries CRUD, ID generation, migration
public/
app.mjs SPA — state, rendering, dialogs
app.mjs SPA — state, routing, rendering, dialogs
index.html Shell
style.css All styles
templates.html HTML templates (injected at init)
@@ -118,6 +132,7 @@ public/
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
tasks.ndjson All entry records and entry type definitions
deployment/
task-inventory.service systemd unit file
```