things-api 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/audit/schema.d.ts +8 -0
- package/dist/audit/schema.js.map +1 -1
- package/dist/cli/commands/area.js +1 -1
- package/dist/cli/commands/install-skill.js +9 -2
- package/dist/cli/commands/install-skill.js.map +1 -1
- package/dist/cli/commands/project.js +1 -1
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/reads.js +73 -4
- package/dist/cli/commands/reads.js.map +1 -1
- package/dist/cli/commands/repeat-flags.d.ts +11 -1
- package/dist/cli/commands/repeat-flags.js +23 -0
- package/dist/cli/commands/repeat-flags.js.map +1 -1
- package/dist/cli/commands/todo.js +50 -3
- package/dist/cli/commands/todo.js.map +1 -1
- package/dist/cli/commands/writes.js +202 -61
- package/dist/cli/commands/writes.js.map +1 -1
- package/dist/cli/glyphs.js +9 -10
- package/dist/cli/glyphs.js.map +1 -1
- package/dist/cli/help.js +18 -13
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/read-driver.d.ts +9 -3
- package/dist/cli/read-driver.js +3 -1
- package/dist/cli/read-driver.js.map +1 -1
- package/dist/cli/render.d.ts +33 -2
- package/dist/cli/render.js +60 -9
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/skill.d.ts +24 -0
- package/dist/cli/skill.js +40 -0
- package/dist/cli/skill.js.map +1 -1
- package/dist/client.d.ts +88 -15
- package/dist/client.js +31 -5
- package/dist/client.js.map +1 -1
- package/dist/contracts.d.ts +13 -2
- package/dist/contracts.js +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +187 -36
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/entities.d.ts +97 -40
- package/dist/model/entities.js +0 -4
- package/dist/model/entities.js.map +1 -1
- package/dist/model/mappers.js +24 -43
- package/dist/model/mappers.js.map +1 -1
- package/dist/read/area-view.js +6 -6
- package/dist/read/area-view.js.map +1 -1
- package/dist/read/detail.js +51 -3
- package/dist/read/detail.js.map +1 -1
- package/dist/read/filter-contract.d.ts +1 -1
- package/dist/read/filter-contract.js +3 -0
- package/dist/read/filter-contract.js.map +1 -1
- package/dist/read/log-boundary.d.ts +46 -2
- package/dist/read/log-boundary.js +43 -2
- package/dist/read/log-boundary.js.map +1 -1
- package/dist/read/predicates.d.ts +13 -0
- package/dist/read/predicates.js +13 -0
- package/dist/read/predicates.js.map +1 -1
- package/dist/read/project-view.js +5 -5
- package/dist/read/project-view.js.map +1 -1
- package/dist/read/queries.d.ts +12 -0
- package/dist/read/queries.js +14 -0
- package/dist/read/queries.js.map +1 -1
- package/dist/read/search-rank.js +2 -2
- package/dist/read/search-rank.js.map +1 -1
- package/dist/read/shape.d.ts +30 -26
- package/dist/read/shape.js +130 -101
- package/dist/read/shape.js.map +1 -1
- package/dist/read/snapshot.js +2 -2
- package/dist/read/snapshot.js.map +1 -1
- package/dist/read/stage.d.ts +36 -18
- package/dist/read/stage.js +30 -13
- package/dist/read/stage.js.map +1 -1
- package/dist/read/views.d.ts +62 -0
- package/dist/read/views.js +134 -14
- package/dist/read/views.js.map +1 -1
- package/dist/surface-copy.d.ts +2 -2
- package/dist/surface-copy.js +2 -2
- package/dist/write/batch.d.ts +28 -3
- package/dist/write/batch.js +162 -46
- package/dist/write/batch.js.map +1 -1
- package/dist/write/clear-reminder.js +3 -1
- package/dist/write/clear-reminder.js.map +1 -1
- package/dist/write/clone.d.ts +4 -0
- package/dist/write/clone.js +531 -0
- package/dist/write/clone.js.map +1 -0
- package/dist/write/commands.js +196 -29
- package/dist/write/commands.js.map +1 -1
- package/dist/write/guards.d.ts +1 -1
- package/dist/write/guards.js +44 -12
- package/dist/write/guards.js.map +1 -1
- package/dist/write/make-repeating-project.d.ts +2 -3
- package/dist/write/make-repeating-project.js +2 -73
- package/dist/write/make-repeating-project.js.map +1 -1
- package/dist/write/move.js +53 -1
- package/dist/write/move.js.map +1 -1
- package/dist/write/operations.d.ts +134 -11
- package/dist/write/operations.js +5 -1
- package/dist/write/operations.js.map +1 -1
- package/dist/write/pipeline.d.ts +38 -0
- package/dist/write/pipeline.js +71 -6
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/pre-state.d.ts +41 -0
- package/dist/write/pre-state.js +71 -8
- package/dist/write/pre-state.js.map +1 -1
- package/dist/write/preserve-modified.d.ts +55 -0
- package/dist/write/preserve-modified.js +106 -0
- package/dist/write/preserve-modified.js.map +1 -0
- package/dist/write/promote-clone.d.ts +32 -0
- package/dist/write/promote-clone.js +620 -0
- package/dist/write/promote-clone.js.map +1 -0
- package/dist/write/reorder.js +49 -2
- package/dist/write/reorder.js.map +1 -1
- package/dist/write/resolution-timestamps.js +29 -7
- package/dist/write/resolution-timestamps.js.map +1 -1
- package/dist/write/reversibility.js +32 -14
- package/dist/write/reversibility.js.map +1 -1
- package/dist/write/scope-guard.js +2 -0
- package/dist/write/scope-guard.js.map +1 -1
- package/dist/write/undo.d.ts +12 -2
- package/dist/write/undo.js +145 -22
- package/dist/write/undo.js.map +1 -1
- package/dist/write/vectors/applescript.js +9 -0
- package/dist/write/vectors/applescript.js.map +1 -1
- package/dist/write/vectors/simulator.js +242 -10
- package/dist/write/vectors/simulator.js.map +1 -1
- package/dist/write/vectors/ui-certification.d.ts +1 -1
- package/dist/write/vectors/ui-certification.js +10 -3
- package/dist/write/vectors/ui-certification.js.map +1 -1
- package/dist/write/verify/delta.d.ts +55 -10
- package/dist/write/verify/delta.js +73 -10
- package/dist/write/verify/delta.js.map +1 -1
- package/package.json +1 -1
- package/schema/envelope.schema.json +25 -0
- package/skills/things-cli/SKILL.md +10 -7
- package/skills/things-cli/references/banner.md +1 -1
- package/skills/things-cli/references/contracts.md +17 -9
- package/skills/things-cli/references/{model.md → data-model.md} +5 -2
- package/skills/things-cli/references/ordering.md +2 -0
package/dist/write/commands.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { decodeReminderTime, encodeReminderTime, localToday, reminderUrlToken, zonedWallInstant, } from "../model/dates.js";
|
|
2
2
|
import { byUuid } from "../read/detail.js";
|
|
3
|
+
import { manualLogDateEpoch, pendingLogCount } from "../read/log-boundary.js";
|
|
3
4
|
import { isLooseRef } from "../read/pseudo-area.js";
|
|
4
5
|
import { reminderIsLive } from "../read/stage.js";
|
|
5
6
|
import { areaMemberCounts, childTagTitles, classifyHeadingConvert, classifyHeadingDissolve, classifyHeadingMoveToProject, classifyProjectRepeat, computeHeadingMovePre, computeReorderPre, emptyPreState, loadTarget, projectChildren, projectStatus, projectSubtreeUuids, resolveArea, resolveHeading, resolveProject, resolveTag, sameTitleTaskUuids, trashedCount, } from "./pre-state.js";
|
|
@@ -62,7 +63,7 @@ function q(value) {
|
|
|
62
63
|
function unsupportedVector(op, vector) {
|
|
63
64
|
throw new Error(`${op} cannot be compiled for vector ${vector} (planner bug)`);
|
|
64
65
|
}
|
|
65
|
-
function whenAssertions(when, todayIso) {
|
|
66
|
+
function whenAssertions(when, todayIso, opts = { mode: "add" }) {
|
|
66
67
|
// Strict shape check: an unvalidated string used to flow straight into the
|
|
67
68
|
// URL (e.g. "2026-07-20@09:30", the raw URL grammar) — the app would SET
|
|
68
69
|
// date+reminder while verification asserted the literal string as the date,
|
|
@@ -78,16 +79,35 @@ function whenAssertions(when, todayIso) {
|
|
|
78
79
|
}
|
|
79
80
|
switch (when) {
|
|
80
81
|
case "today":
|
|
82
|
+
// Today's non-evening section: in Today (the `today` marker) AND NOT in the
|
|
83
|
+
// evening sub-bucket (the presence-keyed `evening` marker absent — asserted
|
|
84
|
+
// as null, which valuesEqual treats as absent). Gated to Today members under
|
|
85
|
+
// the verify clock exactly as the retired `todaySection` was.
|
|
86
|
+
//
|
|
87
|
+
// The `startDate` assertion differs by op (field bug §0½.8):
|
|
88
|
+
// - ADD mints a fresh Today item with no schedule history, so the app dates
|
|
89
|
+
// it EXACTLY today — exact equality is right.
|
|
90
|
+
// - UPDATE of an item ALREADY in Today whose `startDate` has already arrived
|
|
91
|
+
// (past or today): the app PRESERVES that historical date rather than
|
|
92
|
+
// rewriting the storage byte to today (arrived-date law). Assert the
|
|
93
|
+
// arrived-date PREDICATE (non-null and <= today) so a preserved historical
|
|
94
|
+
// date verifies, while an undated deadline-only pull (null startDate) is
|
|
95
|
+
// still rejected — the item's Today membership then rests only on a
|
|
96
|
+
// deadline, not on the requested schedule.
|
|
81
97
|
return [
|
|
82
98
|
{ field: "start", equals: "active" },
|
|
83
|
-
|
|
84
|
-
|
|
99
|
+
opts.mode === "update"
|
|
100
|
+
? { field: "startDate", satisfies: { predicate: "arrived-on-or-before", date: todayIso } }
|
|
101
|
+
: { field: "startDate", equals: todayIso },
|
|
102
|
+
{ field: "today", equals: true },
|
|
103
|
+
{ field: "evening", equals: null },
|
|
85
104
|
];
|
|
86
105
|
case "evening":
|
|
106
|
+
// The This-Evening sub-bucket: the `evening` marker (which implies `today`).
|
|
87
107
|
return [
|
|
88
108
|
{ field: "start", equals: "active" },
|
|
89
109
|
{ field: "startDate", equals: todayIso },
|
|
90
|
-
{ field: "
|
|
110
|
+
{ field: "evening", equals: true },
|
|
91
111
|
];
|
|
92
112
|
case "anytime":
|
|
93
113
|
return [
|
|
@@ -304,8 +324,11 @@ const todoAdd = {
|
|
|
304
324
|
}
|
|
305
325
|
if (params.tags !== undefined)
|
|
306
326
|
attrs["tags"] = pre.resolvedTagTitles;
|
|
307
|
-
if (params.checklistItems !== undefined)
|
|
308
|
-
|
|
327
|
+
if (params.checklistItems !== undefined) {
|
|
328
|
+
// §9y: things:///json rejects a bare STRING array wholesale (silent
|
|
329
|
+
// no-op) — emit the OBJECT-array shape the app accepts.
|
|
330
|
+
attrs["checklist-items"] = checklistItemsJsonAttr(params.checklistItems.map((title) => ({ title })));
|
|
331
|
+
}
|
|
309
332
|
if (container?.uuid !== undefined)
|
|
310
333
|
attrs["list-id"] = container.uuid;
|
|
311
334
|
if (pre.destHeading?.resolved?.title !== undefined) {
|
|
@@ -353,7 +376,12 @@ function effectiveReminder(pre, params) {
|
|
|
353
376
|
return null;
|
|
354
377
|
if (!reminderIsLive(target.startDate, pre.todayIso))
|
|
355
378
|
return null;
|
|
356
|
-
|
|
379
|
+
// Read the RAW stored byte off the substrate (top-level `reminder` is
|
|
380
|
+
// live-gated under the LOAD clock, which can differ from `pre.todayIso` under a
|
|
381
|
+
// pinned THINGS_NOW). The `reminderIsLive` guard above applies liveness under
|
|
382
|
+
// the injected clock explicitly, so the raw byte + this gate reproduce the
|
|
383
|
+
// former semantics exactly.
|
|
384
|
+
return target.derived.reminder;
|
|
357
385
|
}
|
|
358
386
|
function assertNotesModesExclusive(params) {
|
|
359
387
|
if (params.notes !== undefined &&
|
|
@@ -396,7 +424,7 @@ const todoUpdate = {
|
|
|
396
424
|
if (joined !== undefined)
|
|
397
425
|
assert.push({ field: "notes", equals: joined });
|
|
398
426
|
if (params.when !== undefined) {
|
|
399
|
-
assert.push(...whenAssertions(params.when, ctx.todayIso));
|
|
427
|
+
assert.push(...whenAssertions(params.when, ctx.todayIso, { mode: "update" }));
|
|
400
428
|
const reminder = effectiveReminder(pre, params);
|
|
401
429
|
assert.push({
|
|
402
430
|
field: "reminder",
|
|
@@ -601,6 +629,48 @@ const todoSetTags = {
|
|
|
601
629
|
},
|
|
602
630
|
};
|
|
603
631
|
/** Normalize the string | spec union; decide whether states force the json form. */
|
|
632
|
+
/**
|
|
633
|
+
* The `checklist-items` value `things:///json` accepts: an array of
|
|
634
|
+
* `{type:"checklist-item",attributes:{title[,completed]}}` OBJECTS. A bare
|
|
635
|
+
* STRING array is rejected wholesale — the whole import silently no-ops, no row,
|
|
636
|
+
* no error (§9y; RESID1 R-JSONPAR RAW-a..d). `completed` is emitted only when a
|
|
637
|
+
* spec carries it, so an at-creation add (all items open) yields the minimal
|
|
638
|
+
* `{title}` attributes the probe validated (OBJ/OBJ3), while the checklist
|
|
639
|
+
* replace path forwards its per-item state.
|
|
640
|
+
*/
|
|
641
|
+
function checklistItemsJsonAttr(specs) {
|
|
642
|
+
return specs.map((s) => ({
|
|
643
|
+
type: "checklist-item",
|
|
644
|
+
attributes: s.completed === undefined ? { title: s.title } : { title: s.title, completed: s.completed },
|
|
645
|
+
}));
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Build a project json import's `items` array from a structured
|
|
649
|
+
* {@link ProjectItemSpec} list (the clone / rich-import path). Order IS the
|
|
650
|
+
* layout: a `heading` node produces a `{type:"heading"}` row and every following
|
|
651
|
+
* `to-do` node inherits it positionally (A4); a `to-do` before the first heading
|
|
652
|
+
* is a project-root child. Children are born OPEN — no `completed` attribute.
|
|
653
|
+
*/
|
|
654
|
+
function projectItemsJsonAttr(items) {
|
|
655
|
+
return items.map((it) => {
|
|
656
|
+
if (it.kind === "heading") {
|
|
657
|
+
return { type: "heading", attributes: { title: it.title } };
|
|
658
|
+
}
|
|
659
|
+
const attrs = { title: it.title };
|
|
660
|
+
if (it.notes !== undefined)
|
|
661
|
+
attrs["notes"] = it.notes;
|
|
662
|
+
if (it.when !== undefined)
|
|
663
|
+
attrs["when"] = it.when;
|
|
664
|
+
if (it.deadline !== undefined)
|
|
665
|
+
attrs["deadline"] = it.deadline;
|
|
666
|
+
if (it.tags !== undefined)
|
|
667
|
+
attrs["tags"] = it.tags;
|
|
668
|
+
if (it.checklistItems !== undefined) {
|
|
669
|
+
attrs["checklist-items"] = checklistItemsJsonAttr(it.checklistItems.map((title) => ({ title })));
|
|
670
|
+
}
|
|
671
|
+
return { type: "to-do", attributes: attrs };
|
|
672
|
+
});
|
|
673
|
+
}
|
|
604
674
|
function checklistSpecs(items) {
|
|
605
675
|
const specs = items.map((i) => typeof i === "string"
|
|
606
676
|
? { title: i, completed: false }
|
|
@@ -647,10 +717,7 @@ const todoReplaceChecklist = {
|
|
|
647
717
|
operation: "update",
|
|
648
718
|
id: params.uuid,
|
|
649
719
|
attributes: {
|
|
650
|
-
"checklist-items": specs
|
|
651
|
-
type: "checklist-item",
|
|
652
|
-
attributes: { title: s.title, completed: s.completed },
|
|
653
|
-
})),
|
|
720
|
+
"checklist-items": checklistItemsJsonAttr(specs),
|
|
654
721
|
},
|
|
655
722
|
},
|
|
656
723
|
]);
|
|
@@ -675,6 +742,28 @@ const todoEditChecklistItem = {
|
|
|
675
742
|
throw new Error(ORCHESTRATED_ONLY);
|
|
676
743
|
},
|
|
677
744
|
};
|
|
745
|
+
const CLONE_ORCHESTRATED_ONLY = "todo.clone / project.clone are delivered by the runCloneTodo / runCloneProject orchestrators (a " +
|
|
746
|
+
"compound over todo.add / project.add plus checklist / terminal-state follow-up legs); they have " +
|
|
747
|
+
"no atomic surface and are never dispatched directly through the pipeline";
|
|
748
|
+
function cloneStub(op) {
|
|
749
|
+
return {
|
|
750
|
+
op,
|
|
751
|
+
hazards: [],
|
|
752
|
+
preRead(db, params) {
|
|
753
|
+
const pre = emptyPreState();
|
|
754
|
+
pre.target = loadTarget(db, params.uuid);
|
|
755
|
+
return pre;
|
|
756
|
+
},
|
|
757
|
+
expectedDelta() {
|
|
758
|
+
throw new Error(CLONE_ORCHESTRATED_ONLY);
|
|
759
|
+
},
|
|
760
|
+
compile() {
|
|
761
|
+
throw new Error(CLONE_ORCHESTRATED_ONLY);
|
|
762
|
+
},
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
const todoClone = cloneStub("todo.clone");
|
|
766
|
+
const projectClone = cloneStub("project.clone");
|
|
678
767
|
const todoDelete = {
|
|
679
768
|
op: "todo.delete",
|
|
680
769
|
hazards: ["H-UNKNOWN-DESTINATION", "H-REPEAT-SCHEDULE"],
|
|
@@ -704,6 +793,13 @@ const projectAdd = {
|
|
|
704
793
|
throw new RangeError("--completed-at cannot seed child to-dos: a completed-project import reverts to open " +
|
|
705
794
|
"unless every child is resolved (§5b) — create the project resolved, then add logged children");
|
|
706
795
|
}
|
|
796
|
+
if (params.todos !== undefined && params.items !== undefined) {
|
|
797
|
+
throw new RangeError("project.add takes either `todos` or structured `items`, not both");
|
|
798
|
+
}
|
|
799
|
+
if (params.completedAt !== undefined && params.items !== undefined && params.items.length > 0) {
|
|
800
|
+
throw new RangeError("--completed-at cannot seed child items: a completed-project import reverts to open " +
|
|
801
|
+
"unless every child is resolved (§5b)");
|
|
802
|
+
}
|
|
707
803
|
const pre = emptyPreState();
|
|
708
804
|
if (containerGiven(params.area))
|
|
709
805
|
pre.destArea = resolveArea(db, params.area);
|
|
@@ -739,7 +835,9 @@ const projectAdd = {
|
|
|
739
835
|
compile(params, vector, pre, ctx) {
|
|
740
836
|
if (vector !== "url-scheme")
|
|
741
837
|
unsupportedVector(this.op, vector);
|
|
742
|
-
|
|
838
|
+
// The json import is required for born-timestamps AND for structured `items`
|
|
839
|
+
// (headings / rich children) — the plain add-project URL carries neither.
|
|
840
|
+
if (addHasTimestamps(params) || params.items !== undefined) {
|
|
743
841
|
const attrs = { title: params.title };
|
|
744
842
|
if (params.notes !== undefined)
|
|
745
843
|
attrs["notes"] = params.notes;
|
|
@@ -749,7 +847,10 @@ const projectAdd = {
|
|
|
749
847
|
attrs["when"] = params.when;
|
|
750
848
|
if (params.deadline !== undefined)
|
|
751
849
|
attrs["deadline"] = params.deadline;
|
|
752
|
-
if (params.
|
|
850
|
+
if (params.items !== undefined) {
|
|
851
|
+
attrs["items"] = projectItemsJsonAttr(params.items);
|
|
852
|
+
}
|
|
853
|
+
else if (params.todos !== undefined) {
|
|
753
854
|
attrs["items"] = params.todos.map((t) => ({ type: "to-do", attributes: { title: t } }));
|
|
754
855
|
}
|
|
755
856
|
}
|
|
@@ -787,7 +888,7 @@ const projectUpdate = {
|
|
|
787
888
|
if (joined !== undefined)
|
|
788
889
|
assert.push({ field: "notes", equals: joined });
|
|
789
890
|
if (params.when !== undefined) {
|
|
790
|
-
assert.push(...whenAssertions(params.when, ctx.todayIso));
|
|
891
|
+
assert.push(...whenAssertions(params.when, ctx.todayIso, { mode: "update" }));
|
|
791
892
|
// Projects carry the same reminderTime codec as to-dos (A3); a bare
|
|
792
893
|
// when= clears an existing reminder unless auto-preserved.
|
|
793
894
|
const reminder = effectiveReminder(pre, params);
|
|
@@ -1046,7 +1147,10 @@ const projectReopen = {
|
|
|
1046
1147
|
};
|
|
1047
1148
|
const projectRestore = {
|
|
1048
1149
|
op: "project.restore",
|
|
1049
|
-
|
|
1150
|
+
// H-REPEAT-SCHEDULE: a trashed repeating PROJECT template cannot be restored
|
|
1151
|
+
// headlessly (Put Back only) — refuse categorically instead of the raw AS-301
|
|
1152
|
+
// no-op. (The to-do path already carries it; kind-parity, 2026-08-13.)
|
|
1153
|
+
hazards: ["H-UNKNOWN-DESTINATION", "H-REPEAT-SCHEDULE"],
|
|
1050
1154
|
preRead(db, params) {
|
|
1051
1155
|
const pre = emptyPreState();
|
|
1052
1156
|
pre.target = loadTarget(db, params.uuid);
|
|
@@ -1390,6 +1494,13 @@ const reorder = {
|
|
|
1390
1494
|
// Verify the REQUESTED sequence (strictly ascending ranks). The wire
|
|
1391
1495
|
// list pins the unrequested tail too, but the caller's contract is the
|
|
1392
1496
|
// requested prefix; tail members are covered by pre-rank tripwires.
|
|
1497
|
+
// TODWIRE: on the `today` scope the wire is now MINIMAL — only the named
|
|
1498
|
+
// block gets fresh (front, monotonic) `todayIndex`; the unnamed tail keeps
|
|
1499
|
+
// its cohort-interleaved (non-monotonic) `todayIndex`, so the FULL
|
|
1500
|
+
// `params.uuids` is NOT strictly-ascending after the write. Verify the
|
|
1501
|
+
// `todayWire` (the named block) instead — it lands at the visible front in
|
|
1502
|
+
// wire order with fresh ascending values, and the untouched tail rides the
|
|
1503
|
+
// pre-rank tripwires. Every other scope verifies the requested prefix.
|
|
1393
1504
|
// LOGSORT ORD-13 byte-lock: any admitted UNSWEPT-resolved movee must read
|
|
1394
1505
|
// back index-only — status still closed, stoppedDate intact, umd unbumped
|
|
1395
1506
|
// (a reopen would flip all three). Frozen assertions carry that into verify.
|
|
@@ -1400,11 +1511,14 @@ const reorder = {
|
|
|
1400
1511
|
{ field: "stoppedDate", equals: m.stoppedDate },
|
|
1401
1512
|
],
|
|
1402
1513
|
}));
|
|
1514
|
+
const sequence = params.scope === "today" && pre.reorder?.todayWire != null
|
|
1515
|
+
? pre.reorder.todayWire
|
|
1516
|
+
: params.uuids;
|
|
1403
1517
|
return {
|
|
1404
1518
|
mode: "ordering",
|
|
1405
1519
|
key: pre.reorder?.key ??
|
|
1406
1520
|
(params.scope === "today" || params.scope === "evening" ? "todayIndex" : "index"),
|
|
1407
|
-
sequence
|
|
1521
|
+
sequence,
|
|
1408
1522
|
...(frozen.length > 0 && { frozen }),
|
|
1409
1523
|
};
|
|
1410
1524
|
},
|
|
@@ -1429,7 +1543,14 @@ const reorder = {
|
|
|
1429
1543
|
: params.scope === "upcoming"
|
|
1430
1544
|
? `list "Upcoming"`
|
|
1431
1545
|
: `list "Today"`;
|
|
1432
|
-
|
|
1546
|
+
// TODWIRE — the `today` scope sends the MINIMAL visible-order wire (names only
|
|
1547
|
+
// what must move; unnamed rows keep their entry cohorts + visible positions),
|
|
1548
|
+
// not the OLD full `wireList` that fused every cohort (MOVPLC/ORD-20). Every
|
|
1549
|
+
// other native scope keeps the full wire (their `index`/day axes do not
|
|
1550
|
+
// re-stamp `tiRef`, so a full re-rank is non-damaging).
|
|
1551
|
+
const wire = (params.scope === "today" ? pre.reorder?.todayWire : undefined) ??
|
|
1552
|
+
pre.reorder?.wireList ??
|
|
1553
|
+
params.uuids;
|
|
1433
1554
|
if (params.scope === "someday") {
|
|
1434
1555
|
// The Someday handler STACKS each sent id above the list's current top
|
|
1435
1556
|
// (the current top itself never moves), with OPPOSITE stack directions
|
|
@@ -1514,8 +1635,8 @@ function setDatesSpec(op, addressor) {
|
|
|
1514
1635
|
op,
|
|
1515
1636
|
hazards: ["H-UNKNOWN-DESTINATION", "H-BACKDATE-OPEN"],
|
|
1516
1637
|
preRead(db, params) {
|
|
1517
|
-
if (params.
|
|
1518
|
-
throw new RangeError("nothing to set: give
|
|
1638
|
+
if (params.completedAt === undefined && params.createdAt === undefined) {
|
|
1639
|
+
throw new RangeError("nothing to set: give completedAt and/or createdAt");
|
|
1519
1640
|
}
|
|
1520
1641
|
const pre = emptyPreState();
|
|
1521
1642
|
pre.target = loadTarget(db, params.uuid);
|
|
@@ -1523,16 +1644,16 @@ function setDatesSpec(op, addressor) {
|
|
|
1523
1644
|
},
|
|
1524
1645
|
expectedDelta(_pre, params, ctx) {
|
|
1525
1646
|
const assert = [];
|
|
1526
|
-
if (params.
|
|
1647
|
+
if (params.completedAt !== undefined) {
|
|
1527
1648
|
assert.push({
|
|
1528
1649
|
field: "stoppedDate",
|
|
1529
|
-
equals: hostLocalDate(resolveResolutionInstant(params.
|
|
1650
|
+
equals: hostLocalDate(resolveResolutionInstant(params.completedAt, ctx.zone)),
|
|
1530
1651
|
});
|
|
1531
1652
|
}
|
|
1532
|
-
if (params.
|
|
1653
|
+
if (params.createdAt !== undefined) {
|
|
1533
1654
|
assert.push({
|
|
1534
1655
|
field: "createdDate",
|
|
1535
|
-
equals: hostLocalDate(resolveResolutionInstant(params.
|
|
1656
|
+
equals: hostLocalDate(resolveResolutionInstant(params.createdAt, ctx.zone)),
|
|
1536
1657
|
});
|
|
1537
1658
|
}
|
|
1538
1659
|
return { mode: "update", uuid: params.uuid, assert };
|
|
@@ -1541,11 +1662,11 @@ function setDatesSpec(op, addressor) {
|
|
|
1541
1662
|
if (vector !== "applescript")
|
|
1542
1663
|
unsupportedVector(this.op, vector);
|
|
1543
1664
|
const statements = [];
|
|
1544
|
-
if (params.
|
|
1545
|
-
statements.push(...asDateBlockFromInstant("compDate", resolveResolutionInstant(params.
|
|
1665
|
+
if (params.completedAt !== undefined) {
|
|
1666
|
+
statements.push(...asDateBlockFromInstant("compDate", resolveResolutionInstant(params.completedAt, ctx.zone)), `set completion date of ${addressor} id ${q(params.uuid)} to compDate`);
|
|
1546
1667
|
}
|
|
1547
|
-
if (params.
|
|
1548
|
-
statements.push(...asDateBlockFromInstant("createDate", resolveResolutionInstant(params.
|
|
1668
|
+
if (params.createdAt !== undefined) {
|
|
1669
|
+
statements.push(...asDateBlockFromInstant("createDate", resolveResolutionInstant(params.createdAt, ctx.zone)), `set creation date of ${addressor} id ${q(params.uuid)} to createDate`);
|
|
1549
1670
|
}
|
|
1550
1671
|
return osaBlock(statements);
|
|
1551
1672
|
},
|
|
@@ -2013,7 +2134,7 @@ const projectResumeRepeat = {
|
|
|
2013
2134
|
};
|
|
2014
2135
|
// project.make-repeating: the pure-AX row-selection op (UIC4). The command
|
|
2015
2136
|
// spec drives the area / someday cases; the area-less-anytime coercion is done
|
|
2016
|
-
// by the
|
|
2137
|
+
// by the promoteProjectViaGui internal orchestrator BEFORE this spec sees the target,
|
|
2017
2138
|
// so by drive time the taxonomy is always area or someday. A DIRECT dispatch on
|
|
2018
2139
|
// an anytime project is refused by H-PROJECT-REPEAT (with the orchestrator as
|
|
2019
2140
|
// the remediation).
|
|
@@ -2091,6 +2212,25 @@ const projectAddRepeating = {
|
|
|
2091
2212
|
throw new Error(ADD_REPEATING_ORCHESTRATED);
|
|
2092
2213
|
},
|
|
2093
2214
|
};
|
|
2215
|
+
// todo.add-repeating is delivered by the runAddRepeatingTodo orchestrator
|
|
2216
|
+
// (todo.add THEN native promote); it has no atomic surface and is never
|
|
2217
|
+
// dispatched directly through the pipeline (promote-clone.ts).
|
|
2218
|
+
const TODO_ADD_REPEATING_ORCHESTRATED = "todo.add-repeating is delivered by the runAddRepeatingTodo orchestrator (create the to-do, " +
|
|
2219
|
+
"then promote it to a repeating series); it has no atomic surface and is never dispatched " +
|
|
2220
|
+
"directly through the pipeline";
|
|
2221
|
+
const todoAddRepeating = {
|
|
2222
|
+
op: "todo.add-repeating",
|
|
2223
|
+
hazards: [],
|
|
2224
|
+
preRead() {
|
|
2225
|
+
return emptyPreState();
|
|
2226
|
+
},
|
|
2227
|
+
expectedDelta() {
|
|
2228
|
+
throw new Error(TODO_ADD_REPEATING_ORCHESTRATED);
|
|
2229
|
+
},
|
|
2230
|
+
compile() {
|
|
2231
|
+
throw new Error(TODO_ADD_REPEATING_ORCHESTRATED);
|
|
2232
|
+
},
|
|
2233
|
+
};
|
|
2094
2234
|
const todoConvertToProject = {
|
|
2095
2235
|
op: "todo.convert-to-project",
|
|
2096
2236
|
hazards: UI_HAZARDS,
|
|
@@ -2356,6 +2496,29 @@ const trashEmpty = {
|
|
|
2356
2496
|
return osa("empty trash");
|
|
2357
2497
|
},
|
|
2358
2498
|
};
|
|
2499
|
+
const logNow = {
|
|
2500
|
+
op: "log-now",
|
|
2501
|
+
hazards: [],
|
|
2502
|
+
preRead(db, _params, now, zone) {
|
|
2503
|
+
const pre = emptyPreState();
|
|
2504
|
+
// The resolved-but-unlogged census the verb will move (the disclosed count),
|
|
2505
|
+
// and the pre-op boundary stamp the delta compares against.
|
|
2506
|
+
pre.logNow = {
|
|
2507
|
+
pending: pendingLogCount(db, now, zone),
|
|
2508
|
+
manualLogDatePre: manualLogDateEpoch(db),
|
|
2509
|
+
};
|
|
2510
|
+
return pre;
|
|
2511
|
+
},
|
|
2512
|
+
expectedDelta(pre) {
|
|
2513
|
+
const ln = pre.logNow ?? { pending: 0, manualLogDatePre: null };
|
|
2514
|
+
return { mode: "logged-now", pending: ln.pending, manualLogDatePre: ln.manualLogDatePre };
|
|
2515
|
+
},
|
|
2516
|
+
compile(_params, vector) {
|
|
2517
|
+
if (vector !== "applescript")
|
|
2518
|
+
unsupportedVector(this.op, vector);
|
|
2519
|
+
return osa("log completed now");
|
|
2520
|
+
},
|
|
2521
|
+
};
|
|
2359
2522
|
export const COMMANDS = {
|
|
2360
2523
|
"todo.add": todoAdd,
|
|
2361
2524
|
"todo.update": todoUpdate,
|
|
@@ -2409,5 +2572,9 @@ export const COMMANDS = {
|
|
|
2409
2572
|
"area.reorder": areaReorderSidebar,
|
|
2410
2573
|
"project.make-repeating": projectMakeRepeating,
|
|
2411
2574
|
"project.add-repeating": projectAddRepeating,
|
|
2575
|
+
"todo.add-repeating": todoAddRepeating,
|
|
2576
|
+
"todo.clone": todoClone,
|
|
2577
|
+
"project.clone": projectClone,
|
|
2578
|
+
"log-now": logNow,
|
|
2412
2579
|
};
|
|
2413
2580
|
//# sourceMappingURL=commands.js.map
|