npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hasna/todos

v0.17.4

Published

Universal task management for AI coding agents - CLI + MCP server + interactive TUI

Readme

@hasna/todos

Universal task management for AI coding agents - CLI + MCP server + interactive TUI

npm License

Install

bun install -g @hasna/todos

Upgrading From 0.15.52

0.16.0 puts every surface on one credential resolver and moves three command families to the shared API. Upgrading from 0.15.52 needs no data migration, but four things change:

  • Configure a credential. The ./sdk client no longer reads ~/.todos/config.json, and the unprefixed TODOS_URL / key spellings are legacy. Put the key in the macOS Keychain, in ~/.hasna/todos/config/credentials (owner-only 0400/0600), or in HASNA_TODOS_API_KEY, then confirm with todos storage status --json. With no credential the CLI exits non-zero (REMOTE_API_CONFIG_MISSING) instead of quietly serving local rows, and MCP tool calls fail closed rather than falling back to the on-box store (see the third bullet below). The retired locations — ~/.hasna/fleet-env, ~/.hasna/cloud, ~/.config/hasna, $XDG_CONFIG_HOME — are not read.
  • todos plans, todos task-lists (lists, tl) and the template commands are shared-API only. They refuse HASNA_TODOS_DB_PATH, TODOS_DB_PATH, HASNA_TODOS_LOCAL and TODOS_LOCAL before startup — any one of them set is enough to refuse the command, and the refusal names the ones that are set. todos template-library is unchanged and still credential-free. The MCP plan tools (create_plan, list_plans, get_plan, update_plan, delete_plan) and task-list tools (create_task_list, list_task_lists, get_task_list, update_task_list, delete_task_list) are shared-API only as well.
  • Most MCP tools that read the on-box store need HASNA_TODOS_LOCAL=1, and every refusal is typed. The MCP server no longer opens the on-box SQLite store implicitly, so tools that still read it directly — the template family, tags and labels, stale/blocked work, doctor, run ledger, handoffs, review queues, retrospectives, risks, knowledge records, backups, calendar, boards, focus/time reports and dispatches — fail on the default posture with the typed {"code":"API_DATABASE_FALLBACK_FORBIDDEN"} payload, whose suggestion names the opt-in; the server also logs the same code. Measured at 0.16.0 with TODOS_PROFILE=full, that is 68 of the 125 zero-required-argument tools. A further 18 return the typed REMOTE_API_CONFIG_MISSING — the 16 shared-API tools that need a credential (list_tasks, list_projects, list_agents, get_next_task, get_status, bootstrap, get_context, get_my_tasks, get_my_workload, get_health, standup, list_my_tasks, machines_register, machines_list, machines_heartbeat, machines_topology) plus list_plans and list_task_lists. Five more refuse caller input or local state with the typed INVALID_INPUT / ENCRYPTION_KEY_UNAVAILABLE / ENCRYPTED_PAYLOAD_INVALID, and five answer a readable text refusal ("Provide agent_id, id, or name."). No zero-argument tool returns an opaque UNKNOWN_ERROR on either posture — the default one measured above, or the HASNA_TODOS_LOCAL=1 opt-in these tools are served under. Run the server with HASNA_TODOS_LOCAL=1 to keep using the on-box tools; that opt-in is ignored when HASNA_TODOS_API_KEY or HASNA_TODOS_API_URL is set, because a configured environment outranks it.
  • The advertised command list is route-dependent. 0.15.52 listed all 166 commands to every caller because the local fallback was implicit. 0.16.0 lists only what the resolved route exposes: 76 root commands on the hosted route once a credential resolves, 75 with none configured (the extra verb, stale-lock-handoff, is advertised only when the shared-API authority is), and the full 166 only with HASNA_TODOS_LOCAL=1 set, because the on-box families fail closed on the default posture. No command was removed — todos --help, todos manual and the generated completions all honour the same rule, and every verb still resolves once its posture is configured. todos storage status also reports credential-source disagreements as a warnings array (--json) and a yellow stderr line (human mode); the entries name sources and env key names, never a value.

Everything else still runs offline with HASNA_TODOS_LOCAL=1 (when the environment configures no authority or credential of its own). The per-surface detail lives under apps/todos/docs/ (PLAN_API.md, TASK_LIST_API.md, TEMPLATE_API.md, TASK_QUERY_API.md, native-storage.md). The npm tarball ships this README, CHANGELOG.md and those five documents alongside dist/, so they are readable from an installed package; docs/cli-help.md is repo-only.

HTTP request budget

todos-serve defaults to 12,000 requests per minute per network peer, per server process. Set HASNA_TODOS_RATE_LIMIT_MAX to an integer from 1 to 1000000 to choose a different finite budget. The legacy TODOS_RATE_LIMIT_MAX variable still works; the canonical variable takes precedence when both are set. An absent or blank value uses the default. Invalid values fail startup before the database is opened or the server listens.

The fixed one-minute window covers all routes, including health checks and unauthenticated requests, except CORS OPTIONS. Exceeding the budget returns HTTP 429 with Retry-After in seconds. Authentication requirements stay the same. This setting is a request allowance, not a throughput guarantee; size a deployment using observed latency, database load, and its gateway limits.

Behind a reverse proxy, callers share the proxy's bucket unless the operator has explicitly configured trusted forwarding. Keep proxy trust off for direct traffic. Only enable TODOS_TRUST_PROXY=1 when network rules restrict access to proxies that overwrite forwarding headers; TODOS_TRUSTED_PROXIES helps resolve the forwarded chain and does not itself restrict which sockets may connect.

Credentials and Service Authority

The CLI, the MCP server and the ./sdk client all resolve their credential through one resolver — the client chain in @hasna/contracts — so there is exactly one place to put a key and exactly one precedence to learn. Resolution happens on every call, never once at process start, so rotating a key heals a long-running shell, MCP server or agent without restarting it: a TodosClient built at startup and held for hours re-reads the Keychain and the credentials file on each request and picks up the new key on the next one.

Two things are deliberately not re-resolved. An explicit apiKey argument is tier 1 — a pin the caller owns — and the service authority is fixed for the life of a client, because a credential written for one authority must never be sent to another; build a new client to point somewhere else.

That second rule binds the credential too. new TodosClient({ baseUrl }) with no apiKey pins the authority and sends no credential at all: the key in your Keychain or ~/.hasna/todos/config/credentials was written for the fleet, so it is never re-resolved onto a URL you named — a local todos-serve, a staging box, a test double. Pass apiKey alongside baseUrl when that authority is supposed to get a credential. Per-call re-resolution applies to a client that resolved its own hosted authority, which is where rotation matters.

Credential, highest to lowest:

