things-api 0.11.0 → 0.13.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.
Files changed (208) hide show
  1. package/README.md +16 -5
  2. package/dist/audit/schema.d.ts +7 -0
  3. package/dist/audit/schema.js.map +1 -1
  4. package/dist/cli/canonical-ref.d.ts +21 -0
  5. package/dist/cli/canonical-ref.js +23 -0
  6. package/dist/cli/canonical-ref.js.map +1 -0
  7. package/dist/cli/commands/area.d.ts +18 -11
  8. package/dist/cli/commands/area.js +81 -77
  9. package/dist/cli/commands/area.js.map +1 -1
  10. package/dist/cli/commands/doctor.d.ts +1 -2
  11. package/dist/cli/commands/doctor.js +11 -1
  12. package/dist/cli/commands/doctor.js.map +1 -1
  13. package/dist/cli/commands/install-skill.d.ts +19 -4
  14. package/dist/cli/commands/install-skill.js +130 -52
  15. package/dist/cli/commands/install-skill.js.map +1 -1
  16. package/dist/cli/commands/mcp.js +21 -1
  17. package/dist/cli/commands/mcp.js.map +1 -1
  18. package/dist/cli/commands/project.d.ts +1 -0
  19. package/dist/cli/commands/project.js +92 -48
  20. package/dist/cli/commands/project.js.map +1 -1
  21. package/dist/cli/commands/reads.d.ts +5 -1
  22. package/dist/cli/commands/reads.js +110 -31
  23. package/dist/cli/commands/reads.js.map +1 -1
  24. package/dist/cli/commands/setup.js +11 -4
  25. package/dist/cli/commands/setup.js.map +1 -1
  26. package/dist/cli/commands/show.js +75 -36
  27. package/dist/cli/commands/show.js.map +1 -1
  28. package/dist/cli/commands/todo.js +2 -2
  29. package/dist/cli/commands/todo.js.map +1 -1
  30. package/dist/cli/commands/writes.js +795 -144
  31. package/dist/cli/commands/writes.js.map +1 -1
  32. package/dist/cli/did-you-mean.d.ts +14 -3
  33. package/dist/cli/did-you-mean.js +44 -7
  34. package/dist/cli/did-you-mean.js.map +1 -1
  35. package/dist/cli/dry-run.d.ts +55 -0
  36. package/dist/cli/dry-run.js +92 -0
  37. package/dist/cli/dry-run.js.map +1 -0
  38. package/dist/cli/glyphs.d.ts +16 -2
  39. package/dist/cli/glyphs.js +60 -12
  40. package/dist/cli/glyphs.js.map +1 -1
  41. package/dist/cli/help.js +69 -13
  42. package/dist/cli/help.js.map +1 -1
  43. package/dist/cli/main.js +8 -1
  44. package/dist/cli/main.js.map +1 -1
  45. package/dist/cli/move-hint.d.ts +3 -2
  46. package/dist/cli/move-hint.js +2 -2
  47. package/dist/cli/move-hint.js.map +1 -1
  48. package/dist/cli/read-driver.d.ts +53 -5
  49. package/dist/cli/read-driver.js +82 -15
  50. package/dist/cli/read-driver.js.map +1 -1
  51. package/dist/cli/ref-render.d.ts +14 -0
  52. package/dist/cli/ref-render.js +9 -0
  53. package/dist/cli/ref-render.js.map +1 -0
  54. package/dist/cli/render.d.ts +40 -14
  55. package/dist/cli/render.js +128 -55
  56. package/dist/cli/render.js.map +1 -1
  57. package/dist/cli/resolve-invocation.d.ts +7 -5
  58. package/dist/cli/resolve-invocation.js +16 -7
  59. package/dist/cli/resolve-invocation.js.map +1 -1
  60. package/dist/cli/skill-check.d.ts +12 -6
  61. package/dist/cli/skill-check.js +31 -14
  62. package/dist/cli/skill-check.js.map +1 -1
  63. package/dist/cli/skill.d.ts +8 -0
  64. package/dist/cli/skill.js +10 -0
  65. package/dist/cli/skill.js.map +1 -1
  66. package/dist/cli/verb-hint.js +1 -1
  67. package/dist/cli/verb-hint.js.map +1 -1
  68. package/dist/client.d.ts +158 -26
  69. package/dist/client.js +254 -38
  70. package/dist/client.js.map +1 -1
  71. package/dist/config.d.ts +66 -2
  72. package/dist/config.js +120 -12
  73. package/dist/config.js.map +1 -1
  74. package/dist/contracts.d.ts +187 -28
  75. package/dist/contracts.js +22 -1
  76. package/dist/contracts.js.map +1 -1
  77. package/dist/db/baselines/db-v26.js +4 -1
  78. package/dist/db/baselines/db-v26.js.map +1 -1
  79. package/dist/diagnose.d.ts +34 -2
  80. package/dist/diagnose.js +36 -1
  81. package/dist/diagnose.js.map +1 -1
  82. package/dist/index.d.ts +21 -8
  83. package/dist/index.js +26 -3
  84. package/dist/index.js.map +1 -1
  85. package/dist/mcp/server.d.ts +9 -0
  86. package/dist/mcp/server.js +790 -322
  87. package/dist/mcp/server.js.map +1 -1
  88. package/dist/model/entities.d.ts +57 -4
  89. package/dist/model/entities.js.map +1 -1
  90. package/dist/model/mappers.d.ts +2 -0
  91. package/dist/model/mappers.js +44 -2
  92. package/dist/model/mappers.js.map +1 -1
  93. package/dist/model/recurrence.d.ts +8 -1
  94. package/dist/model/recurrence.js.map +1 -1
  95. package/dist/model/serialize.js +15 -3
  96. package/dist/model/serialize.js.map +1 -1
  97. package/dist/read/area-filter.d.ts +76 -0
  98. package/dist/read/area-filter.js +58 -0
  99. package/dist/read/area-filter.js.map +1 -0
  100. package/dist/read/area-view.d.ts +22 -11
  101. package/dist/read/area-view.js +54 -28
  102. package/dist/read/area-view.js.map +1 -1
  103. package/dist/read/detail.js +15 -8
  104. package/dist/read/detail.js.map +1 -1
  105. package/dist/read/predicates.d.ts +18 -0
  106. package/dist/read/predicates.js +19 -0
  107. package/dist/read/predicates.js.map +1 -1
  108. package/dist/read/project-view.d.ts +91 -15
  109. package/dist/read/project-view.js +135 -37
  110. package/dist/read/project-view.js.map +1 -1
  111. package/dist/read/pseudo-area.d.ts +29 -0
  112. package/dist/read/pseudo-area.js +27 -0
  113. package/dist/read/pseudo-area.js.map +1 -0
  114. package/dist/read/queries.d.ts +175 -19
  115. package/dist/read/queries.js +376 -32
  116. package/dist/read/queries.js.map +1 -1
  117. package/dist/read/scope.d.ts +126 -0
  118. package/dist/read/scope.js +161 -0
  119. package/dist/read/scope.js.map +1 -0
  120. package/dist/read/search-rank.d.ts +2 -5
  121. package/dist/read/shape.d.ts +181 -0
  122. package/dist/read/shape.js +737 -0
  123. package/dist/read/shape.js.map +1 -0
  124. package/dist/read/show-target.d.ts +9 -1
  125. package/dist/read/show-target.js +77 -17
  126. package/dist/read/show-target.js.map +1 -1
  127. package/dist/read/stage.d.ts +199 -0
  128. package/dist/read/stage.js +125 -0
  129. package/dist/read/stage.js.map +1 -0
  130. package/dist/read/truncation.d.ts +7 -12
  131. package/dist/read/truncation.js +56 -21
  132. package/dist/read/truncation.js.map +1 -1
  133. package/dist/read/views.d.ts +68 -16
  134. package/dist/read/views.js +206 -52
  135. package/dist/read/views.js.map +1 -1
  136. package/dist/surface-copy.d.ts +16 -0
  137. package/dist/surface-copy.js +16 -0
  138. package/dist/surface-copy.js.map +1 -1
  139. package/dist/write/batch.d.ts +49 -10
  140. package/dist/write/batch.js +423 -71
  141. package/dist/write/batch.js.map +1 -1
  142. package/dist/write/commands.js +268 -55
  143. package/dist/write/commands.js.map +1 -1
  144. package/dist/write/guards.d.ts +1 -1
  145. package/dist/write/guards.js +71 -10
  146. package/dist/write/guards.js.map +1 -1
  147. package/dist/write/heading.d.ts +10 -1
  148. package/dist/write/heading.js +35 -5
  149. package/dist/write/heading.js.map +1 -1
  150. package/dist/write/make-repeating-project.d.ts +2 -2
  151. package/dist/write/make-repeating-project.js +9 -9
  152. package/dist/write/make-repeating-project.js.map +1 -1
  153. package/dist/write/move.d.ts +134 -0
  154. package/dist/write/move.js +1874 -0
  155. package/dist/write/move.js.map +1 -0
  156. package/dist/write/operations.d.ts +105 -19
  157. package/dist/write/operations.js +56 -7
  158. package/dist/write/operations.js.map +1 -1
  159. package/dist/write/opid.d.ts +31 -0
  160. package/dist/write/opid.js +30 -0
  161. package/dist/write/opid.js.map +1 -0
  162. package/dist/write/pipeline.d.ts +70 -3
  163. package/dist/write/pipeline.js +198 -33
  164. package/dist/write/pipeline.js.map +1 -1
  165. package/dist/write/pre-state.d.ts +172 -8
  166. package/dist/write/pre-state.js +467 -25
  167. package/dist/write/pre-state.js.map +1 -1
  168. package/dist/write/reorder.d.ts +23 -3
  169. package/dist/write/reorder.js +2106 -86
  170. package/dist/write/reorder.js.map +1 -1
  171. package/dist/write/reversibility.js +19 -7
  172. package/dist/write/reversibility.js.map +1 -1
  173. package/dist/write/scope-guard.d.ts +31 -0
  174. package/dist/write/scope-guard.js +162 -0
  175. package/dist/write/scope-guard.js.map +1 -0
  176. package/dist/write/undo.js +120 -13
  177. package/dist/write/undo.js.map +1 -1
  178. package/dist/write/vectors/applescript.js +16 -7
  179. package/dist/write/vectors/applescript.js.map +1 -1
  180. package/dist/write/vectors/shortcuts.js +1 -1
  181. package/dist/write/vectors/shortcuts.js.map +1 -1
  182. package/dist/write/vectors/simulator.js +83 -27
  183. package/dist/write/vectors/simulator.js.map +1 -1
  184. package/dist/write/vectors/types.d.ts +11 -0
  185. package/dist/write/vectors/ui-certification.d.ts +3 -3
  186. package/dist/write/vectors/ui-certification.js +41 -5
  187. package/dist/write/vectors/ui-certification.js.map +1 -1
  188. package/dist/write/vectors/ui-drag.d.ts +21 -1
  189. package/dist/write/vectors/ui-drag.js +138 -27
  190. package/dist/write/vectors/ui-drag.js.map +1 -1
  191. package/dist/write/vectors/ui-recipes.d.ts +21 -1
  192. package/dist/write/vectors/ui-recipes.js +166 -7
  193. package/dist/write/vectors/ui-recipes.js.map +1 -1
  194. package/dist/write/vectors/ui.d.ts +25 -1
  195. package/dist/write/vectors/ui.js +142 -33
  196. package/dist/write/vectors/ui.js.map +1 -1
  197. package/dist/write/verify/delta.d.ts +17 -1
  198. package/dist/write/verify/delta.js +106 -29
  199. package/dist/write/verify/delta.js.map +1 -1
  200. package/package.json +7 -1
  201. package/schema/envelope.schema.json +397 -0
  202. package/skills/things-cli/SKILL.md +36 -17
  203. package/skills/things-cli/references/banner.md +35 -0
  204. package/skills/things-cli/references/contracts.md +32 -9
  205. package/skills/things-cli/references/errors.md +57 -0
  206. package/skills/things-cli/references/gui.md +1 -1
  207. package/skills/things-cli/references/model.md +18 -7
  208. package/skills/things-cli/references/ordering.md +88 -0
