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
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Container-scoped sandbox — the ONE membership relation, shared by reads,
3
+ * writes, and reference resolution. Generalizes the `--area` post-filter
4
+ * (`area-filter.ts`) from "one area" to "one container" (an area OR a project),
5
+ * and adds the leak-critical SQL fragments the single-row / candidate paths and
6
+ * the write ancestry check share so an out-of-scope item is indistinguishable
7
+ * from a nonexistent one (the no-oracle guarantee — see
8
+ * docs/design/container-scope.md).
9
+ *
10
+ * A scope is resolved ONCE at `openThings()` and pinned for the client's life
11
+ * (never re-resolved per call — a deleted container becomes a safe empty jail,
12
+ * a recreated same-named one never silently re-binds). Every read reaches the
13
+ * consumer through `client.read.*` and every write through `runMutation`, so the
14
+ * two forms below (the entity predicate `inScopeItem` and the SQL fragments) are
15
+ * the only two places the relation is expressed.
16
+ */
17
+ import type { DatabaseSync } from "node:sqlite";
18
+ import type { AnyTask } from "../model/entities.ts";
19
+ import { ReferenceResolutionError } from "./queries.ts";
20
+ import type { ListItem, SidebarSection, TodayView } from "./views.ts";
21
+ import type { IsoDate } from "../model/dates.ts";
22
+ /** Where a scope declaration came from — surfaced as `meta.scope.source`. */
23
+ export type ScopeSource = "flag" | "env" | "config";
24
+ /**
25
+ * A resolved container scope: pinned at open, carried on the client. `kind`
26
+ * distinguishes an area jail from a project jail; `uuid`/`title` name the
27
+ * container; `areaUuid` is the containing area (the scope area itself for an
28
+ * area scope; the project's own area — or null for an area-less project — for a
29
+ * project scope), used to scope the `areas` view and area-ref resolution.
30
+ */
31
+ export interface ResolvedScope {
32
+ kind: "area" | "project";
33
+ uuid: string;
34
+ title: string;
35
+ source: ScopeSource;
36
+ /** The relevant area context (area scope: itself; project scope: the project's area, null if area-less). */
37
+ areaUuid: string | null;
38
+ }
39
+ /** The `meta.scope` wire shape (a subset of {@link ResolvedScope}). */
40
+ export interface ScopeMeta {
41
+ kind: "area" | "project";
42
+ uuid: string;
43
+ title: string;
44
+ source: ScopeSource;
45
+ }
46
+ /**
47
+ * Fail-closed if a requested scope is unresolvable — `things mcp --scope <bogus>`
48
+ * refuses to start, a CLI invocation errors as usage — so the daemon never runs
49
+ * unscoped when a scope was asked for. A subclass of {@link ReferenceResolutionError}
50
+ * so existing usage-class handlers treat it uniformly.
51
+ */
52
+ export declare class ScopeResolutionError extends ReferenceResolutionError {
53
+ constructor(message: string, ref: string);
54
+ }
55
+ /**
56
+ * Resolve a scope ref (uuid, uuid-prefix, or unique area/project name) to a
57
+ * pinned {@link ResolvedScope}. Areas win over same-named projects (mirroring
58
+ * `classifyShowTarget`). A to-do / heading / tag ref is a usage error — only an
59
+ * area or a project can be a container. Fail-closed: an unresolvable ref throws
60
+ * {@link ScopeResolutionError}.
61
+ */
62
+ export declare function resolveScope(db: DatabaseSync, ref: string, source: ScopeSource): ResolvedScope;
63
+ /** The `meta.scope` projection of a resolved scope (drops the internal `areaUuid`). */
64
+ export declare function scopeMeta(scope: ResolvedScope): ScopeMeta;
65
+ /**
66
+ * The entity-level membership predicate — the SAME relation as
67
+ * {@link scopeMembershipSql}, for an already-shaped view row. Area scope: the
68
+ * row's EFFECTIVE area is the scope (its `area` Ref, which queries.ts already
69
+ * resolves transitively). Project scope: the row IS the project, is a direct
70
+ * child, or is nested under one of the project's headings.
71
+ */
72
+ export declare function inScopeItem(item: ListItem | AnyTask, scope: ResolvedScope): boolean;
73
+ /**
74
+ * The SQL fragment on alias `t` — the single membership source the leak-critical
75
+ * query paths AND the write ancestry check share. Area: the effective-area
76
+ * COALESCE equals the scope. Project: the row is the project, its direct child,
77
+ * or a heading-nested child.
78
+ */
79
+ export declare function scopeMembershipSql(scope: ResolvedScope): {
80
+ where: string;
81
+ binds: string[];
82
+ };
83
+ /** True when the given uuid is an in-scope TMTask row (single-row leak paths, write parity). */
84
+ export declare function isUuidInScope(db: DatabaseSync, uuid: string, scope: ResolvedScope): boolean;
85
+ /**
86
+ * The membership clause for {@link resolveTaskUuidPrefix} (alias `t`): the same
87
+ * fragment as {@link scopeMembershipSql}, so an out-of-scope uuid resolves to
88
+ * "not found" through the identical code path a nonexistent one does.
89
+ */
90
+ export declare function taskMembershipClause(scope: ResolvedScope): {
91
+ where: string;
92
+ binds: string[];
93
+ };
94
+ /**
95
+ * The membership clause for {@link resolveNamedRef} over TMTask projects
96
+ * (UNqualified columns — that resolver aliases nothing). A project carries its
97
+ * area directly (no inheritance), so area scope compares `area`; project scope
98
+ * pins the one project uuid.
99
+ */
100
+ export declare function namedProjectClause(scope: ResolvedScope): {
101
+ where: string;
102
+ binds: string[];
103
+ };
104
+ /**
105
+ * The membership clause for {@link resolveNamedRef} over TMArea (UNqualified).
106
+ * Area scope: the one scope area. Project scope: the project's own area (an
107
+ * area-less project has none, so nothing resolves — `0`).
108
+ */
109
+ export declare function namedAreaClause(scope: ResolvedScope): {
110
+ where: string;
111
+ binds: string[];
112
+ };
113
+ /** Filter a flat view (upcoming/logbook/trash/search/changes) to in-scope rows. */
114
+ export declare function filterListByScope<T extends ListItem>(items: T[], scope: ResolvedScope): T[];
115
+ /**
116
+ * Filter the grouped sidebar catalogue (anytime/someday) to in-scope rows: each
117
+ * section's items are filtered and empty sections drop, so the per-block caps
118
+ * size the survivors.
119
+ */
120
+ export declare function filterSectionsByScope(sections: SidebarSection[], scope: ResolvedScope): SidebarSection[];
121
+ /**
122
+ * Filter the today view to in-scope rows, RECOMPUTING the counts over the
123
+ * surviving OPEN members so they never count a dropped (out-of-scope) row — the
124
+ * same treatment `filterTodayByArea` gives the area filter.
125
+ */
126
+ export declare function filterTodayByScope(view: TodayView, scope: ResolvedScope, todayIso: IsoDate): TodayView;
@@ -0,0 +1,161 @@
1
+ import { EFFECTIVE_AREA, ReferenceResolutionError, resolveAreaUuid, resolveProjectUuid, } from "./queries.js";
2
+ /**
3
+ * Fail-closed if a requested scope is unresolvable — `things mcp --scope <bogus>`
4
+ * refuses to start, a CLI invocation errors as usage — so the daemon never runs
5
+ * unscoped when a scope was asked for. A subclass of {@link ReferenceResolutionError}
6
+ * so existing usage-class handlers treat it uniformly.
7
+ */
8
+ export class ScopeResolutionError extends ReferenceResolutionError {
9
+ constructor(message, ref) {
10
+ super(message, { code: "not-found", ref });
11
+ this.name = "ScopeResolutionError";
12
+ }
13
+ }
14
+ /**
15
+ * Resolve a scope ref (uuid, uuid-prefix, or unique area/project name) to a
16
+ * pinned {@link ResolvedScope}. Areas win over same-named projects (mirroring
17
+ * `classifyShowTarget`). A to-do / heading / tag ref is a usage error — only an
18
+ * area or a project can be a container. Fail-closed: an unresolvable ref throws
19
+ * {@link ScopeResolutionError}.
20
+ */
21
+ export function resolveScope(db, ref, source) {
22
+ // Area first (an area outranks a same-named project, like the show router).
23
+ try {
24
+ const uuid = resolveAreaUuid(db, ref, { prefixTier: false });
25
+ const row = db.prepare("SELECT title FROM TMArea WHERE uuid = ?").get(uuid);
26
+ return { kind: "area", uuid, title: row?.title ?? "", source, areaUuid: uuid };
27
+ }
28
+ catch (err) {
29
+ // An ambiguous area name is a real conflict — surface it. A plain not-found
30
+ // falls through to project resolution.
31
+ if (err instanceof RangeError && err.message.includes("matches")) {
32
+ throw new ScopeResolutionError(`scope "${ref}" is ambiguous — ${err.message}`, ref);
33
+ }
34
+ }
35
+ try {
36
+ const uuid = resolveProjectUuid(db, ref, { prefixTier: false });
37
+ const row = db.prepare("SELECT title, area FROM TMTask WHERE uuid = ?").get(uuid);
38
+ return {
39
+ kind: "project",
40
+ uuid,
41
+ title: row?.title ?? "",
42
+ source,
43
+ areaUuid: row?.area ?? null,
44
+ };
45
+ }
46
+ catch (err) {
47
+ if (err instanceof RangeError && err.message.includes("matches")) {
48
+ throw new ScopeResolutionError(`scope "${ref}" is ambiguous — ${err.message}`, ref);
49
+ }
50
+ }
51
+ throw new ScopeResolutionError(`scope "${ref}" matches no area or project — a scope must name a container (only areas and ` +
52
+ "projects can be a container; to-dos, headings, and tags cannot)", ref);
53
+ }
54
+ /** The `meta.scope` projection of a resolved scope (drops the internal `areaUuid`). */
55
+ export function scopeMeta(scope) {
56
+ return { kind: scope.kind, uuid: scope.uuid, title: scope.title, source: scope.source };
57
+ }
58
+ /**
59
+ * The entity-level membership predicate — the SAME relation as
60
+ * {@link scopeMembershipSql}, for an already-shaped view row. Area scope: the
61
+ * row's EFFECTIVE area is the scope (its `area` Ref, which queries.ts already
62
+ * resolves transitively). Project scope: the row IS the project, is a direct
63
+ * child, or is nested under one of the project's headings.
64
+ */
65
+ export function inScopeItem(item, scope) {
66
+ if (scope.kind === "area") {
67
+ const area = "area" in item ? item.area : null;
68
+ return area != null && area.uuid === scope.uuid;
69
+ }
70
+ const p = scope.uuid;
71
+ if (item.uuid === p)
72
+ return true;
73
+ const project = "project" in item ? item.project : null;
74
+ if (project != null && project.uuid === p)
75
+ return true;
76
+ const hp = "headingProject" in item ? item.headingProject : undefined;
77
+ if (hp != null && hp.uuid === p)
78
+ return true;
79
+ return false;
80
+ }
81
+ /**
82
+ * The SQL fragment on alias `t` — the single membership source the leak-critical
83
+ * query paths AND the write ancestry check share. Area: the effective-area
84
+ * COALESCE equals the scope. Project: the row is the project, its direct child,
85
+ * or a heading-nested child.
86
+ */
87
+ export function scopeMembershipSql(scope) {
88
+ if (scope.kind === "area") {
89
+ return { where: `${EFFECTIVE_AREA} = ?`, binds: [scope.uuid] };
90
+ }
91
+ return {
92
+ where: "(t.uuid = ? OR t.project = ? OR t.heading IN " +
93
+ "(SELECT uuid FROM TMTask WHERE type = 2 AND project = ?))",
94
+ binds: [scope.uuid, scope.uuid, scope.uuid],
95
+ };
96
+ }
97
+ /** True when the given uuid is an in-scope TMTask row (single-row leak paths, write parity). */
98
+ export function isUuidInScope(db, uuid, scope) {
99
+ const mem = scopeMembershipSql(scope);
100
+ const row = db
101
+ .prepare(`SELECT 1 AS ok FROM TMTask t WHERE t.uuid = ? AND ${mem.where} LIMIT 1`)
102
+ .get(uuid, ...mem.binds);
103
+ return row !== undefined;
104
+ }
105
+ /**
106
+ * The membership clause for {@link resolveTaskUuidPrefix} (alias `t`): the same
107
+ * fragment as {@link scopeMembershipSql}, so an out-of-scope uuid resolves to
108
+ * "not found" through the identical code path a nonexistent one does.
109
+ */
110
+ export function taskMembershipClause(scope) {
111
+ return scopeMembershipSql(scope);
112
+ }
113
+ /**
114
+ * The membership clause for {@link resolveNamedRef} over TMTask projects
115
+ * (UNqualified columns — that resolver aliases nothing). A project carries its
116
+ * area directly (no inheritance), so area scope compares `area`; project scope
117
+ * pins the one project uuid.
118
+ */
119
+ export function namedProjectClause(scope) {
120
+ if (scope.kind === "area")
121
+ return { where: "area = ?", binds: [scope.uuid] };
122
+ return { where: "uuid = ?", binds: [scope.uuid] };
123
+ }
124
+ /**
125
+ * The membership clause for {@link resolveNamedRef} over TMArea (UNqualified).
126
+ * Area scope: the one scope area. Project scope: the project's own area (an
127
+ * area-less project has none, so nothing resolves — `0`).
128
+ */
129
+ export function namedAreaClause(scope) {
130
+ if (scope.kind === "area")
131
+ return { where: "uuid = ?", binds: [scope.uuid] };
132
+ return scope.areaUuid === null
133
+ ? { where: "0", binds: [] }
134
+ : { where: "uuid = ?", binds: [scope.areaUuid] };
135
+ }
136
+ /** Filter a flat view (upcoming/logbook/trash/search/changes) to in-scope rows. */
137
+ export function filterListByScope(items, scope) {
138
+ return items.filter((i) => inScopeItem(i, scope));
139
+ }
140
+ /**
141
+ * Filter the grouped sidebar catalogue (anytime/someday) to in-scope rows: each
142
+ * section's items are filtered and empty sections drop, so the per-block caps
143
+ * size the survivors.
144
+ */
145
+ export function filterSectionsByScope(sections, scope) {
146
+ return sections
147
+ .map((s) => ({ area: s.area, items: s.items.filter((i) => inScopeItem(i, scope)) }))
148
+ .filter((s) => s.items.length > 0);
149
+ }
150
+ /**
151
+ * Filter the today view to in-scope rows, RECOMPUTING the counts over the
152
+ * surviving OPEN members so they never count a dropped (out-of-scope) row — the
153
+ * same treatment `filterTodayByArea` gives the area filter.
154
+ */
155
+ export function filterTodayByScope(view, scope, todayIso) {
156
+ const items = view.items.filter((i) => inScopeItem(i, scope));
157
+ const open = items.filter((i) => i.status === "open");
158
+ const dueOrOverdue = open.filter((i) => i.deadline !== null && i.deadline <= todayIso).length;
159
+ return { items, counts: { dueOrOverdue, other: open.length - dueOrOverdue } };
160
+ }
161
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/read/scope.ts"],"names":[],"mappings":"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"}
@@ -18,12 +18,9 @@ import type { ListItem } from "./views.ts";
18
18
  export type MatchField = "title" | "notes" | "heading" | "checklist";
