| name | work |
| description | Manage work items and tasks using the work todo system. Use when creating, updating, listing, or tracking work items with dependencies. (user) |
work todo system
Manage work items stored as Work{} callback-based lua files in ~/*/*/work/.
Data structure
Work items are stored in individual lua files using the Work{} callback pattern:
Work{
id = "01KBB8VWCGH1CMSNDGNXD54F2J",
title = "implement feature",
created = "2025-11-30",
completed = "2025-11-30T14:30:00",
due = "2025-12-15",
description = "optional details",
blocks = {
"01KBB8VWCGH1CMSNDGNXD54F2K"
},
log = {
["2025-11-30T10:30:00"] = "started investigation",
["2025-11-30T14:15:00"] = "found root cause"
}
}
Each item is stored in ~/*/*/work/{id}.lua.
Dependency model: blocks field
CRITICAL: The blocks field lists what this item blocks (what waits on this item).
The blocks field specifies IDs that cannot start until THIS item completes:
- If item Y has
blocks = [Z], then Y blocks Z (Z waits for Y)
- Y must complete before Z can start
- Y is a "prerequisite" or "blocker" of Z
Example hierarchy:
Work{id = "X", title = "code", blocks = ["Y"]}
Work{id = "Y", title = "test", blocks = ["Z"]}
Work{id = "Z", title = "deploy"}
Execution order: X → Y → Z
Common terminology:
- "Y blocks Z" = Y has
blocks = [Z] = Z waits for Y to complete
- "Z is blocked by Y" = Y has Z in its blocks field = Z cannot start until Y completes
- "Y is a blocker" = Y has items in its blocks field that wait on it
Due dates
Due dates can be specified in two ways:
- Absolute dates:
"2025-12-15" (YYYY-MM-DD format)
- Relative dates:
"-1d" or "-4w" (days or weeks before dependent items)
Relative dates are computed based on the earliest due date among all items that transitively depend on (block on) the current item. For example:
- Item A has
due = "2025-12-15"
- Item B blocks on A with
due = "-1d" → resolves to 2025-12-14
- Item C blocks on B with
due = "-2d" → resolves to 2025-12-12 (2 days before A's date)
Warnings are shown if:
- A relative due date has no dependent items
- No dependent items have due dates set
Supported units:
d - days
w - weeks (7 days)
Global flags
All commands support the --json flag for machine-readable output:
bin/work --json <command> [args...]
JSON output format:
add: returns the created item
list: returns array of all items
show: returns the item
done: returns the updated item
update: returns the updated item
rm: returns the removed item
blocked: returns array of blocked items with unresolved_blocks field
ready: returns array of ready (unblocked) items
log: returns object with id, timestamp, and message
Commands
add
Create new work item:
bin/work add "task title"
bin/work add "task title" due=2025-12-15
bin/work add "task title" due=-1d description="details" priority=5
bin/work add "task title" blocks=FK0FHM,21S2FX
Accepts optional field=value arguments:
due=YYYY-MM-DD or due=-Xd - Set due date (absolute or relative)
description="text" - Add description
priority=N - Set priority (numeric)
blocks=id1,id2 - Set blocking dependencies (IDs that THIS item will block)
Returns the generated ULID on stdout. Short IDs (both prefixes and suffixes) are supported in the blocks argument.
IMPORTANT: When adding an item with blocking relationships:
list
Show all work items with status:
bin/work list
Output format:
◉ K0FHM: completed task
○ W8NAE: pending task
○ 4F2K: blocked task (blocks: W8NAE)
Items are sorted by creation time (ULID order). IDs are displayed as the last 6 characters of the ULID (suffix) for better uniqueness, since ULIDs have more entropy at the end.
tree
Show work items as a dependency tree:
bin/work tree
Output format:
○ FK0FHM +13w 2026 ATR
○ 21S2FX +3d self-review
○ JRV0BB +3d request feedback
○ W8NAEP +1d identify feedbackers
○ MCFMG3 +12d peer feedback
Displays items in dependency hierarchy with indentation, showing:
- Status marker (○ for incomplete, ◉ for completed)
- 6-character suffix ID
- Relative due date if present (e.g., +3d, +13w)
- Task title
Root items (no dependencies) are shown at the top level, with dependent items indented beneath them. Items are sorted by due date (ascending), priority (descending), and creation time (ascending) at each level.
show
Display detailed information about a work item:
bin/work show {id}
bin/work show FK0FHM
bin/work show 01KBBAH
Shows: id, title, created date, completed timestamp (if present), due date (with resolution for relative dates), description (if present), blocking dependencies, and log entries.
Note: All commands accept short IDs (6+ characters). The system matches both prefixes and suffixes, preferring suffixes since they're displayed in list and tree. If ambiguous, the command will error and show matching IDs.
done
Mark work item as complete:
bin/work done {id}
Sets the completed field to the current timestamp (ISO 8601 format) in the item's lua file.
update
Modify work item fields:
bin/work update {id} field=value...
bin/work update {id} description="new description"
bin/work update {id} blocks=id1,id2,id3
bin/work update {id} due=2025-12-15
bin/work update {id} due=-1d
bin/work update {id} priority=5
To remove a field, use an empty value:
bin/work update {id} field=
bin/work update {id} completed=
bin/work update {id} description=
bin/work update {id} due=
Any optional field can be removed by setting it to an empty value.
IMPORTANT: When updating blocking relationships:
blocks=id1,id2 sets what THIS item blocks (items that wait on THIS item)
- To make item A block item B: update A with
blocks=B (not the other way around)
- The item specified in the update command is the blocker, values are what it blocks
rm
Remove work item:
bin/work rm {id}
Deletes the item's lua file from the work data directory.
blocked
List items blocked by incomplete dependencies:
bin/work blocked
Shows only items with blocks fields containing IDs without a completed timestamp.
ready
Show next ready (unblocked) items:
bin/work ready
bin/work ready --limit=10
bin/work ready --shuffle
bin/work --json ready
Returns incomplete items that have no incomplete prerequisites (not waiting on anything). These are the items that can be started immediately.
Sorting:
- Default: due date (ascending), priority (descending), created date (ascending)
--shuffle: sorts by reversed ID for pseudo-random order (entropy is at end of ULID)
Options:
--limit=N: maximum number of items to show (default: 5)
--shuffle: randomize the order instead of scheduling sort
Output format:
W8NAEP +1d send email
3MCZQM +3w code review [p10]
1MHKK2 +3w documentation [p10]
Shows: 6-char suffix ID, relative due date (if set), title, and priority (if non-zero).
log
Append a timestamped message to a work item's log:
bin/work log id={id} <message>
Automatically generates an ISO 8601 timestamp (YYYY-MM-DDTHH:MM:SS) and appends the message to the item's log field. If the log field doesn't exist, it will be created.
Library
The work system uses a three-layer architecture:
lib/work/data.lua - Data layer
Handles storage, validation, and I/O operations:
Functions:
load_all(dir) - Load all work items from directory
load_file(path, kinds) - Load lua file with Work{} callbacks
save(item, dir) - Save item with validation and atomic write
delete(item) - Remove item file
get(id) - Retrieve item by full ID
get_all() - Return all items sorted by created date
get_by_file(source) - Get items from specific file
validate(item) - Validate item schema
clean(item) - Remove internal fields (_meta, _computed)
resolve_id(short_id) - Resolve short ID to full ID
generate_id() - Generate new ULID
write(item, callback_name) - Serialize item to Work{} format
render(data, opts) - Render data structure to lua format
Validation:
- Required fields:
id, title
- Optional fields:
created, completed, due, description, priority, blocks, log
- Collision detection for duplicate IDs
- Metadata tracking via
_meta.source and _meta.kind
- Atomic writes with signal handling for cleanup
lib/work/process.lua - Processing layer
Handles business logic, enrichment, and dependencies:
Functions:
enrich(item) - Add _computed fields to item
enrich_all(items) - Enrich array of items
resolve_due_date(item) - Resolve absolute/relative/inferred due dates
date_relative_to_today(date_str) - Format date as "+3w", "-2d"
get_blocked_items() - Items with unresolved blocking dependencies
get_ready_items() - Items with no incomplete prerequisites
validate_blocks(item_id, blocks) - Check for cycles and self-blocking
sort_by_schedule(items) - Sort by due (asc), priority (desc), created (asc)
build_tree(items) - Build dependency tree with roots and children
Enrichment:
Items are enriched with _computed fields:
short_id - Last 6 chars of ID
resolved_due - Actual date after resolution
relative_due - Human-readable ("+3w", "-2d")
is_blocked - Boolean status
unresolved_blocks - Incomplete dependencies
dependent_count - Items depending on this one
lib/work/render.lua - Presentation layer
Handles formatting and display:
Functions:
list(items, opts) - List format output
tree(tree_data, opts) - Tree format with hierarchy
detail(item, opts) - Detailed item view
ready(items, opts) - Ready items format
blocked(items, opts) - Blocked items format
json(data) - JSON output
- Component functions:
status_mark(), short_id(), due_display(), priority_badge(), blocks_info()
ULID generation
IDs use ULID (Universally Unique Lexicographically Sortable Identifier):
local ulid = require("ulid")
local id = ulid.generate()
- 26 characters (Crockford Base32)
- Time-ordered: first 10 chars = millisecond timestamp
- Globally unique: last 16 chars = random
- Lexicographically sortable
ULID library location: ~/.local/lib/lua/ulid.lua
Workflow patterns
Basic task tracking
bin/work add "implement auth"
bin/work add "write tests"
bin/work add "deploy to staging"
bin/work list
bin/work done {auth-id}
Dependencies
id1=$(bin/work add "research database options" blocks=$id2)
id2=$(bin/work add "implement database layer" blocks=$id3)
id3=$(bin/work add "write tests")
bin/work blocked
bin/work ready
bin/work done $id1
bin/work blocked
bin/work ready
bin/work done $id2
bin/work blocked
bin/work ready
Adding details
bin/work update {id} description="Use PostgreSQL with connection pooling"
bin/work show {id}
Implementation notes
- Work items are stored in
~/*/*/work/ (discovered via glob pattern)
- Library uses three-layer architecture in
~/.local/lib/lua/work/:
data.lua - Storage and validation
process.lua - Business logic and enrichment
render.lua - Formatting and display
- ULID library is global (
~/.local/lib/lua/ulid.lua)
- Each work item is a separate file for version control friendliness
- Uses
Work{} callback pattern for data definition
- Commands follow Load → Process → Render pattern
- Enrichment pattern computes derived fields once, renders many times