package/dist/client.d.ts CHANGED
@@ -5,21 +5,25 @@
5
5
  import type { AuditWriter } from "./audit/log.ts";
6
6
  import { type ThingsApiConfig } from "./config.ts";
7
7
  import { type ClockMeta } from "./model/clock.ts";
8
- import { type GroupedTruncation, type Truncation } from "./contracts.ts";
8
+ import { type Truncation } from "./contracts.ts";
9
9
  import { type FingerprintStatus, type SchemaStatus } from "./db/fingerprint.ts";
10
10
  import type { AnyTask, Area, Project, Tag } from "./model/entities.ts";
11
+ import type { RefPromoter } from "./read/shape.ts";
11
12
  import { type AreaView } from "./read/area-view.ts";
12
13
  import { type ProjectView } from "./read/project-view.ts";
13
14
  import { type Snapshot } from "./read/snapshot.ts";
14
15
  import { type ShowTarget } from "./read/show-target.ts";
15
16
  import { type ChangedItem, type InboxFilter, type LiteSearchResult, type ListItem, type LogbookFilter, type SearchOptions, type SearchResultItem, type SidebarSection, type SomedayFilter, type TodayFilter, type TodayView, type UpcomingFilter, type ViewFilter } from "./read/views.ts";
16
- import type { AreaAddParams, AreaUpdateParams, ContainerRef, ProjectCancelParams, OperationKind, OperationParamsMap, ProjectAddParams, ProjectCompleteParams, ProjectCreateRepeatingParams, ProjectUpdateParams, RepeatRuleParams, ReorderParams, TagAddParams, TagUpdateParams, HeadingArchiveParams, HeadingUnarchiveParams, TodoAddLoggedParams, TodoAddParams, TodoBackdateParams, TodoMoveParams, TodoUpdateParams } from "./write/operations.ts";
17
+ import { type AreaScopedRead, type ViewFilterMeta } from "./read/area-filter.ts";
18
+ import { type ScopeMeta } from "./read/scope.ts";
19
+ import type { AreaAddParams, AreaUpdateParams, ContainerRef, ProjectCancelParams, OperationKind, OperationParamsMap, ProjectAddParams, ProjectCompleteParams, ProjectAddRepeatingParams, ProjectUpdateParams, RepeatRuleParams, ReorderParams, TagAddParams, TagUpdateParams, HeadingArchiveParams, HeadingUnarchiveParams, HeadingPlacement, TodoAddLoggedParams, TodoAddParams, TodoBackdateParams, TodoMoveParams, TodoUpdateParams } from "./write/operations.ts";
17
20
  import { type MutationResult, type WriteOptions } from "./write/pipeline.ts";
