things-api 0.12.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 (112) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/canonical-ref.d.ts +21 -0
  3. package/dist/cli/canonical-ref.js +23 -0
  4. package/dist/cli/canonical-ref.js.map +1 -0
  5. package/dist/cli/commands/area.js +27 -11
  6. package/dist/cli/commands/area.js.map +1 -1
  7. package/dist/cli/commands/install-skill.d.ts +2 -2
  8. package/dist/cli/commands/install-skill.js +29 -0
  9. package/dist/cli/commands/install-skill.js.map +1 -1
  10. package/dist/cli/commands/mcp.js +12 -1
  11. package/dist/cli/commands/mcp.js.map +1 -1
  12. package/dist/cli/commands/project.js +73 -21
  13. package/dist/cli/commands/project.js.map +1 -1
  14. package/dist/cli/commands/reads.d.ts +5 -1
  15. package/dist/cli/commands/reads.js +10 -3
  16. package/dist/cli/commands/reads.js.map +1 -1
  17. package/dist/cli/commands/setup.js +11 -4
  18. package/dist/cli/commands/setup.js.map +1 -1
  19. package/dist/cli/commands/show.js +45 -30
  20. package/dist/cli/commands/show.js.map +1 -1
  21. package/dist/cli/commands/todo.js +1 -1
  22. package/dist/cli/commands/todo.js.map +1 -1
  23. package/dist/cli/commands/writes.js +31 -6
  24. package/dist/cli/commands/writes.js.map +1 -1
  25. package/dist/cli/did-you-mean.js +19 -5
  26. package/dist/cli/did-you-mean.js.map +1 -1
  27. package/dist/cli/dry-run.d.ts +55 -0
  28. package/dist/cli/dry-run.js +92 -0
  29. package/dist/cli/dry-run.js.map +1 -0
  30. package/dist/cli/help.js +7 -6
  31. package/dist/cli/help.js.map +1 -1
  32. package/dist/cli/main.js +7 -0
  33. package/dist/cli/main.js.map +1 -1
  34. package/dist/cli/read-driver.d.ts +16 -6
  35. package/dist/cli/read-driver.js +19 -18
  36. package/dist/cli/read-driver.js.map +1 -1
  37. package/dist/cli/ref-render.d.ts +14 -0
  38. package/dist/cli/ref-render.js +9 -0
  39. package/dist/cli/ref-render.js.map +1 -0
  40. package/dist/cli/render.d.ts +21 -6
  41. package/dist/cli/render.js +73 -34
  42. package/dist/cli/render.js.map +1 -1
  43. package/dist/cli/resolve-invocation.d.ts +5 -3
  44. package/dist/cli/resolve-invocation.js +13 -4
  45. package/dist/cli/resolve-invocation.js.map +1 -1
  46. package/dist/client.d.ts +18 -7
  47. package/dist/client.js +2 -1
  48. package/dist/client.js.map +1 -1
  49. package/dist/contracts.d.ts +12 -1
  50. package/dist/contracts.js +1 -1
  51. package/dist/contracts.js.map +1 -1
  52. package/dist/db/baselines/db-v26.js +4 -1
  53. package/dist/db/baselines/db-v26.js.map +1 -1
  54. package/dist/index.d.ts +3 -1
  55. package/dist/index.js +7 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/mcp/server.js +81 -42
  58. package/dist/mcp/server.js.map +1 -1
  59. package/dist/model/entities.d.ts +11 -1
  60. package/dist/model/entities.js.map +1 -1
  61. package/dist/model/mappers.js +3 -0
  62. package/dist/model/mappers.js.map +1 -1
  63. package/dist/model/serialize.js +15 -3
  64. package/dist/model/serialize.js.map +1 -1
  65. package/dist/read/area-filter.d.ts +2 -2
  66. package/dist/read/area-filter.js +5 -6
  67. package/dist/read/area-filter.js.map +1 -1
  68. package/dist/read/area-view.d.ts +11 -0
  69. package/dist/read/area-view.js +5 -1
  70. package/dist/read/area-view.js.map +1 -1
  71. package/dist/read/project-view.d.ts +61 -1
  72. package/dist/read/project-view.js +74 -9
  73. package/dist/read/project-view.js.map +1 -1
  74. package/dist/read/queries.d.ts +71 -2
  75. package/dist/read/queries.js +199 -10
  76. package/dist/read/queries.js.map +1 -1
  77. package/dist/read/scope.d.ts +2 -2
  78. package/dist/read/scope.js +5 -6
  79. package/dist/read/scope.js.map +1 -1
  80. package/dist/read/shape.d.ts +36 -17
  81. package/dist/read/shape.js +251 -200
  82. package/dist/read/shape.js.map +1 -1
  83. package/dist/read/show-target.js +52 -33
  84. package/dist/read/show-target.js.map +1 -1
  85. package/dist/read/truncation.d.ts +0 -7
  86. package/dist/read/truncation.js +24 -14
  87. package/dist/read/truncation.js.map +1 -1
  88. package/dist/read/views.d.ts +19 -7
  89. package/dist/read/views.js +13 -12
  90. package/dist/read/views.js.map +1 -1
  91. package/dist/surface-copy.d.ts +7 -0
  92. package/dist/surface-copy.js +7 -0
  93. package/dist/surface-copy.js.map +1 -1
  94. package/dist/write/commands.js +3 -1
  95. package/dist/write/commands.js.map +1 -1
  96. package/dist/write/move.d.ts +10 -6
  97. package/dist/write/move.js +417 -76
  98. package/dist/write/move.js.map +1 -1
  99. package/dist/write/operations.d.ts +1 -1
  100. package/dist/write/operations.js.map +1 -1
  101. package/dist/write/pre-state.d.ts +10 -0
  102. package/dist/write/pre-state.js +118 -41
  103. package/dist/write/pre-state.js.map +1 -1
  104. package/dist/write/reorder.js +263 -29
  105. package/dist/write/reorder.js.map +1 -1
  106. package/package.json +1 -1
  107. package/schema/envelope.schema.json +14 -0
  108. package/skills/things-cli/SKILL.md +6 -4
  109. package/skills/things-cli/references/contracts.md +3 -1
  110. package/skills/things-cli/references/errors.md +9 -1
  111. package/skills/things-cli/references/model.md +6 -4
  112. package/skills/things-cli/references/ordering.md +21 -4
