things-api 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -5
- package/dist/audit/schema.d.ts +7 -0
- package/dist/audit/schema.js.map +1 -1
- package/dist/cli/commands/area.d.ts +18 -11
- package/dist/cli/commands/area.js +55 -67
- package/dist/cli/commands/area.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +1 -2
- package/dist/cli/commands/doctor.js +11 -1
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/install-skill.d.ts +19 -4
- package/dist/cli/commands/install-skill.js +101 -52
- package/dist/cli/commands/install-skill.js.map +1 -1
- package/dist/cli/commands/mcp.js +9 -0
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/project.d.ts +1 -0
- package/dist/cli/commands/project.js +20 -28
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/reads.js +100 -28
- package/dist/cli/commands/reads.js.map +1 -1
- package/dist/cli/commands/show.js +32 -8
- package/dist/cli/commands/show.js.map +1 -1
- package/dist/cli/commands/todo.js +1 -1
- package/dist/cli/commands/todo.js.map +1 -1
- package/dist/cli/commands/writes.js +769 -143
- package/dist/cli/commands/writes.js.map +1 -1
- package/dist/cli/did-you-mean.d.ts +14 -3
- package/dist/cli/did-you-mean.js +26 -3
- package/dist/cli/did-you-mean.js.map +1 -1
- package/dist/cli/glyphs.d.ts +16 -2
- package/dist/cli/glyphs.js +60 -12
- package/dist/cli/glyphs.js.map +1 -1
- package/dist/cli/help.js +67 -12
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/main.js +1 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/move-hint.d.ts +3 -2
- package/dist/cli/move-hint.js +2 -2
- package/dist/cli/move-hint.js.map +1 -1
- package/dist/cli/read-driver.d.ts +43 -5
- package/dist/cli/read-driver.js +81 -15
- package/dist/cli/read-driver.js.map +1 -1
- package/dist/cli/render.d.ts +19 -8
- package/dist/cli/render.js +59 -25
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/resolve-invocation.d.ts +2 -2
- package/dist/cli/resolve-invocation.js +3 -3
- package/dist/cli/resolve-invocation.js.map +1 -1
- package/dist/cli/skill-check.d.ts +12 -6
- package/dist/cli/skill-check.js +31 -14
- package/dist/cli/skill-check.js.map +1 -1
- package/dist/cli/skill.d.ts +8 -0
- package/dist/cli/skill.js +10 -0
- package/dist/cli/skill.js.map +1 -1
- package/dist/cli/verb-hint.js +1 -1
- package/dist/cli/verb-hint.js.map +1 -1
- package/dist/client.d.ts +140 -19
- package/dist/client.js +253 -38
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +66 -2
- package/dist/config.js +120 -12
- package/dist/config.js.map +1 -1
- package/dist/contracts.d.ts +176 -28
- package/dist/contracts.js +22 -1
- package/dist/contracts.js.map +1 -1
- package/dist/diagnose.d.ts +34 -2
- package/dist/diagnose.js +36 -1
- package/dist/diagnose.js.map +1 -1
- package/dist/index.d.ts +19 -8
- package/dist/index.js +19 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +9 -0
- package/dist/mcp/server.js +733 -304
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/entities.d.ts +46 -3
- package/dist/model/entities.js.map +1 -1
- package/dist/model/mappers.d.ts +2 -0
- package/dist/model/mappers.js +41 -2
- package/dist/model/mappers.js.map +1 -1
- package/dist/model/recurrence.d.ts +8 -1
- package/dist/model/recurrence.js.map +1 -1
- package/dist/read/area-filter.d.ts +76 -0
- package/dist/read/area-filter.js +59 -0
- package/dist/read/area-filter.js.map +1 -0
- package/dist/read/area-view.d.ts +11 -11
- package/dist/read/area-view.js +50 -28
- package/dist/read/area-view.js.map +1 -1
- package/dist/read/detail.js +15 -8
- package/dist/read/detail.js.map +1 -1
- package/dist/read/predicates.d.ts +18 -0
- package/dist/read/predicates.js +19 -0
- package/dist/read/predicates.js.map +1 -1
- package/dist/read/project-view.d.ts +31 -15
- package/dist/read/project-view.js +72 -39
- package/dist/read/project-view.js.map +1 -1
- package/dist/read/pseudo-area.d.ts +29 -0
- package/dist/read/pseudo-area.js +27 -0
- package/dist/read/pseudo-area.js.map +1 -0
- package/dist/read/queries.d.ts +105 -18
- package/dist/read/queries.js +185 -30
- package/dist/read/queries.js.map +1 -1
- package/dist/read/scope.d.ts +126 -0
- package/dist/read/scope.js +162 -0
- package/dist/read/scope.js.map +1 -0
- package/dist/read/search-rank.d.ts +2 -5
- package/dist/read/shape.d.ts +162 -0
- package/dist/read/shape.js +686 -0
- package/dist/read/shape.js.map +1 -0
- package/dist/read/show-target.d.ts +9 -1
- package/dist/read/show-target.js +45 -4
- package/dist/read/show-target.js.map +1 -1
- package/dist/read/stage.d.ts +199 -0
- package/dist/read/stage.js +125 -0
- package/dist/read/stage.js.map +1 -0
- package/dist/read/truncation.d.ts +7 -5
- package/dist/read/truncation.js +33 -8
- package/dist/read/truncation.js.map +1 -1
- package/dist/read/views.d.ts +49 -9
- package/dist/read/views.js +193 -40
- package/dist/read/views.js.map +1 -1
- package/dist/surface-copy.d.ts +9 -0
- package/dist/surface-copy.js +9 -0
- package/dist/surface-copy.js.map +1 -1
- package/dist/write/batch.d.ts +49 -10
- package/dist/write/batch.js +423 -71
- package/dist/write/batch.js.map +1 -1
- package/dist/write/commands.js +266 -55
- package/dist/write/commands.js.map +1 -1
- package/dist/write/guards.d.ts +1 -1
- package/dist/write/guards.js +71 -10
- package/dist/write/guards.js.map +1 -1
- package/dist/write/heading.d.ts +10 -1
- package/dist/write/heading.js +35 -5
- package/dist/write/heading.js.map +1 -1
- package/dist/write/make-repeating-project.d.ts +2 -2
- package/dist/write/make-repeating-project.js +9 -9
- package/dist/write/make-repeating-project.js.map +1 -1
- package/dist/write/move.d.ts +130 -0
- package/dist/write/move.js +1533 -0
- package/dist/write/move.js.map +1 -0
- package/dist/write/operations.d.ts +105 -19
- package/dist/write/operations.js +56 -7
- package/dist/write/operations.js.map +1 -1
- package/dist/write/opid.d.ts +31 -0
- package/dist/write/opid.js +30 -0
- package/dist/write/opid.js.map +1 -0
- package/dist/write/pipeline.d.ts +70 -3
- package/dist/write/pipeline.js +198 -33
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/pre-state.d.ts +162 -8
- package/dist/write/pre-state.js +385 -20
- package/dist/write/pre-state.js.map +1 -1
- package/dist/write/reorder.d.ts +23 -3
- package/dist/write/reorder.js +1871 -85
- package/dist/write/reorder.js.map +1 -1
- package/dist/write/reversibility.js +19 -7
- package/dist/write/reversibility.js.map +1 -1
- package/dist/write/scope-guard.d.ts +31 -0
- package/dist/write/scope-guard.js +162 -0
- package/dist/write/scope-guard.js.map +1 -0
- package/dist/write/undo.js +120 -13
- package/dist/write/undo.js.map +1 -1
- package/dist/write/vectors/applescript.js +16 -7
- package/dist/write/vectors/applescript.js.map +1 -1
- package/dist/write/vectors/shortcuts.js +1 -1
- package/dist/write/vectors/shortcuts.js.map +1 -1
- package/dist/write/vectors/simulator.js +83 -27
- package/dist/write/vectors/simulator.js.map +1 -1
- package/dist/write/vectors/types.d.ts +11 -0
- package/dist/write/vectors/ui-certification.d.ts +3 -3
- package/dist/write/vectors/ui-certification.js +41 -5
- package/dist/write/vectors/ui-certification.js.map +1 -1
- package/dist/write/vectors/ui-drag.d.ts +21 -1
- package/dist/write/vectors/ui-drag.js +138 -27
- package/dist/write/vectors/ui-drag.js.map +1 -1
- package/dist/write/vectors/ui-recipes.d.ts +21 -1
- package/dist/write/vectors/ui-recipes.js +166 -7
- package/dist/write/vectors/ui-recipes.js.map +1 -1
- package/dist/write/vectors/ui.d.ts +25 -1
- package/dist/write/vectors/ui.js +142 -33
- package/dist/write/vectors/ui.js.map +1 -1
- package/dist/write/verify/delta.d.ts +17 -1
- package/dist/write/verify/delta.js +106 -29
- package/dist/write/verify/delta.js.map +1 -1
- package/package.json +7 -1
- package/schema/envelope.schema.json +383 -0
- package/skills/things-cli/SKILL.md +34 -17
- package/skills/things-cli/references/banner.md +35 -0
- package/skills/things-cli/references/contracts.md +30 -9
- package/skills/things-cli/references/errors.md +49 -0
- package/skills/things-cli/references/gui.md +1 -1
- package/skills/things-cli/references/model.md +15 -6
- package/skills/things-cli/references/ordering.md +71 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# The error contract (deep reference)
|
|
2
|
+
|
|
3
|
+
A failed `--json` call carries `ok: false`, `kind: "error"`, and an `error` object with a machine-readable `code`, a human `message`, an optional `likelyCause` and `remediation`, and at most one structured `detail`. The exit code mirrors the family (see [contracts.md](contracts.md)). A nonzero exit is informative, not a dead end — the change did not silently half-apply, and the message usually names the fix.
|
|
4
|
+
|
|
5
|
+
## Candidates — self-correcting from an ambiguous or missing ref
|
|
6
|
+
|
|
7
|
+
When a name or partial-uuid resolves to more than one row, or to none, `error.detail.candidates` offers a disambiguation list so you self-correct without another round-trip. Each entry is ONE fixed, slim shape:
|
|
8
|
+
|
|
9
|
+
- `uuid`, `title`, and `type` (`"to-do" | "project" | "heading" | "area" | "tag"`) — always.
|
|
10
|
+
- A container hint `area` and/or `project` as a TITLE string — only when set.
|
|
11
|
+
- `stage` / `when` (the same derivations as the item wire) — only for a to-do/project candidate whose row carries them. A trashed/logged candidate needs no boolean: `stage` already reads `"trash"` / `"logbook"`.
|
|
12
|
+
- NOTHING else — no notes, counts, dates, or raw fields.
|
|
13
|
+
|
|
14
|
+
The shape is **flag-invariant**: `--full` / `--all` never widen it (an error payload is the most determinism-critical surface). The list is **capped at 8**; when more matched, the `message` states the total (e.g. "matches 12 projects … 4 more"). `ambiguous` carries the candidate list; `not-found` carries `candidates: []`.
|
|
15
|
+
|
|
16
|
+
**Live-scoped pool + dead-row hints.** By default candidates are LIVE rows only — a trashed or logged row never appears as a "did you mean". A trash/logbook-domain op (e.g. `project restore`) widens its own pool to that domain. When a name matches ZERO live rows but a DEAD row exists, the `message` gains an honest tail ("1 trashed item matches this name — see `things trash`", or the logbook equivalent) and `candidates` stays `[]` — no dangling-ref invitation. A write whose only name-match is a completed/logged project resolves to nothing by name (target it by uuid if intended); this prevents stranding an open child inside an invisible done project.
|
|
17
|
+
|
|
18
|
+
Never guess between candidates for a destructive action — inspect details or ask.
|
|
19
|
+
|
|
20
|
+
## Guarded writes and their acknowledgments
|
|
21
|
+
|
|
22
|
+
Some writes have a cascading or permanent effect. Each is refused BEFORE touching the app until you pass the flag that names the consequence; a `--dry-run` shows the same refusal. The CLI describes each by its consequence, but the JSON error's `blocked:<hazard>` code is the machine-readable handle:
|
|
23
|
+
|
|
24
|
+
- **Delete a NON-EMPTY area** → `--allow-non-empty`. Deleting an area sends its to-dos AND its projects (with their children) to the Trash and destroys the area row permanently. A delete pre-counts live members and refuses with the counts ("the area is not empty — it contains 3 projects and 12 to-dos"), naming both remediations: empty the area first, or pass `--allow-non-empty`. An empty area is unaffected (still just `--dangerously-permanent`). Code: `blocked:H-AREA-NOT-EMPTY`.
|
|
25
|
+
- **Permanent delete** (an area; emptying the Trash) → `--dangerously-permanent`. There is no inverse; `undo` names the manual one rather than pretending. Code: `blocked:H-PERMANENT-DELETE`.
|
|
26
|
+
- **Drive the live UI** (the ops the app offers nowhere else — e.g. `area reorder`, and some heading/repeat ops) → the two-key gate: the `--dangerously-drive-gui` flag AND `things config set ui-enabled true` (plus Accessibility granted to the process). These visibly drive the Things window. Code: `blocked:H-UI-DRIVE`. (Separately, the `--allow-disruptive` / `--allow-very-disruptive` flags raise the process's disruption ceiling for any op that steals focus or drives the UI.)
|
|
27
|
+
- **Reopen or reuse a resolved project** — moving or adding an OPEN child into a completed/canceled project reopens it via the app; that is acknowledged (`blocked:H-REOPEN-RESOLVED-PROJECT`) so it is never a silent side effect.
|
|
28
|
+
- Other guards name their own consequence the same way (heading cascades, backdating an open item, checklist replacement, repeat scheduling, tag-subtree delete, …).
|
|
29
|
+
|
|
30
|
+
`things capabilities` lists every operation's support and preconditions; `--dry-run` previews any plan. If a request needs a capability the tool reports as unsupported, say so plainly rather than improvising through unrelated commands.
|
|
31
|
+
|
|
32
|
+
## The error-code families
|
|
33
|
+
|
|
34
|
+
Every `error.code` is drawn from a frozen registry. Route on the code, and for the two template families route on the prefix:
|
|
35
|
+
|
|
36
|
+
| Family | Meaning | Exit |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `usage` | Bad flags/arguments, mutually-exclusive flags, an unparseable date, or a move that meant to schedule. | 2 |
|
|
39
|
+
| `not-found` | A ref or subject resolved to nothing (`candidates: []`). | 2 |
|
|
40
|
+
| `ambiguous` | A name/partial-uuid matched several rows (carries `candidates`). | 2 |
|
|
41
|
+
| `unsupported` | No available write vector supports the operation (`detail.considered`). | 6 |
|
|
42
|
+
| `environment` | Database not found, Things not installed, or a permission problem. | 7 |
|
|
43
|
+
| `unexpected` | An internal error (a bug) — stop and report. | 1 |
|
|
44
|
+
| `verify-failed:<reason>` | A single mutation executed but the read-back check failed. `<reason>` ∈ `timeout | mismatch | silent-noop`. | 3 |
|
|
45
|
+
| `blocked:<suffix>` | Refused before dispatch — a hazard id (above) or a reason (`disruption-tier`, `lock`, `scope`, `clock`, `environment`, `drift`). `blocked:drift` is exit 5; all other `blocked:` are exit 4. | 4 / 5 |
|
|
46
|
+
| `bounce-aborted` | A reorder bounce aborted part-way (`detail.placed`/`remaining`/`cause`). | 3 |
|
|
47
|
+
| `verify-failed` | A multi-leg move/reorder failed mid-way (`detail.failed`/`completed`). | 3 |
|
|
48
|
+
|
|
49
|
+
A consumer that does not recognize a specific suffix routes on the prefix (`blocked:` / `verify-failed:`) and the exit code.
|
|
@@ -5,7 +5,7 @@ Curated rendering facts — the bench loop may compress or relocate these but mu
|
|
|
5
5
|
<!-- STATUS: v0 seeded from Mike's description 2026-07-17; awaiting his review pass. -->
|
|
6
6
|
|
|
7
7
|
- **Sidebar**: top-level views (Inbox, Today, Upcoming, Anytime, Someday, Logbook, Trash), then each **area** with its active **projects** nested beneath it — in the same order this CLI/API reports them. Reordering areas or projects changes what the user sees there.
|
|
8
|
-
- **List rows are compact**: a to-do in any list shows its title, tags, and
|
|
8
|
+
- **List rows are compact**: a to-do in any list shows its checkbox/status, title, tags, deadline, and small marker chips — the Today ★, This Evening ⏾, and (in the Today view only) the provisional `•` "new item" pip, plus has-notes / reminder / checklist glyphs (`things legend` names them all). The **notes TEXT and checklist CONTENTS are invisible until the user opens the item**. Put must-see-at-a-glance information in the title; supporting detail belongs in notes. Projects carry the same Today ★ / This Evening ⏾ pips as to-dos. Why some chips vanish once a row's date goes stale — the reminder bell, the This-Evening section, and the provisional pip — is in [banner.md](banner.md).
|
|
9
9
|
- **Project notes** are likewise visible only when the project itself is opened in project view — in lists, a project is just its title (and progress ring).
|
|
10
10
|
- **Today** shows the day's scheduled items, with **This Evening** as a separate section beneath. **Upcoming** is a forward-looking date-ordered calendar of scheduled items. **Logbook** is where completed/canceled items go — after completing something for the user, that's where they'll find it.
|
|
11
11
|
- When telling the user where something landed, name the container and view ("in project P under area A"; "it'll show in Today this evening").
|
|
@@ -37,14 +37,23 @@ Entities, relationships, and how the sidebar views are computed over them, as ex
|
|
|
37
37
|
|
|
38
38
|
`things projects`/`areas`/`tags` list containers; `things projects <ref>` / `things areas <ref>` / `things show <ref>` show one item's full detail — notes, checklist, effective tags — which the compact list rows do NOT display.
|
|
39
39
|
|
|
40
|
-
### Reading view membership from JSON
|
|
40
|
+
### Reading view membership from JSON — `stage` and `when`
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
Reads decompose an item's position onto two derived, presence-keyed words (they REPLACED the old `start`/`startDate`/`logged`/`trashed`/`todaySection` wire fields, which no longer appear):
|
|
43
43
|
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
44
|
+
- **`stage`** — the sidebar BUCKET: `inbox | upcoming | anytime | someday | logbook | trash`. Read view membership off it directly. It is dropped inside a section/catalogue that already states it (the stage-pure `inbox`/`anytime`/`someday`/`logbook`/`trash` lists and the `today` view's sections) and kept everywhere it is not implied (the mixed `upcoming` catalogue, `search`, `changes`, the projects/areas listings, and `detail`).
|
|
45
|
+
- **`when`** — the TIME POSITION: `today | evening | a future ISO date`, or absent (unscheduled and not in Today). `evening` implies today. Someday is a bucket, never a `when`.
|
|
46
|
+
- The two are DIFFERENT facts. A due deadline pulls an UNDATED row into Today: it reads `when: "today"` and derives `stage: "anytime"` — the app re-files a deadline-pulled Inbox/Someday row into Anytime at pull time (R13/BANNER1b), so it drops out of the Inbox/Someday lists and joins Anytime while its `when` reads `today`. Completed/canceled → `stage: "logbook"`, trashed → `stage: "trash"`, regardless of any other hint.
|
|
47
|
+
- `provisional: true` marks a Today member the app has not yet materialized — see [banner.md](banner.md).
|
|
48
|
+
|
|
49
|
+
## Tiers — compact vs full (absence is meaningful)
|
|
50
|
+
|
|
51
|
+
Every row comes back at one of two densities, selected by view kind + flag, never by a caller-supplied field list:
|
|
52
|
+
|
|
53
|
+
- **Compact** (the list default) keeps identity + structural + non-default facts. A field at its default is OMITTED, so absence = the default: no `status` = open, no `checklist` = none, no `todos` = no child to-dos, no `when` = not in Today and unscheduled, no `provisional` = materialized. The full `notes` string is dropped and replaced by presence-keyed `hasNotes: true`; `startDate`, `created`, and `modified` are dropped.
|
|
54
|
+
- **Full** (`show`/`detail`, or a list forced with `--full`) is the whole record — the complete `notes`, the raw `startDate` substrate behind `when`, the checklist `items` array, and timestamps.
|
|
55
|
+
- Compact rows still carry the useful summaries: `checklist:{open,total}` on a to-do, `todos:{open,total}` on a project (app-maintained leaf-action counts — never headings, checklist items, or trashed rows), and `match:{field,text}` on a `search` hit whose match was NOT the title (`field` ∈ `heading | notes | checklist`).
|
|
56
|
+
- **Container absence rule:** inside a single-container node (a project/area card, an `anytime`/`someday` section, a heading group) an item omits any ancestry the node already states, so absent `project`/`area`/`heading` there means *inherited from the enclosing node*. A mixed list (`inbox`/`today`/`search`/`changes`) still names each row's own `project`/`area` (the `heading` ref is compact-dropped outside a project view; `--full` keeps it).
|
|
48
57
|
|
|
49
58
|
## Filters over views
|
|
50
59
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Move and reorder (deep reference)
|
|
2
|
+
|
|
3
|
+
Two verbs, kept strictly apart:
|
|
4
|
+
|
|
5
|
+
- **move** changes WHAT an item belongs to — a to-do's project/area/heading, a project's area.
|
|
6
|
+
- **reorder** changes only the ARRANGEMENT of items already sharing a container and bucket — never membership. Unmentioned siblings keep their order.
|
|
7
|
+
|
|
8
|
+
The SKILL.md summary is enough for most calls; this page is the full contract. Exact flags always come from `things todo move --help`, `things project move --help`, and `things reorder --help` (current for the binary you invoke).
|
|
9
|
+
|
|
10
|
+
## Move — changing membership
|
|
11
|
+
|
|
12
|
+
**To-dos** (`things todo move <refs…> [destination] [position]`, one destination):
|
|
13
|
+
|
|
14
|
+
- `--to-project <ref>` · `--to-heading <sel>` · `--to-area <ref>` — place into a container.
|
|
15
|
+
- Detach family: `--no-heading` (leave the heading, stay in the project) · `--loose` (leave heading, project, AND area) · `--inbox` (back to the Inbox — this also DROPS the schedule).
|
|
16
|
+
- There is no `--detach` (removed) and no `--no-area` on a to-do — a to-do's area is inherited, so use `--loose`.
|
|
17
|
+
|
|
18
|
+
**Projects** (`things project move <refs…> [--to-area <ref> | --no-area] [position]`): `--no-area` is a project's detach (the to-do word `--loose` is refused on a project).
|
|
19
|
+
|
|
20
|
+
**`--to-heading` scoping.** A heading selector (exact title or uuid) resolves within the movees' shared project; when the movees are not already all in that project, name it with `--to-project`. A heading belongs to one project and cannot hold projects.
|
|
21
|
+
|
|
22
|
+
## Reorder — changing arrangement
|
|
23
|
+
|
|
24
|
+
`things reorder <refs…> [--first | --last | --before <ref> | --after <ref>] [--in <target>]` is the ONE kind-neutral in-place verb (it replaced the old `todo reorder` subcommand and the raw `reorder --scope`). It rearranges to-dos AND the project rows the Today/Evening/day lists intermix with them.
|
|
25
|
+
|
|
26
|
+
- **Bare (no position)**: the named items assemble as ONE block at the EARLIEST one's current slot, in argument order (`--first` is NOT implied). Partial selection is fine — unmentioned siblings keep their order.
|
|
27
|
+
- **Positioned**: `--first`/`--last` send the block to the top/bottom of its bucket; `--before <ref>`/`--after <ref>` place it relative to an anchor.
|
|
28
|
+
- **Selection order = landing order** (reverse by naming the refs backwards).
|
|
29
|
+
- **Mixed to-do + project refs are allowed** in the Today/Evening/day buckets those kinds share; elsewhere reorder operates within one container+bucket.
|
|
30
|
+
|
|
31
|
+
Sibling verbs for the two things `reorder` does NOT touch: `things project move-heading` reorders a project's HEADINGS; `things area reorder` reorders the sidebar AREAS.
|
|
32
|
+
|
|
33
|
+
## Anchors POSITION, never MIGRATE
|
|
34
|
+
|
|
35
|
+
A `--before`/`--after` anchor only says WHERE in a bucket — it never moves the anchor and never reschedules a movee to reach it. The anchor must already sit in the movees' container+bucket; a cross-container or cross-bucket anchor **fails closed** with a message naming where the rows and the anchor actually are (no silent rescheduling). For a global-axis bucket (today/evening/tomorrow/a future day-group) the anchor need only share the movees' day-group, not their structural container — the app permits exactly that drag.
|
|
36
|
+
|
|
37
|
+
## The dual axis and `--in` (fail-closed)
|
|
38
|
+
|
|
39
|
+
A Today/Evening member has TWO order slots: its slot in the Today VIEW (`todayIndex`) and its slot in its own CONTAINER (`index` — a project/area/heading child, or the loose Anytime bucket). When a set (and its anchor) is coherent on BOTH axes and no `--in` is given, the reorder is REFUSED with a message naming both readings and their exact `--in` spellings — this replaces any silent always-Today guess.
|
|
40
|
+
|
|
41
|
+
- `--in` accepts `today | evening | anytime | someday | inbox`, or a project/area/heading ref (uuid or unique title). `loose` is refused (it is a read view, not a bucket).
|
|
42
|
+
- Forcing the container index axis on a Today/Evening member PRESERVES the Today/Evening flag — a flag-safe move protocol routes it off the de-Today path. Only the someday/inbox loose axes still refuse a flagged member (their re-entry cannot preserve the flag).
|
|
43
|
+
|
|
44
|
+
## Mixed-stage move placement
|
|
45
|
+
|
|
46
|
+
A `move` selection spanning stage sub-buckets (anytime + scheduled + someday + templates):
|
|
47
|
+
|
|
48
|
+
- `--before`/`--after` is REFUSED unless every movee shares the anchor's sub-bucket (remediation: split the call, or drop the anchor).
|
|
49
|
+
- `--first`/`--last` apply PER sub-bucket — each stage-group lands at the top/bottom of ITS matching bucket in the destination, and the result note states every group's placement outcome.
|
|
50
|
+
|
|
51
|
+
## Gates, caps, and automatic fallbacks
|
|
52
|
+
|
|
53
|
+
Three config knobs (`things config get/set …`) tune ordering; every default gives full sortability:
|
|
54
|
+
|
|
55
|
+
- **`allow-experimental`** (default `true`) — enables the private NATIVE re-rank command for the scopes only it can reach directly (`inbox`, `someday`, a project's unheaded children, an area's members, a container's same-day children, `tomorrow`). It is the off-switch, not an opt-in.
|
|
56
|
+
- **`bounce-enabled`** (default `true`) — permits the verified `when=`/move round-trip protocols the other scopes use. `false` REFUSES a bounce-dependent placement rather than degrading destructively.
|
|
57
|
+
- **`bounce-max-items`** (default `30`) — caps how many items one bounce may touch; a set larger than the cap is refused, not truncated.
|
|
58
|
+
|
|
59
|
+
**Automatic non-experimental fallbacks (SIT7).** When `allow-experimental` is off (or the native surface is unavailable), the native-only scopes DO NOT fail — each degrades to a proven, verified, flag-safe move protocol (park-and-re-home for `inbox`/`project`/`area`, a `when=` bounce for `someday` and day-groups). Collateral is preserved (Today/Evening flag, live reminder, deadline, container FKs). The result's `warnings` note discloses which fallback ran (e.g. "reordered via the non-experimental PROJROOT fallback because the native reorder is unavailable") — a native placement is never silently mistaken for a degraded one.
|
|
60
|
+
|
|
61
|
+
**Flag-aware routing (SIT6).** A reorder touching a Today/Evening-FLAGGED row never de-Todays it: the whole touched set swaps to a flag-safe MOVE protocol on the same axis (the `when=` bounce would strip the flag). This is transparent — you still call `things reorder`; the chosen strategy is disclosed in the result.
|
|
62
|
+
|
|
63
|
+
## Placement guarantees and the one dead class
|
|
64
|
+
|
|
65
|
+
"Top of bucket in selection order" is GUARANTEED wherever a lab-clean protocol exists: loose inbox/today/evening/someday/anytime; a project's or area's members (anytime AND someday); a heading's anytime/someday children; any container child's evening slot; area-less someday/anytime projects; and a whole future day-group across containers (including scheduled project rows, area'd ones too). The result's placement class names which guarantee you got, and a bounce that co-touches unnamed siblings to honor a `--before`/`--after` anchor lists them.
|
|
66
|
+
|
|
67
|
+
**The one class that cannot be reordered is a repeating TEMPLATE** — a dated `when=` leg crashes it, so a template movee or anchor is refused and template placement stays app-default (disclosed). Everything non-template is sortable.
|
|
68
|
+
|
|
69
|
+
## MCP parity
|
|
70
|
+
|
|
71
|
+
The MCP server exposes reorder as two tools: **`reorder`** (the planner form — `refs`, an optional position, an optional `in` axis) mirrors `things reorder`, and **`reorder_areas`** mirrors `things area reorder` (sidebar areas, with the same two-key GUI gate). Both call the same library entries the CLI does, so the dual-axis refusal, the flag-safe routing, and the automatic fallbacks behave identically.
|