18
- import { type BatchItemResult, type BatchOp, type BatchOptions } from "./write/batch.ts";
21
+ import { type BatchItemResult, type BatchOp, type BatchOptions, type BatchResult } from "./write/batch.ts";
19
22
  import { type EnvironmentTracker } from "./write/environment.ts";
20
23
  import { type HeadingArchiveResult, type HeadingUnarchiveResult } from "./write/heading.ts";
21
24
  import type { ChecklistEdit } from "./write/checklist.ts";
22
25
  import { type ReorderResult } from "./write/reorder.ts";
26
+ import { type MoveResult, type ProjectMoveRequest, type ReorderRequest, type TodoMoveRequest } from "./write/move.ts";
23
27
  import { type UndoItemResult, type UndoOptions } from "./write/undo.ts";
24
28
  import { type ProjectReopenOptions, type ProjectReopenResult } from "./write/reopen.ts";
25
29
  import type { WriteVector } from "./write/vectors/types.ts";
@@ -38,6 +42,16 @@ export interface OpenOptions {
38
42
  vectors?: WriteVector[];
39
43
  /** Env for config/state-dir resolution (tests). */
40
44
  env?: NodeJS.ProcessEnv;
45
+ /**
46
+ * Container scope: a ref (uuid / uuid-prefix / unique area or project name)
47
+ * that jails this client to one container — reads see only in-scope rows,
48
+ * writes are refused (or redirected) outside it, and out-of-scope refs are
49
+ * indistinguishable from nonexistent ones. This is the MCP `--scope` flag's
50
+ * entry point and OUTRANKS `THINGS_API_SCOPE` / the stored `scope` config
51
+ * (the launcher's boundary must not be agent-overridable). Unresolvable →
52
+ * fail closed (ScopeResolutionError). See docs/design/container-scope.md.
53
+ */
54
+ scope?: string;
41
55
  /** Test seams for the mutation pipeline. */
42
56
  writeOverrides?: {
43
57
  ensureRunning?: (alreadyRunning: boolean) => Promise<boolean>;
@@ -81,33 +95,61 @@ export interface GroupedBound {
81
95
  export interface BoundedList<T> {
82
96
  items: T[];
83
97
  truncation: Truncation;
98
+ /** The active `area` scope, when one was applied (surfaced as `meta.filter`). */
99
+ filter?: ViewFilterMeta;
84
100
  }
85
101
  /**
86
- * A bounded Today view: `view` is the shown split (capped in render order —
87
- * Today, then This Evening) and `truncation` the exact counts, including the
88
- * per-section (`today`/`evening`) breakdown a renderer needs to stay honest.
102
+ * A bounded today view: `view` is the shown flat `items[]` (capped in comparator
103
+ * order) plus the whole-view `counts`, and `truncation` the exact counts,
104
+ * including the per-render-section (`today`/`evening`) breakdown derived per row
105
+ * from `when` — a renderer needs to stay honest.
89
106
  */
90
107
  export interface BoundedTodayView {
91
108
  view: TodayView;
92
109
  truncation: Truncation;
110
+ /** The active `area` scope, when one was applied (surfaced as `meta.filter`). */
111
+ filter?: ViewFilterMeta;
93
112
  }
94
113
  /**
95
114
  * A bounded sidebar catalogue (anytime/someday): `view` is the
96
- * per-block-capped sections and `grouped` the per-block counts (identity-
97
- * carrying, project blocks nested under their area/loose block).
115
+ * per-block-capped sections and `truncation` the unified completeness metadata
116
+ * whose `blocks` carry the identity-bearing per-block counts (project blocks
117
+ * nested under their area/loose block).
98
118
  */
99
119
  export interface BoundedSectionsView {
100
120
  view: SidebarSection[];
101
- grouped: GroupedTruncation;
121
+ truncation: Truncation;
122
+ /** The active `area` scope, when one was applied (surfaced as `meta.filter`). */
123
+ filter?: ViewFilterMeta;
102
124
  }
103
- /** A bounded composite area card: the per-section-capped view and the per-block counts. */
125
+ /** A bounded composite area card: the per-section-capped view and the per-block truncation. */
104
126
  export interface BoundedAreaView {
105
127
  view: AreaView;
106
- grouped: GroupedTruncation;
128
+ truncation: Truncation;
129
+ /**
130
+ * A resolution disclosure — present ONLY on the `loose` pseudo-area read when
131
+ * a real area shadows the reserved word (names it, by uuid, for targeting).
132
+ * Surfaced by the consumers as a `meta.warnings` advisory.
133
+ */
134
+ notice?: string;
135
+ /**
136
+ * The live count of the area's logged rows (subtree-inclusive, past the
137
+ * log-move boundary — the population `things logbook --area <ref>` returns).
138
+ * Present for a real area, absent for the `loose` pseudo-area. A DISPLAY
139
+ * sibling of `view`: it feeds the TTY card's logbook footer and never enters
140
+ * the JSON area-view `data`.
141
+ */
142
+ loggedCount?: number;
107
143
  }
108
144
  export interface ThingsClient {
109
145
  dbPath: string;
110
146
  config: ThingsApiConfig;
147
+ /**
148
+ * The active container scope (pinned at open), or undefined when unscoped.
149
+ * The consumer surfaces surface it as the additive `meta.scope` and the
150
+ * one-line "scoped to …" banner so the jail is never silently on.
151
+ */
152
+ scope?: ScopeMeta;
111
153
  fingerprint(): FingerprintStatus;
112
154
  /**
113
155
  * The read-path schema check: the cached fingerprint comparison reduced to a
@@ -123,30 +165,58 @@ export interface ThingsClient {
123
165
  * `tz` argument) so the reported `today` matches what that read computed.
124
166
  */
125
167
  clockMeta(zoneOverride?: string): ClockMeta | undefined;
168
+ /**
169
+ * Reference resolvers the consumer surfaces (CLI/MCP) call to turn a project
170
+ * ref + heading selector into uuids before invoking a heading verb. Both
171
+ * throw {@link ReferenceResolutionError} (uuid candidates on ambiguity),
172
+ * scope-aware when a container scope is active. `heading` shares the one
173
+ * heading-selector core (title | uuid | empty-string literal; no ordinal).
174
+ */
175
+ resolve: {
176
+ project(ref: string): {
177
+ uuid: string;
178
+ title: string;
179
+ };
180
+ heading(projectUuid: string, sel: string): {
181
+ uuid: string;
182
+ title: string;
183
+ };
184
+ };
185
+ /**
186
+ * A fresh {@link RefPromoter} (fresh memo) the consumer surfaces hand to
187
+ * {@link shapeReadPayload} so the flat `area`/`project`/`heading` refs promote
188
+ * a `*Uuid` sibling exactly when their bare title would not resolve back — the
189
+ * JSON round-trip law. Build one per response emission (the memo is scoped to
190
+ * that one shaping pass).
191
+ */
192
+ refPromoter(): RefPromoter;
126
193
  read: {
127
194
  /**
128
- * The Today list (Today + This Evening split) with the sidebar badge,
129
- * bounded to `limit` rows (default 50) counted in render order — Today
130
- * first, then This Evening. `all`/`limit: null` returns every row; the
131
- * `truncation` metadata carries the per-section (`today`/`evening`) counts.
195
+ * The Today list one flat `items[]` of Today members (Today-proper and
196
+ * This-Evening interleaved in comparator order, each row's `when` carrying
197
+ * which) plus the whole-view `counts` aggregate, bounded to `limit` rows
198
+ * (default 50). `all`/`limit: null` returns every row; the `truncation`
199
+ * metadata carries the per-render-section (`today`/`evening`) counts.
132
200
  */
133
- today(options?: TodayFilter & ListBound & ClockScopedRead): BoundedTodayView;
201
+ today(options?: TodayFilter & ListBound & ClockScopedRead & AreaScopedRead): BoundedTodayView;
134
202
  /** Inbox captures, bounded (default 50). */
135
203
  inbox(options?: InboxFilter & ListBound & ClockScopedRead): BoundedList<ListItem>;
136
204
  /**
137
205
  * Anytime catalogue: every area header and project row is always present;
138
206
  * `areaLimit` (default 30) caps each area/loose block, `projectLimit`
139
- * (default 3) each project block. `all` lifts both.
207
+ * (default 3) each project block. `all` lifts both. `area` restricts the
208
+ * catalogue to one area (its rows survive; the rest drop).
140
209
  */
141
- anytime(options?: ViewFilter & GroupedBound & ClockScopedRead): BoundedSectionsView;
210
+ anytime(options?: ViewFilter & GroupedBound & ClockScopedRead & AreaScopedRead): BoundedSectionsView;
142
211
  /** Future-scheduled items in date order, bounded (default 50). */
143
- upcoming(options?: UpcomingFilter & ListBound & ClockScopedRead): BoundedList<ListItem>;
212
+ upcoming(options?: UpcomingFilter & ListBound & ClockScopedRead & AreaScopedRead): BoundedList<ListItem>;
144
213
  /**
145
214
  * Someday catalogue: `areaLimit` (default 30) caps each group; with
146
215
  * `activeProjectItems`, `projectLimit` (default: every item) caps each
147
- * active project's trailing child list. `all` lifts both.
216
+ * active project's trailing child list. `all` lifts both. `area` restricts
217
+ * the catalogue to one area.
148
218
  */
149
- someday(options?: SomedayFilter & GroupedBound & ClockScopedRead): BoundedSectionsView;
219
+ someday(options?: SomedayFilter & GroupedBound & ClockScopedRead & AreaScopedRead): BoundedSectionsView;
150
220
  /** Logbook entries (most recent first), bounded (default 50). */
151
221
  logbook(options?: Omit<LogbookFilter, "limit"> & ListBound & ClockScopedRead): BoundedList<ListItem>;
152
222
  /** Trashed items (most recently modified first), bounded (default 50). */
@@ -216,6 +286,23 @@ export interface ThingsClient {
216
286
  cancelTodo(uuid: string, options?: WriteOptions): Promise<MutationResult>;
217
287
  reopenTodo(uuid: string, options?: WriteOptions): Promise<MutationResult>;
218
288
  moveTodo(uuid: string, dest: Omit<TodoMoveParams, "uuid">, options?: WriteOptions): Promise<MutationResult>;
289
+ /**
290
+ * Move one or more to-dos as an ordered block (spec §4). Give a destination
291
+ * (--to-project / --to-heading / --to-area / --no-heading / --loose) and an
292
+ * optional position (first/last/before/after), or a position alone to
293
+ * reposition items already sharing a container. Membership always succeeds;
294
+ * placement is guaranteed top-of-bucket only where a reorder protocol exists
295
+ * (the result states the placement class). Compiles onto the todo.move +
296
+ * reorder wire primitives — no new op kind.
297
+ */
298
+ moveTodos(request: TodoMoveRequest, options?: WriteOptions): Promise<MoveResult>;
299
+ /**
300
+ * Reorder to-dos IN PLACE within their shared container+bucket (spec §4).
301
+ * Bare (no position) assembles the movees as a contiguous block at the
302
+ * earliest movee's current slot, in argument order. Cross-container operands
303
+ * fail closed.
304
+ */
305
+ reorderTodos(request: ReorderRequest, options?: WriteOptions): Promise<MoveResult>;
219
306
  /** Replace the full tag set (an empty list clears all tags). */
220
307
  setTags(uuid: string, tags: string[], options?: WriteOptions): Promise<MutationResult>;
221
308
  /** Merge: current direct tags + new ones, then replace. */
@@ -248,11 +335,43 @@ export interface ThingsClient {
248
335
  /**
249
336
  * Create a heading inside an EXISTING project; the new heading's uuid is
250
337
  * on the result. Delivered through the Things proxy shortcuts (run
251
- * `things setup shortcuts` once first).
338
+ * `things setup shortcuts` once first). A `placement` positions the new
339
+ * heading among the project's headings via a native `move-heading` leg
340
+ * (requires allow-experimental); omitted, it appends. Anchor uuids in the
341
+ * placement are resolved by the caller.
252
342
  */
253
- createHeading(project: ContainerRef, title: string, options?: WriteOptions): Promise<MutationResult>;
343
+ addHeading(project: ContainerRef, title: string, placement?: HeadingPlacement, options?: WriteOptions): Promise<MutationResult>;
254
344
  /** Rename a heading in place (works on archived headings too). */
255
345
  renameHeading(uuid: string, title: string, options?: WriteOptions): Promise<MutationResult>;
346
+ /**
347
+ * Reposition one or more of a project's headings as an ordered block
348
+ * (selection order = resulting order; children follow). `headings` and the
349
+ * placement anchors are resolved heading uuids. Native reorder wire
350
+ * (requires allow-experimental).
351
+ */
352
+ moveHeading(project: ContainerRef, headings: string[], placement: HeadingPlacement, options?: WriteOptions): Promise<MutationResult>;
353
+ /**
354
+ * Relocate ONE heading (with its children) to a DIFFERENT project — the
355
+ * cross-project move the within-project `moveHeading` reorder cannot express
356
+ * (HEADXPROJ). GUI-only: drives the heading row's `…` ellipsis → Move… →
357
+ * keyboard-driven project picker, so it needs `ui.enabled` + the
358
+ * `dangerouslyDriveGui` acknowledgement. `heading` is an exact title or uuid
359
+ * within `project`; both same-titled source headings AND a same-titled
360
+ * destination project fail closed (the drive addresses by title). No wired
361
+ * undo — it is app-reversible by moving it back.
362
+ */
363
+ moveHeadingToProject(project: ContainerRef, heading: string, toProject: ContainerRef, options?: WriteOptions): Promise<MutationResult>;
364
+ /**
365
+ * Dissolve a heading: remove it while its to-dos SURVIVE as direct children
366
+ * of the project (heading→NULL, keeping their order) — NOT trashed (DISS1).
367
+ * This is the OPPOSITE of a delete cascade (contrast the Shortcuts heading
368
+ * delete, P12, which trashes the children). GUI-only (heading row's `…`
369
+ * ellipsis → Delete), so it needs `ui.enabled` + `dangerouslyDriveGui`. Fails
370
+ * closed on a title shared by another heading in the project. No wired undo —
371
+ * the children are kept, so re-create the heading and move them back to
372
+ * reverse. `uuid` is the heading.
373
+ */
374
+ dissolveHeading(uuid: string, options?: WriteOptions): Promise<MutationResult>;
256
375
  /**
257
376
  * Archive a heading (the UI's Archive — it leaves the active project
258
377
  * view, reversibly). With open children the policy is mandatory:
@@ -278,6 +397,14 @@ export interface ThingsClient {
278
397
  moveProject(uuid: string, area: ContainerRef, options?: WriteOptions): Promise<MutationResult>;
279
398
  /** Detach a project from its current area. */
280
399
  detachProject(uuid: string, options?: WriteOptions): Promise<MutationResult>;
400
+ /**
401
+ * Move one or more projects as an ordered block (spec §4/§5): --to-area, or
402
+ * --no-area to leave the area, plus an optional position — or a position
403
+ * alone to reorder them among their siblings.
404
+ */
405
+ moveProjects(request: ProjectMoveRequest, options?: WriteOptions): Promise<MoveResult>;
406
+ /** Reorder projects IN PLACE among their siblings (spec §4). */
407
+ reorderProjects(request: ReorderRequest, options?: WriteOptions): Promise<MoveResult>;
281
408
  /** Cancel a project — open children are canceled with it, so the children policy is mandatory. */
282
409
  cancelProject(uuid: string, policy: Pick<ProjectCancelParams, "children">, options?: WriteOptions): Promise<MutationResult>;
283
410
  /**
@@ -307,7 +434,7 @@ export interface ThingsClient {
307
434
  * two-key gated, same as makeRepeatingProject). Give an `area` to place it,
308
435
  * or omit it to create in Someday. The new template's uuid is on the result.
309
436
  */
310
- createRepeatingProject(params: ProjectCreateRepeatingParams, options?: WriteOptions): Promise<MutationResult>;
437
+ addRepeatingProject(params: ProjectAddRepeatingParams, options?: WriteOptions): Promise<MutationResult>;
311
438
  /** Replace a project's full tag set (an empty list clears all tags). */
312
439
  setProjectTags(uuid: string, tags: string[], options?: WriteOptions): Promise<MutationResult>;
313
440
  /** Merge: current project tags + new ones, then replace. */
@@ -326,9 +453,14 @@ export interface ThingsClient {
326
453
  reorder(params: ReorderParams, options?: WriteOptions): Promise<ReorderResult>;
327
454
  /**
328
455
  * Run N ops sequentially and independently — no transactions, a failure
329
- * does not roll back earlier ops. Per-op results.
456
+ * does not roll back earlier ops. Per-op results stream through `onResult`;
457
+ * the resolved {@link BatchResult} adds the temp-id → uuid mapping and the
458
+ * batch-level undo token (undo the whole submission as one unit). An op may
459
+ * carry a `tempId` (a handle bound to its created uuid, referenceable as
460
+ * `"$name"` by a later op) and/or an `opId` (idempotency id — a resubmitted
461
+ * op with a matching applied id is skipped, not re-created).
330
462
  */
331
- batch(ops: BatchOp[], options?: BatchOptions, onResult?: (result: BatchItemResult) => void): Promise<BatchItemResult[]>;
463
+ batch(ops: BatchOp[], options?: BatchOptions, onResult?: (result: BatchItemResult) => void): Promise<BatchResult>;
332
464
  /**
333
465
  * Undo changes made through this client, newest first, by applying the
334
466
  * inverse change. Selection: `last` trailing changes (default 1), narrowed