@@ -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, schemaWarnings, shapeReadPayload, 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,16 +32,22 @@ 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
- { type: "text", text: JSON.stringify({ truncation, ...(note !== undefined && { note }) }) },
47
+ {
48
+ type: "text",
49
+ text: JSON.stringify({ truncation, ...meta, ...(note !== undefined && { note }) }),
50
+ },
45
51
  ],
46
52
  };
47
53
  }
@@ -173,7 +179,11 @@ function moveResult(result) {
173
179
  return jsonResult(result.plan);
174
180
  case "move-refused":
175
181
  return errorResult({
176
- code: result.refusal,
182
+ // A hoisted placement block names its hazard — surface the canonical
183
+ // `blocked:<hazard>` code, matching a direct reorder hazard block.
184
+ code: result.hazard !== undefined
185
+ ? blockedCode({ hazard: result.hazard, reason: "hazard" })
186
+ : result.refusal,
177
187
  message: result.detail,
178
188
  ...(result.remediation !== undefined && { remediation: result.remediation }),
179
189
  ...(result.candidates !== undefined && { details: { candidates: result.candidates } }),
@@ -382,9 +392,10 @@ function buildInstructions(getClient) {
382
392
  "confirmation parameter named in their description; refused calls return an error saying " +
383
393
  "what to pass.",
384
394
  "- Read-result semantics: an item's tags are its direct tags; its effective tags also include " +
385
- "tags inherited from its containing project and area. The today and evening markers are " +
386
- "present only for an item in Today (evening implies today); an unscheduled start=active item " +
387
- "is in Anytime and carries neither. Completing an item makes it findable in Logbook.",
395
+ "tags inherited from its containing project and area. An item's when is today or evening for " +
396
+ "a Today member (evening implies today), a future date for a scheduled item, else absent; an " +
397
+ "unscheduled start=active item is in Anytime and carries no when. Completing an item makes it " +
398
+ "findable in Logbook.",
388
399
  "- For capped reads, pass limit to cap rows or all: true for everything; if both are set, all wins.",
389
400
  `- Read results are compact: ${OMIT_EMPTY_NOTE}`,
390
401
  ];
@@ -566,8 +577,9 @@ export function createThingsMcpServer(options = {}) {
566
577
  };
567
578
  // ------------------------------------------------------------------ reads
568
579
  server.registerTool("read_view", {
569
- description: "Read a Things list as the app presents it: today (split into Today and This " +
570
- "Evening), inbox, anytime, upcoming, someday, logbook, or trash. For upcoming, " +
580
+ description: "Read a Things list as the app presents it: today (one flat list of Today " +
581
+ "members, each item's when marking Today vs This Evening), inbox, anytime, " +
582
+ "upcoming, someday, logbook, or trash. For upcoming, " +
571
583
  "horizon > 1 also includes future occurrences of repeating items (up to 10 each). " +
572
584
  "anytime/someday return sections in canonical order (area + items; null area = the " +
573
585
  "top-level block); children of someday/future-scheduled projects are excluded " +
@@ -578,7 +590,9 @@ export function createThingsMcpServer(options = {}) {
578
590
  `area_limit (default ${AREA_PREVIEW_LIMIT}) per area block, and on anytime ` +
579
591
  `project_limit (default ${PROJECT_PREVIEW_LIMIT}) per project block. ` +
580
592
  "all: true lifts every cap; the result's second block reports the counts. " +
581
- OMIT_EMPTY_NOTE,
593
+ OMIT_EMPTY_NOTE +
594
+ " " +
595
+ REF_RULE_NOTE,
582
596
  inputSchema: {
583
597
  view: z.enum(["today", "inbox", "anytime", "upcoming", "someday", "logbook", "trash"]),
584
598
  ...tagFilterShape,
@@ -587,7 +601,10 @@ export function createThingsMcpServer(options = {}) {
587
601
  .string()
588
602
  .optional()
589
603
  .describe(`today/anytime/someday/upcoming/logbook only: ${AREA_FILTER_DESC}`),
590
- evening: z.boolean().optional().describe("today only: show only the This Evening section"),
604
+ evening: z
605
+ .boolean()
606
+ .optional()
607
+ .describe("today only: show only This-Evening members (when = evening)"),
591
608
  show_active_project_items: z
592
609
  .union([z.boolean(), z.number().int().min(1)])
593
610
  .optional()
@@ -698,11 +715,11 @@ export function createThingsMcpServer(options = {}) {
698
715
  limit,
699
716
  });
700
717
  filterMeta = fm;
701
- return truncatedResult(shapeReadPayload("today", view, full), truncation);
718
+ return truncatedResult(shapeReadPayload("today", view, full, c.refPromoter()), truncation, { counts: view.counts });
702
719
  }
703
720
  case "inbox": {
704
721
  const { items, truncation } = c.read.inbox({ ...filter, ...zone, limit });
705
- return truncatedResult(shapeReadPayload("inbox", items, full), truncation);
722
+ return truncatedResult(shapeReadPayload("inbox", items, full, c.refPromoter()), truncation);
706
723
  }
707
724
  case "anytime": {
708
725
  const { view, truncation, filter: fm, } = c.read.anytime({
@@ -713,7 +730,7 @@ export function createThingsMcpServer(options = {}) {
713
730
  projectLimit,
714
731
  });
715
732
  filterMeta = fm;
716
- return groupedResult(shapeReadPayload("anytime", view, full), truncation);
733
+ return groupedResult(shapeReadPayload("anytime", view, full, c.refPromoter()), truncation);
717
734
  }
718
735
  case "upcoming": {
719
736
  const { items, truncation, filter: fm, } = c.read.upcoming({
@@ -724,7 +741,7 @@ export function createThingsMcpServer(options = {}) {
724
741
  limit,
725
742
  });
726
743
  filterMeta = fm;
727
- return truncatedResult(shapeReadPayload("upcoming", items, full), truncation);
744
+ return truncatedResult(shapeReadPayload("upcoming", items, full, c.refPromoter()), truncation);
728
745
  }
729
746
  case "someday": {
730
747
  const active = showActiveProjectItems;
@@ -743,7 +760,7 @@ export function createThingsMcpServer(options = {}) {
743
760
  projectLimit: typeof active === "number" ? active : null,
744
761
  });
745
762
  filterMeta = fm;
746
- return groupedResult(shapeReadPayload("someday", view, full), truncation);
763
+ return groupedResult(shapeReadPayload("someday", view, full, c.refPromoter()), truncation);
747
764
  }
748
765
  case "logbook": {
749
766
  const { items, truncation, filter: fm, } = c.read.logbook({
@@ -753,11 +770,11 @@ export function createThingsMcpServer(options = {}) {
753
770
  limit,
754
771
  });
755
772
  filterMeta = fm;
756
- return truncatedResult(shapeReadPayload("logbook", items, full), truncation);
773
+ return truncatedResult(shapeReadPayload("logbook", items, full, c.refPromoter()), truncation);
757
774
  }
758
775
  case "trash": {
759
776
  const { items, truncation } = c.read.trash({ ...zone, limit });
760
- return truncatedResult(shapeReadPayload("trash", items, full), truncation);
777
+ return truncatedResult(shapeReadPayload("trash", items, full, c.refPromoter()), truncation);
761
778
  }
762
779
  }
763
780
  }, args.tz, () => (filterMeta !== undefined ? { filter: filterMeta } : undefined), () => looseAreaWarnings(getClient(), args.area));
@@ -766,7 +783,9 @@ export function createThingsMcpServer(options = {}) {
766
783
  description: "Find items by title/notes substring. Returns open, untrashed items by default; " +
767
784
  "include more with logged/trashed/all. Scope with project/area/tag — scope " +
768
785
  "references must name existing items. " +
769
- OMIT_EMPTY_NOTE,
786
+ OMIT_EMPTY_NOTE +
787
+ " " +
788
+ REF_RULE_NOTE,
770
789
  inputSchema: {
771
790
  query: z.string(),
772
791
  ...tagFilterShape,
@@ -821,13 +840,15 @@ export function createThingsMcpServer(options = {}) {
821
840
  ...(args.trashed === true && { trashed: true }),
822
841
  ...(args.all === true && { all: true }),
823
842
  });
824
- return truncatedResult(shapeReadPayload("search", items, args.full === true), truncation);
843
+ return truncatedResult(shapeReadPayload("search", items, args.full === true, getClient().refPromoter()), truncation);
825
844
  }, args.tz, undefined, () => looseAreaWarnings(getClient(), args.area)));
826
845
  server.registerTool("changes_since", {
827
846
  description: "List items created or modified since a moment — including trashed, logged, and " +
828
847
  "repeating items (inspect each item's fields to tell them apart). Edits to tags, " +
829
848
  "areas, and checklist items do not mark the containing item as modified. " +
830
- OMIT_EMPTY_NOTE,
849
+ OMIT_EMPTY_NOTE +
850
+ " " +
851
+ REF_RULE_NOTE,
831
852
  inputSchema: {
832
853
  since: z.string().describe("ISO date-time, e.g. 2026-07-06T08:00:00"),
833
854
  ...limitShape,
@@ -849,26 +870,34 @@ export function createThingsMcpServer(options = {}) {
849
870
  limit,
850
871
  ...(args.tz !== undefined && { zone: args.tz }),
851
872
  });
852
- return truncatedResult(shapeReadPayload("changes", items, args.full === true), truncation);
873
+ return truncatedResult(shapeReadPayload("changes", items, args.full === true, getClient().refPromoter()), truncation);
853
874
  }, args.tz));
854
875
  server.registerTool("get_item", {
855
876
  description: "Full detail for one item by uuid: notes, schedule, reminder, deadline, tags " +
856
877
  "(direct and inherited), checklist with per-item state, repeat schedule, and its " +
857
878
  "project/area/heading. " +
858
- OMIT_EMPTY_NOTE,
879
+ OMIT_EMPTY_NOTE +
880
+ " " +
881
+ REF_RULE_NOTE,
859
882
  inputSchema: { uuid: z.string() },
860
883
  annotations: READ_ONLY,
861
884
  }, async (args) => readGuard(() => {
862
885
  const item = getClient().read.byUuid(args.uuid);
863
886
  return item === null
864
887
  ? errorResult({ code: "not-found", message: noUuidMatch("item", args.uuid) })
865
- : readResult(shapeReadPayload("detail", item, false));
888
+ : readResult(shapeReadPayload("detail", item, false, getClient().refPromoter()));
866
889
  }));
867
890
  server.registerTool("get_project", {
868
- description: "One project's full contents: metadata plus its to-dos grouped under their headings. " +
869
- "The tag filters keep only the child to-dos matching by their own tags (a heading left " +
870
- "with none is dropped). " +
871
- OMIT_EMPTY_NOTE,
891
+ description: "One project's full contents: metadata, its live child to-dos as one flat list (items, " +
892
+ "in index order each carrying its stage, when, and, when it lives under a heading, " +
893
+ "its heading ref), the heading catalog (headings — every heading in index order, " +
894
+ "carrying archived once archived), and its logged children (logbook, most recent " +
895
+ "first, each carrying its heading ref). Reconstruct a heading's members by filtering " +
896
+ "items (or logbook) on the heading ref. The tag filters keep only the child to-dos " +
897
+ "matching by their own tags (a heading left with none is dropped). " +
898
+ OMIT_EMPTY_NOTE +
899
+ " " +
900
+ REF_RULE_NOTE,
872
901
  inputSchema: {
873
902
  uuid: z.string().describe("Project uuid or unique name"),
874
903
  ...tagOnlyShape,
@@ -890,17 +919,19 @@ export function createThingsMcpServer(options = {}) {
890
919
  overdue: args.overdue === true,
891
920
  ...tagFilterFields(tagPresence(args)),
892
921
  ...(args.tz !== undefined && { zone: args.tz }),
893
- }), args.full === true));
922
+ }), args.full === true, getClient().refPromoter()));
894
923
  }, args.tz));