19
19
  export interface SearchMatch {
20
20
  item: ListItem;
21
+ /** The RANK field (strongest match by rank order); the displayed provenance
22
+ * annotation rides the item as `match` and follows its own precedence. */
21
23
  field: MatchField;
22
- /** Present when `field === "heading"`: the heading whose title matched. */
23
- matchedVia?: {
24
- kind: "heading";
25
- title: string;
26
- };
27
24
  }
28
25
  export declare function fieldRank(field: MatchField): number;
29
26
  /** Containers (projects, areas) sort above to-dos. */
@@ -0,0 +1,181 @@
1
+ /**
2
+ * The read-payload SHAPING transform: the token-economy rules R6 and R7, the
3
+ * universal item-DTO reshapes (R9), and the R10 lifecycle taxonomy — applied at
4
+ * the JSON emit boundary of the read surfaces (the CLI `--json` read envelope,
5
+ * src/cli/read-driver.ts, and the MCP read tool results, src/mcp/server.ts), the
6
+ * same boundaries omit-empty runs at. Shaping runs BEFORE omit-empty. The
7
+ * human-render path keeps the full, unshaped entities, so this is JSON-only.
8
+ *
9
+ * Both R6/R7 are deterministic BY VIEW KIND / SECTION — the emitter knows whether
10
+ * it is inside a single-container view, a lifecycle bucket, or a mixed list —
11
+ * never a per-item heuristic.
12
+ *
13
+ * ## R10 — the `stage` lifecycle taxonomy (every tier, every kind incl. detail)
14
+ * The three former wire fields `start` / `logged` / `trashed` are DELETED from
15
+ * every item and replaced by ONE derived `stage` ∈ `inbox | upcoming | anytime |
16
+ * someday | logbook | trash` (src/read/stage.ts `deriveStage`, the single pure
17
+ * derivation reused by the card bucketing so `stage` can never disagree with the
18
+ * bucket a view puts an item in). Today/evening membership is a SEPARATE
19
+ * presence-keyed axis — `today: true` / `evening: true` (evening implies today) —
20
+ * derived in the mapper with the Today view's own two-arm predicate.
21
+ * - `stage` is DROPPED only where the enclosing node PROVABLY states it — the R6
22
+ * rule (drop only what the node provably says). That is the stage-PURE flat
23
+ * views (inbox, `anytime`, someday, logbook, trash) and the stage-named card
24
+ * sub-buckets (anytime/upcoming/someday/logbook/trash, which the bucketer splits
25
+ * BY stage). The `anytime` catalogue is stage-PURE (R10.2): every member is an
26
+ * Anytime-view row (ANYTIME_SELF) — undated-active, arrived-active, or arrived
27
+ * someday-scheduled — and an ARRIVED dated row derives `anytime` (Upcoming is
28
+ * STRICTLY FUTURE, UPC1), so the field is redundant there.
29
+ * `stage` is KEPT on the stage-MIXED or derived surfaces: the `upcoming` view
30
+ * carries deadline-forecast stage-`anytime`/`someday` rows alongside its
31
+ * future-dated stage-`upcoming` ones — dropping `stage` there would delete
32
+ * non-redundant information — plus `today` (mixes upcoming + anytime-deadline
33
+ * rows), search, changes, the projects/areas listings, the card NODE, and
34
+ * detail.
35
+ * - the former `todaySection` field is RETIRED from the wire entirely (R10.1):
36
+ * `todaySection: "evening"` merely duplicated the `evening: true` marker. It
37
+ * remains an internal entity field (the human render and the write-verify delta
38
+ * still read it); shaping deletes it from the JSON copy.
39
+ *
40
+ * ## R12 — `when`, the derived TIME-AXIS position (replaces startDate + markers)
41
+ * Today/evening membership and the scheduled/projected date collapse onto ONE
42
+ * derived, presence-keyed field `when` (src/read/stage.ts `deriveWhen`): `"today"`
43
+ * / `"evening"` (Today-view membership, from the SAME `today`/`evening` markers the
44
+ * mapper stamps — never re-derived, so `when` can never disagree with the star), a
45
+ * FUTURE ISO date (a strictly-future scheduled row, or a template's projected next
46
+ * occurrence), or absent (unscheduled + not in Today; also an unprojected template
47
+ * and every logged/trashed row). The doctrine line: **`stage` enumerates the
48
+ * sidebar BUCKETS an item lives in; `when` enumerates its TIME POSITIONS (today |
49
+ * evening | a future date).** Someday is deliberately NOT a `when` value (it is a
50
+ * bucket → stage). Tier/drop rules:
51
+ * - the former `today`/`evening` marker KEYS are DELETED from the wire on EVERY
52
+ * tier — `when` carries the fact; the markers stay internal (they feed `when`).
53
+ * - the raw `startDate` is DELETED in COMPACT (a list needs the position, not the
54
+ * substrate) and KEPT in FULL/DETAIL beside `when` — different facts: `startDate`
55
+ * = what is stored, `when` = where it sits.
56
+ * - a template's `repeating.nextOccurrence` is GONE from the wire — `when` replaces
57
+ * it (same fact, one word); an unprojected template has no `when`.
58
+ * - `when` is KEPT on every row it is present on. (The read-shape doctrine
59
+ * flattened the card `upcoming` date-groups into the project-/area-view flat
60
+ * `items[]`, so no enclosing node states the position anymore.) It rides the
61
+ * today view's flat `items[]` (Today-proper vs This-Evening), the project-/area-
62
+ * view `items[]`, and the flat `upcoming`/`anytime`/`inbox`/`someday` catalogues,
63
+ * search, changes (a deadline-pulled row reads `when: "today"` in the mixed
64
+ * search/changes surfaces, informatively; note R13 re-files it to stage `anytime`
65
+ * and the flat inbox/someday views now EXCLUDE it — it appears in the `anytime`
66
+ * catalogue instead, `when: "today"` kept, stage dropped as pure).
67
+ *
68
+ * ## R13 — provisional Today members + GUI-faithful pulled-row membership
69
+ * BANNER1 / BANNER1b (docs/lab/banner1-research.md). Two coupled facts:
70
+ * - **`provisional: true`** — a presence-keyed marker on every Today member the GUI
71
+ * pips / counts in the "You have N new to-dos" banner: a Today member NOT yet
72
+ * materialized (`start != active OR startDate IS NULL`, BANNER1 L1). Derived from
73
+ * the SAME today/evening markers + fields the stage/`when` axes use (never
74
+ * re-derived). Emitted on EVERY tier, NEVER dropped (the banner is not a section,
75
+ * so no node implies it). Absent on non-Today rows and on materialized ones.
76
+ * Read-only: the app clears it by materializing the row on banner-OK, a GUI-only
77
+ * side effect our read cannot perform (watchers beware).
78
+ * - **stage `anytime` for a deadline pull** — a due-deadline pull re-files an undated
79
+ * Inbox/Someday row into Anytime (deriveStage step 2½, L-A). So EVERY Today member
80
+ * derives stage `anytime`, and the today view's flat `items[]` is stage-PURE →
81
+ * `stage` is DROPPED there (TODAY_ITEM_DROP) while `when` is KEPT. The flat
82
+ * someday/inbox views EXCLUDE pulled rows and the anytime view INCLUDES them
83
+ * (src/read/views.ts + predicates.ts DEADLINE_PULLED) — GUI fidelity.
84
+ *
85
+ * ## Universal item-DTO reshapes (R9 — EVERY tier, EVERY read kind incl. detail)
86
+ * - **checklist nesting** — flat counts → presence-keyed `checklist: {open,total}`.
87
+ * - **todos counts** — a project's flat leaf-action counts → presence-keyed
88
+ * `todos: {open, total}` (omit when total 0).
89
+ * - **repeating template/instance split (R11)** — the wire drops the
90
+ * `isTemplate`/`isInstance` discriminators; key presence carries the fact. A
91
+ * TEMPLATE keeps a nested `repeating: {paused?, deadlined?, rule?,
92
+ * latestInstance?}` — the series object (rule config + backward pointer +
93
+ * state flags); presence MEANS template. The forward pointer `nextOccurrence`
94
+ * moved to the top-level `when` (R12 — a template's projected date IS its time
95
+ * position); `latestInstance` is detail-only (SL1). An INSTANCE keeps a flat
96
+ * `instanceOf: <templateUuid>` and no `repeating`. A plain row keeps neither.
97
+ * See {@link reshapeRepeatingWire}.
98
+ * - **string tags** — `tags`/`inheritedTags` become plain arrays of names.
99
+ * - **one project key** — a headed item's owning project (formerly
100
+ * `headingProject`) is merged into `project`; `headingProject` never appears.
101
+ *
102
+ * ## R6 — no-redundant-ancestry (both tiers)
103
+ * project-view children drop `project`+`area` (heading-group members also drop
104
+ * `heading`); area-view children/project-cards drop `area`; anytime/someday
105
+ * section items drop `area`. Mixed lists keep every ref. (In the COMPACT tier the
106
+ * `heading` ref is additionally dropped everywhere — R7.)
107
+ *
108
+ * ## R7 — named detail tiers (compact | full)
109
+ * List contexts default to COMPACT; `detail`/`show` and `--full` / `full:true`
110
+ * use FULL. Compact drops `created`/`modified`, the full `notes` string (a
111
+ * presence-keyed `hasNotes: true` marks a row with notes), and the `heading` ref;
112
+ * `status` is omitted when `open`. FULL keeps them but still applies R6, the
113
+ * universal reshapes, and R10.
114
+ */
115
+ import { type Stage, type When } from "./stage.ts";
116
+ /** The container-ref kinds whose bare title is round-trip-tested for uuid promotion. */
117
+ export type RefKind = "area" | "project" | "heading";
118
+ /**
119
+ * The emit-side promotion oracle: does a container ref's bare TITLE round-trip
120
+ * through its own resolver, in its own scope, back to THIS entity? Built by
121
+ * {@link makeRefPromoter} (src/read/queries.ts) over the live DB — it runs the
122
+ * REAL resolution path for the kind (areas = all areas; projects = the live+open
123
+ * write-target pool, uuid-prefix tier first; headings = within `projectUuid`)
124
+ * and returns true only when the sole resolution is this uuid; not-found or
125
+ * ambiguous is false. Memoized per (kind, title, scope) within one response
126
+ * emission. When a promoter is absent (a DB-less unit shaping), the default
127
+ * assumes every title round-trips — bare titles, no uuid siblings.
128
+ */
129
+ export interface RefPromoter {
130
+ roundTrips(kind: RefKind, title: string, entityUuid: string, projectUuid?: string): boolean;
131
+ }
132
+ /**
133
+ * Apply the universal reshapes + R6 + R7 + R10 to a read payload for one view
134
+ * `kind`. `full` forces the FULL tier (R7 default-pruning off, everything else
135
+ * applied); an unrecognized kind passes through unchanged. The input is never
136
+ * mutated (shallow copies throughout), so the human-render path keeps the full
137
+ * entities.
138
+ */
139
+ export declare function shapeReadPayload(kind: string, data: unknown, full: boolean, promoter?: RefPromoter): unknown;
140
+ /** The disambiguation-candidate kinds an error may list (a subset of the entity kinds). */
141
+ export type CandidateType = "to-do" | "project" | "heading" | "area" | "tag";
142
+ /**
143
+ * The ONE fixed error-candidate shape — the presence-keyed disambiguation DTO a
144
+ * not-found / ambiguous resolution lists under `error.detail.candidates` (and the
145
+ * did-you-mean fallback under the same key). Deliberately minimal and INVARIANT
146
+ * across request flags: `--full` / `--all` / etc. NEVER widen it, because an
147
+ * error payload is the most determinism-critical surface. A candidate carries
148
+ * ONLY material that helps a caller pick the right entity, drawn from the SAME
149
+ * single-source derivations the read wire uses — never the raw internal entity
150
+ * (no counts, no notes, no dates, no null-stuffed keys):
151
+ * - `uuid` / `title` — always present.
152
+ * - `type` — names the kind, EXCEPT to-do: absent `type` = to-do (present for
153
+ * `project` / `heading` / `area` / `tag`), the same convention the item wire uses.
154
+ * - `area` / `project` — container hint as a TITLE string, present only when set.
155
+ * - `stage` / `when` — the R10/R12 lifecycle words, present only for a to-do /
156
+ * project candidate whose source row carries the materialized lifecycle fields
157
+ * (a thin uuid+title resolver row carries neither — presence-keyed, so absent).
158
+ * A trashed / logged candidate needs no boolean: `stage` already reads `"trash"`
159
+ * / `"logbook"` for it — the same vocabulary the wire uses.
160
+ */
161
+ export interface CandidateRef {
162
+ uuid: string;
163
+ title: string;
164
+ /** The kind — omitted for a to-do (absent `type` = to-do), present otherwise. */
165
+ type?: CandidateType;
166
+ area?: string;
167
+ project?: string;
168
+ stage?: Stage;
169
+ when?: When;
170
+ }
171
+ /** The fixed cap on a listed candidate array; the error `message` states the total when it overflows. */
172
+ export declare const CANDIDATE_CAP = 8;
173
+ /**
174
+ * Project ONE entity — a materialized to-do/project/area/heading, or a thin
175
+ * `{uuid, title}` resolver row — to the fixed {@link CandidateRef}. The SINGLE
176
+ * source every error-candidate emit flows through (the did-you-mean fallback and
177
+ * every not-found/ambiguous resolver), so the candidate shape can never vary by
178
+ * site. Reuses the wire's own {@link stageOf}/{@link whenOf} derivations — the
179
+ * lifecycle words are never re-derived here.
180
+ */
181
+ export declare function candidateRef(type: CandidateType, src: unknown): CandidateRef;