things-api 0.9.0 → 0.11.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 (261) hide show
  1. package/README.md +57 -12
  2. package/dist/audit/log.js +56 -2
  3. package/dist/audit/log.js.map +1 -1
  4. package/dist/audit/schema.d.ts +26 -8
  5. package/dist/audit/schema.js +42 -1
  6. package/dist/audit/schema.js.map +1 -1
  7. package/dist/cli/clock.d.ts +8 -0
  8. package/dist/cli/clock.js +24 -0
  9. package/dist/cli/clock.js.map +1 -0
  10. package/dist/cli/commands/area.d.ts +26 -3
  11. package/dist/cli/commands/area.js +129 -75
  12. package/dist/cli/commands/area.js.map +1 -1
  13. package/dist/cli/commands/doctor.d.ts +1 -1
  14. package/dist/cli/commands/doctor.js +44 -5
  15. package/dist/cli/commands/doctor.js.map +1 -1
  16. package/dist/cli/commands/install-skill.d.ts +45 -0
  17. package/dist/cli/commands/install-skill.js +207 -0
  18. package/dist/cli/commands/install-skill.js.map +1 -0
  19. package/dist/cli/commands/mcp.js +23 -4
  20. package/dist/cli/commands/mcp.js.map +1 -1
  21. package/dist/cli/commands/project.d.ts +18 -1
  22. package/dist/cli/commands/project.js +74 -35
  23. package/dist/cli/commands/project.js.map +1 -1
  24. package/dist/cli/commands/reads.d.ts +15 -2
  25. package/dist/cli/commands/reads.js +459 -165
  26. package/dist/cli/commands/reads.js.map +1 -1
  27. package/dist/cli/commands/repeat-flags.d.ts +20 -0
  28. package/dist/cli/commands/repeat-flags.js +76 -0
  29. package/dist/cli/commands/repeat-flags.js.map +1 -0
  30. package/dist/cli/commands/setup.js +8 -4
  31. package/dist/cli/commands/setup.js.map +1 -1
  32. package/dist/cli/commands/show.js +53 -30
  33. package/dist/cli/commands/show.js.map +1 -1
  34. package/dist/cli/commands/todo.d.ts +1 -1
  35. package/dist/cli/commands/todo.js +20 -13
  36. package/dist/cli/commands/todo.js.map +1 -1
  37. package/dist/cli/commands/writes.js +388 -161
  38. package/dist/cli/commands/writes.js.map +1 -1
  39. package/dist/cli/did-you-mean.d.ts +1 -1
  40. package/dist/cli/excess-args.d.ts +15 -0
  41. package/dist/cli/excess-args.js +51 -0
  42. package/dist/cli/excess-args.js.map +1 -0
  43. package/dist/cli/glyphs.d.ts +55 -2
  44. package/dist/cli/glyphs.js +114 -21
  45. package/dist/cli/glyphs.js.map +1 -1
  46. package/dist/cli/help.d.ts +54 -0
  47. package/dist/cli/help.js +405 -0
  48. package/dist/cli/help.js.map +1 -0
  49. package/dist/cli/main.js +64 -26
  50. package/dist/cli/main.js.map +1 -1
  51. package/dist/cli/move-hint.d.ts +45 -0
  52. package/dist/cli/move-hint.js +196 -0
  53. package/dist/cli/move-hint.js.map +1 -0
  54. package/dist/cli/period.d.ts +9 -8
  55. package/dist/cli/period.js +56 -30
  56. package/dist/cli/period.js.map +1 -1
  57. package/dist/cli/read-driver.d.ts +26 -19
  58. package/dist/cli/read-driver.js +77 -33
  59. package/dist/cli/read-driver.js.map +1 -1
  60. package/dist/cli/render.d.ts +92 -28
  61. package/dist/cli/render.js +333 -103
  62. package/dist/cli/render.js.map +1 -1
  63. package/dist/cli/resolve-invocation.d.ts +42 -4
  64. package/dist/cli/resolve-invocation.js +97 -14
  65. package/dist/cli/resolve-invocation.js.map +1 -1
  66. package/dist/cli/skill-check.d.ts +21 -0
  67. package/dist/cli/skill-check.js +82 -0
  68. package/dist/cli/skill-check.js.map +1 -0
  69. package/dist/cli/skill.d.ts +62 -0
  70. package/dist/cli/skill.js +142 -0
  71. package/dist/cli/skill.js.map +1 -0
  72. package/dist/cli/tag-filters.d.ts +63 -0
  73. package/dist/cli/tag-filters.js +59 -0
  74. package/dist/cli/tag-filters.js.map +1 -0
  75. package/dist/cli/verb-hint.d.ts +25 -0
  76. package/dist/cli/verb-hint.js +163 -0
  77. package/dist/cli/verb-hint.js.map +1 -0
  78. package/dist/cli/version.d.ts +4 -0
  79. package/dist/cli/version.js +18 -0
  80. package/dist/cli/version.js.map +1 -0
  81. package/dist/cli/width.d.ts +135 -0
  82. package/dist/cli/width.js +313 -0
  83. package/dist/cli/width.js.map +1 -0
  84. package/dist/client.d.ts +160 -24
  85. package/dist/client.js +132 -19
  86. package/dist/client.js.map +1 -1
  87. package/dist/config.d.ts +11 -0
  88. package/dist/config.js +3 -0
  89. package/dist/config.js.map +1 -1
  90. package/dist/contracts.d.ts +97 -17
  91. package/dist/contracts.js +44 -1
  92. package/dist/contracts.js.map +1 -1
  93. package/dist/db/fingerprint.d.ts +12 -0
  94. package/dist/db/fingerprint.js +15 -1
  95. package/dist/db/fingerprint.js.map +1 -1
  96. package/dist/db/locate.js +1 -1
  97. package/dist/db/locate.js.map +1 -1
  98. package/dist/diagnose.d.ts +63 -0
  99. package/dist/diagnose.js +38 -1
  100. package/dist/diagnose.js.map +1 -1
  101. package/dist/index.d.ts +36 -5
  102. package/dist/index.js +38 -2
  103. package/dist/index.js.map +1 -1
  104. package/dist/mcp/server.d.ts +9 -1
  105. package/dist/mcp/server.js +1038 -436
  106. package/dist/mcp/server.js.map +1 -1
  107. package/dist/model/clock.d.ts +61 -0
  108. package/dist/model/clock.js +71 -0
  109. package/dist/model/clock.js.map +1 -0
  110. package/dist/model/dates.d.ts +26 -3
  111. package/dist/model/dates.js +84 -3
  112. package/dist/model/dates.js.map +1 -1
  113. package/dist/model/entities.d.ts +48 -10
  114. package/dist/model/entities.js.map +1 -1
  115. package/dist/model/mappers.d.ts +9 -2
  116. package/dist/model/mappers.js +32 -9
  117. package/dist/model/mappers.js.map +1 -1
  118. package/dist/model/serialize.d.ts +28 -0
  119. package/dist/model/serialize.js +80 -0
  120. package/dist/model/serialize.js.map +1 -0
  121. package/dist/model/when-sugar.d.ts +47 -0
  122. package/dist/model/when-sugar.js +45 -0
  123. package/dist/model/when-sugar.js.map +1 -0
  124. package/dist/read/area-view.d.ts +8 -1
  125. package/dist/read/area-view.js +55 -17
  126. package/dist/read/area-view.js.map +1 -1
  127. package/dist/read/detail.d.ts +1 -1
  128. package/dist/read/detail.js +11 -4
  129. package/dist/read/detail.js.map +1 -1
  130. package/dist/read/filter-contract.d.ts +117 -0
  131. package/dist/read/filter-contract.js +78 -0
  132. package/dist/read/filter-contract.js.map +1 -0
  133. package/dist/read/log-boundary.d.ts +1 -1
  134. package/dist/read/log-boundary.js +13 -3
  135. package/dist/read/log-boundary.js.map +1 -1
  136. package/dist/read/predicates.d.ts +30 -0
  137. package/dist/read/predicates.js +30 -0
  138. package/dist/read/predicates.js.map +1 -1
  139. package/dist/read/project-view.d.ts +12 -1
  140. package/dist/read/project-view.js +45 -12
  141. package/dist/read/project-view.js.map +1 -1
  142. package/dist/read/queries.d.ts +152 -7
  143. package/dist/read/queries.js +277 -36
  144. package/dist/read/queries.js.map +1 -1
  145. package/dist/read/sections.d.ts +51 -0
  146. package/dist/read/sections.js +37 -0
  147. package/dist/read/sections.js.map +1 -0
  148. package/dist/read/sidebar-order.js +2 -1
  149. package/dist/read/sidebar-order.js.map +1 -1
  150. package/dist/read/snapshot.d.ts +1 -1
  151. package/dist/read/snapshot.js +9 -2
  152. package/dist/read/snapshot.js.map +1 -1
  153. package/dist/read/tags.d.ts +27 -3
  154. package/dist/read/tags.js +83 -12
  155. package/dist/read/tags.js.map +1 -1
  156. package/dist/read/truncation.d.ts +71 -0
  157. package/dist/read/{pagination.js → truncation.js} +103 -94
  158. package/dist/read/truncation.js.map +1 -0
  159. package/dist/read/views.d.ts +147 -22
  160. package/dist/read/views.js +282 -80
  161. package/dist/read/views.js.map +1 -1
  162. package/dist/surface-copy.d.ts +19 -0
  163. package/dist/surface-copy.js +32 -0
  164. package/dist/surface-copy.js.map +1 -1
  165. package/dist/sync-health.d.ts +78 -0
  166. package/dist/sync-health.js +312 -0
  167. package/dist/sync-health.js.map +1 -0
  168. package/dist/write/accessibility-probe.d.ts +12 -0
  169. package/dist/write/accessibility-probe.js +71 -0
  170. package/dist/write/accessibility-probe.js.map +1 -0
  171. package/dist/write/automation-probe.d.ts +8 -1
  172. package/dist/write/automation-probe.js +16 -1
  173. package/dist/write/automation-probe.js.map +1 -1
  174. package/dist/write/batch.js +5 -2
  175. package/dist/write/batch.js.map +1 -1
  176. package/dist/write/capabilities.d.ts +8 -0
  177. package/dist/write/capabilities.js +13 -6
  178. package/dist/write/capabilities.js.map +1 -1
  179. package/dist/write/commands.d.ts +3 -1
  180. package/dist/write/commands.js +602 -49
  181. package/dist/write/commands.js.map +1 -1
  182. package/dist/write/edit-checklist.js +3 -2
  183. package/dist/write/edit-checklist.js.map +1 -1
  184. package/dist/write/guards.d.ts +2 -7
  185. package/dist/write/guards.js +87 -6
  186. package/dist/write/guards.js.map +1 -1
  187. package/dist/write/heading.js +2 -0
  188. package/dist/write/heading.js.map +1 -1
  189. package/dist/write/lock.d.ts +40 -2
  190. package/dist/write/lock.js +91 -14
  191. package/dist/write/lock.js.map +1 -1
  192. package/dist/write/make-repeating-project.d.ts +4 -0
  193. package/dist/write/make-repeating-project.js +253 -0
  194. package/dist/write/make-repeating-project.js.map +1 -0
  195. package/dist/write/operations.d.ts +145 -1
  196. package/dist/write/operations.js +48 -0
  197. package/dist/write/operations.js.map +1 -1
  198. package/dist/write/pipeline.d.ts +73 -2
  199. package/dist/write/pipeline.js +218 -43
  200. package/dist/write/pipeline.js.map +1 -1
  201. package/dist/write/planner.js +8 -1
  202. package/dist/write/planner.js.map +1 -1
  203. package/dist/write/pre-state.d.ts +105 -3
  204. package/dist/write/pre-state.js +144 -3
  205. package/dist/write/pre-state.js.map +1 -1
  206. package/dist/write/recurrence-rule-blob.d.ts +65 -0
  207. package/dist/write/recurrence-rule-blob.js +150 -0
  208. package/dist/write/recurrence-rule-blob.js.map +1 -0
  209. package/dist/write/reopen.js +3 -2
  210. package/dist/write/reopen.js.map +1 -1
  211. package/dist/write/reorder.js +27 -17
  212. package/dist/write/reorder.js.map +1 -1
  213. package/dist/write/repeat-rule.d.ts +22 -0
  214. package/dist/write/repeat-rule.js +258 -0
  215. package/dist/write/repeat-rule.js.map +1 -0
  216. package/dist/write/reversibility.js +49 -0
  217. package/dist/write/reversibility.js.map +1 -1
  218. package/dist/write/tag-refs.d.ts +47 -0
  219. package/dist/write/tag-refs.js +126 -0
  220. package/dist/write/tag-refs.js.map +1 -0
  221. package/dist/write/undo.d.ts +74 -5
  222. package/dist/write/undo.js +493 -80
  223. package/dist/write/undo.js.map +1 -1
  224. package/dist/write/vectors/registry.d.ts +15 -1
  225. package/dist/write/vectors/registry.js +65 -2
  226. package/dist/write/vectors/registry.js.map +1 -1
  227. package/dist/write/vectors/simulator.d.ts +44 -0
  228. package/dist/write/vectors/simulator.js +1136 -0
  229. package/dist/write/vectors/simulator.js.map +1 -0
  230. package/dist/write/vectors/types.d.ts +154 -3
  231. package/dist/write/vectors/ui-certification.d.ts +54 -0
  232. package/dist/write/vectors/ui-certification.js +43 -0
  233. package/dist/write/vectors/ui-certification.js.map +1 -0
  234. package/dist/write/vectors/ui-drag.d.ts +188 -0
  235. package/dist/write/vectors/ui-drag.js +1095 -0
  236. package/dist/write/vectors/ui-drag.js.map +1 -0
  237. package/dist/write/vectors/ui-recipes.d.ts +83 -0
  238. package/dist/write/vectors/ui-recipes.js +610 -0
  239. package/dist/write/vectors/ui-recipes.js.map +1 -0
  240. package/dist/write/vectors/ui.d.ts +152 -0
  241. package/dist/write/vectors/ui.js +704 -0
  242. package/dist/write/vectors/ui.js.map +1 -0
  243. package/dist/write/verify/delta.d.ts +141 -4
  244. package/dist/write/verify/delta.js +199 -21
  245. package/dist/write/verify/delta.js.map +1 -1
  246. package/dist/write/verify/poller.d.ts +7 -1
  247. package/dist/write/verify/poller.js +21 -1
  248. package/dist/write/verify/poller.js.map +1 -1
  249. package/package.json +18 -3
  250. package/skills/things-cli/SKILL.md +53 -0
  251. package/skills/things-cli/references/contracts.md +41 -0
  252. package/skills/things-cli/references/gui.md +11 -0
  253. package/skills/things-cli/references/model.md +51 -0
  254. package/dist/cli/exit-codes.d.ts +0 -26
  255. package/dist/cli/exit-codes.js +0 -26
  256. package/dist/cli/exit-codes.js.map +0 -1
  257. package/dist/cli/output.d.ts +0 -42
  258. package/dist/cli/output.js +0 -16
  259. package/dist/cli/output.js.map +0 -1
  260. package/dist/read/pagination.d.ts +0 -104
  261. package/dist/read/pagination.js.map +0 -1
