things-api 0.12.0 → 0.14.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 +2 -2
- package/dist/cli/canonical-ref.d.ts +21 -0
- package/dist/cli/canonical-ref.js +23 -0
- package/dist/cli/canonical-ref.js.map +1 -0
- package/dist/cli/commands/area.d.ts +5 -5
- package/dist/cli/commands/area.js +39 -18
- package/dist/cli/commands/area.js.map +1 -1
- package/dist/cli/commands/install-skill.d.ts +2 -2
- package/dist/cli/commands/install-skill.js +29 -0
- package/dist/cli/commands/install-skill.js.map +1 -1
- package/dist/cli/commands/mcp.js +12 -1
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/project.js +74 -22
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/reads.d.ts +5 -1
- package/dist/cli/commands/reads.js +31 -15
- package/dist/cli/commands/reads.js.map +1 -1
- package/dist/cli/commands/setup.js +11 -4
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/show.js +51 -32
- package/dist/cli/commands/show.js.map +1 -1
- package/dist/cli/commands/todo.js +2 -2
- package/dist/cli/commands/todo.js.map +1 -1
- package/dist/cli/commands/writes.js +138 -67
- package/dist/cli/commands/writes.js.map +1 -1
- package/dist/cli/did-you-mean.js +19 -5
- package/dist/cli/did-you-mean.js.map +1 -1
- package/dist/cli/dry-run.d.ts +55 -0
- package/dist/cli/dry-run.js +92 -0
- package/dist/cli/dry-run.js.map +1 -0
- package/dist/cli/glyphs.d.ts +9 -2
- package/dist/cli/glyphs.js +11 -5
- package/dist/cli/glyphs.js.map +1 -1
- package/dist/cli/help.js +7 -6
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/main.js +7 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/read-driver.d.ts +47 -7
- package/dist/cli/read-driver.js +43 -21
- package/dist/cli/read-driver.js.map +1 -1
- package/dist/cli/ref-render.d.ts +14 -0
- package/dist/cli/ref-render.js +9 -0
- package/dist/cli/ref-render.js.map +1 -0
- package/dist/cli/render.d.ts +39 -25
- package/dist/cli/render.js +102 -62
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/resolve-invocation.d.ts +5 -3
- package/dist/cli/resolve-invocation.js +13 -4
- package/dist/cli/resolve-invocation.js.map +1 -1
- package/dist/client.d.ts +100 -32
- package/dist/client.js +63 -19
- package/dist/client.js.map +1 -1
- package/dist/contracts.d.ts +34 -33
- package/dist/contracts.js +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/db/baselines/db-v26.js +4 -1
- package/dist/db/baselines/db-v26.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +192 -139
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/dates.d.ts +26 -0
- package/dist/model/dates.js +46 -4
- package/dist/model/dates.js.map +1 -1
- package/dist/model/entities.d.ts +11 -1
- package/dist/model/entities.js.map +1 -1
- package/dist/model/mappers.js +3 -0
- package/dist/model/mappers.js.map +1 -1
- package/dist/model/serialize.js +15 -3
- package/dist/model/serialize.js.map +1 -1
- package/dist/read/area-filter.d.ts +2 -2
- package/dist/read/area-filter.js +3 -3
- package/dist/read/area-filter.js.map +1 -1
- package/dist/read/project-view.d.ts +76 -1
- package/dist/read/project-view.js +80 -9
- package/dist/read/project-view.js.map +1 -1
- package/dist/read/queries.d.ts +71 -2
- package/dist/read/queries.js +199 -10
- package/dist/read/queries.js.map +1 -1
- package/dist/read/scope.d.ts +2 -2
- package/dist/read/scope.js +3 -3
- package/dist/read/scope.js.map +1 -1
- package/dist/read/shape.d.ts +87 -9
- package/dist/read/shape.js +483 -124
- package/dist/read/shape.js.map +1 -1
- package/dist/read/show-target.js +52 -33
- package/dist/read/show-target.js.map +1 -1
- package/dist/read/truncation.d.ts +73 -7
- package/dist/read/truncation.js +62 -29
- package/dist/read/truncation.js.map +1 -1
- package/dist/read/views.d.ts +20 -7
- package/dist/read/views.js +7 -7
- package/dist/read/views.js.map +1 -1
- package/dist/surface-copy.d.ts +13 -0
- package/dist/surface-copy.js +13 -0
- package/dist/surface-copy.js.map +1 -1
- package/dist/write/batch.js +0 -1
- package/dist/write/batch.js.map +1 -1
- package/dist/write/commands.d.ts +25 -1
- package/dist/write/commands.js +259 -122
- package/dist/write/commands.js.map +1 -1
- package/dist/write/guards.js +27 -11
- package/dist/write/guards.js.map +1 -1
- package/dist/write/move.d.ts +34 -9
- package/dist/write/move.js +730 -78
- package/dist/write/move.js.map +1 -1
- package/dist/write/operations.d.ts +50 -20
- package/dist/write/operations.js +2 -2
- package/dist/write/operations.js.map +1 -1
- package/dist/write/pipeline.js +25 -4
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/pre-state.d.ts +58 -28
- package/dist/write/pre-state.js +247 -49
- package/dist/write/pre-state.js.map +1 -1
- package/dist/write/reorder.js +266 -32
- package/dist/write/reorder.js.map +1 -1
- package/dist/write/resolution-timestamps.d.ts +34 -0
- package/dist/write/resolution-timestamps.js +286 -0
- package/dist/write/resolution-timestamps.js.map +1 -0
- package/dist/write/reversibility.js +5 -5
- package/dist/write/reversibility.js.map +1 -1
- package/dist/write/scope-guard.js +0 -5
- package/dist/write/scope-guard.js.map +1 -1
- package/dist/write/undo.d.ts +0 -6
- package/dist/write/undo.js +44 -4
- package/dist/write/undo.js.map +1 -1
- package/dist/write/vectors/applescript.js +14 -4
- package/dist/write/vectors/applescript.js.map +1 -1
- package/dist/write/vectors/url-scheme.js +0 -8
- package/dist/write/vectors/url-scheme.js.map +1 -1
- package/dist/write/verify/delta.d.ts +16 -2
- package/dist/write/verify/delta.js +21 -2
- package/dist/write/verify/delta.js.map +1 -1
- package/package.json +1 -1
- package/schema/envelope.schema.json +16 -78
- package/skills/things-cli/SKILL.md +10 -6
- package/skills/things-cli/references/contracts.md +3 -2
- package/skills/things-cli/references/errors.md +9 -1
- package/skills/things-cli/references/model.md +9 -7
- package/skills/things-cli/references/ordering.md +36 -10
package/dist/mcp/server.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { z } from "zod";
|
|
18
18
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
|
-
import { ALL_DESC, AREA_FILTER_DESC, AREA_LIMIT_DESC, AREA_PREVIEW_LIMIT, blockedCode, capabilitiesTable, DATE_FORMAT, DEFAULT_LIST_LIMIT, diagnose, FILTER_CONTRACT, FULL_DESC, hasTagPresence, isLooseRef, isValidTimeZone, LIMIT_DESC, looseShadowNotice, MCP_WHEN_LABELS, mutationWireData, noUuidMatch, omitEmpty, OMIT_EMPTY_NOTE, OP_ID_RE, OPERATION_KINDS, openThings, PKG_VERSION, PROJECT_LIMIT_DESC, PROJECT_PREVIEW_LIMIT, REF_FORMAT, ReferenceResolutionError, REMINDER_FORMAT, schemaWarnings, shapeReadPayload, splitWhenSugar, tagFilterFields, tagFlagConflict, validateViewArgs, verifyFailedCode, WHEN_VALUES, } from "../index.js";
|
|
19
|
+
import { ALL_DESC, AREA_FILTER_DESC, AREA_LIMIT_DESC, AREA_PREVIEW_LIMIT, blockedCode, capabilitiesTable, DATE_FORMAT, DEFAULT_LIST_LIMIT, diagnose, FILTER_CONTRACT, FULL_DESC, hasTagPresence, isLooseRef, isValidTimeZone, LIMIT_DESC, looseShadowNotice, MCP_WHEN_LABELS, mutationWireData, noUuidMatch, omitEmpty, OMIT_EMPTY_NOTE, OP_ID_RE, OPERATION_KINDS, openThings, PKG_VERSION, PROJECT_LIMIT_DESC, PROJECT_PREVIEW_LIMIT, REF_FORMAT, REF_RULE_NOTE, ReferenceResolutionError, REMINDER_FORMAT, RESOLUTION_DATE_FORMAT, schemaWarnings, shapeReadPayload, withTodayBucketTotals, withAreaBucketTotals, withSectionTotals, withUpcomingBlockTotals, splitWhenSugar, tagFilterFields, tagFlagConflict, validateViewArgs, verifyFailedCode, WHEN_VALUES, } from "../index.js";
|
|
20
20
|
function jsonResult(data) {
|
|
21
21
|
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
22
22
|
}
|
|
@@ -32,24 +32,31 @@ function readResult(data) {
|
|
|
32
32
|
/**
|
|
33
33
|
* A read result carrying truncation metadata: the data (already limited) in
|
|
34
34
|
* the first content block, and a second block with the {@link Truncation}
|
|
35
|
-
* numbers plus a one-line note the agent can read when rows were dropped.
|
|
35
|
+
* numbers plus a one-line note the agent can read when rows were dropped. `meta`
|
|
36
|
+
* merges extra whole-view metadata into that second block — the today view's
|
|
37
|
+
* `counts` aggregate (the analog of the CLI envelope's `meta.counts`), so `data`
|
|
38
|
+
* stays pure domain rows.
|
|
36
39
|
*/
|
|
37
|
-
function truncatedResult(data, truncation) {
|
|
40
|
+
function truncatedResult(data, truncation, meta) {
|
|
38
41
|
const note = truncation.truncated
|
|
39
42
|
? `showing ${truncation.shown} of ${truncation.total} items — pass limit (or all: true) to see more`
|
|
40
43
|
: undefined;
|
|
41
44
|
return {
|
|
42
45
|
content: [
|
|
43
46
|
{ type: "text", text: JSON.stringify(omitEmpty(data)) },
|
|
44
|
-
{
|
|
47
|
+
{
|
|
48
|
+
type: "text",
|
|
49
|
+
text: JSON.stringify({ truncation, ...meta, ...(note !== undefined && { note }) }),
|
|
50
|
+
},
|
|
45
51
|
],
|
|
46
52
|
};
|
|
47
53
|
}
|
|
48
54
|
/**
|
|
49
|
-
* Grouped read result (anytime/someday): the per-block-truncated sections
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* agent can read.
|
|
55
|
+
* Grouped read result (anytime/someday): the per-block-truncated sections (each
|
|
56
|
+
* capped section carrying its inline `total`, R1) plus a second block carrying
|
|
57
|
+
* the WHOLE-VIEW {@link Truncation} rollup and, when anything was hidden, a
|
|
58
|
+
* one-line note the agent can read. The pre-v2 `blocks[]` sidecar is retired —
|
|
59
|
+
* completeness rides each section's inline `total` (doctrine v2 PR 5).
|
|
53
60
|
*/
|
|
54
61
|
function groupedResult(data, truncation) {
|
|
55
62
|
const note = truncation.truncated
|
|
@@ -173,7 +180,11 @@ function moveResult(result) {
|
|
|
173
180
|
return jsonResult(result.plan);
|
|
174
181
|
case "move-refused":
|
|
175
182
|
return errorResult({
|
|
176
|
-
|
|
183
|
+
// A hoisted placement block names its hazard — surface the canonical
|
|
184
|
+
// `blocked:<hazard>` code, matching a direct reorder hazard block.
|
|
185
|
+
code: result.hazard !== undefined
|
|
186
|
+
? blockedCode({ hazard: result.hazard, reason: "hazard" })
|
|
187
|
+
: result.refusal,
|
|
177
188
|
message: result.detail,
|
|
178
189
|
...(result.remediation !== undefined && { remediation: result.remediation }),
|
|
179
190
|
...(result.candidates !== undefined && { details: { candidates: result.candidates } }),
|
|
@@ -228,6 +239,26 @@ function movePositionArgs(args) {
|
|
|
228
239
|
return { after: args.after };
|
|
229
240
|
return undefined;
|
|
230
241
|
}
|
|
242
|
+
/** The universal-reorder anchor grammar (start/end/before/after → MovePosition). */
|
|
243
|
+
function reorderPositionArgs(args) {
|
|
244
|
+
const chosen = [
|
|
245
|
+
args.start === true,
|
|
246
|
+
args.end === true,
|
|
247
|
+
args.before !== undefined,
|
|
248
|
+
args.after !== undefined,
|
|
249
|
+
].filter(Boolean).length;
|
|
250
|
+
if (chosen > 1)
|
|
251
|
+
return "conflict";
|
|
252
|
+
if (args.start === true)
|
|
253
|
+
return { at: "first" };
|
|
254
|
+
if (args.end === true)
|
|
255
|
+
return { at: "last" };
|
|
256
|
+
if (args.before !== undefined)
|
|
257
|
+
return { before: args.before };
|
|
258
|
+
if (args.after !== undefined)
|
|
259
|
+
return { after: args.after };
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
231
262
|
/** The shared position input schema for the move tools. */
|
|
232
263
|
const positionShape = {
|
|
233
264
|
first: z.boolean().optional().describe("place the block at the top of its bucket"),
|
|
@@ -566,19 +597,23 @@ export function createThingsMcpServer(options = {}) {
|
|
|
566
597
|
};
|
|
567
598
|
// ------------------------------------------------------------------ reads
|
|
568
599
|
server.registerTool("read_view", {
|
|
569
|
-
description: "Read a Things list as the app presents it: today (
|
|
570
|
-
"
|
|
600
|
+
description: "Read a Things list as the app presents it: today (two children buckets — " +
|
|
601
|
+
"children.today and children.evening, evening expires daily; the whole-view " +
|
|
602
|
+
"count due/overdue vs. other rides the result's second block), inbox, anytime, " +
|
|
603
|
+
"upcoming, someday, logbook, or trash. For upcoming, " +
|
|
571
604
|
"horizon > 1 also includes future occurrences of repeating items (up to 10 each). " +
|
|
572
605
|
"anytime/someday return sections in canonical order (area + items; null area = the " +
|
|
573
606
|
"top-level block); children of someday/future-scheduled projects are excluded " +
|
|
574
607
|
"from anytime — the project row represents them; someday lists each group's " +
|
|
575
|
-
"project rows before its to-dos.
|
|
608
|
+
"project rows before its to-dos. Row-capped views (today/inbox/upcoming/logbook/trash) " +
|
|
576
609
|
`return at most ${DEFAULT_LIST_LIMIT} items by default (raise with limit); ` +
|
|
577
610
|
"anytime/someday always return every group and cap per block instead — " +
|
|
578
611
|
`area_limit (default ${AREA_PREVIEW_LIMIT}) per area block, and on anytime ` +
|
|
579
612
|
`project_limit (default ${PROJECT_PREVIEW_LIMIT}) per project block. ` +
|
|
580
613
|
"all: true lifts every cap; the result's second block reports the counts. " +
|
|
581
|
-
OMIT_EMPTY_NOTE
|
|
614
|
+
OMIT_EMPTY_NOTE +
|
|
615
|
+
" " +
|
|
616
|
+
REF_RULE_NOTE,
|
|
582
617
|
inputSchema: {
|
|
583
618
|
view: z.enum(["today", "inbox", "anytime", "upcoming", "someday", "logbook", "trash"]),
|
|
584
619
|
...tagFilterShape,
|
|
@@ -690,7 +725,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
690
725
|
const area = args.area !== undefined ? { area: args.area } : {};
|
|
691
726
|
switch (args.view) {
|
|
692
727
|
case "today": {
|
|
693
|
-
const { view, truncation, filter: fm, } = c.read.today({
|
|
728
|
+
const { view, truncation, totals, filter: fm, } = c.read.today({
|
|
694
729
|
...filter,
|
|
695
730
|
...zone,
|
|
696
731
|
...area,
|
|
@@ -698,14 +733,17 @@ export function createThingsMcpServer(options = {}) {
|
|
|
698
733
|
limit,
|
|
699
734
|
});
|
|
700
735
|
filterMeta = fm;
|
|
701
|
-
|
|
736
|
+
// The data block is the two `children` bucket records (each with its
|
|
737
|
+
// inline `total` when capped); the whole-view `counts` aggregate
|
|
738
|
+
// rides the metadata block (the CLI meta.counts analog).
|
|
739
|
+
return truncatedResult(withTodayBucketTotals(shapeReadPayload("today", view, full, c.refPromoter()), totals), truncation, { counts: view.counts });
|
|
702
740
|
}
|
|
703
741
|
case "inbox": {
|
|
704
742
|
const { items, truncation } = c.read.inbox({ ...filter, ...zone, limit });
|
|
705
|
-
return truncatedResult(shapeReadPayload("inbox", items, full), truncation);
|
|
743
|
+
return truncatedResult(shapeReadPayload("inbox", items, full, c.refPromoter()), truncation);
|
|
706
744
|
}
|
|
707
745
|
case "anytime": {
|
|
708
|
-
const { view, truncation, filter: fm, } = c.read.anytime({
|
|
746
|
+
const { view, truncation, sectionTotals, filter: fm, } = c.read.anytime({
|
|
709
747
|
...filter,
|
|
710
748
|
...zone,
|
|
711
749
|
...area,
|
|
@@ -713,10 +751,10 @@ export function createThingsMcpServer(options = {}) {
|
|
|
713
751
|
projectLimit,
|
|
714
752
|
});
|
|
715
753
|
filterMeta = fm;
|
|
716
|
-
return groupedResult(shapeReadPayload("anytime", view, full), truncation);
|
|
754
|
+
return groupedResult(withSectionTotals(shapeReadPayload("anytime", view, full, c.refPromoter()), sectionTotals), truncation);
|
|
717
755
|
}
|
|
718
756
|
case "upcoming": {
|
|
719
|
-
const { items, truncation, filter: fm, } = c.read.upcoming({
|
|
757
|
+
const { items, truncation, upcomingTotals, filter: fm, } = c.read.upcoming({
|
|
720
758
|
...filter,
|
|
721
759
|
...zone,
|
|
722
760
|
...area,
|
|
@@ -724,14 +762,16 @@ export function createThingsMcpServer(options = {}) {
|
|
|
724
762
|
limit,
|
|
725
763
|
});
|
|
726
764
|
filterMeta = fm;
|
|
727
|
-
|
|
765
|
+
// The data block is the day-block `sections` array, each capped block
|
|
766
|
+
// carrying its inline `total` (R1); the flat rollup rides truncation.
|
|
767
|
+
return truncatedResult(withUpcomingBlockTotals(shapeReadPayload("upcoming", items, full, c.refPromoter()), upcomingTotals), truncation);
|
|
728
768
|
}
|
|
729
769
|
case "someday": {
|
|
730
770
|
const active = showActiveProjectItems;
|
|
731
771
|
if (typeof active === "number" && args.all === true) {
|
|
732
772
|
return usage("pass at most one of a numeric show_active_project_items / all");
|
|
733
773
|
}
|
|
734
|
-
const { view, truncation, filter: fm, } = c.read.someday({
|
|
774
|
+
const { view, truncation, sectionTotals, filter: fm, } = c.read.someday({
|
|
735
775
|
...filter,
|
|
736
776
|
...zone,
|
|
737
777
|
...area,
|
|
@@ -743,7 +783,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
743
783
|
projectLimit: typeof active === "number" ? active : null,
|
|
744
784
|
});
|
|
745
785
|
filterMeta = fm;
|
|
746
|
-
return groupedResult(shapeReadPayload("someday", view, full), truncation);
|
|
786
|
+
return groupedResult(withSectionTotals(shapeReadPayload("someday", view, full, c.refPromoter()), sectionTotals), truncation);
|
|
747
787
|
}
|
|
748
788
|
case "logbook": {
|
|
749
789
|
const { items, truncation, filter: fm, } = c.read.logbook({
|
|
@@ -753,11 +793,11 @@ export function createThingsMcpServer(options = {}) {
|
|
|
753
793
|
limit,
|
|
754
794
|
});
|
|
755
795
|
filterMeta = fm;
|
|
756
|
-
return truncatedResult(shapeReadPayload("logbook", items, full), truncation);
|
|
796
|
+
return truncatedResult(shapeReadPayload("logbook", items, full, c.refPromoter()), truncation);
|
|
757
797
|
}
|
|
758
798
|
case "trash": {
|
|
759
799
|
const { items, truncation } = c.read.trash({ ...zone, limit });
|
|
760
|
-
return truncatedResult(shapeReadPayload("trash", items, full), truncation);
|
|
800
|
+
return truncatedResult(shapeReadPayload("trash", items, full, c.refPromoter()), truncation);
|
|
761
801
|
}
|
|
762
802
|
}
|
|
763
803
|
}, args.tz, () => (filterMeta !== undefined ? { filter: filterMeta } : undefined), () => looseAreaWarnings(getClient(), args.area));
|
|
@@ -766,7 +806,9 @@ export function createThingsMcpServer(options = {}) {
|
|
|
766
806
|
description: "Find items by title/notes substring. Returns open, untrashed items by default; " +
|
|
767
807
|
"include more with logged/trashed/all. Scope with project/area/tag — scope " +
|
|
768
808
|
"references must name existing items. " +
|
|
769
|
-
OMIT_EMPTY_NOTE
|
|
809
|
+
OMIT_EMPTY_NOTE +
|
|
810
|
+
" " +
|
|
811
|
+
REF_RULE_NOTE,
|
|
770
812
|
inputSchema: {
|
|
771
813
|
query: z.string(),
|
|
772
814
|
...tagFilterShape,
|
|
@@ -821,13 +863,15 @@ export function createThingsMcpServer(options = {}) {
|
|
|
821
863
|
...(args.trashed === true && { trashed: true }),
|
|
822
864
|
...(args.all === true && { all: true }),
|
|
823
865
|
});
|
|
824
|
-
return truncatedResult(shapeReadPayload("search", items, args.full === true), truncation);
|
|
866
|
+
return truncatedResult(shapeReadPayload("search", items, args.full === true, getClient().refPromoter()), truncation);
|
|
825
867
|
}, args.tz, undefined, () => looseAreaWarnings(getClient(), args.area)));
|
|
826
868
|
server.registerTool("changes_since", {
|
|
827
869
|
description: "List items created or modified since a moment — including trashed, logged, and " +
|
|
828
870
|
"repeating items (inspect each item's fields to tell them apart). Edits to tags, " +
|
|
829
871
|
"areas, and checklist items do not mark the containing item as modified. " +
|
|
830
|
-
OMIT_EMPTY_NOTE
|
|
872
|
+
OMIT_EMPTY_NOTE +
|
|
873
|
+
" " +
|
|
874
|
+
REF_RULE_NOTE,
|
|
831
875
|
inputSchema: {
|
|
832
876
|
since: z.string().describe("ISO date-time, e.g. 2026-07-06T08:00:00"),
|
|
833
877
|
...limitShape,
|
|
@@ -849,26 +893,32 @@ export function createThingsMcpServer(options = {}) {
|
|
|
849
893
|
limit,
|
|
850
894
|
...(args.tz !== undefined && { zone: args.tz }),
|
|
851
895
|
});
|
|
852
|
-
return truncatedResult(shapeReadPayload("changes", items, args.full === true), truncation);
|
|
896
|
+
return truncatedResult(shapeReadPayload("changes", items, args.full === true, getClient().refPromoter()), truncation);
|
|
853
897
|
}, args.tz));
|
|
854
898
|
server.registerTool("get_item", {
|
|
855
899
|
description: "Full detail for one item by uuid: notes, schedule, reminder, deadline, tags " +
|
|
856
900
|
"(direct and inherited), checklist with per-item state, repeat schedule, and its " +
|
|
857
901
|
"project/area/heading. " +
|
|
858
|
-
OMIT_EMPTY_NOTE
|
|
902
|
+
OMIT_EMPTY_NOTE +
|
|
903
|
+
" " +
|
|
904
|
+
REF_RULE_NOTE,
|
|
859
905
|
inputSchema: { uuid: z.string() },
|
|
860
906
|
annotations: READ_ONLY,
|
|
861
907
|
}, async (args) => readGuard(() => {
|
|
862
908
|
const item = getClient().read.byUuid(args.uuid);
|
|
863
909
|
return item === null
|
|
864
910
|
? errorResult({ code: "not-found", message: noUuidMatch("item", args.uuid) })
|
|
865
|
-
: readResult(shapeReadPayload("detail", item, false));
|
|
911
|
+
: readResult(shapeReadPayload("detail", item, false, getClient().refPromoter()));
|
|
866
912
|
}));
|
|
867
913
|
server.registerTool("get_project", {
|
|
868
914
|
description: "One project's full contents: metadata plus its to-dos grouped under their headings. " +
|
|
869
|
-
"
|
|
870
|
-
"
|
|
871
|
-
|
|
915
|
+
"Open headings (and archived ones not yet swept to the logbook) stay live, carrying " +
|
|
916
|
+
"archived once archived; a swept archived heading moves into the logged region as a " +
|
|
917
|
+
"group with its children. The tag filters keep only the child to-dos matching by their " +
|
|
918
|
+
"own tags (a heading left with none is dropped). " +
|
|
919
|
+
OMIT_EMPTY_NOTE +
|
|
920
|
+
" " +
|
|
921
|
+
REF_RULE_NOTE,
|
|
872
922
|
inputSchema: {
|
|
873
923
|
uuid: z.string().describe("Project uuid or unique name"),
|
|
874
924
|
...tagOnlyShape,
|
|
@@ -890,7 +940,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
890
940
|
overdue: args.overdue === true,
|
|
891
941
|
...tagFilterFields(tagPresence(args)),
|
|
892
942
|
...(args.tz !== undefined && { zone: args.tz }),
|
|
893
|
-
}), args.full === true));
|
|
943
|
+
}), args.full === true, getClient().refPromoter()));
|
|
894
944
|
}, args.tz));
|
|
895
945
|
server.registerTool("get_area", {
|
|
896
946
|
description: "One area's contents: metadata plus its direct to-dos (active first), its " +
|
|
@@ -900,7 +950,9 @@ export function createThingsMcpServer(options = {}) {
|
|
|
900
950
|
`The project-rows and direct-to-dos sections are capped at ${AREA_PREVIEW_LIMIT} each ` +
|
|
901
951
|
"by default (project_limit / area_limit adjust them; all: true lifts both); the " +
|
|
902
952
|
"second result block reports the counts. " +
|
|
903
|
-
OMIT_EMPTY_NOTE
|
|
953
|
+
OMIT_EMPTY_NOTE +
|
|
954
|
+
" " +
|
|
955
|
+
REF_RULE_NOTE,
|
|
904
956
|
inputSchema: {
|
|
905
957
|
ref: z.string().describe("Area uuid or unique name"),
|
|
906
958
|
...tagOnlyShape,
|
|
@@ -941,7 +993,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
941
993
|
if (areaLimit === "conflict" || projectLimit === "conflict") {
|
|
942
994
|
return usage("pass at most one of area_limit/project_limit / all");
|
|
943
995
|
}
|
|
944
|
-
const { view, truncation, notice } = getClient().read.areaView(args.ref, {
|
|
996
|
+
const { view, truncation, totals, notice } = getClient().read.areaView(args.ref, {
|
|
945
997
|
overdue: args.overdue === true,
|
|
946
998
|
...tagFilterFields(tagPresence(args)),
|
|
947
999
|
...(args.tz !== undefined && { zone: args.tz }),
|
|
@@ -949,14 +1001,18 @@ export function createThingsMcpServer(options = {}) {
|
|
|
949
1001
|
projectLimit,
|
|
950
1002
|
});
|
|
951
1003
|
areaNotice = notice;
|
|
952
|
-
|
|
1004
|
+
// Each capped scope's completeness rides its inline `total` (R1, PR 3);
|
|
1005
|
+
// the whole-view rollup rides `truncation` (no `blocks[]` sidecar).
|
|
1006
|
+
return groupedResult(withAreaBucketTotals(shapeReadPayload("area-view", view, args.full === true, getClient().refPromoter()), totals), truncation);
|
|
953
1007
|
}, args.tz, undefined, () => (areaNotice !== undefined ? [areaNotice] : undefined));
|
|
954
1008
|
});
|
|
955
1009
|
server.registerTool("list_collections", {
|
|
956
1010
|
description: "List every project, area, or tag (tags include their parent-tag nesting). Use to " +
|
|
957
1011
|
"refresh the inventory summarized in the server instructions. The tag filters scope " +
|
|
958
1012
|
"the projects list by each project's own tags (areas/tags reject them). " +
|
|
959
|
-
OMIT_EMPTY_NOTE
|
|
1013
|
+
OMIT_EMPTY_NOTE +
|
|
1014
|
+
" " +
|
|
1015
|
+
REF_RULE_NOTE,
|
|
960
1016
|
inputSchema: {
|
|
961
1017
|
kind: z.enum(["projects", "areas", "tags"]),
|
|
962
1018
|
...tagOnlyShape,
|
|
@@ -1001,7 +1057,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1001
1057
|
})
|
|
1002
1058
|
: args.kind === "areas"
|
|
1003
1059
|
? c.read.areas()
|
|
1004
|
-
: c.read.tags(), args.full === true));
|
|
1060
|
+
: c.read.tags(), args.full === true, c.refPromoter()));
|
|
1005
1061
|
}, args.tz));
|
|
1006
1062
|
// ---------------------------------------------------------------- to-dos
|
|
1007
1063
|
const whenSchema = z.string().optional().describe(WHEN_VALUES);
|
|
@@ -1026,6 +1082,15 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1026
1082
|
.boolean()
|
|
1027
1083
|
.optional()
|
|
1028
1084
|
.describe("Confirm adding into a completed/canceled project (this reopens it)"),
|
|
1085
|
+
created_at: z
|
|
1086
|
+
.string()
|
|
1087
|
+
.optional()
|
|
1088
|
+
.describe(`Born with this creation timestamp (${RESOLUTION_DATE_FORMAT})`),
|
|
1089
|
+
completed_at: z
|
|
1090
|
+
.string()
|
|
1091
|
+
.optional()
|
|
1092
|
+
.describe(`Born completed, in the Logbook, with this completion timestamp ` +
|
|
1093
|
+
`(${RESOLUTION_DATE_FORMAT}); drop when/reminder`),
|
|
1029
1094
|
...createTagsShape,
|
|
1030
1095
|
...tzShape,
|
|
1031
1096
|
...dryRunShape,
|
|
@@ -1052,6 +1117,8 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1052
1117
|
...(args.project !== undefined && { project: containerRef(args.project) }),
|
|
1053
1118
|
...(args.area !== undefined && { area: containerRef(args.area) }),
|
|
1054
1119
|
...(args.heading !== undefined && { heading: args.heading }),
|
|
1120
|
+
...(args.created_at !== undefined && { createdAt: args.created_at }),
|
|
1121
|
+
...(args.completed_at !== undefined && { completedAt: args.completed_at }),
|
|
1055
1122
|
}, writeOptions(args)));
|
|
1056
1123
|
}));
|
|
1057
1124
|
server.registerTool("update", {
|
|
@@ -1061,7 +1128,10 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1061
1128
|
"deadline/clear_deadline; changing the schedule keeps an existing reminder unless a new " +
|
|
1062
1129
|
"one is set, schedule and deadline changes are unavailable for repeating items, and " +
|
|
1063
1130
|
"clear_reminder needs the item scheduled for today or this evening (a reminder on a " +
|
|
1064
|
-
"future date can only be changed, not cleared).
|
|
1131
|
+
"future date can only be changed, not cleared). created_at/completed_at rewrite the " +
|
|
1132
|
+
"resolution timestamps of an already-resolved item; created_at is status-safe, but " +
|
|
1133
|
+
"completed_at on an open item is refused — resolving it is set_status's boundary, not " +
|
|
1134
|
+
"update's. kind area: title and/or tags (the full " +
|
|
1065
1135
|
"replacement set). kind tag: title, parent (nest under it) or unnest (to the top level; " +
|
|
1066
1136
|
"exclusive), and shortcut or clear_shortcut (exclusive). Tags must exist unless " +
|
|
1067
1137
|
"create_tags is set.",
|
|
@@ -1080,6 +1150,16 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1080
1150
|
clear_reminder: z.boolean().optional().describe("todo/project: remove the reminder"),
|
|
1081
1151
|
deadline: z.string().optional().describe(`todo/project: ${DATE_FORMAT}`),
|
|
1082
1152
|
clear_deadline: z.boolean().optional().describe("todo/project: remove the deadline"),
|
|
1153
|
+
created_at: z
|
|
1154
|
+
.string()
|
|
1155
|
+
.optional()
|
|
1156
|
+
.describe(`todo/project: rewrite the creation timestamp (${RESOLUTION_DATE_FORMAT}); status-safe`),
|
|
1157
|
+
completed_at: z
|
|
1158
|
+
.string()
|
|
1159
|
+
.optional()
|
|
1160
|
+
.describe("todo/project: rewrite the completion timestamp of an already-resolved item " +
|
|
1161
|
+
`(${RESOLUTION_DATE_FORMAT}; a canceled one stays canceled); an open item is ` +
|
|
1162
|
+
"refused — use set_status to resolve it"),
|
|
1083
1163
|
tags: z
|
|
1084
1164
|
.array(z.string())
|
|
1085
1165
|
.optional()
|
|
@@ -1127,6 +1207,8 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1127
1207
|
...(args.clear_reminder === true && { reminder: null }),
|
|
1128
1208
|
...(args.deadline !== undefined && { deadline: args.deadline }),
|
|
1129
1209
|
...(args.clear_deadline === true && { deadline: null }),
|
|
1210
|
+
...(args.created_at !== undefined && { createdAt: args.created_at }),
|
|
1211
|
+
...(args.completed_at !== undefined && { completedAt: args.completed_at }),
|
|
1130
1212
|
}, opts));
|
|
1131
1213
|
}
|
|
1132
1214
|
return mutationResult(await c.write.updateProject(args.uuid, {
|
|
@@ -1139,6 +1221,8 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1139
1221
|
...(args.clear_reminder === true && { reminder: null }),
|
|
1140
1222
|
...(args.deadline !== undefined && { deadline: args.deadline }),
|
|
1141
1223
|
...(args.clear_deadline === true && { deadline: null }),
|
|
1224
|
+
...(args.created_at !== undefined && { createdAt: args.created_at }),
|
|
1225
|
+
...(args.completed_at !== undefined && { completedAt: args.completed_at }),
|
|
1142
1226
|
}, opts));
|
|
1143
1227
|
}
|
|
1144
1228
|
if (args.kind === "area") {
|
|
@@ -1178,7 +1262,10 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1178
1262
|
"scope project, completing or canceling requires a children policy: 'require-resolved' " +
|
|
1179
1263
|
"errors if open to-dos remain; 'auto-complete'/'auto-cancel' resolves them together with " +
|
|
1180
1264
|
"the project (canceling never alters already-completed children). scope project, status " +
|
|
1181
|
-
"open, restore_children also reopens the to-dos that were resolved with the project."
|
|
1265
|
+
"open, restore_children also reopens the to-dos that were resolved with the project. " +
|
|
1266
|
+
"completed_at (status completed or canceled) sets the completion timestamp — also the " +
|
|
1267
|
+
'"Completed on" stamp for a canceled item — backdating it; reaching a backdated canceled ' +
|
|
1268
|
+
"item is a multi-leg, non-atomic sequence, disclosed in the result and in dry_run.",
|
|
1182
1269
|
inputSchema: {
|
|
1183
1270
|
scope: z.enum(["todo", "project"]),
|
|
1184
1271
|
uuid: z
|
|
@@ -1193,21 +1280,33 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1193
1280
|
.boolean()
|
|
1194
1281
|
.optional()
|
|
1195
1282
|
.describe("scope project, open only: also reopen the to-dos resolved with the project"),
|
|
1283
|
+
completed_at: z
|
|
1284
|
+
.string()
|
|
1285
|
+
.optional()
|
|
1286
|
+
.describe(`status completed or canceled: the completion timestamp (${RESOLUTION_DATE_FORMAT})`),
|
|
1287
|
+
...tzShape,
|
|
1196
1288
|
...dryRunShape,
|
|
1197
1289
|
...opIdShape,
|
|
1198
1290
|
},
|
|
1199
1291
|
annotations: NON_DESTRUCTIVE,
|
|
1200
1292
|
}, async (args) => guard(async () => {
|
|
1293
|
+
const badZone = badTz(args.tz);
|
|
1294
|
+
if (badZone !== null)
|
|
1295
|
+
return badZone;
|
|
1201
1296
|
const c = getClient();
|
|
1202
1297
|
const opts = writeOptions(args);
|
|
1298
|
+
if (args.completed_at !== undefined && args.status === "open") {
|
|
1299
|
+
return usage("completed_at applies only to status 'completed' or 'canceled'");
|
|
1300
|
+
}
|
|
1301
|
+
const resolution = args.completed_at !== undefined ? { completedAt: args.completed_at } : {};
|
|
1203
1302
|
if (args.scope === "todo") {
|
|
1204
1303
|
if (args.children !== undefined || args.restore_children !== undefined) {
|
|
1205
1304
|
return usage("children/restore_children apply only to scope project");
|
|
1206
1305
|
}
|
|
1207
1306
|
return mutationResult(args.status === "completed"
|
|
1208
|
-
? await c.write.completeTodo(args.uuid, opts)
|
|
1307
|
+
? await c.write.completeTodo(args.uuid, resolution, opts)
|
|
1209
1308
|
: args.status === "canceled"
|
|
1210
|
-
? await c.write.cancelTodo(args.uuid, opts)
|
|
1309
|
+
? await c.write.cancelTodo(args.uuid, resolution, opts)
|
|
1211
1310
|
: await c.write.reopenTodo(args.uuid, opts));
|
|
1212
1311
|
}
|
|
1213
1312
|
// scope project
|
|
@@ -1218,13 +1317,13 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1218
1317
|
if (args.children !== "require-resolved" && args.children !== "auto-complete") {
|
|
1219
1318
|
return usage("status 'completed' requires children: 'require-resolved' or 'auto-complete'");
|
|
1220
1319
|
}
|
|
1221
|
-
return mutationResult(await c.write.completeProject(args.uuid, { children: args.children }, opts));
|
|
1320
|
+
return mutationResult(await c.write.completeProject(args.uuid, { children: args.children, ...resolution }, opts));
|
|
1222
1321
|
}
|
|
1223
1322
|
if (args.status === "canceled") {
|
|
1224
1323
|
if (args.children !== "require-resolved" && args.children !== "auto-cancel") {
|
|
1225
1324
|
return usage("status 'canceled' requires children: 'require-resolved' or 'auto-cancel'");
|
|
1226
1325
|
}
|
|
1227
|
-
return mutationResult(await c.write.cancelProject(args.uuid, { children: args.children }, opts));
|
|
1326
|
+
return mutationResult(await c.write.cancelProject(args.uuid, { children: args.children, ...resolution }, opts));
|
|
1228
1327
|
}
|
|
1229
1328
|
if (args.children !== undefined) {
|
|
1230
1329
|
return usage("children applies only to status 'completed' or 'canceled'");
|
|
@@ -1488,41 +1587,12 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1488
1587
|
? await c.write.restoreTodo(args.uuid, writeOptions(args))
|
|
1489
1588
|
: await c.write.restoreProject(args.uuid, writeOptions(args)));
|
|
1490
1589
|
}));
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
completion_date: z.string().optional().describe(DATE_FORMAT),
|
|
1498
|
-
creation_date: z.string().optional().describe(DATE_FORMAT),
|
|
1499
|
-
...dryRunShape,
|
|
1500
|
-
...opIdShape,
|
|
1501
|
-
},
|
|
1502
|
-
annotations: NON_DESTRUCTIVE,
|
|
1503
|
-
}, async (args) => guard(async () => mutationResult(await getClient().write.backdateTodo(args.uuid, {
|
|
1504
|
-
...(args.completion_date !== undefined && { completionDate: args.completion_date }),
|
|
1505
|
-
...(args.creation_date !== undefined && { creationDate: args.creation_date }),
|
|
1506
|
-
}, writeOptions(args)))));
|
|
1507
|
-
server.registerTool("add_logged_todo", {
|
|
1508
|
-
description: "Create a to-do directly in the Logbook: completed, with the given past " +
|
|
1509
|
-
"completion date (and optionally a past creation date). For importing history " +
|
|
1510
|
-
"from another system.",
|
|
1511
|
-
inputSchema: {
|
|
1512
|
-
title: z.string(),
|
|
1513
|
-
completion_date: z.string().describe(DATE_FORMAT),
|
|
1514
|
-
creation_date: z.string().optional().describe(`${DATE_FORMAT}; <= completion_date`),
|
|
1515
|
-
notes: z.string().optional(),
|
|
1516
|
-
...dryRunShape,
|
|
1517
|
-
...opIdShape,
|
|
1518
|
-
},
|
|
1519
|
-
annotations: NON_DESTRUCTIVE,
|
|
1520
|
-
}, async (args) => guard(async () => mutationResult(await getClient().write.addLoggedTodo({
|
|
1521
|
-
title: args.title,
|
|
1522
|
-
completionDate: args.completion_date,
|
|
1523
|
-
...(args.creation_date !== undefined && { creationDate: args.creation_date }),
|
|
1524
|
-
...(args.notes !== undefined && { notes: args.notes }),
|
|
1525
|
-
}, writeOptions(args)))));
|
|
1590
|
+
// NB: the bespoke backdate_todo / add_logged_todo tools were removed with the
|
|
1591
|
+
// engine ops (plan PR A). MCP parity for the resolution-timestamp surface is
|
|
1592
|
+
// restored (PR B) as created_at/completed_at params folded onto the existing
|
|
1593
|
+
// write tools: add_todo / add_project (Logbook import), update (rewrite an
|
|
1594
|
+
// already-resolved item's timestamps), and set_status (backdate on
|
|
1595
|
+
// complete/cancel) — matching the CLI's --created-at/--completed-at flags.
|
|
1526
1596
|
server.registerTool("heading", {
|
|
1527
1597
|
description: "Manage a project's headings — action selects which; project is always required, and a " +
|
|
1528
1598
|
"heading is selected by its exact title or its uuid (never an ordinal). add_heading: a " +
|
|
@@ -1912,6 +1982,15 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1912
1982
|
when: whenSchema,
|
|
1913
1983
|
deadline: z.string().optional().describe(DATE_FORMAT),
|
|
1914
1984
|
todos: z.array(z.string()).optional().describe("Initial child to-do titles"),
|
|
1985
|
+
created_at: z
|
|
1986
|
+
.string()
|
|
1987
|
+
.optional()
|
|
1988
|
+
.describe(`Born with this creation timestamp (${RESOLUTION_DATE_FORMAT})`),
|
|
1989
|
+
completed_at: z
|
|
1990
|
+
.string()
|
|
1991
|
+
.optional()
|
|
1992
|
+
.describe(`Born completed, in the Logbook, with this completion timestamp ` +
|
|
1993
|
+
`(${RESOLUTION_DATE_FORMAT}); cannot seed open child to-dos`),
|
|
1915
1994
|
...tzShape,
|
|
1916
1995
|
...dryRunShape,
|
|
1917
1996
|
...opIdShape,
|
|
@@ -1928,6 +2007,8 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1928
2007
|
...(args.when !== undefined && { when: args.when }),
|
|
1929
2008
|
...(args.deadline !== undefined && { deadline: args.deadline }),
|
|
1930
2009
|
...(args.todos !== undefined && { todos: args.todos }),
|
|
2010
|
+
...(args.created_at !== undefined && { createdAt: args.created_at }),
|
|
2011
|
+
...(args.completed_at !== undefined && { completedAt: args.completed_at }),
|
|
1931
2012
|
}, writeOptions(args)));
|
|
1932
2013
|
}));
|
|
1933
2014
|
server.registerTool("move_project", {
|
|
@@ -2108,81 +2189,53 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2108
2189
|
};
|
|
2109
2190
|
}));
|
|
2110
2191
|
server.registerTool("reorder", {
|
|
2111
|
-
description: "
|
|
2112
|
-
"
|
|
2113
|
-
"use move_todo / move_project).
|
|
2114
|
-
"
|
|
2115
|
-
"
|
|
2116
|
-
"
|
|
2117
|
-
"
|
|
2118
|
-
"
|
|
2119
|
-
"
|
|
2120
|
-
"
|
|
2121
|
-
"
|
|
2122
|
-
"
|
|
2123
|
-
"
|
|
2192
|
+
description: "The ONE reorder tool — rearrange a single-KIND set IN PLACE: to-dos, projects, headings, " +
|
|
2193
|
+
"OR sidebar areas. This REARRANGES, never changes what an item belongs to (to change " +
|
|
2194
|
+
"membership use move_todo / move_project). All refs must be one kind; only to-dos and " +
|
|
2195
|
+
"projects may intermix, and only on the shared Today/Evening/day axes. A mixed-kind set, " +
|
|
2196
|
+
"a cross-container set, and a non-member anchor each get one precise refusal. The refs " +
|
|
2197
|
+
"order is the resulting order; unmentioned siblings keep theirs. Bare (no position) " +
|
|
2198
|
+
"assembles the named items as a block at the EARLIEST one's current slot; start/end/" +
|
|
2199
|
+
"before/after position the block. A Today or This Evening member also holds a slot in its " +
|
|
2200
|
+
"container, so a set coherent on BOTH axes is ambiguous — pass `in` to name the axis. " +
|
|
2201
|
+
"HEADINGS: same-project heading re-ranking runs the native heading-block wire (children " +
|
|
2202
|
+
"follow); an archived heading is reorderable but repositioning it brings it back to open " +
|
|
2203
|
+
"(disclosed in the result). AREAS: this drives the local Things app (sidebar drag) and " +
|
|
2204
|
+
"must be turned on with `things config set ui-enabled true` plus dangerously_drive_gui; " +
|
|
2205
|
+
"the areas' projects and to-dos are untouched. Ordering the Today, Inbox, or Someday " +
|
|
2206
|
+
"lists, or a project's to-dos, must first be enabled once via `things config set " +
|
|
2207
|
+
"allow-experimental true`.",
|
|
2124
2208
|
inputSchema: {
|
|
2125
2209
|
refs: z
|
|
2126
2210
|
.array(z.string())
|
|
2127
|
-
.describe("The items to rearrange
|
|
2128
|
-
|
|
2211
|
+
.describe("The items to rearrange (one kind: to-dos, projects, headings, or areas), in the " +
|
|
2212
|
+
"order they should land (may be a subset)"),
|
|
2213
|
+
start: z.boolean().optional().describe("place the block at the start of its scope"),
|
|
2214
|
+
end: z.boolean().optional().describe("place the block at the end of its scope"),
|
|
2215
|
+
before: z.string().optional().describe("place the block immediately before this item"),
|
|
2216
|
+
after: z.string().optional().describe("place the block immediately after this item"),
|
|
2129
2217
|
in: z
|
|
2130
2218
|
.string()
|
|
2131
2219
|
.optional()
|
|
2132
|
-
.describe("
|
|
2133
|
-
"someday | inbox,
|
|
2220
|
+
.describe("to-dos/projects only — name the axis to reorder on: today | evening | anytime | " +
|
|
2221
|
+
"someday | inbox, a project/area/heading ref (uuid or unique title), upcoming (the " +
|
|
2222
|
+
"one future day the set shares), or a YYYY-MM-DD day-block. A stage-list or " +
|
|
2223
|
+
"container axis sorts one KIND at a time; only today | evening | upcoming | a " +
|
|
2224
|
+
"day-block intermix both kinds."),
|
|
2225
|
+
...driveGuiShape,
|
|
2134
2226
|
...dryRunShape,
|
|
2135
2227
|
},
|
|
2136
2228
|
annotations: NON_DESTRUCTIVE,
|
|
2137
2229
|
}, async (args) => guard(async () => {
|
|
2138
|
-
const position =
|
|
2230
|
+
const position = reorderPositionArgs(args);
|
|
2139
2231
|
if (position === "conflict")
|
|
2140
|
-
return usage("pass at most one of
|
|
2232
|
+
return usage("pass at most one of start/end/before/after");
|
|
2141
2233
|
const request = {
|
|
2142
2234
|
uuids: args.refs,
|
|
2143
2235
|
...(position !== undefined && { position }),
|
|
2144
2236
|
...(args.in !== undefined && { in: args.in }),
|
|
2145
2237
|
};
|
|
2146
|
-
return moveResult(await getClient().write.
|
|
2147
|
-
}));
|
|
2148
|
-
server.registerTool("reorder_areas", {
|
|
2149
|
-
description: "Move a sidebar area to a new position in the area order (target by uuid or unique " +
|
|
2150
|
-
"name). Pass exactly one destination: before/after another area, or first/last. This " +
|
|
2151
|
-
"visibly drives the local Things app (the window comes forward and the sidebar may " +
|
|
2152
|
-
"scroll) and must be turned on first with `things config set ui-enabled true`; the " +
|
|
2153
|
-
"area's projects and to-dos are untouched.",
|
|
2154
|
-
inputSchema: {
|
|
2155
|
-
target: z.string().describe(`the area to move (${REF_FORMAT})`),
|
|
2156
|
-
before: z
|
|
2157
|
-
.string()
|
|
2158
|
-
.optional()
|
|
2159
|
-
.describe(`place it immediately above this area (${REF_FORMAT})`),
|
|
2160
|
-
after: z
|
|
2161
|
-
.string()
|
|
2162
|
-
.optional()
|
|
2163
|
-
.describe(`place it immediately below this area (${REF_FORMAT})`),
|
|
2164
|
-
first: z.boolean().optional().describe("move it to the top of the area list"),
|
|
2165
|
-
last: z.boolean().optional().describe("move it to the bottom of the area list"),
|
|
2166
|
-
...driveGuiShape,
|
|
2167
|
-
...dryRunShape,
|
|
2168
|
-
},
|
|
2169
|
-
annotations: NON_DESTRUCTIVE,
|
|
2170
|
-
}, async (args) => guard(async () => {
|
|
2171
|
-
const chosen = [
|
|
2172
|
-
args.before !== undefined,
|
|
2173
|
-
args.after !== undefined,
|
|
2174
|
-
args.first === true,
|
|
2175
|
-
args.last === true,
|
|
2176
|
-
].filter(Boolean).length;
|
|
2177
|
-
if (chosen !== 1)
|
|
2178
|
-
return usage("pass exactly one of before / after / first / last");
|
|
2179
|
-
return mutationResult(await getClient().write.run("area.reorder", {
|
|
2180
|
-
target: args.target,
|
|
2181
|
-
...(args.before !== undefined && { before: args.before }),
|
|
2182
|
-
...(args.after !== undefined && { after: args.after }),
|
|
2183
|
-
...(args.first === true && { position: "first" }),
|
|
2184
|
-
...(args.last === true && { position: "last" }),
|
|
2185
|
-
}, writeOptions(args)));
|
|
2238
|
+
return moveResult(await getClient().write.reorderAny(request, writeOptions(args)));
|
|
2186
2239
|
}));
|
|
2187
2240
|
server.registerTool("undo", {
|
|
2188
2241
|
description: "Undo the last N changes, newest first (changes made directly in the Things app " +
|