| # | Tier | Where | | --- | --- | --- | | 1 | explicit argument | apiKey on new TodosClient({...}) — the todos CLI exposes no hosted-credential flag of its own (todos ai --profile is a runtime profile, not a credential tier) | | 2 | deliberate env pointer | HASNA_TODOS_API_KEY_OVERRIDE, HASNA_PROFILE, HASNA_TODOS_API_KEY_REF (a secrets-vault item key, never a value) | | 3 | macOS Keychain | generic password hasna.credentials.todos.api-key, account HASNA_STATION → hostname -s → USER | | 4 | disk | ~/.hasna/todos/config/credentials, owner-only 0400/0600 | | 5 | environment | HASNA_TODOS_API_KEY |

Tier 3 is account-scoped, not host-scoped: the item is looked up under the station name, so a key stored for one station is invisible on another. Set HASNA_STATION explicitly (a wrapper, a launchd plist, a CI job) when the account name is not the first of hostname -s / USER; with none of the three matching the item's account, the lookup misses and the run fails closed with REMOTE_API_CONFIG_MISSING rather than silently resolving something else.

Tiers 1 and 2 are deliberate: if one is set and cannot be honoured, the run fails — it never quietly authenticates as a different principal. Tier 5 is a first-class tier, not a deprecated one (a wrapper that injects the key per process is correct), and it sits below disk so a rotated on-disk key beats a stale export in an old shell. Nothing is read from ~/.hasna/fleet-env, ~/.hasna/cloud, ~/.config/hasna, $XDG_CONFIG_HOME, or ~/.todos/config.json.

Service authority, same shape:

HASNA_TODOS_API_URL → the Keychain hasna.credentials.todos.api-url item → ~/.hasna/todos/config/credentials → the fleet gateway https://api.hasna.com/todos. Configure the URL only to point somewhere else; the client appends /v1 itself, and sources that disagree are refused rather than silently preferred. HASNA_HOME and HASNA_CONFIG_HOME move the disk root; the older unprefixed spellings of these two variables (without the HASNA_ prefix) still work as a silent fallback for one release, but the HASNA_TODOS_* names are canonical and are what every diagnostic names.

The credentials file is a plain env file, and its permissions are enforced — a group- or world-readable one is refused rather than read:

install -m 700 -d ~/.hasna/todos/config
printf 'HASNA_TODOS_API_KEY=%s\n' "$KEY" > ~/.hasna/todos/config/credentials
chmod 600 ~/.hasna/todos/config/credentials

Hosted mode fails closed. With no credential the CLI exits non-zero (REMOTE_API_CONFIG_MISSING) and names the tiers it consulted; the stdio MCP server exits the same way before answering initialize. Once a hosted authority resolves, each call fails instead of serving local rows — the credential-gated tools with the typed REMOTE_API_CONFIG_MISSING and the on-box tools with the typed REMOTE_COMMAND_UNSUPPORTED (or API_DATABASE_FALLBACK_FORBIDDEN on surfaces without a startup refusal), both described under Upgrading From 0.15.52. Neither surface falls back to the local SQLite store, because serving local rows while authentication is broken prints healthy output for a broken system.

The ./sdk surface fails closed the same way: with nothing resolved, new TodosClient() and createTodosV1Client() both throw TODOS_CREDENTIAL_MISSING naming every tier they consulted. TodosClient can speak to the on-box todos-serve at http://localhost:19427 — that is a real mode for it — but only under the explicit opt-in below, never as a fallback. Every other refusal is a throw on all three surfaces too — aliases that disagree, an unreadable credential file, a URL with no key — because those are misconfigurations, not an absence of configuration.

A declared-but-blank authority variable is deliberately not one of those refusals. At the Todos seam a blank has always meant "unset" — helpers in the wild blank rather than delete — so every authority variable that is declared but empty is removed before the resolver runs. HASNA_TODOS_API_KEY= therefore resolves the machine's ambient Keychain item exactly as an unset variable does: it neither configures a credential nor withholds one. To force the on-box store from a wrapper, set HASNA_TODOS_LOCAL=1 — a blank authority variable counts as absent for the opt-in too, so the opt-in is still honoured when HASNA_TODOS_API_KEY is present but empty — instead of blanking a credential variable.

On the hosted route the MCP server never opens the local store either: the tools and todos:// resources that only exist for the on-box SQLite file (dispatch, templates, handoffs, boards, runs, todos://projects, …) answer REMOTE_COMMAND_UNSUPPORTED naming the opt-in, instead of reading an empty local file and reporting it as the fleet. Machine and task tools are shared-API routed on that route and refuse under their own REMOTE_API_* code when the authority cannot serve them.

Local mode is deliberate, and it says so. @hasna/todos is usable entirely offline against an on-box SQLite store — set HASNA_TODOS_LOCAL=1 (alias TODOS_LOCAL=1). It is honoured only when the environment configures no authority and no credential, it is answered before the resolver runs (so no Keychain item and no credential file is read), and every local run prints one line on stderr saying it is local.

Three command families are exempt from local mode and are shared-API only: todos plans, todos task-lists (aliases lists, tl) and the template commands (templates, template-init, template-preview, template-export, template-import, template-history). They refuse HASNA_TODOS_DB_PATH, TODOS_DB_PATH, HASNA_TODOS_LOCAL and TODOS_LOCAL before startup and need a credential. todos template-library is the exception on the other side: it renders the library bundled in the package and stays credential-free.

todos storage status --json    # which tier supplied the key, and which URL applied

CLI Usage

todos --help

Generate shell completions directly from the registered CLI command tree:

todos completions bash > ~/.local/share/bash-completion/completions/todos
todos completions zsh > ~/.zsh/completions/_todos
todos completions fish > ~/.config/fish/completions/todos.fish

--help, manual and the completions all derive from the same tree, and that tree is filtered by the resolved route: the default posture advertises the commands that can run against the hosted surface, and HASNA_TODOS_LOCAL=1 advertises the full on-box set. See Upgrading From 0.15.52 and apps/todos/docs/cli-help.md.

Print the local CLI manual when you need install/update commands, examples, JSON output contracts, error behavior, and the command catalog:

todos manual
todos manual --json

Deterministic loops can upsert a task by stable fingerprint without creating duplicates. The fingerprint is stored as metadata.fingerprint, and later upserts shallow-merge metadata so expectation fields can be refreshed safely:

todos --json task upsert \
  --fingerprint "loop:expectation:key" \
  --title "Expectation failed" \
  --metadata-json '{"expectation_id":"exp-1"}' \
  --evidence-paths "logs/loop.txt" \
  --expected '{"status":"ok"}' \
  --observed '{"status":"failed"}'

Current-state summaries

A row's title and description are what an agent reads first, while the decision that changes them usually lands in a comment. todos realign applies an operator-written current state to the row under an exact version guard, anchored to the comments that carry the decisions:

todos realign L8-00010 \
  --title "SOU HASNA — recovery approved; $700.00 wire received and credited" \
  --state "Owner approved the recovery path; the $700.00 wire was received and is a confirmed credit. Nothing is filed or paid before the final invoice." \
  --owner legal \
  --blocker "RL final invoice" \
  --next "Wait for the final invoice" \
  --source comment:d9af9e97-8539-4919-bb5b-767152986613 \
  --source comment:83c77867-86f2-4bc9-b579-3d5f4e1cc853 \
  --receipt-comment

It never summarises. At least one --source comment:<uuid> naming a comment on that row is required, the comment id is validated against the row's comments, and the stored text is exactly what the caller supplied. The write carries the version that was read (or --expect-version), so a concurrent writer is refused rather than clobbered; an ambiguous transport outcome is settled by reading the row back and the receipt says which of applied / not applied / unknown it is; comments are never touched, so retractions stay in history. The previous summary is retained in the row's change log where the authority records description changes (todos history) and, with --receipt-comment, verbatim in a comment on any authority. --dry-run composes and validates without writing.

Terminal Dashboard

todos dashboard launches a local Ink TUI with keyboard tabs for overview, projects, tasks, plans, runs, dependencies, inbox, and search. It reads only the local SQLite database and can also print deterministic snapshots for scripts or tests:

todos dashboard
todos dashboard --snapshot --view tasks --search "release" --json

Keyboard hints are shown in the interface: h/left and l/right move between tabs, 1-8 jumps to a tab, / opens local search, r refreshes, and q quits.

Local Project Bootstrap

Bootstrap discovers the current local workspace, registers a project identity, creates the default task list, records local source metadata, and works for monorepo package roots without contacting hosted services:

todos project-bootstrap .
todos project-bootstrap packages/cli --name todos-cli --task-list todos-cli
todos project-bootstrap . --dry-run --json

MCP clients can use bootstrap_project for the same local-only workflow. The command is idempotent, so running it again refreshes machine-local paths without duplicating projects, task lists, or source records.

Local Machine Topology

Machine registry state stays in local SQLite. Machines can record identity, last-seen heartbeats, workspace paths, git roots, and user-provided Tailscale or LAN addresses without probing the network:

todos machines register my-machine --ssh user@my-machine --tailscale-name my-machine.tailnet --tailscale-ip 100.100.100.100 --lan-address 192.0.2.10 --workspace ~/workspace
todos machines heartbeat my-machine --workspace ~/workspace
todos machines topology --json
todos machines sync --machine my-machine --dry-run --json
todos machines sync --machine my-machine --push
todos machines sync --ssh user@my-machine --dry-run
todos projects-path set <project-id> ~/workspace/my-project

todos machines topology reports stale machines, missing local path overrides, missing local paths, and projects whose machine-local paths differ across registered machines. MCP clients can use machines_register, machines_heartbeat, machines_topology, and machines_list for the same offline diagnostics.

todos machines sync exchanges the same local bridge bundles used by todos export --format bridge over SSH. Pulls import remote projects, task lists, plans, tasks, comments, run evidence, boards, calendar items, and stored artifact contents with dry-run-first reporting and safe conflict recording. --push sends the local bundle back to the peer and asks the peer's installed todos bridge-import to apply or preview it. No hosted service is contacted; use --ssh for a one-off bootstrap peer before it has a registered machine address.

Local Workspace Trust

Workspace trust profiles live in ~/.hasna/todos/config.json and keep agent permissions local. Profiles declare trusted roots, command allowlists and denylists, tool permissions, write scopes, environment-key redaction patterns, and whether unsafe checks should require an explicit prompt:

todos trust add . --preset standard --allow-command bun,git,todos --write-scope src,tests --redact-env API_KEY,TOKEN
todos trust status .
todos trust check . --command "bun test" --write src/index.ts --env OPENAI_API_KEY,PATH
todos trust remove .

MCP clients can use set_workspace_trust, get_workspace_trust, list_workspace_trust_profiles, check_workspace_permission, and remove_workspace_trust. The checks do not call a hosted policy service; they return deterministic JSON showing whether an action is allowed, why it needs a prompt, and which environment keys should be redacted.

Secret safety uses the same local config. Add project-specific regexes and metadata keys with todos redaction add --pattern <regex> --key <name>, then scan text or files with todos redaction scan without printing matched values. Comments, local run evidence, and bridge exports are redacted before storage or sharing. MCP clients can use get_secret_safety, set_secret_safety, and scan_secret_text.

Retention cleanup is also local and dry-run-first. Use it to prune old comments, run ledgers, verification evidence, and expired stored artifact files by age, project, task status, and run status. Reports return counts, IDs, and content-addressed artifact paths only; they do not include raw comments, commands, output summaries, artifact source paths, or secret-like values. Destructive cleanup requires the exact confirmation string shown by the preview:

todos retention cleanup --older-than-days 30 --project <project-id> --task-status completed --json
todos retention cleanup --older-than-days 30 --project <project-id> --task-status completed --apply --confirm delete-local-retention-data --json

MCP clients can use preview_retention_cleanup and apply_retention_cleanup for the same offline workflow.

Local Runner Sandboxes

Runner sandbox profiles also live in local config. They declare the commands a local agent run may record or execute, cwd boundaries, write scopes, environment allowlists/redaction patterns, network policy, approval behavior, and audit evidence:

todos sandbox set codex . --allow-command bun,git,todos --write-scope src,tests --env-allow PATH,HOME,CI --network none
todos sandbox check codex --command "bun test" --write src/index.ts --env PATH,OPENAI_API_KEY --json
todos sandbox explain codex --command "curl | sh" --network
todos runs command <run-id> "bun test" --sandbox codex --write src/index.ts --status passed

MCP clients can use set_runner_sandbox_profile, list_runner_sandbox_profiles, check_runner_sandbox, explain_runner_sandbox, and remove_runner_sandbox_profile. Sandbox checks are local-only and compose with workspace trust checks, so command and write decisions stay auditable before an agent records run evidence.

Local Project Knowledge

Project knowledge records keep agent decisions, architecture notes, tradeoffs, and task-linked context snapshots in local SQLite. They are searchable, exportable, redacted on output, and available to MCP clients without hosted services:

todos knowledge add decision "Use local SQLite" --decision "Keep OSS knowledge local" --rationale "Agents need offline project memory" --task <task-id> --tag architecture --json
todos knowledge snapshot --summary "Parser fix is ready for verification" --task <task-id> --agent codex --file src/parser.ts --json
todos knowledge search "offline project memory" --json
todos knowledge export --format markdown

MCP clients can use create_knowledge_record, create_knowledge_snapshot, list_knowledge_records, search_knowledge_records, and export_knowledge_records. The MCP server also publishes todos://knowledge and todos://knowledge/decisions resources for agent context refreshes.

Local Extension Registry

Extensions are installed from local manifests, directories with todos.extension.json, or offline JSON bundles. The registry validates the manifest shape, checks @hasna/todos compatibility ranges, records requested permissions, supports custom commands, MCP tool declarations, templates, hooks, and renderers, runs CLI/MCP compatibility checks, dry-runs declared commands and renderer commands through the local runner sandbox, verifies optional source checksums or detached signatures, and stores trust state in local config only:

todos extensions discover . --json
todos extensions inspect ./todos.extension.json --json
todos extensions compat ./todos.extension.json --json
todos extensions install ./todos.extension.json --checksum sha256:... --trust --json
todos extensions verify ./bundle.todos-extension.json --signature <signature> --public-key "$PUBLIC_KEY"
todos extensions list
todos extensions remove my-extension

Unsigned extensions are allowed but installed as local records with warnings. Without --trust, installs remain in needs_review so agents can discover custom commands, MCP tools, hooks, and permissions without treating them as approved. MCP clients can use inspect_local_extension, test_local_extension_compatibility, install_local_extension, list_local_extensions, and remove_local_extension for the same offline workflow.

Local Workflow Prompts

The package includes bundled MCP prompts for common agent workflows: goal_planning, task_claiming, review, verification, handoff, release_prep, import_triage, and incident_response. They are static, local-only prompt resources that can be listed or rendered without a model call:

todos workflows list
todos workflows show goal_planning --objective "Ship release" --task 1234abcd --json
todos workflows export --format markdown

MCP clients can discover the same catalog at todos://workflow-prompts and call the matching prompt by ID. Prompt output is deterministic and is intended for Codex, Claude Code, Takumi, and other agent-native clients that need reusable local guidance for planning, claiming, review, verification, handoff, release, triage, and incident workflows.

Agent setup recipes for MCP registration, /goal planning, task claim/update/complete loops, evidence comments, and no-cloud verification live in docs/agent-adapters.md.

Local Policy Packs

Policy packs are project-local done gates for agents. They validate task status, passed verification commands, prohibited commands, linked commits and pull requests, approvals, branch names, run ledgers, artifacts, changed paths, and minimum evidence counts from the local SQLite database and config only:

todos policies set release . \
  --required-status completed \
  --required-command "bun test,bun run typecheck" \
  --prohibited-command "npm install -g,git reset --hard" \
  --require-passed-verification \
  --require-commit \
  --require-pr \
  --require-run \
  --require-artifact
todos policies validate release <task-id> --json
todos policies explain release <task-id>

MCP clients can use set_policy_pack, list_policy_packs, validate_policy_pack, explain_policy_pack, and remove_policy_pack. Validation is a dry local read of recorded task evidence; it never calls a hosted enforcement service.

Local Source TODO Index

Source extraction scans local code for TODO, FIXME, HACK, BUG, XXX, and NOTE comments, respects .gitignore plus explicit excludes, records source files, line anchors, nearby symbols, and stable dedupe fingerprints, and can run as a finite local watcher:

todos extract . --dry-run --index --json
todos extract . --exclude fixtures/** --tags tech-debt
todos extract-watch . --dry-run --max-runs 1 --json

Created tasks are tagged with extracted and linked back to the source file. The source index is CLI-only: the former extract_todos / watch_source_todos MCP tools were removed (a filesystem watcher is not an MCP tool call, and the scan runs against the local checkout, not the hosted fleet). No hosted code search, hosted sync, or telemetry is used.

Local Editor Integrations

Editor recipes live in docs/editor-integrations.md and examples/editor-integrations/. They include VS Code task definitions, JetBrains external tool recipes, Neovim Lua helpers, a shell statusline snippet, and a Bun task picker. Every example uses only todos CLI JSON output or MCP tool names, so editors can claim tasks, inspect local queues, build context packs, and link source files without importing private modules or hosted code.

Task Contracts and Reviews

Task contracts make acceptance criteria, required verification, expected artifacts, relevant files, risk, and review state machine-readable for agents:

todos contracts set <task-id> \
  --criteria "Parser handles quotes;Parser rejects malformed checkboxes" \
  --verify "bun test src/parser.test.ts" \
  --artifact logs/parser.txt \
  --file src/parser.ts \
  --risk medium \
  --done "review approved" \
  --json
todos contracts request-review <task-id> --requester codex --reviewer reviewer
todos record-verification <task-id> "bun test src/parser.test.ts" --status passed --artifact logs/parser.txt
todos contracts review <task-id> --state approved --reviewer reviewer
todos contracts check <task-id> --json

Contracts are stored in local task metadata, mirror acceptance criteria for context packs, and are checked only against local status, review state, and recorded verification evidence. MCP clients can use set_task_contract, get_task_contract, request_task_review, record_task_review, and check_task_done_contract.

Local Approval Gates

Approval gates are manual checkpoints stored in the local task database. Agents can require, approve, reject, expire, list, and check gates before risky plan or run work. Blocked checks exit nonzero, including JSON mode, so local automation cannot silently bypass a missing or denied checkpoint:

todos approvals require <task-id> deploy --requester codex --reviewer reviewer --run <run-id> --reason "production-affecting action"
todos approvals check <task-id> deploy --json
todos approvals approve <task-id> deploy --reviewer reviewer --note "safe to proceed"
todos approvals list <task-id> --json

MCP clients can use require_approval_gate, approve_approval_gate, reject_approval_gate, expire_approval_gate, check_approval_gate, and list_approval_gates. Gate events are written to task audit history and, when a run is linked, to the local run ledger.

OpenAutomations Approval And Evidence Handoff

OpenAutomations approval gates can be mirrored to Todos approval gates when an automation action affects external systems, spends money, mutates production state, or needs human consent. Use the automation run id as the linked run or metadata reference, and keep the approval decision id in task/run evidence:

todos approvals require <task-id> automation:<action-id> \
  --requester automations \
  --reviewer operator \
  --reason "OpenAutomations action requires consent"
todos approvals approve <task-id> automation:<action-id> \
  --reviewer operator \
  --note "Approved action execution"
todos record-verification <task-id> "automations queue complete <action-id>" \
  --status passed \
  --summary "Action completed by runner open-loops:<worker-id>"

Todos owns task approvals, task comments, verification evidence, audit-ledger checkpoints, and release evidence. OpenAutomations owns automation specs, action queue leases, DLQ/replay, and action approval state. Store only redacted summaries, action ids, run ids, decision ids, artifact paths, and verification commands in Todos; do not store raw event payloads, secret values, connector tokens, or full action inputs unless the task explicitly requires them and they have been redacted.

Local Review Queues

Review queues turn local task review into an explicit agent workflow: request a review, route it to a queue, claim it, return it with changes, reopen it, or approve it. Routing rules live in local config and can match tags, priorities, and projects without hosted users, orgs, or cloud services:

todos reviews rules set security --queue security-review --reviewers reviewer --tags security --priorities high
todos reviews request <task-id> --requester codex --reason "security-sensitive change"
todos reviews claim <task-id> --reviewer reviewer
todos reviews return <task-id> --reviewer reviewer --changes "Add tests;Record verification"
todos reviews approve <task-id> --reviewer reviewer
todos reviews list --queue security-review --json

MCP clients can use list_review_queue, request_review_queue, claim_review_item, return_review_item, approve_review_item, reopen_review_item, set_review_routing_rule, list_review_routing_rules, and remove_review_routing_rule. Queue transitions are written to audit history and emitted to local event hooks as review.requested, review.claimed, review.returned, review.approved, and review.reopened.

Local Roadmaps

Roadmaps group local tasks, plans, runs, milestones, and release labels into a portable planning view. They live in local config, summarize dependency readiness from the task graph, and export deterministic JSON or Markdown bundles:

todos roadmaps create "Public package launch" --release v1 --json
todos roadmaps milestones add <roadmap-id> "Docs and examples" --tasks <task-id> --due 2026-06-01 --release v1 --json
todos roadmaps releases set <roadmap-id> v1 --milestones <milestone-id> --release-version 1.0.0 --json
todos roadmaps show <roadmap-id> --format markdown
todos roadmaps export <roadmap-id> --out roadmap.json
todos roadmaps import roadmap.json --apply --json

MCP clients can use create_roadmap, list_roadmaps, get_roadmap_summary, update_roadmap, delete_roadmap, create_milestone, update_milestone, delete_milestone, set_release_group, export_roadmap, and import_roadmap.

Local Event Hooks

Event hooks are local subscriptions for task, plan, run, approval, import, and export events. They can append redacted JSONL to a file, deliver to a Unix socket, expose a stdout test payload, or run a sandbox-checked local script with retry/backoff and SHA-256 integrity metadata:

todos event-hooks set audit --event task.completed,run.failed --target file --file .todos/events.jsonl
todos event-hooks set notify --event task.blocked --target script --command "notify-send \"$TODOS_EVENT_TYPE\"" --sandbox codex --attempts 2
todos event-hooks test audit --event task.completed --payload '{"id":"demo"}' --json
todos event-hooks list --json

MCP clients can use set_local_event_hook, list_local_event_hooks, test_local_event_hook, and remove_local_event_hook. Hook delivery is local-only; it does not call hosted webhooks or cloud automation services.

Shared Task Events And Route State

Task lifecycle changes emit shared @hasna/events events with source todos. Todos is the human-visible task ledger: it emits task intent, routing gates, and workflow artifact pointers. OpenEvents owns durable channel delivery/replay, and OpenLoops owns workflow admission, work-item leases, agent execution, evaluator cycles, and run manifests.

Use project-bootstrap --route-enabled to opt a project task list into task-created routing, then inspect route state with deterministic JSON before a route or workflow starts:

todos project-bootstrap . --route-enabled --json
todos ready --json
todos task route-state <task-id> --json

todos ready --json includes a route_state object for each pending unblocked task. route_state.schema_version is todos.task_route_state.v1. It exposes eligible, denial reasons, dependency blockers, route context, boolean gates, and compact OpenLoops pointers:

  • current_workflow_invocation_id
  • current_run_id
  • latest_manifest_path
  • latest_evaluation_path
  • workflow_state

OpenLoops updates those pointers after admission or evaluator progress:

todos task workflow-pointers <task-id> \
  --invocation <workflow-invocation-id> \
  --run <workflow-run-id> \
  --manifest ~/.hasna/loops/runs/<project>/<subject>/<run>/manifest.json \
  --state working \
  --json

Shared task event data includes task identity, title, description, project/list ids, working directory, tags, metadata, status, priority, approval state, and timestamps. Event metadata includes routing-safe project/list/path fields, route_enabled when task or task-list metadata opts in, route-state schema version, compact workflow pointers, and an automation object containing only boolean routing gates such as no_auto, manual_required, requires_approval, and approval_required.

When running route smokes or tests with an explicit temp/in-memory task database through TODOS_DB_PATH or HASNA_TODOS_DB_PATH, also set HASNA_EVENTS_DIR or HASNA_EVENTS_HOME to a temp event store. Without an explicit event directory, Todos suppresses shared event emission for temp task databases so route-enabled scratch tasks cannot trigger global OpenLoops workers. Set HASNA_TODOS_ALLOW_GLOBAL_EVENTS_FROM_TEMP_DB=1 only for deliberate integration tests that must exercise the global event bus.

Production task-created routes should fail closed:

  • Require one explicit opt-in, either task metadata route_enabled=true or an approved routing tag such as auto:route.
  • Add negative automation predicates so no_auto, manual, and approval-gated tasks do not invoke the route.
  • Scope by project path, task list, tags, or repo metadata before invoking an OpenLoops route.
  • Avoid overlapping opt-in channels for the same task family unless the target handler is idempotent. OpenLoops task-event admission dedupes by task id and event type, but a narrower subscription still avoids wasted invocations.

For tag opt-in, use a second route with the same deny predicates and tags=auto:route instead of route_enabled=true. Tasks without one of those opt-ins are intentionally no-route. Local event hooks remain available for local-only JSONL/socket/script integrations.

Local Terminal Notifications

Terminal notification rules are local watch rules for agents that want concise event signals in a shell, tmux pane, or editor terminal. Rules match task, run, plan, approval, import, and export events by severity, agent, project, priority, status, and payload text, then render deterministic line or JSON notifications:

todos terminal-notifications set blocked --event task.blocked,task.failed --min-severity warning --agent codex --priority high --contains deploy --bell
todos terminal-notifications set due --event task.due,task.sla_breached --min-severity warning --quiet-hours 22:00-07:00
todos notifications check --emit-hooks --terminal --quiet-hours 22:00-07:00 --json
todos terminal-notifications test blocked --event task.failed --payload '{"id":"demo","title":"Deploy failed","agent_id":"codex","priority":"high"}' --json
todos terminal-notifications list --json

MCP clients can use set_terminal_notification_rule, list_terminal_notification_rules, test_terminal_notification_rule, evaluate_terminal_watch_rules, check_local_notifications, and remove_terminal_notification_rule. Notifications are evaluated from local event payloads, can respect quiet hours, and do not require a desktop notification daemon, hosted queue, or cloud webhook service.

Local Encryption Profiles

Encryption profiles are optional local config entries for sensitive fields and secure bridge exports. Profiles store algorithm metadata, a nonsecret salt, and the name of the environment variable that contains key material. The key itself is never written to config, bundles, artifacts, or logs:

export TODOS_ENCRYPTION_KEY="use a strong local passphrase from your secret manager"
todos encryption set default --key-env TODOS_ENCRYPTION_KEY
todos encryption status default --json
todos encryption test default --json
todos export --format bridge --encrypt --output todos-bridge.enc.json
todos bridge-import todos-bridge.enc.json --decrypt --json
todos bridge-import todos-bridge.enc.json --decrypt --apply

Plain bridge exports are still supported for compatibility, but the CLI prints a warning because bridge bundles may contain task metadata, evidence summaries, comments, and stored artifact content. MCP clients can use set_encryption_profile, list_encryption_profiles, get_encryption_status, encrypt_local_value, decrypt_local_value, and remove_encryption_profile for local-only encrypted field workflows.

Local Agent Run Queue

Agent run adapters and queue entries are local. Queueing a task creates a run ledger immediately, then run-next launches the configured command template with {task_id}, {run_id}, and {agent_id} placeholders. Dry-runs show the command without execution, and cancellation/retry are recorded in the same local run ledger:

todos agent-runs adapter-set codex --command "codex exec --task {task_id}" --sandbox codex
todos agent-runs queue <task-id> --adapter codex --agent codex --claim --json
todos agent-runs run-next --dry-run --json
todos agent-runs run-next --json
todos agent-runs retry <run-id>

MCP clients can use set_agent_run_adapter, queue_agent_run, list_agent_run_queue, run_next_agent_dispatch, cancel_agent_run_dispatch, and retry_agent_run_dispatch. These commands launch only local processes and do not call hosted runners.

Local Agent Replay Simulation

Replay simulation turns a recorded context pack or run fixture into a deterministic dry-run snapshot without opening the project database or mutating tasks. Use it to debug agent plans, verification commands, task transitions, failures, touched files, artifacts, and approval decisions offline:

todos context-pack <task-id> --format json > replay.json
todos runs simulate replay.json --agent codex --scenario parser-failure --json
todos runs simulate replay.json --format markdown

MCP clients can use simulate_agent_replay with a fixture object and optional agent_id or scenario. The simulator redacts fixture values before hashing or rendering, reports mutates_database: false, and emits stable command, approval, failure, file, artifact, and warning summaries for local debugging.

Local Dependency Workflows

Dependencies are stored in the local SQLite database and never require hosted services. Use them to keep agents from starting blocked work:

todos deps <task-id> --needs <blocking-task-id>
todos deps <task-id> --graph
todos deps <task-id> --json            # machine-readable edges (ids + status) for this task
todos deps --project <ref> --json      # the whole-project graph (nodes + edges + cycles) in one read
todos blocked
todos ready

--json returns versioned, machine-readable shapes for schedulers: todos.task_dependency_edges.v1 for a single task (dependencies = upstream prerequisites, blocked_by = the incomplete prerequisites currently blocking the task — empty means dispatchable, blocks = downstream dependents) and todos.project_dependency_graph.v1 for --project (a nodes/edges/cycles adjacency list). Both are identical on the sqlite and http transports. Human output and --graph --json are unchanged.

The same workflow is available to MCP clients through add_task_dependency, remove_task_dependency, get_task_dependencies, and get_blocked_tasks. Dependency writes reject cycles, ready omits locked or blocked pending tasks, and startup schema repair recreates the local dependency table for older databases.

Local Risk Register And Health

Risks are stored in local SQLite and can be linked to projects, plans, or tasks with an owner, mitigation, due date, severity, probability, tags, and metadata:

todos risks add "Release blocker" --plan 1234abcd --severity high --owner codex --mitigation "Ship fallback" --json
todos risks list --plan 1234abcd --json
todos risks score --plan 1234abcd --json
todos risks export --project my-project --json

Health reports score a plan or project from local evidence only: blocked tasks, overdue open work, failed verification records, failed run ledgers, dependency depth, and open risks. MCP clients get the same surface through create_risk, list_risks, update_risk, close_risk, score_plan_health, score_project_health, and export_risk_register.

Local Retrospectives

Retrospectives summarize a project or plan using local evidence: completed plans, missed estimates, repeated blockers, failed verification records, lessons learned, and suggested follow-up tasks.

todos retrospectives create --plan 1234abcd --json
todos retrospectives list --project my-project --json
todos retrospectives export --plan 1234abcd --format markdown

Use --create-followups to create the suggested follow-up tasks locally. MCP clients get the same reports through create_retrospective, list_retrospectives, and export_retrospectives.

Local Agent Reliability Scorecards

Reliability scorecards summarize each agent from local evidence only: completed and failed tasks, passed and failed verification records, failed run ledgers, stale task/resource locks, retry history, and handoff quality.

todos reliability show codex --json
todos reliability list --project my-project --json
todos reliability export --format markdown

MCP clients get the same summaries through get_agent_reliability_scorecard, export_agent_reliability_scorecards, and the todos://agents/reliability resource.

Local Agent Reports

Agent reports compose the local planning surfaces into one report for standups, handoffs, and agent run planning: ready tasks, blocked tasks, overdue work, plan progress, run outcomes, verification evidence, and per-agent summaries.

todos reports local --agent codex --format markdown
todos reports local --project <project-id> --json

MCP clients can list sections with list_local_report_types and build the same local-only local_report contract with build_local_report. The report uses the local SQLite store only and does not call hosted analytics or external services.

Local Agent Locking

Task claims and locks are local SQLite leases. Agents can claim the next ready task, renew their lock by re-locking it during long work, inspect stale work, and safely steal or redistribute stale tasks without hosted coordination:

todos claim codex
todos --agent codex lock <task-id>
todos stale --minutes 30
todos claim codex --steal-stale --stale-minutes 30
todos redistribute codex --max-age 60

MCP clients get the same local coordination through claim_next_task, lock_task, unlock_task, check_task_lock, and get_stale_tasks. claim_next_task can opt into stale recovery with steal_stale and stale_minutes.

get_stale_tasks (and the other MCP tools that read the on-box store) require the deliberate local opt-in: start the MCP server with HASNA_TODOS_LOCAL=1, or the call fails on the default posture with the typed API_DATABASE_FALLBACK_FORBIDDEN refusal. See Upgrading From 0.15.52 for the affected families and the one case where the opt-in is ignored.

Plan Templates

The package ships a marketplace-free template library for bug fixes, feature implementation, security review, releases, migrations, incidents, docs refreshes, QA, and open source package bootstraps. Templates can create one task or a full ordered plan with dependencies, variables, priorities, tags, and descriptions.

Two surfaces are involved, and they are not the same store:

  • todos template-library renders the library bundled in the package. It is credential-free and never opens a database, so it works offline:

    todos template-library --json
    todos template-library --write .todos/templates
  • The remaining template commands are served by the authenticated shared API. todos template-init, todos template-preview, todos templates --use, todos template-export, todos template-import and todos template-history require HASNA_TODOS_API_URL and HASNA_TODOS_API_KEY (or saved account credentials) and refuse an on-box database selection before startup:

    todos template-init
    todos template-preview <template-id> --var name=api
    todos templates --use <template-id> --var name=api
    todos template-export <template-id> > plan-template.json
    todos template-import plan-template.json

todos template-library --write writes editable JSON files that use the same shape as todos template-import, so teams can fork a built-in workflow without contacting any hosted marketplace. todos templates --use creates every task in a multi-task template and wires its dependency graph in the shared account, so agents can immediately run todos ready, todos blocked, or todos deps <task-id> --graph against the generated plan. MCP clients reach the bundled library through list_template_library and write_template_library, which are credential-free and open no store. The remaining MCP template tools — init_templates, create_template, list_templates, create_task_from_template, preview_template, export_template, and import_template — read the on-box SQLite store and are not served on the default posture: start the MCP server with HASNA_TODOS_LOCAL=1 to use them. That opt-in is ignored when the environment sets HASNA_TODOS_API_KEY or HASNA_TODOS_API_URL, which leaves these tools unreachable (see Upgrading From 0.15.52).

Moving Tasks Between Plans

Plan assignment is editable after creation, so reorganizing a backlog never requires deleting and recreating tasks (which would lose history, comments, and assignments):

todos add "My task" --plan <plan-id>          # assign at creation
todos update <task-id> --plan <plan-id>       # move one task to another plan
todos update <task-id> --clear-plan           # detach from its plan
todos bulk plan --plan <plan-id> <id> <id>    # move many tasks at once
todos bulk move-plan --clear-plan <id> <id>   # detach many tasks at once
todos bulk tag <id> <id> --tag a,b            # add tags to many tasks (merges; never replaces)
todos bulk untag <id> <id> --tag a            # remove tags from many tasks
todos plans --show <plan-id>                  # verify the plan's task set

Plan references accept a UUID, a plan slug, a plan name, or a unique id prefix. An unknown or ambiguous reference exits non-zero before any task is modified. todos add, todos update and todos bulk follow whichever store the CLI is configured for — the local SQLite file, or the shared dataset behind a hosted /v1 authority. todos plans does not: it is served by the authenticated shared API only and refuses HASNA_TODOS_DB_PATH, TODOS_DB_PATH, HASNA_TODOS_LOCAL and TODOS_LOCAL before startup.

todos plans --artifact <id-or-slug> and --write-artifacts compare local Markdown against the shared plan record and therefore require an explicit --artifact-root <directory> — a trusted local project directory the caller chooses. Before 0.16.0 both options defaulted to the project path recorded in local SQLite, so the older form todos plans --artifact <id-or-slug> --json still appears in notes from that release; it now exits non-zero with --artifact-root is required. The files stay under <artifact-root>/.hasna/todos/plans/<project-id>/, and a path returned by the API is never used.

Local Git Traceability

Tasks can be linked to local git evidence without contacting hosted services:

todos link-commit <task-id> <sha> --message "fix parser" --files src/parser.ts
todos link-ref <task-id> task/parser-fix --type branch
todos link-ref <task-id> 42 --type pr --url https://github.com/hasna/todos/pull/42
todos record-verification <task-id> "bun test" --status passed --summary "1522 pass"
todos trace <task-id>
todos find-commit <sha-prefix>
todos find-ref <branch-or-pr>
todos blame src/parser.ts

MCP clients get the same local data through link_task_to_commit, find_task_by_commit, link_task_git_ref, find_tasks_by_git_ref, add_task_verification, and get_task_traceability, so agents can explain which task changed a commit, branch, PR, file, or verification command.

Local Mention Resolution

Agents can resolve task references before adding them to descriptions, plans, or handoffs. The resolver validates local files and line anchors, scans local source declarations for symbols, checks local git commits, branches, and fetched pull request refs, and resolves plans, runs, tasks, and agents from the local SQLite state:

todos references resolve file:src/index.ts:12 symbol:createTask branch:main --json
todos refs resolve plan:release run:abc123 agent:marcus --workspace .

The JSON output includes canonical reference keys and validated backlinks such as file:src/index.ts:12, symbol:createTask@src/index.ts:40, commit:<sha>, plan:<id>, and run:<id>. MCP clients can call resolve_mentions for the same local-only report; pull request refs are validated only when present in local git refs, and the resolver never calls hosted code search.

Local Branch-Safe Work Plans

Before an agent starts a branch, it can ask for a local branch work plan that checks the task or plan scope, planned files, active file conflicts, local git status, and suggested branch/traceability commands:

todos branch-plan 1234abcd --branch task/parser-fix --path src/parser.ts --json
todos branch-plan --plan <plan-id> --branch task/release-plan --no-git-status --json

MCP clients can call create_branch_work_plan with task_id or plan_id. The result is local-only: it does not create a branch, fetch from a remote, or contact hosted code review services. Agents can inspect safe_to_start, conflicts, reasons, and commands before running any git operation.

Local Release Notes

Generate changelogs from completed local tasks and their linked plans, commits, verification records, breaking-change notes, and migration notes:

todos release-notes --project . --format markdown
todos release-notes --tag release --since 2026-01-01T00:00:00.000Z --json

Tasks can add release metadata through metadata.breaking_change, metadata.breaking_changes, metadata.migration_note, or metadata.migration_notes. MCP clients use generate_release_notes for the same deterministic JSON or Markdown output without hosted release tooling.

Local Verification Providers

Optional provider adapters let agents standardize local verification without a hosted dependency. Providers can classify CI logs, verify browser/screenshot artifacts, or run explicitly configured command, script, and testbox-style commands with retry and redacted evidence capture:

todos verify-providers set local --kind command --command "bun test" --attempts 2 --json
todos verify-providers set ci --kind ci_log --json
todos verify-providers capabilities local --json
todos verify-providers run local --task <task-id> --agent codex --json
todos verify-providers run ci --task <task-id> --log-file /tmp/ci.log --json

Blacksmith/testbox-style providers are inert until a local command is explicitly configured, so the package never calls a cloud runner by default. MCP clients use set_verification_provider, list_verification_providers, get_verification_provider_capabilities, run_verification_provider, and remove_verification_provider for the same local-only workflow.

Local Agent Handoffs

Handoffs let one local agent leave continuation context for another without a hosted inbox. A handoff records the session, referenced tasks, relevant files, run ids, completed work, current blockers, and next steps. Readers can filter for unread handoffs and acknowledge them per agent:

todos handoff --create --agent codex --session codex-42 --summary "Parser work ready for review" --tasks <task-id> --files src/parser.ts --runs <run-id> --next "Review failing fixture" --json
todos handoff --unread-for claude --json
todos handoff --read <handoff-id> --json
todos handoff --ack <handoff-id> --agent claude --json
todos handoff --recover --agent codex --session codex-42 --json
todos handoff --export <handoff-id> --output handoff.json --json
todos handoff --import handoff.json --json
todos handoff --import handoff.json --apply --json

MCP clients can use create_handoff, list_handoffs, read_handoff, export_handoff, import_handoff, acknowledge_handoff, recover_stale_session_handoff, and get_latest_handoff. Recovery handoffs inspect local in-progress tasks, file links, and run evidence for the agent/session and create a deterministic continuation packet; no hosted queue or cloud service is involved. Handoff imports default to a dry-run preview; --apply writes the local handoff and preserves per-agent acknowledgement state.

Local Run Ledger

Agent runs can record local evidence without uploading artifacts or calling a hosted API:

RUN_ID=$(todos runs start <task-id> --agent codex --title "Parser fix" --claim --json | jq -r .id)
todos runs event "$RUN_ID" progress "writing regression tests"
todos runs command "$RUN_ID" "bun test src/parser.test.ts" --status passed --summary "14 pass"
todos runs file "$RUN_ID" src/parser.ts --status modified
todos runs artifact "$RUN_ID" logs/parser-test.txt --type log --description "focused test output" --require-file
todos runs artifact-verify "$RUN_ID"
todos runs finish "$RUN_ID" --status completed --summary "parser fixed and verified"
todos runs show "$RUN_ID"

Run command evidence is also mirrored into task verification evidence, file events are linked to task file tracking, and comments can be recorded into the task timeline. Sensitive-looking tokens, keys, passwords, and bearer values are redacted before evidence is stored. Artifact files are copied into a local content-addressed store beside the SQLite database, with SHA-256 integrity metadata, redaction status, retention metadata, and metadata-only fallback when the original path is unavailable. Use --no-store to record only artifact metadata.

Loops that need bounded, idempotent JSON can use the transaction commands instead of scripting around comments and run lookup:

todos runs begin <task-id> --key nightly:parser:42 --agent codex --title "Nightly parser loop"
RUN_ID=$(todos runs begin <task-id> --key nightly:parser:42 --agent codex --title "Nightly parser loop" --apply | jq -r .run.id)
todos findings upsert --task <task-id> --run "$RUN_ID" --fingerprint parser-timeout --title "Parser timeout" --severity high --source nightly-parser --artifact logs/parser-loop.txt --apply
todos findings resolve-missing --task <task-id> --source nightly-parser --fingerprints parser-timeout --run "$RUN_ID"
todos findings resolve-missing --task <task-id> --source nightly-parser --fingerprints parser-timeout --run "$RUN_ID" --apply
todos runs finish "$RUN_ID" --status completed --summary "nightly loop complete"

runs begin is dry-run by default and only creates a run when --apply is provided. Reusing the same --key, --loop-run-id, or --loop-id returns the existing compact run summary instead of creating duplicates. runs finish is idempotent for already-finished runs and can resolve a run by --key --task. Finding upserts are deduped by task and fingerprint, redacted before storage, and expose only artifact paths/references by default. findings upsert and findings resolve-missing are dry-run by default; add --apply to mutate local state. MCP clients use begin_task_run_transaction, finish_task_run, upsert_task_finding, list_task_findings, and resolve_missing_task_findings; these compact loop tools are included in the default TODOS_PROFILE=minimal surface.

Local Time Tracking

Manual time logs and focus sessions stay in the local SQLite database and roll up into task.actual_minutes for planning and retrospectives:

todos time log <task-id> 25 --agent codex --notes "reviewed parser"
SESSION=$(todos time start <task-id> --agent codex --idle-after 30 --json | jq -r .id)
todos time pause "$SESSION"
todos time resume "$SESSION"
todos time stop "$SESSION" --notes "implemented and tested"
todos time report --include-open --json

Focus sessions can be linked to tasks, plans, or run ledgers. Stopping a completed task-linked session writes a time log with the session id and run id, then recalculates actual minutes from all local logs. todos time idle and the get_idle_focus_prompts MCP tool report active sessions that exceeded their local idle threshold; no desktop notification service or hosted telemetry is required.

Local Capacity Forecasts

Capacity profiles give agents a local way to forecast whether a project or plan is realistic from task estimates, actual minutes, due dates, and available minutes per day:

todos capacity set codex --minutes-per-day 240 --days 1,2,3,4,5 --json
todos capacity forecast --plan <plan-id> --agent codex --start-date 2026-06-01 --json
todos capacity forecast --project <project-id> --format markdown
todos capacity list --json

Forecasts report remaining estimated minutes, logged actual minutes, forecast work days, projected completion date, missing estimates, overdue open tasks, and risk flags. MCP clients use set_capacity_profile, list_capacity_profiles, remove_capacity_profile, and get_planning_forecast.

Local Audit Ledger

Audit ledger checkpoints hash local evidence into a deterministic chain so an agent can seal task, run, verification, approval, and handoff records and verify later that the local evidence still matches:

todos audit-ledger show --task <task-id> --entries --json
todos audit-ledger seal release-checkpoint --task <task-id> --json
todos audit-ledger verify release-checkpoint --json
todos audit-ledger list --json

The ledger stores only local checkpoint metadata in config. It does not call a hosted service and it does not claim to prevent local deletion; it detects changes against a previously sealed root hash. MCP clients use get_audit_ledger, seal_audit_ledger, list_audit_ledger_checkpoints, and verify_audit_ledger.

Release Compatibility

Release compatibility checks give agents a local dry-run report before publish or update work. They verify the package stays @hasna/todos, public, pointed at hasna/todos, export-stable, migration-compatible from recent local schema levels, and ready for Bun global install smoke tests:

todos release-compat check --json
todos release-compat check --format markdown

The report also includes changelog surfaces and rollback commands. MCP clients use check_release_compatibility for the same release_compatibility_report JSON contract.

Local Usage Ledger

Usage reports summarize local tasks, projects, runs, commands, durations, agent-provided token and cost metadata, and run artifact storage. Quota flags are simulated locally so agents can check free/pro limits or project budgets without sending data anywhere:

todos usage report --agent codex --max-tasks 1000 --max-projects 10 --json
todos usage report --project <project-id> --format markdown

The report is aggregate-only: raw command strings and artifact paths are not included. MCP clients use get_usage_ledger for the same local_usage_ledger JSON contract.

Local Scale Hardening

Scale reports benchmark common local queries, count archive-ready terminal tasks, check expected SQLite indexes, run integrity diagnostics, and preview database compaction without network access:

todos scale report --older-than-days 30 --json
todos scale report --format markdown
todos scale compact --json

todos scale compact --apply runs PRAGMA optimize and VACUUM against the local SQLite database. The default is a dry run.

Local Activity Timeline

The timeline command gives agents one ordered, redacted view of local comments, task history, run events, command evidence, and artifacts:

todos timeline --task <task-id> --json
todos timeline --project <project-id> --limit 50
todos timeline --run <run-id> --order asc

MCP clients can call get_activity_timeline with entity_type, entity_id, limit, offset, since, and until. Timeline entries are derived from the local SQLite store and local bridge exports already include the underlying comments, runs, run evidence, files, commits, and verification records needed to rebuild the same timeline after import.

Local Scheduling and SLA Escalation

Tasks can carry local due dates, recurrence rules, and SLA thresholds without a hosted scheduler. Recurring tasks spawn their next local task from the previous scheduled due date, preserving cadence even when completion happens late:

todos add "Weekly review" --due 2026-06-01 --recurrence "every week" --sla-minutes 120 --json
todos update <task-id> --due 2026-06-08 --recurrence "every monday" --sla 90 --json
todos overdue --json
todos sla --json
todos notifications check --due-within-minutes 60 --stale-minutes 30 --terminal --json

todos overdue returns unfinished tasks past due_at. todos sla returns unfinished tasks that are past due_at or whose sla_minutes threshold has elapsed from started_at when present, otherwise created_at. MCP clients use create_task and update_task with deadline, recurrence_rule, and sla_minutes, and can call get_sla_breaches for the same local escalation view. todos notifications check turns due, due-soon, SLA, stale task, completed run, and local reminder records into redacted local alerts; it can emit configured file/socket/script/stdout event hooks, evaluate terminal watch rules, and suppress del