895
924
  server.registerTool("get_area", {
896
- description: "One area's contents: metadata plus its direct to-dos (active first), its " +
897
- "projects in canonical order, and later (scheduled/repeating/someday). The area " +
898
- "logbook is not returned here — read it with read_view logbook + area; trashed " +
899
- "rows live in read_view trash. " +
900
- `The project-rows and direct-to-dos sections are capped at ${AREA_PREVIEW_LIMIT} each ` +
925
+ description: "One area's contents: metadata plus its direct to-dos as one flat list (items, in " +
926
+ "index order each row carrying its stage and when) and its projects in canonical " +
927
+ "order (projects). The area logbook is not returned here — read it with read_view " +
928
+ "logbook + area; trashed rows live in read_view trash. " +
929
+ `The project rows and the open/current direct to-dos are capped at ${AREA_PREVIEW_LIMIT} each ` +
901
930
  "by default (project_limit / area_limit adjust them; all: true lifts both); the " +
902
931
  "second result block reports the counts. " +
903
- OMIT_EMPTY_NOTE,
932
+ OMIT_EMPTY_NOTE +
933
+ " " +
934
+ REF_RULE_NOTE,
904
935
  inputSchema: {
905
936
  ref: z.string().describe("Area uuid or unique name"),
906
937
  ...tagOnlyShape,
@@ -949,14 +980,16 @@ export function createThingsMcpServer(options = {}) {
949
980
  projectLimit,
950
981
  });
951
982
  areaNotice = notice;
952
- return groupedResult(shapeReadPayload("area-view", view, args.full === true), truncation);
983
+ return groupedResult(shapeReadPayload("area-view", view, args.full === true, getClient().refPromoter()), truncation);
953
984
  }, args.tz, undefined, () => (areaNotice !== undefined ? [areaNotice] : undefined));
954
985
  });
955
986
  server.registerTool("list_collections", {
956
987
  description: "List every project, area, or tag (tags include their parent-tag nesting). Use to " +
957
988
  "refresh the inventory summarized in the server instructions. The tag filters scope " +
958
989
  "the projects list by each project's own tags (areas/tags reject them). " +
959
- OMIT_EMPTY_NOTE,
990
+ OMIT_EMPTY_NOTE +
991
+ " " +
992
+ REF_RULE_NOTE,
960
993
  inputSchema: {
961
994
  kind: z.enum(["projects", "areas", "tags"]),
962
995
  ...tagOnlyShape,
@@ -1001,7 +1034,7 @@ export function createThingsMcpServer(options = {}) {
1001
1034
  })
1002
1035
  : args.kind === "areas"
1003
1036
  ? c.read.areas()
1004
- : c.read.tags(), args.full === true));
1037
+ : c.read.tags(), args.full === true, c.refPromoter()));
1005
1038
  }, args.tz));
1006
1039
  // ---------------------------------------------------------------- to-dos
1007
1040
  const whenSchema = z.string().optional().describe(WHEN_VALUES);
@@ -2116,7 +2149,9 @@ export function createThingsMcpServer(options = {}) {
2116
2149
  "position the block instead. Refs that span different containers or buckets are refused. " +
2117
2150
  "A Today or This Evening member also holds a slot in its own container, so a set that is " +
2118
2151
  "coherent on BOTH axes is ambiguous — pass `in` to name the axis (the refusal names both " +
2119
- "choices). The project rows the Today/Evening/day lists intermix with to-dos may be " +
2152
+ "choices). A deadline-forecast set sharing one Upcoming day is dual-axis the same way (the " +
2153
+ "day-block vs its container order); `in: upcoming` or `in: <YYYY-MM-DD>` names the day-block. " +
2154
+ "The project rows the Today/Evening/day lists intermix with to-dos may be " +
2120
2155
  "reordered alongside them. Ordering the Today, Inbox, or Someday lists, a project's " +
2121
2156
  "to-dos, or an area must first be enabled once via `things config set allow-experimental " +
2122
2157
  "true`. To reorder a project's HEADINGS (children follow) use the heading tool's " +
@@ -2129,8 +2164,12 @@ export function createThingsMcpServer(options = {}) {
2129
2164
  in: z
2130
2165
  .string()
2131
2166
  .optional()
2132
- .describe("Disambiguate the axis of a Today/This Evening set: today | evening | anytime | " +
2133
- "someday | inbox, or a project/area/heading ref (uuid or unique title)"),
2167
+ .describe("Name the axis to reorder on: today | evening | anytime | someday | inbox, a project/" +
2168
+ "area/heading ref (uuid or unique title), upcoming (the one future day the set " +
2169
+ "shares), or a YYYY-MM-DD day-block. A stage-list or container axis sorts one KIND " +
2170
+ "at a time — a mixed to-do+project set is refused (even sharing a container); only " +
2171
+ "today | evening | upcoming | a day-block intermix both kinds. Reorder a project's " +
2172
+ "headings with the heading tool's move_heading action."),
2134
2173
  ...dryRunShape,
2135
2174
  },
2136
2175
  annotations: NON_DESTRUCTIVE,