@@ -12,6 +12,27 @@ export type TaskType = "to-do" | "project" | "heading";
12
12
  export interface Ref {
13
13
  uuid: string;
14
14
  title: string;
15
+ /**
16
+ * Set (and only ever `true`) when this ref points at a repeating-template
17
+ * PROJECT — the blueprint row whose `rt1_recurrenceRule`/`repeater` is
18
+ * non-null (probe-verified: the template's CHILDREN are plain rows, so the
19
+ * fact lives on the parent project). Only container/project refs ever carry
20
+ * it — area and heading refs never do — so a consumer reads its absence as
21
+ * "not a template". It disambiguates the two identical-looking same-title
22
+ * copies a project conversion leaves behind (the template blueprint vs. its
23
+ * spawned instance; see RepeatingInfo). Omitted when false (never `false`).
24
+ */
25
+ isRepeatingTemplate?: true;
26
+ }
27
+ /**
28
+ * A surfaced tag: its NAME only. Tag uuids are a fully internal implementation
29
+ * detail (like checklist item ids) — names are app-enforced-unique (TAGW1-c),
30
+ * so a name is a sufficient reference, and no surface emits or accepts a tag
31
+ * uuid. Nested-tag context, where it matters, is conveyed by the tag listing's
32
+ * `parent` field, not here on the pill.
33
+ */
34
+ export interface TagRef {
35
+ title: string;
15
36
  }
