Skip to content

jalbarrang/taskman

Repository files navigation

@dreki-gg/taskman

A standalone task-management engine — and a taskman CLI — over a plain JSONL plan ledger (default .taskman/plans/, configurable via .taskmanrc). It is the core extracted from @dreki-gg/pi-plan-mode, so any harness (not just pi) can drive the same plans, initiatives, and tasks.

Why it exists

Planning agents need durable, file-based task state that survives across sessions and tools. taskman owns that state machine — task status, plan and initiative lifecycle, and the projection rules that keep them consistent — with no dependency on any specific agent harness. Use it from a shell, a CI job, a different agent, or as a library.

The ledger (the one durable contract)

Everything lives under the plans root — .taskman/plans/ in the current working directory by default:

  • <root>/plans.jsonl — the plan registry.
  • <root>/initiatives.jsonl — the initiative registry (initiatives group plans).
  • <root>/<plan>/tasks.jsonl — one plan's task list (first line is metadata).
  • <root>/<plan>/HANDOFF.md, <root>/<initiative>/INITIATIVE.md — prose docs.

Configuring the plans root (.taskmanrc)

Drop a .taskmanrc JSON file in the working directory to relocate the ledger — useful when another workflow already claims a similar folder:

{ "plans-root": "some/dir" }

The value IS the ledger folder (some/dir/plans.jsonl, some/dir/<plan>/). Resolution is cwd-only by design — no directory walk-up, no environment variable — so you (and agents) can always predict which folder a command targets. taskman root (add --json for machines) prints the resolved root and whether it came from .taskmanrc or the default.

Three invariants are worth knowing; everything else is mechanism:

  1. Status is a projection, not a flag. A plan is done when its active tasks are all resolved (and no follow-ups remain); an initiative is done when every member plan is terminal. Writing task state re-derives the registry — you do not set plan status by hand for the normal path.
  2. Plan resolution is stateless. A command targets a plan via --plan <name> (a path prefix like .taskman/plans/<name> is stripped), else the single in-progress plan. Ambiguous or missing → it exits non-zero and lists the candidates.
  3. Terminal statuses set manually are never auto-reverted. reconcile only moves in-progress ⇄ done; it never resurrects a superseded/abandoned plan or regresses a finished one.

Usage

taskman --help            # full, always-current command list
taskman <command> --help  # flags + arguments for one command

--help is the source of truth for commands and flags — this README does not duplicate it (so it cannot drift). Every command prints human text by default and accepts --json for machine consumption.

Create a plan from any harness (handoff/tasks accept an inline value, a --*-file <path>, or piped stdin):

echo "$MARKDOWN" | taskman create-plan --name my-plan --title "My Plan" \
  --handoff-file - --tasks '[{"description":"do it"}]'
taskman create-handoff --plan my-plan --file HANDOFF.md   # write/replace prose

Create an initiative, then link member plans; revise a plan in place when follow-up changes arrive (omitted fields stay as-is; matching task ids keep status/notes):

taskman create-initiative --name auth-overhaul --title "Auth Overhaul" --overview-file INITIATIVE.md
taskman create-plan --name auth-api --title "Auth API" --initiative auth-overhaul \
  --handoff "..." --tasks '[{"description":"scaffold"}]'
taskman revise-plan --plan auth-api --title "Auth API v2" --tasks '[{"id":"t-001","description":"scaffold"},{"id":"t-002","description":"tests"}]'

A typical execution loop:

taskman status                       # what's the active plan and its tasks?
taskman update-task t-003 done       # mark progress (auto-reconciles the plan)
taskman add-task "handle empty case" --reason "found gap while implementing"
taskman reconcile --apply            # repair safe status drift

As a library

import {
  makePlanRuntime,
  resolveLedgerRoot,
  resolvePlanByName,
  setTaskStatus,
} from '@dreki-gg/taskman';

// Honour a .taskmanrc if present (the library never reads it implicitly);
// makePlanRuntime() alone uses the default root, .taskman/plans.
const run = makePlanRuntime(resolveLedgerRoot().root);
const { planDir } = await run(resolvePlanByName({ name: 'my-plan' }));
await run(setTaskStatus(planDir!, 't-001', 'done'));

makePlanRuntime(root) takes the ledger folder itself — storage programs use ledger-relative paths, so the root places the whole registry.

The public surface (storage, schema, reconcile, initiative projection, resolution, and composite write flows) is exported from the package root. The engine is built on Effect with a single FileSystem seam, so it is straightforward to test and to run against an alternate backend.

Agent skill

This package ships a TanStack Intent skill (skills/taskman/core) — versioned guidance that AI coding agents discover from node_modules. If you use an AI agent, run:

npx @tanstack/intent@latest install

License

MIT

Releasing

No changesets here — plain npm: npm version minor && npm publish && git push --follow-tags. Requires npm 2FA.

Skill

The canonical agent-facing skill for this CLI lives in jalbarrang/skills/taskman (npx skills add jalbarrang/skills/taskman). The copy under skills/taskman/core ships inside the npm package for pi consumers and mirrors it.

About

Harness-agnostic plan ledger CLI for agent workflows (.plans/)

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors