From 31d758d4ede1202cfefb48ae101383964bf7c4bf Mon Sep 17 00:00:00 2001 From: Ryan Lewis Date: Thu, 10 Sep 2026 11:11:00 +0100 Subject: [PATCH] docs(projects): document taskCount and openCount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `things projects -j` has reported `taskCount` and `openCount` since the counts were added for the progress icon, but nothing said what they count, so the only documented way to find a project whose work has all landed was one `things list` per project. Document both fields in the three places project JSON is described, and add a "projects with no open to-dos" recipe for the daily reconcile. The semantics were verified against the live database rather than inferred from the column names: across 82 projects the cached counters `untrashedLeafActionsCount` and `openUntrashedLeafActionsCount` match a recount of untrashed type-0 children exactly, including to-dos filed under a heading, which carry `heading` but not `project`. Heading rows, trashed to-dos and checklist items are excluded. Three caveats are documented because each one can mislead a caller acting on the recipe: - `taskCount - openCount` is to-dos that are no longer open, which means completed or cancelled. A project whose to-dos were all cancelled matches the same filter. - The `●` icon marks any completed project under `--completed`, including one with no to-dos at all, so it is not identical to the filter there. - A repeating to-do makes a project unreachable by the filter: Things counts the hidden template row as an open to-do and it never completes, while `things list -p` hides that template, so the two disagree. No behaviour change; no Go code touched. Closes #203 --- docs/content/agents.md | 21 +++++++++++++++++++++ docs/content/commands.md | 26 ++++++++++++++++++++++++++ internal/skill/SKILL.md | 12 +++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/content/agents.md b/docs/content/agents.md index 38ed7dac..9da2fab8 100644 --- a/docs/content/agents.md +++ b/docs/content/agents.md @@ -172,6 +172,12 @@ Every command accepts `-j` / `--json`, and it changes more than the format: encodings a to-do uses, so a scheduled project reads the same way without a per-project `show`. `startDate` and `deadline` are omitted when unset. +- **Projects carry their progress too.** `things projects` reports + `taskCount`, every untrashed to-do in the project, and `openCount`, the + ones still open. The difference is the ones no longer open, which means + completed or cancelled. To-dos under a project heading count towards + both; the heading rows themselves never do, and neither do trashed + to-dos or checklist items. ```console $ things show milk --json; echo "exit=$?" @@ -221,8 +227,23 @@ things upcoming --area Work -j | jq -r '.[] | select(.type==0) | .uuid' | # Bulk create or update in one call via the Things JSON URL scheme. things import --file payload.json + +# Projects whose work has landed, for a reconcile that offers to close them. +things projects -j | jq -r '.[] | select(.openCount == 0 and .taskCount > 0) | .title' ``` +`taskCount > 0` keeps out empty projects, which have nothing done rather +than everything done. It does not tell done from cancelled: a project +whose to-dos were all cancelled matches too, so confirm before offering +to close one. Plain output marks the same projects with a filled `●` +progress icon, so an agent reading plain output is not blind to them — +under `--completed` that icon also marks every completed project, +including empty ones. A project holding a repeating to-do never appears +while the repeat is live: Things counts the hidden template row itself as +an open to-do, and a template never completes. `things list -p ` +hides that template, so it can report no open to-dos for a project whose +`openCount` is 1. + Colour and column alignment are for terminals; they switch off when the output is piped or under `NO_COLOR`, and `--json` is never styled. diff --git a/docs/content/commands.md b/docs/content/commands.md index fe5bf834..3d1a9dd8 100644 --- a/docs/content/commands.md +++ b/docs/content/commands.md @@ -68,6 +68,32 @@ reports that with the same field names and encodings: `start`, `startBucket`, `startDate` and `deadline`. A caller can tell a scheduled project from an anytime one without a `things show` per project. +`things projects -j` also reports two counts per project. `taskCount` is +every untrashed to-do in the project; `openCount` is the ones still open. +The difference is the ones no longer open, which means completed or +cancelled. To-dos filed under a project heading count towards both; the +heading rows themselves never do, and neither do trashed to-dos or +checklist items. Both numbers are Things' own bookkeeping, read straight +from the database rather than recounted by the CLI. + +That makes it one call to find projects whose work has landed but which +are still open: + +```sh +things projects -j | jq '.[] | select(.openCount == 0 and .taskCount > 0)' +``` + +`taskCount > 0` keeps out empty projects, which have nothing done rather +than everything done. It does not tell done from cancelled: a project +whose to-dos were all cancelled matches the same filter. Plain output +marks the same projects with a filled `●` progress icon, and under +`--completed` that icon also marks every completed project, including +empty ones. A project holding a repeating to-do never appears while the +repeat is live: Things counts the hidden template row itself as an open +to-do, and a template never completes. `things list -p ` hides +that template, so it can report no open to-dos for a project whose +`openCount` is 1. + ## Inspecting a task ```sh diff --git a/internal/skill/SKILL.md b/internal/skill/SKILL.md index 04957450..563c8464 100644 --- a/internal/skill/SKILL.md +++ b/internal/skill/SKILL.md @@ -36,6 +36,7 @@ Most commands accept `--json` / `-j`. Prefer it when parsing. It also guarantees - `"repeating": true` marks an item Things treats as repeating; the field is omitted otherwise. Projects also carry `"type": 1`. - `things projects` reports `start`, `startBucket`, `startDate` and `deadline` under the same names and encodings a to-do uses, so a scheduled project reads the same way without a per-project `show`. `startDate` and `deadline` are omitted when unset. - The `today`, `upcoming` and `anytime` views list scheduled projects alongside to-dos, as the app does. Split them on `"type"` — `jq 'select(.type==1)'` for the projects, `select(.type==0)` for the to-dos. Plain output tags a project row `(project)`. +- `things projects` also reports `taskCount` and `openCount`. `taskCount` is every untrashed to-do in the project; `openCount` is the ones still open. The difference is the ones that are no longer open, which means completed or cancelled. To-dos filed under a project heading count towards both; the heading rows themselves never do, and neither do trashed to-dos or checklist items. Both numbers are Things' own bookkeeping, read straight from the database rather than recounted by the CLI. - Human output is styled and column-aligned; colour auto-disables when piping or under `NO_COLOR`. `--color=always|never` overrides. JSON is unaffected. **A failure under `--json` prints one JSON object to stdout and exits non-zero.** Branch on the exit status and read the failure off stdout — not stderr. On success the read commands print their result there and the write commands print nothing — except `tag add`, which reports what it created and what it skipped. `error` is a stable token; `message` is the human text. @@ -164,7 +165,8 @@ things list [view] [--project P] [--area A] [--tag T] [--on D | --from D --to D] things show [--agent] # detail; --agent prints a Markdown brief (see below) things projects [-a|--area A] [--completed] - # carries start/startBucket/startDate/deadline like a to-do + # carries start/startBucket/startDate/deadline like a to-do, + # plus taskCount/openCount in JSON things areas things tags things search # titles and notes; a lookup, not a view @@ -251,6 +253,14 @@ things import <<'JSON' JSON ``` +Find projects with no open to-dos — the work has landed but the project is still open, so a reconcile can offer to close it: + +``` +things projects -j | jq -r '.[] | select(.openCount == 0 and .taskCount > 0) | "\(.uuid)\t\(.title)"' +``` + +`taskCount > 0` keeps out empty projects, which have nothing done rather than everything done. It does not tell done from cancelled — a project whose to-dos were all cancelled matches too — so confirm before offering to close one. Plain output marks the same projects with a filled `●` progress icon, and under `--completed` that icon also marks every completed project, empty ones included. A project holding a repeating to-do never shows up while the repeat is live: Things counts the hidden template row itself as an open to-do, and a template never completes. Note that `things list -p ` hides that template, so it can show no open to-dos for a project whose `openCount` is 1. + ## Shell completions `things completions ` prints a completion script that delegates back to the binary (which must be on `PATH`), so it stays in sync with the CLI. The Homebrew cask generates these on install; otherwise the user loads it with `source <(things completions zsh)` (bash/zsh) or `things completions fish | source`. Completion is flag and subcommand names only — it never reads the Things database.