16
37
  export interface RepeatingInfo {
17
38
  /** This row is a repeating template (rt1_recurrenceRule / repeater present). Invisible in normal lists. */
@@ -51,19 +72,29 @@ interface TaskCommon {
51
72
  /** The "When" date (packed int in DB), null when unscheduled. */
52
73
  startDate: IsoDate | null;
53
74
  /**
54
- * Raw This-Evening assignment (startBucket). Effective only while
55
- * startDate == today the UI rolls stale evening items back into Today
56
- * proper. Use TodayView.evening for UI-faithful placement.
75
+ * Today-view section ("today" | "evening"), emitted ONLY for items actually
76
+ * in Today under the evaluation clock: start=active, dated, and not
77
+ * future-scheduled (overdue rows stay in Today). Anytime (undated) and
78
+ * Upcoming (future startDate) rows carry a raw startBucket in the DB but are
79
+ * NOT in Today, so the field is null (omitted) for them. Use
80
+ * TodayView.evening for UI-faithful placement.
57
81
  */
58
82
  todaySection: TodaySection | null;
59
83
  deadline: IsoDate | null;
60
84
  /** Time-of-day reminder (`HH:mm`, 24h); requires a scheduled startDate. */
61
85
  reminder: ReminderTime | null;
62
86
  area: Ref | null;
63
- /** Direct tags only — mirrors DB truth (inherited tags are computed; see inheritedTags). */
64
- tags: Ref[];
65
- /** Opt-in: tags inherited from ancestor area/project (native UI filtering includes these). */
66
- inheritedTags?: Ref[];
87
+ /** Direct tags only, by name — mirrors DB truth (inherited tags are computed; see inheritedTags). */
88
+ tags: TagRef[];
89
+ /**
90
+ * Tags inherited from an ancestor project/area, by NAME — a plain list
91
+ * parallel to {@link tags} (native UI tag filtering includes these — T18).
92
+ * Populated on the detail/card reads (`todo show`, `project show`) as an
93
+ * array (empty when there are none); the emit-time omit-empty transform (#163)
94
+ * drops it from JSON when empty, and it is absent (undefined) on list rows,
95
+ * which surface direct tags only. Never merged into {@link tags}.
96
+ */
97
+ inheritedTags?: TagRef[];
67
98
  repeating: RepeatingInfo;
68
99
  created: Date;
69
100
  modified: Date;
@@ -102,13 +133,20 @@ export interface Area {
102
133
  uuid: string;
103
134
  title: string;
104
135
  visible: boolean;
105
- tags: Ref[];
136
+ tags: TagRef[];
106
137
  }
138
+ /**
139
+ * A tag in the `things tags` taxonomy listing. Uuid-free (an internal detail):
140
+ * the tree is conveyed by `parent` — the parent tag's NAME (null for a root) —
141
+ * so a consumer can reconstruct the hierarchy, and by indentation in the human
142
+ * render. Names are globally unique (TAGW1-c), so the leaf name alone is a
143
+ * usable tag reference.
144
+ */
107
145
  export interface Tag {
108
- uuid: string;
109
146
  title: string;
110
147
  shortcut: string | null;
111
- parent: Ref | null;
148
+ /** Parent tag's NAME, or null for a root tag. */
149
+ parent: string | null;
112
150
  }
113
151
  /**
114
152
  * A checklist item as the API surfaces it: title + status only. The DB uuid
@@ -1 +1 @@
1
- {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../src/model/entities.ts"],"names":[],"mappings":"AA+IA,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iBAAiB,GAA6B;IACzD,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,SAAS;CACb,CAAC;AACF,MAAM,CAAC,MAAM,mBAAmB,GAA+B;IAC7D,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,UAAU;IACb,CAAC,EAAE,WAAW;CACf,CAAC;AACF,MAAM,CAAC,MAAM,mBAAmB,GAA+B;IAC7D,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,QAAQ;IACX,CAAC,EAAE,SAAS;CACb,CAAC;AACF,MAAM,CAAC,MAAM,qBAAqB,GAAiC;IACjE,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,SAAS;CACb,CAAC"}
1
+ {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../src/model/entities.ts"],"names":[],"mappings":"AAsLA,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iBAAiB,GAA6B;IACzD,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,SAAS;CACb,CAAC;AACF,MAAM,CAAC,MAAM,mBAAmB,GAA+B;IAC7D,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,UAAU;IACb,CAAC,EAAE,WAAW;CACf,CAAC;AACF,MAAM,CAAC,MAAM,mBAAmB,GAA+B;IAC7D,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,QAAQ;IACX,CAAC,EAAE,SAAS;CACb,CAAC;AACF,MAAM,CAAC,MAAM,qBAAqB,GAAiC;IACjE,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,SAAS;CACb,CAAC"}
@@ -22,6 +22,13 @@ export interface TaskRow {
22
22
  index: number | null;
23
23
  todayIndex: number | null;
24
24
  area: string | null;
25
+ /**
26
+ * The row's EFFECTIVE area (queries.ts EFFECTIVE_AREA): its own `area`, else
27
+ * its project's, else its heading's project's. Equals `area` for projects and
28
+ * for area-direct to-dos; resolves the container's area for nested to-dos
29
+ * (whose own `area` is NULL). Surfaced as the entity's `area` Ref.
30
+ */
31
+ effectiveArea: string | null;
25
32
  project: string | null;
26
33
  heading: string | null;
27
34
  untrashedLeafActionsCount: number | null;
@@ -49,7 +56,7 @@ export type RefResolver = (uuid: string | null) => Ref | null;
49
56
  export declare class EnumDomainError extends RangeError {
50
57
  constructor(field: string, value: unknown, uuid: string);
51
58
  }
52
- export declare function mapTodo(row: TaskRow, refs: RefResolver, tags: Ref[]): Todo;
53
- export declare function mapProject(row: TaskRow, refs: RefResolver, tags: Ref[]): Project;
59
+ export declare function mapTodo(row: TaskRow, refs: RefResolver, tags: Ref[], packedToday: number): Todo;
60
+ export declare function mapProject(row: TaskRow, refs: RefResolver, tags: Ref[], packedToday: number): Project;
54
61
  export declare function mapHeading(row: TaskRow, refs: RefResolver): Heading;
55
62
  export declare function mapChecklistItem(row: ChecklistRow): ChecklistItem;
@@ -23,12 +23,30 @@ function mapStart(row) {
23
23
  throw new EnumDomainError("start", row.start, row.uuid);
24
24
  return start;
25
25
  }
26
- function mapTodaySection(row) {
26
+ /**
27
+ * `todaySection` is meaningful ONLY for items actually in the Today view. The
28
+ * DB stores a raw `startBucket` (0/1) on EVERY active row — prod carries
29
+ * startBucket=0 on every undated Anytime to-do, and future-scheduled
30
+ * (Upcoming) rows carry it too — so the raw bucket cannot be surfaced as-is:
31
+ * emitting "today" for an Anytime/Upcoming item wrongly implies Today
32
+ * membership (architecture.md; the MCP server note). We gate on the injected
33
+ * clock's `packedToday`: the field is emitted only when the row is active,
34
+ * dated, and not future-scheduled ("evening" via startBucket=1, else "today");
35
+ * overdue scheduled rows (startDate < today) stay in Today and keep it.
36
+ * Undated or future-dated rows OMIT the field (null).
37
+ */
38
+ function mapTodaySection(row, packedToday) {
27
39
  if (row.startBucket === null)
28
40
  return null;
29
41
  const section = TODAY_SECTION_FROM_DB[row.startBucket];
30
42
  if (!section)
31
43
  throw new EnumDomainError("startBucket", row.startBucket, row.uuid);
44
+ if ((row.start ?? 0) !== 1)
45
+ return null; // not start=active → not in Today
46
+ if (row.startDate === null)
47
+ return null; // undated (Anytime)
48
+ if (row.startDate > packedToday)
49
+ return null; // future startDate (Upcoming)
32
50
  return section;
33
51
  }
34
52
  function mapRepeating(row) {
@@ -47,7 +65,7 @@ function mapRepeating(row) {
47
65
  }
48
66
  return info;
49
67
  }
50
- function commonFields(row, refs, tags) {
68
+ function commonFields(row, refs, tags, packedToday) {
51
69
  return {
52
70
  uuid: row.uuid,
53
71
  title: row.title ?? "",
@@ -60,7 +78,7 @@ function commonFields(row, refs, tags) {
60
78
  trashed: row.trashed === 1,
61
79
  start: mapStart(row),
62
80
  startDate: decodePackedDate(row.startDate),
63
- todaySection: mapTodaySection(row),
81
+ todaySection: mapTodaySection(row, packedToday),
64
82
  // A template's own `deadline` column is not a real date: it is NULL
65
83
  // (deadline-less) or a far-future sentinel (4001-01-01, deadlined) that
66
84
  // flags whether spawned instances deadline. Surface it via
@@ -69,17 +87,22 @@ function commonFields(row, refs, tags) {
69
87
  ? null
70
88
  : decodePackedDate(row.deadline),
71
89
  reminder: decodeReminderTime(row.reminderTime),
72
- area: refs(row.area),
73
- tags,
90
+ // The EFFECTIVE area: a to-do nested in a project (own `area` NULL) reports
91
+ // its container's area, restoring useful area info omit-empty (#163) would
92
+ // otherwise hide. Projects and area-direct to-dos are unaffected (effective
93
+ // == direct). Direct-vs-inherited stays derivable from project/heading.
94
+ area: refs(row.effectiveArea),
95
+ // Surface tags by NAME only — tag uuids are an internal detail (TAGW1-c).
96
+ tags: tags.map((t) => ({ title: t.title })),
74
97
  repeating: mapRepeating(row),
75
98
  created: decodeEpochReal(row.creationDate) ?? new Date(0),
76
99
  modified: decodeEpochReal(row.userModificationDate) ?? new Date(0),
77
100
  stopped: decodeEpochReal(row.stopDate),
78
101
  };
79
102
  }
80
- export function mapTodo(row, refs, tags) {
103
+ export function mapTodo(row, refs, tags, packedToday) {
81
104
  return {
82
- ...commonFields(row, refs, tags),
105
+ ...commonFields(row, refs, tags, packedToday),
83
106
  type: "to-do",
84
107
  project: refs(row.project),
85
108
  heading: refs(row.heading),
@@ -87,9 +110,9 @@ export function mapTodo(row, refs, tags) {
87
110
  openChecklistItemsCount: row.openChecklistItemsCount ?? 0,
88
111
  };
89
112
  }
90
- export function mapProject(row, refs, tags) {
113
+ export function mapProject(row, refs, tags, packedToday) {
91
114
  return {
92
- ...commonFields(row, refs, tags),
115
+ ...commonFields(row, refs, tags, packedToday),
93
116
  type: "project",
94
117
  untrashedLeafActionsCount: row.untrashedLeafActionsCount ?? 0,
95
118
  openUntrashedLeafActionsCount: row.openUntrashedLeafActionsCount ?? 0,
@@ -1 +1 @@
1
- {"version":3,"file":"mappers.js","sourceRoot":"","sources":["../../src/model/mappers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GAUtB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAgDnF,MAAM,OAAO,eAAgB,SAAQ,UAAU;IAC7C,YAAY,KAAa,EAAE,KAAc,EAAE,IAAY;QACrD,KAAK,CACH,cAAc,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,uCAAuC;YACpF,+CAA+C,CAClD,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED,SAAS,SAAS,CAAC,GAAqC;IACtD,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,eAAe,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACvE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,QAAQ,CAAC,GAA2C;IAC3D,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,eAAe,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACpE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,eAAe,CAAC,GAAiD;IACxE,IAAI,GAAG,CAAC,WAAW,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,OAAO,GAAG,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACvD,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,eAAe,CAAC,aAAa,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAClF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,YAAY,CAAC,GAAY;IAChC,MAAM,UAAU,GAAG,GAAG,CAAC,kBAAkB,KAAK,IAAI,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC;IAC5E,MAAM,YAAY,GAAG,GAAG,CAAC,qBAAqB,CAAC;IAC/C,MAAM,IAAI,GAAkB,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,KAAK,IAAI,EAAE,YAAY,EAAE,CAAC;IAC5F,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,CAAC,cAAc,GAAG,gBAAgB,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QACtE,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,0BAA0B,KAAK,CAAC,CAAC;QACnD,yEAAyE;QACzE,0EAA0E;QAC1E,0EAA0E;QAC1E,wEAAwE;QACxE,kCAAkC;QAClC,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC;IACzC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,GAAY,EAAE,IAAiB,EAAE,IAAW;IAChE,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;QACtB,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;QACtB,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC;QACtB,mEAAmE;QACnE,sEAAsE;QACtE,6DAA6D;QAC7D,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,KAAK,MAAM;QACjC,OAAO,EAAE,GAAG,CAAC,OAAO,KAAK,CAAC;QAC1B,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC;QACpB,SAAS,EAAE,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC;QAC1C,YAAY,EAAE,eAAe,CAAC,GAAG,CAAC;QAClC,oEAAoE;QACpE,wEAAwE;QACxE,2DAA2D;QAC3D,wEAAwE;QACxE,QAAQ,EACN,GAAG,CAAC,kBAAkB,KAAK,IAAI,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI;YACtD,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC;QACpC,QAAQ,EAAE,kBAAkB,CAAC,GAAG,CAAC,YAAY,CAAC;QAC9C,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QACpB,IAAI;QACJ,SAAS,EAAE,YAAY,CAAC,GAAG,CAAC;QAC5B,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC;QACzD,QAAQ,EAAE,eAAe,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC;QAClE,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC;KACvC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,GAAY,EAAE,IAAiB,EAAE,IAAW;IAClE,OAAO;QACL,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;QAChC,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;QAC1B,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;QAC1B,mBAAmB,EAAE,GAAG,CAAC,mBAAmB,IAAI,CAAC;QACjD,uBAAuB,EAAE,GAAG,CAAC,uBAAuB,IAAI,CAAC;KAC1D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAY,EAAE,IAAiB,EAAE,IAAW;IACrE,OAAO;QACL,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;QAChC,IAAI,EAAE,SAAS;QACf,yBAAyB,EAAE,GAAG,CAAC,yBAAyB,IAAI,CAAC;QAC7D,6BAA6B,EAAE,GAAG,CAAC,6BAA6B,IAAI,CAAC;KACtE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAY,EAAE,IAAiB;IACxD,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;QACtB,oEAAoE;QACpE,uEAAuE;QACvE,mCAAmC;QACnC,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC;QACtB,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;KAC3B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAiB;IAChD,OAAO;QACL,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;QACtB,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC;KACvB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"mappers.js","sourceRoot":"","sources":["../../src/model/mappers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The emit-time omit-empty transform for entity/data payloads (the "Omit-empty"
3
+ * contract in docs/design/contracts.md). Applied at the JSON emit boundary of
4
+ * the read surfaces ONLY — the CLI `--json` read envelope (src/cli/read-driver.ts)
5
+ * and the MCP read tool results (src/mcp/server.ts) — never to the in-memory
6
+ * entities the library returns, and never to the error envelope.
7
+ *
8
+ * The rule: an ENTITY object omits any optional field whose value is empty —
9
+ * null, undefined, "" (empty string), or [] (empty array). A consumer MUST
10
+ * read an absent key as unset/empty/default and MUST NOT distinguish absent
11
+ * from empty. Values that are semantically meaningful when "empty" are kept:
12
+ * booleans (a real `false`) and numbers (a `0` count) are never omitted, and
13
+ * the identity keys ({@link IDENTITY_KEYS}) are always present.
14
+ *
15
+ * Why entity-scoped, not a blanket deep prune: the same key means opposite
16
+ * things on different shapes. A to-do's `area: null` is "no area" (omit), but a
17
+ * sidebar section's `area: null` is the load-bearing "top-level/loose block"
18
+ * discriminant (keep). So pruning is gated to the recognized entity shapes;
19
+ * the view scaffolding that CARRIES entities (today/evening split, project/area
20
+ * card sections, grouped blocks) is structural and passes through untouched —
21
+ * only its nested entities are pruned.
22
+ */
23
+ /**
24
+ * Return a deep copy of `payload` with every entity's empty optional fields
25
+ * omitted (the "Omit-empty" contract). Pure — the input is never mutated, so
26
+ * the human-render path keeps the full entity beside the JSON emit.
27
+ */
28
+ export declare function omitEmpty<T>(payload: T): T;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The emit-time omit-empty transform for entity/data payloads (the "Omit-empty"
3
+ * contract in docs/design/contracts.md). Applied at the JSON emit boundary of
4
+ * the read surfaces ONLY — the CLI `--json` read envelope (src/cli/read-driver.ts)
5
+ * and the MCP read tool results (src/mcp/server.ts) — never to the in-memory
6
+ * entities the library returns, and never to the error envelope.
7
+ *
8
+ * The rule: an ENTITY object omits any optional field whose value is empty —
9
+ * null, undefined, "" (empty string), or [] (empty array). A consumer MUST
10
+ * read an absent key as unset/empty/default and MUST NOT distinguish absent
11
+ * from empty. Values that are semantically meaningful when "empty" are kept:
12
+ * booleans (a real `false`) and numbers (a `0` count) are never omitted, and
13
+ * the identity keys ({@link IDENTITY_KEYS}) are always present.
14
+ *
15
+ * Why entity-scoped, not a blanket deep prune: the same key means opposite
16
+ * things on different shapes. A to-do's `area: null` is "no area" (omit), but a
17
+ * sidebar section's `area: null` is the load-bearing "top-level/loose block"
18
+ * discriminant (keep). So pruning is gated to the recognized entity shapes;
19
+ * the view scaffolding that CARRIES entities (today/evening split, project/area
20
+ * card sections, grouped blocks) is structural and passes through untouched —
21
+ * only its nested entities are pruned.
22
+ */
23
+ /**
24
+ * Keys never omitted from an entity even when empty: the identity a consumer
25
+ * always keys on. `uuid`/`type` are never empty in practice; `title`/`name`
26
+ * can be "" (an untitled to-do) and MUST still be present.
27
+ */
28
+ const IDENTITY_KEYS = new Set(["uuid", "type", "title", "name"]);
29
+ /** A to-do/project/heading row: uuid + a task-type discriminant. */
30
+ function isTaskEntity(o) {
31
+ return (typeof o["uuid"] === "string" &&
32
+ (o["type"] === "to-do" || o["type"] === "project" || o["type"] === "heading"));
33
+ }
34
+ /** An area: uuid + the `visible` flag (distinguishes it from a bare Ref). */
35
+ function isAreaEntity(o) {
36
+ return typeof o["uuid"] === "string" && typeof o["visible"] === "boolean";
37
+ }
38
+ /** A taxonomy tag row (uuid-free): a title plus the parent/shortcut columns. */
39
+ function isTagEntity(o) {
40
+ return (o["uuid"] === undefined && typeof o["title"] === "string" && "parent" in o && "shortcut" in o);
41
+ }
42
+ /** Whether this object's empty optional fields should be omitted. */
43
+ function prunesEmpties(o) {
44
+ return isTaskEntity(o) || isAreaEntity(o) || isTagEntity(o);
45
+ }
46
+ /** null | undefined | "" | [] — the values an entity omits the key for. */
47
+ function isEmpty(v) {
48
+ return v === null || v === undefined || v === "" || (Array.isArray(v) && v.length === 0);
49
+ }
50
+ function prune(value) {
51
+ if (value === null || value === undefined)
52
+ return value;
53
+ // Dates serialize to ISO strings via JSON.stringify — never recurse into them.
54
+ if (value instanceof Date)
55
+ return value;
56
+ if (Array.isArray(value))
57
+ return value.map(prune);
58
+ if (typeof value === "object") {
59
+ const o = value;
60
+ const omitEmpties = prunesEmpties(o);
61
+ const out = {};
62
+ for (const key of Object.keys(o)) {
63
+ const v = o[key];
64
+ if (omitEmpties && !IDENTITY_KEYS.has(key) && isEmpty(v))
65
+ continue;
66
+ out[key] = prune(v);
67
+ }
68
+ return out;
69
+ }
70
+ return value;
71
+ }
72
+ /**
73
+ * Return a deep copy of `payload` with every entity's empty optional fields
74
+ * omitted (the "Omit-empty" contract). Pure — the input is never mutated, so
75
+ * the human-render path keeps the full entity beside the JSON emit.
76
+ */
77
+ export function omitEmpty(payload) {
78
+ return prune(payload);
79
+ }
80
+ //# sourceMappingURL=serialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.js","sourceRoot":"","sources":["../../src/model/serialize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH;;;;GAIG;AACH,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAItF,oEAAoE;AACpE,SAAS,YAAY,CAAC,CAAM;IAC1B,OAAO,CACL,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,QAAQ;QAC7B,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC,CAC9E,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,SAAS,YAAY,CAAC,CAAM;IAC1B,OAAO,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,SAAS,CAAC;AAC5E,CAAC;AAED,gFAAgF;AAChF,SAAS,WAAW,CAAC,CAAM;IACzB,OAAO,CACL,CAAC,CAAC,MAAM,CAAC,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,QAAQ,IAAI,CAAC,IAAI,UAAU,IAAI,CAAC,CAC9F,CAAC;AACJ,CAAC;AAED,qEAAqE;AACrE,SAAS,aAAa,CAAC,CAAM;IAC3B,OAAO,YAAY,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,2EAA2E;AAC3E,SAAS,OAAO,CAAC,CAAU;IACzB,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;AAC3F,CAAC;AAED,SAAS,KAAK,CAAC,KAAc;IAC3B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACxD,+EAA+E;IAC/E,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,KAAY,CAAC;QACvB,MAAM,WAAW,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,GAAG,GAAQ,EAAE,CAAC;QACpB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACjC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACjB,IAAI,WAAW,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC;gBAAE,SAAS;YACnE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAI,OAAU;IACrC,OAAO,KAAK,CAAC,OAAO,CAAM,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The `<when>@<time>` scheduling sugar, shared by every consumer surface. A
3
+ * single string like `2026-07-05@09:00` (or `today@evening`-style keyword +
4
+ * time) carries BOTH the schedule and its reminder; this splits it into the
5
+ * separate `when` + `reminder` the write pipeline expects, or reports the one
6
+ * usage error. Extracted from the CLI so the CLI and MCP validate the suffix
7
+ * identically instead of each re-deriving it — see docs/design/architecture.md
8
+ * (Consumer boundary).
9
+ *
10
+ * Pure: it neither mutates its input nor validates the DATE/TIME values (the
11
+ * pipeline does that downstream) — it only owns the `@` split and the two
12
+ * usage messages. The flag/parameter LABELS are injected so each surface keeps
13
+ * its own vocabulary (`--when`/`--reminder` vs `when`/`reminder`).
14
+ */
15
+ /** The surface's spelling of the two parameters, interpolated into usage copy. */
16
+ export interface WhenSugarLabels {
17
+ when: string;
18
+ reminder: string;
19
+ }
20
+ /** CLI flag spellings — the default, since the sugar originated there. */
21
+ export declare const CLI_WHEN_LABELS: WhenSugarLabels;
22
+ /** MCP tool-parameter spellings. */
23
+ export declare const MCP_WHEN_LABELS: WhenSugarLabels;
24
+ /**
25
+ * The result of inspecting a `when` value for the `@time` suffix:
26
+ * - `unchanged` — no suffix; pass `when` through as-is.
27
+ * - `split` — a valid suffix; use the split `when` + `reminder`.
28
+ * - `error` — a malformed suffix, or a suffix given ALONGSIDE a separate
29
+ * reminder; emit `message` as a usage error.
30
+ */
31
+ export type WhenSugar = {
32
+ kind: "unchanged";
33
+ } | {
34
+ kind: "split";
35
+ when: string;
36
+ reminder: string;
37
+ } | {
38
+ kind: "error";
39
+ message: string;
40
+ };
41
+ /**
42
+ * Split a `when` value on its `@time` suffix. `reminderProvided` reflects
43
+ * whether the caller ALSO passed a separate reminder — a suffix plus an
44
+ * explicit reminder is ambiguous and rejected. A non-string or suffix-free
45
+ * `when` is `unchanged`.
46
+ */
47
+ export declare function splitWhenSugar(when: unknown, reminderProvided: boolean, labels?: WhenSugarLabels): WhenSugar;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The `<when>@<time>` scheduling sugar, shared by every consumer surface. A
3
+ * single string like `2026-07-05@09:00` (or `today@evening`-style keyword +
4
+ * time) carries BOTH the schedule and its reminder; this splits it into the
5
+ * separate `when` + `reminder` the write pipeline expects, or reports the one
6
+ * usage error. Extracted from the CLI so the CLI and MCP validate the suffix
7
+ * identically instead of each re-deriving it — see docs/design/architecture.md
8
+ * (Consumer boundary).
9
+ *
10
+ * Pure: it neither mutates its input nor validates the DATE/TIME values (the
11
+ * pipeline does that downstream) — it only owns the `@` split and the two
12
+ * usage messages. The flag/parameter LABELS are injected so each surface keeps
13
+ * its own vocabulary (`--when`/`--reminder` vs `when`/`reminder`).
14
+ */
15
+ /** CLI flag spellings — the default, since the sugar originated there. */
16
+ export const CLI_WHEN_LABELS = { when: "--when", reminder: "--reminder" };
17
+ /** MCP tool-parameter spellings. */
18
+ export const MCP_WHEN_LABELS = { when: "when", reminder: "reminder" };
19
+ /**
20
+ * Split a `when` value on its `@time` suffix. `reminderProvided` reflects
21
+ * whether the caller ALSO passed a separate reminder — a suffix plus an
22
+ * explicit reminder is ambiguous and rejected. A non-string or suffix-free
23
+ * `when` is `unchanged`.
24
+ */
25
+ export function splitWhenSugar(when, reminderProvided, labels = CLI_WHEN_LABELS) {
26
+ if (typeof when !== "string" || !when.includes("@"))
27
+ return { kind: "unchanged" };
28
+ const at = when.indexOf("@");
29
+ const date = when.slice(0, at);
30
+ const time = when.slice(at + 1);
31
+ if (date === "" || time === "" || time.includes("@")) {
32
+ return {
33
+ kind: "error",
34
+ message: `invalid ${labels.when} "${when}" — expected today | evening | anytime | someday | YYYY-MM-DD (set a reminder with ${labels.reminder} HH:mm)`,
35
+ };
36
+ }
37
+ if (reminderProvided) {
38
+ return {
39
+ kind: "error",
40
+ message: `${labels.when} "${when}" carries an @time suffix and ${labels.reminder} was also given — use one`,
41
+ };
42
+ }
43
+ return { kind: "split", when: date, reminder: time };
44
+ }
45
+ //# sourceMappingURL=when-sugar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"when-sugar.js","sourceRoot":"","sources":["../../src/model/when-sugar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,eAAe,GAAoB,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;AAE3F,oCAAoC;AACpC,MAAM,CAAC,MAAM,eAAe,GAAoB,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AAcvF;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAa,EACb,gBAAyB,EACzB,SAA0B,eAAe;IAEzC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAClF,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAChC,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACrD,OAAO;YACL,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,WAAW,MAAM,CAAC,IAAI,KAAK,IAAI,sFAAsF,MAAM,CAAC,QAAQ,SAAS;SACvJ,CAAC;IACJ,CAAC;IACD,IAAI,gBAAgB,EAAE,CAAC;QACrB,OAAO;YACL,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,KAAK,IAAI,iCAAiC,MAAM,CAAC,QAAQ,2BAA2B;SAC5G,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AACvD,CAAC"}
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import type { DatabaseSync } from "node:sqlite";
8
8
  import type { Area, IsoDateGroup, Project, Todo } from "../model/entities.ts";
9
+ import { type ViewFilter } from "./views.ts";
9
10
  export interface AreaView {
10
11
  area: Area;
11
12
  /** Open, unscheduled/current direct to-dos, by index. */
@@ -22,5 +23,11 @@ export interface AreaView {
22
23
  logged: Todo[];
23
24
  trashed: Todo[];
24
25
  }
26
+ /** A someday project row: open, incubated, no start date. */
27
+ export declare const isSomedayProjectRow: (p: Project) => boolean;
28
+ /** A future-scheduled project row (start date strictly after `todayIso`). */
29
+ export declare const isScheduledProjectRow: (p: Project, todayIso: string) => boolean;
30
+ /** An active project row — everything that is neither someday nor future-scheduled. */
31
+ export declare const isActiveProjectRow: (p: Project, todayIso: string) => boolean;
25
32
  /** Resolves by uuid or unique (case-insensitive) title; throws like the ref resolvers. */
26
- export declare function areaView(db: DatabaseSync, ref: string, now?: Date): AreaView;
33
+ export declare function areaView(db: DatabaseSync, ref: string, now?: Date, filter?: ViewFilter, zone?: string): AreaView;
@@ -2,9 +2,25 @@ import { encodePackedDate, localToday, decodePackedDate } from "../model/dates.j
2
2
  import { mapProject, mapTodo } from "../model/mappers.js";
3
3
  import { fetchTagsForTasks, fetchTaskRows, makeRefResolver, resolveAreaUuid } from "./queries.js";
4
4
  import { logBoundary, markLogged } from "./log-boundary.js";
5
+ import { OVERDUE } from "./predicates.js";
5
6
  import { areaTags } from "./tags.js";
7
+ import { tagFilter } from "./views.js";
8
+ // The area card splits its open projects into three display buckets by their
9
+ // own schedule (the card mirrors the GUI: active projects as rows, scheduled
10
+ // ones under Upcoming, someday ones under Someday). Closed-but-unswept projects
11
+ // stay "active" (checked in place) — start/startDate only classify OPEN rows.
12
+ // Shared by the bounding layer (capAreaSections keeps scheduled/someday whole
13
+ // while capping the active rows) and the human renderer, so the split is
14
+ // defined ONCE and the two never drift.
15
+ /** A someday project row: open, incubated, no start date. */
16
+ export const isSomedayProjectRow = (p) => p.status === "open" && p.start === "someday" && p.startDate === null;
17
+ /** A future-scheduled project row (start date strictly after `todayIso`). */
18
+ export const isScheduledProjectRow = (p, todayIso) => p.status === "open" && p.startDate !== null && p.startDate > todayIso;
19
+ /** An active project row — everything that is neither someday nor future-scheduled. */
20
+ export const isActiveProjectRow = (p, todayIso) => !isSomedayProjectRow(p) && !isScheduledProjectRow(p, todayIso);
6
21
  /** Resolves by uuid or unique (case-insensitive) title; throws like the ref resolvers. */
7
- export function areaView(db, ref, now) {
22
+ export function areaView(db, ref, now, filter = {}, zone) {
23
+ const overdue = filter.overdue === true;
8
24
  const uuid = resolveAreaUuid(db, ref);
9
25
  const row = db
10
26
  .prepare(`SELECT uuid, title, visible, "index" FROM TMArea WHERE uuid = ?`)
@@ -15,33 +31,55 @@ export function areaView(db, ref, now) {
15
31
  uuid: row.uuid,
16
32
  title: row.title ?? "",
17
33
  visible: row.visible !== 0,
18
- tags: areaTags(db, uuid),
34
+ // Area tags by NAME only (tag uuids are internal).
35
+ tags: areaTags(db, uuid).map((t) => ({ title: t.title })),
19
36
  };
20
37
  const refs = makeRefResolver(db);
21
38
  const tagsOf = (rows) => fetchTagsForTasks(db, rows.map((r) => r.uuid));
22
- const boundary = logBoundary(db, now);
39
+ const boundary = logBoundary(db, now, zone);
40
+ // The view's injected clock — gates `todaySection` to Today members in the
41
+ // mapper AND drives the scheduled/overdue bucketing below.
42
+ const packedToday = encodePackedDate(localToday(now, zone));
43
+ // OWN-DEADLINE UNIFORM: `--overdue` filters BOTH displayed row kinds by each
44
+ // row's OWN deadline (open, strictly before today) via the shared OVERDUE
45
+ // predicate — the area's child PROJECTS by the project's own deadline AND the
46
+ // loose to-dos by theirs. There is NO descent into project contents (an
47
+ // overdue to-do inside a non-overdue project never surfaces here — that is
48
+ // `project show --overdue`). Sections with no surviving rows collapse.
49
+ const overdueSql = overdue ? ` AND ${OVERDUE}` : "";
50
+ const overdueBinds = overdue ? [encodePackedDate(localToday(now, zone))] : [];
51
+ // Tag scope (§9a): BOTH displayed row kinds — the child PROJECTS and the
52
+ // loose to-dos — are filtered by a tag carried DIRECTLY on the row (the
53
+ // container semantics). The area's own tags are inherited by every row it
54
+ // holds, so an inheritance-inclusive `--tag` would be vacuous; suppressing the
55
+ // container hop makes `--tag` mean "rows with this tag on themselves" (still
56
+ // descendant-expanded) and `--untagged` "rows with no direct tag". Computed
57
+ // once, spliced into both queries; NO descent into project contents (a child
58
+ // of a matching project is never inspected here). The tag binds trail each
59
+ // query's own binds.
60
+ const tf = tagFilter(db, filter, { container: true });
23
61
  // Open projects PLUS closed ones the log-move sweep has not passed — the
24
- // GUI keeps those checked in place (completion ≠ logged).
62
+ // GUI keeps those checked in place (completion ≠ logged). Under --overdue the
63
+ // OVERDUE predicate's own `status = 0` narrows this to open overdue projects.
25
64
  const projectRows = fetchTaskRows(db, `t.type = 1 AND t.area = ? AND t.trashed = 0
26
- AND (t.status = 0 OR t.stopDate > ?) ORDER BY t."index" ASC`, [uuid, boundary.getTime() / 1000]);
65
+ AND (t.status = 0 OR t.stopDate > ?)${overdueSql}${tf.sql} ORDER BY t."index" ASC`, [uuid, boundary.getTime() / 1000, ...overdueBinds, ...tf.binds]);
27
66
  const projectTags = tagsOf(projectRows);
28
- const projects = markLogged(projectRows.map((r) => mapProject(r, refs, projectTags.get(r.uuid) ?? [])), boundary);
29
- const todoRows = fetchTaskRows(db, `t.type = 0 AND t.area = ? ORDER BY t."index" ASC`, [uuid]);
67
+ const projects = markLogged(projectRows.map((r) => mapProject(r, refs, projectTags.get(r.uuid) ?? [], packedToday)), boundary);
68
+ const todoRows = fetchTaskRows(db, `t.type = 0 AND t.area = ?${overdueSql}${tf.sql} ORDER BY t."index" ASC`, [uuid, ...overdueBinds, ...tf.binds]);
30
69
  const todoTags = tagsOf(todoRows);
31
70
  const todos = todoRows.map((r) => ({
32
71
  row: r,
33
- todo: mapTodo(r, refs, todoTags.get(r.uuid) ?? []),
72
+ todo: mapTodo(r, refs, todoTags.get(r.uuid) ?? [], packedToday),
34
73
  }));
35
74
  markLogged(todos.map((t) => t.todo), boundary);
36
- const packedToday = encodePackedDate(localToday(now));
37
75
  const active = [];
38
76
  const scheduledRows = [];
39
77
  const repeating = [];
40
78
  const someday = [];
41
79
  const logged = [];
42
80
  const trashed = [];
43
- for (const { row, todo } of todos) {
44
- if (row.trashed === 1) {
81
+ for (const { row: todoRow, todo } of todos) {
82
+ if (todoRow.trashed === 1) {
45
83
  trashed.push(todo);
46
84
  continue;
47
85
  }
@@ -49,7 +87,7 @@ export function areaView(db, ref, now) {
49
87
  repeating.push(todo);
50
88
  continue;
51
89
  }
52
- if (row.status !== 0) {
90
+ if (todoRow.status !== 0) {
53
91
  // Completion ≠ logged: closed-but-unswept items stay checked in the
54
92
  // active block, like the GUI.
55
93
  if (todo.logged)
@@ -58,14 +96,14 @@ export function areaView(db, ref, now) {
58
96
  active.push(todo);
59
97
  continue;
60
98
  }
61
- if (row.start === 2 && row.startDate === null) {
99
+ if (todoRow.start === 2 && todoRow.startDate === null) {
62
100
  someday.push(todo);
63
101
  continue;
64
102
  }
65
- if (row.startDate !== null && row.startDate > packedToday) {
103
+ if (todoRow.startDate !== null && todoRow.startDate > packedToday) {
66
104
  scheduledRows.push({
67
- date: decodePackedDate(row.startDate) ?? "",
68
- ti: row.todayIndex ?? 0,
105
+ date: decodePackedDate(todoRow.startDate) ?? "",
106
+ ti: todoRow.todayIndex ?? 0,
69
107
  todo,
70
108
  });
71
109
  continue;
@@ -75,7 +113,7 @@ export function areaView(db, ref, now) {
75
113
  logged.sort((a, b) => (b.stopped?.getTime() ?? 0) - (a.stopped?.getTime() ?? 0));
76
114
  const scheduled = [];
77
115
  // Within a day the UI sorts by todayIndex ASC (Upcoming drag order).
78
- for (const { date, todo } of scheduledRows.sort((a, b) => a.date.localeCompare(b.date) || a.ti - b.ti)) {
116
+ for (const { date, todo } of scheduledRows.toSorted((a, b) => a.date.localeCompare(b.date) || a.ti - b.ti)) {
79
117
  const last = scheduled[scheduled.length - 1];
80
118
  if (last && last.date === date)
81
119
  last.items.push(todo);
@@ -1 +1 @@
1
- {"version":3,"file":"area-view.js","sourceRoot":"","sources":["../../src/read/area-view.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"area-view.js","sourceRoot":"","sources":["../../src/read/area-view.ts"],"names":[],"mappings":"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"}
@@ -4,5 +4,5 @@
4
4
  */
5
5
  import type { DatabaseSync } from "node:sqlite";
6
6
  import type { AnyTask, ChecklistItem } from "../model/entities.ts";
7
- export declare function byUuid(db: DatabaseSync, uuid: string): AnyTask | null;
7
+ export declare function byUuid(db: DatabaseSync, uuid: string, now?: Date, zone?: string): AnyTask | null;
8
8
  export declare function checklistFor(db: DatabaseSync, taskUuid: string): ChecklistItem[];