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.
Files changed (140) hide show
  1. package/README.md +4 -4
  2. package/dist/audit/schema.d.ts +8 -0
  3. package/dist/audit/schema.js.map +1 -1
  4. package/dist/cli/commands/area.js +1 -1
  5. package/dist/cli/commands/install-skill.js +9 -2
  6. package/dist/cli/commands/install-skill.js.map +1 -1
  7. package/dist/cli/commands/project.js +1 -1
  8. package/dist/cli/commands/project.js.map +1 -1
  9. package/dist/cli/commands/reads.js +73 -4
  10. package/dist/cli/commands/reads.js.map +1 -1
  11. package/dist/cli/commands/repeat-flags.d.ts +11 -1
  12. package/dist/cli/commands/repeat-flags.js +23 -0
  13. package/dist/cli/commands/repeat-flags.js.map +1 -1
  14. package/dist/cli/commands/todo.js +50 -3
  15. package/dist/cli/commands/todo.js.map +1 -1
  16. package/dist/cli/commands/writes.js +202 -61
  17. package/dist/cli/commands/writes.js.map +1 -1
  18. package/dist/cli/glyphs.js +9 -10
  19. package/dist/cli/glyphs.js.map +1 -1
  20. package/dist/cli/help.js +18 -13
  21. package/dist/cli/help.js.map +1 -1
  22. package/dist/cli/read-driver.d.ts +9 -3
  23. package/dist/cli/read-driver.js +3 -1
  24. package/dist/cli/read-driver.js.map +1 -1
  25. package/dist/cli/render.d.ts +33 -2
  26. package/dist/cli/render.js +60 -9
  27. package/dist/cli/render.js.map +1 -1
  28. package/dist/cli/skill.d.ts +24 -0
  29. package/dist/cli/skill.js +40 -0
  30. package/dist/cli/skill.js.map +1 -1
  31. package/dist/client.d.ts +88 -15
  32. package/dist/client.js +31 -5
  33. package/dist/client.js.map +1 -1
  34. package/dist/contracts.d.ts +13 -2
  35. package/dist/contracts.js +1 -1
  36. package/dist/contracts.js.map +1 -1
  37. package/dist/index.d.ts +5 -4
  38. package/dist/index.js +1 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/mcp/server.js +187 -36
  41. package/dist/mcp/server.js.map +1 -1
  42. package/dist/model/entities.d.ts +97 -40
  43. package/dist/model/entities.js +0 -4
  44. package/dist/model/entities.js.map +1 -1
  45. package/dist/model/mappers.js +24 -43
  46. package/dist/model/mappers.js.map +1 -1
  47. package/dist/read/area-view.js +6 -6
  48. package/dist/read/area-view.js.map +1 -1
  49. package/dist/read/detail.js +51 -3
  50. package/dist/read/detail.js.map +1 -1
  51. package/dist/read/filter-contract.d.ts +1 -1
  52. package/dist/read/filter-contract.js +3 -0
  53. package/dist/read/filter-contract.js.map +1 -1
  54. package/dist/read/log-boundary.d.ts +46 -2
  55. package/dist/read/log-boundary.js +43 -2
  56. package/dist/read/log-boundary.js.map +1 -1
  57. package/dist/read/predicates.d.ts +13 -0
  58. package/dist/read/predicates.js +13 -0
  59. package/dist/read/predicates.js.map +1 -1
  60. package/dist/read/project-view.js +5 -5
  61. package/dist/read/project-view.js.map +1 -1
  62. package/dist/read/queries.d.ts +12 -0
  63. package/dist/read/queries.js +14 -0
  64. package/dist/read/queries.js.map +1 -1
  65. package/dist/read/search-rank.js +2 -2
  66. package/dist/read/search-rank.js.map +1 -1
  67. package/dist/read/shape.d.ts +30 -26
  68. package/dist/read/shape.js +130 -101
  69. package/dist/read/shape.js.map +1 -1
  70. package/dist/read/snapshot.js +2 -2
  71. package/dist/read/snapshot.js.map +1 -1
  72. package/dist/read/stage.d.ts +36 -18
  73. package/dist/read/stage.js +30 -13
  74. package/dist/read/stage.js.map +1 -1
  75. package/dist/read/views.d.ts +62 -0
  76. package/dist/read/views.js +134 -14
  77. package/dist/read/views.js.map +1 -1
  78. package/dist/surface-copy.d.ts +2 -2
  79. package/dist/surface-copy.js +2 -2
  80. package/dist/write/batch.d.ts +28 -3
  81. package/dist/write/batch.js +162 -46
  82. package/dist/write/batch.js.map +1 -1
  83. package/dist/write/clear-reminder.js +3 -1
  84. package/dist/write/clear-reminder.js.map +1 -1
  85. package/dist/write/clone.d.ts +4 -0
  86. package/dist/write/clone.js +531 -0
  87. package/dist/write/clone.js.map +1 -0
  88. package/dist/write/commands.js +196 -29
  89. package/dist/write/commands.js.map +1 -1
  90. package/dist/write/guards.d.ts +1 -1
  91. package/dist/write/guards.js +44 -12
  92. package/dist/write/guards.js.map +1 -1
  93. package/dist/write/make-repeating-project.d.ts +2 -3
  94. package/dist/write/make-repeating-project.js +2 -73
  95. package/dist/write/make-repeating-project.js.map +1 -1
  96. package/dist/write/move.js +53 -1
  97. package/dist/write/move.js.map +1 -1
  98. package/dist/write/operations.d.ts +134 -11
  99. package/dist/write/operations.js +5 -1
  100. package/dist/write/operations.js.map +1 -1
  101. package/dist/write/pipeline.d.ts +38 -0
  102. package/dist/write/pipeline.js +71 -6
  103. package/dist/write/pipeline.js.map +1 -1
  104. package/dist/write/pre-state.d.ts +41 -0
  105. package/dist/write/pre-state.js +71 -8
  106. package/dist/write/pre-state.js.map +1 -1
  107. package/dist/write/preserve-modified.d.ts +55 -0
  108. package/dist/write/preserve-modified.js +106 -0
  109. package/dist/write/preserve-modified.js.map +1 -0
  110. package/dist/write/promote-clone.d.ts +32 -0
  111. package/dist/write/promote-clone.js +620 -0
  112. package/dist/write/promote-clone.js.map +1 -0
  113. package/dist/write/reorder.js +49 -2
  114. package/dist/write/reorder.js.map +1 -1
  115. package/dist/write/resolution-timestamps.js +29 -7
  116. package/dist/write/resolution-timestamps.js.map +1 -1
  117. package/dist/write/reversibility.js +32 -14
  118. package/dist/write/reversibility.js.map +1 -1
  119. package/dist/write/scope-guard.js +2 -0
  120. package/dist/write/scope-guard.js.map +1 -1
  121. package/dist/write/undo.d.ts +12 -2
  122. package/dist/write/undo.js +145 -22
  123. package/dist/write/undo.js.map +1 -1
  124. package/dist/write/vectors/applescript.js +9 -0
  125. package/dist/write/vectors/applescript.js.map +1 -1
  126. package/dist/write/vectors/simulator.js +242 -10
  127. package/dist/write/vectors/simulator.js.map +1 -1
  128. package/dist/write/vectors/ui-certification.d.ts +1 -1
  129. package/dist/write/vectors/ui-certification.js +10 -3
  130. package/dist/write/vectors/ui-certification.js.map +1 -1
  131. package/dist/write/verify/delta.d.ts +55 -10
  132. package/dist/write/verify/delta.js +73 -10
  133. package/dist/write/verify/delta.js.map +1 -1
  134. package/package.json +1 -1
  135. package/schema/envelope.schema.json +25 -0
  136. package/skills/things-cli/SKILL.md +10 -7
  137. package/skills/things-cli/references/banner.md +1 -1
  138. package/skills/things-cli/references/contracts.md +17 -9
  139. package/skills/things-cli/references/{model.md → data-model.md} +5 -2
  140. package/skills/things-cli/references/ordering.md +2 -0
@@ -7,6 +7,35 @@ import { runRead, withClient } from "../read-driver.js";
7
7
  import { DidYouMeanError } from "../did-you-mean.js";
8
8
  // The opened resource shows its tags green (GUI: list pills are gray).
9
9
  const tagList = (tags) => green(`#${tags.map((t) => t.title).join(" #")}`);
10
+ /** The unit noun the after-completion caption phrases (singular; pluralized by the caller). */
11
+ const REPEAT_UNIT_NOUN = {
12
+ daily: "day",
13
+ weekly: "week",
14
+ monthly: "month",
15
+ yearly: "year",
16
+ };
17
+ /**
18
+ * The GUI's lower-corner repeat caption for a repeating INSTANCE, from its
19
+ * template's joined context (src/read/detail.ts `repeats`): FIXED mode reads
20
+ * `on <date>` (the "Repeats on Aug 19" projection); after-completion reads
21
+ * `<N> <unit>(s) after completion` (the "Repeats 1 day after completion" form —
22
+ * N/unit are the rule's own interval + frequency, singular/plural correct).
23
+ * Returns null when the context carries no renderable caption (no decodable rule,
24
+ * or a fixed rule with no projected date). The `paused` flag is surfaced
25
+ * SEPARATELY so the caller can fold it into the merged `repeats:` line with the
26
+ * instance-of handle. See {@link renderDetail}.
27
+ */
28
+ function repeatContextValue(ctx, todayIso) {
29
+ const rule = ctx.rule;
30
+ if (rule === undefined)
31
+ return null;
32
+ if (rule.type === "fixed") {
33
+ return ctx.next != null ? `on ${shortDate(ctx.next, todayIso)}` : null;
34
+ }
35
+ const noun = REPEAT_UNIT_NOUN[rule.unit] ?? rule.unit;
36
+ const n = rule.interval;
37
+ return `${n} ${noun}${n === 1 ? "" : "s"} after completion`;
38
+ }
10
39
  /**
11
40
  * The detail card, project-card grammar: type-labeled title row (the box
12
41
  * carries open/completed/canceled; only `trashed` needs words), uri, then
@@ -29,7 +58,7 @@ export function renderDetail(item) {
29
58
  const todayIso = localToday(renderNow(), renderZone());
30
59
  const label = item.type === "to-do" ? "To-Do:" : "Project:";
31
60
  const box = item.type === "to-do" ? todoBox(item) : projectCircle(item);
32
- const trashed = item.trashed ? ` ${red("(trashed)")}` : "";
61
+ const trashed = item.derived.trashed ? ` ${red("(trashed)")}` : "";
33
62
  const lines = [
34
63
  `${bold(label)} ${box} ${bold(item.title)}${trashed}`,
35
64
  ` ${dim("uri:")} ${thingsLink(item.uuid)}`,
@@ -83,8 +112,26 @@ export function renderDetail(item) {
83
112
  const state = item.repeating.nextOccurrence != null && st === "waiting" ? "scheduled" : st;
84
113
  meta("repeating", `TEMPLATE, ${state} (occurrences appear in upcoming)`);
85
114
  }
86
- if (item.repeating.isInstance)
87
- meta("repeating", `instance of ${item.repeating.templateUuid}`);
115
+ if (item.repeating.isInstance) {
116
+ // ONE merged line: the GUI's lower-corner caption plus the instance-of write
117
+ // handle, e.g. `repeats: on Aug 19 (instance of <uuid>)` /
118
+ // `repeats: 1 day after completion (instance of <uuid>)`; a paused template
119
+ // folds in as `(paused; instance of <uuid>)`. With no renderable caption
120
+ // (dangling FK / undecodable rule) the line is just `instance of <uuid>`
121
+ // (paused appended). JSON wire is unchanged (`instanceOf` + `repeats` stay
122
+ // separate keys, src/read/shape.ts).
123
+ const tmpl = item.repeating.templateUuid;
124
+ const instanceOf = `instance of ${tmpl}`;
125
+ const ctx = item.repeating.repeats;
126
+ const caption = ctx !== undefined ? repeatContextValue(ctx, todayIso) : null;
127
+ const paused = ctx?.paused === true;
128
+ if (caption !== null) {
129
+ meta("repeats", `${caption} (${paused ? "paused; " : ""}${instanceOf})`);
130
+ }
131
+ else {
132
+ meta("repeats", `${instanceOf}${paused ? dim(" (paused)") : ""}`);
133
+ }
134
+ }
88
135
  if (item.notes !== "")
89
136
  lines.push("", item.notes);
90
137
  if (item.type === "to-do" && item.checklist && item.checklist.length > 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"todo.js","sourceRoot":"","sources":["../../../src/cli/commands/todo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"todo.js","sourceRoot":"","sources":["../../../src/cli/commands/todo.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAoC,MAAM,gBAAgB,CAAC;AAC9F,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EACL,cAAc,EACd,cAAc,EACd,cAAc,EACd,UAAU,EACV,aAAa,EACb,SAAS,EACT,UAAU,EACV,OAAO,EACP,SAAS,GACV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,uEAAuE;AACvE,MAAM,OAAO,GAAG,CAAC,IAA8B,EAAE,EAAE,CACjD,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAEnD,+FAA+F;AAC/F,MAAM,gBAAgB,GAA2B;IAC/C,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,MAAM;CACf,CAAC;AAEF;;;;;;;;;;GAUG;AACH,SAAS,kBAAkB,CAAC,GAAkB,EAAE,QAAgB;IAC9D,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACtB,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACzE,CAAC;IACD,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC;IACtD,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;IACxB,OAAO,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC;AAC9D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAAoB;IAC/C,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAClC,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO;YACL,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YACzC,KAAK,GAAG,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YAC3C,KAAK,GAAG,CAAC,UAAU,CAAC,IAClB,IAAI,CAAC,OAAO,KAAK,IAAI;gBACnB,CAAC,CAAC,GAAG;gBACL,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,mBAAmB,KAAK,IAAI,CAClF,EAAE;SACH,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC;IAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,MAAM,KAAK,GAAG;QACZ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO,EAAE;QACrD,KAAK,GAAG,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;KAC5C,CAAC;IACF,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,KAAgC,EAAE,EAAE;QAC9D,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IACjF,CAAC,CAAC;IACF,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;YAC9B,iEAAiE;YACjE,kEAAkE;YAClE,kEAAkE;YAClE,mEAAmE;YACnE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;YAC3C,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;YACrE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YACjC,MAAM,SAAS,GAAG,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC;YAC5F,IAAI,CACF,UAAU,EACV,SAAS;gBACP,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CACtB,IAAI,CAAC,eAAe,GAAG,CAAC;oBACtB,CAAC,CAAC,uBAAuB,CAAC,IAAI,CAAC,eAAe,wBAAwB;oBACtE,CAAC,CAAC,6CAA6C,CAClD,EAAE;gBACL,CAAC,CAAC,IAAI,CACT,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;YACxC,IAAI,CACF,UAAU,EACV,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,GAAG,MAAM;gBAC9C,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBACzC,CAAC,CAAC,IAAI,CACT,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QACjC,IAAI,CACF,QAAQ,EACR,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CACjF,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC;QACpD,IAAI,CACF,SAAS,EACT,OAAO,IAAI,IAAI;YACb,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,mBAAmB,KAAK,IAAI,CAAC,CACxE,CAAC;QACF,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACvC,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3D,+EAA+E;IAC/E,gEAAgE;IAChE,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC;QACnE,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;IACxD,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,IAAI,IAAI,IAAI,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3F,IAAI,CAAC,WAAW,EAAE,aAAa,KAAK,mCAAmC,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;QAC9B,6EAA6E;QAC7E,2DAA2D;QAC3D,4EAA4E;QAC5E,yEAAyE;QACzE,yEAAyE;QACzE,2EAA2E;QAC3E,qCAAqC;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC;QACzC,MAAM,UAAU,GAAG,eAAe,IAAI,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;QACnC,MAAM,OAAO,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7E,MAAM,MAAM,GAAG,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QACpC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,SAAS,EAAE,GAAG,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,UAAU,GAAG,CAAC,CAAC;QAC3E,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,SAAS,EAAE,GAAG,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,KAAK,EAAE;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;QAClC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC/B,MAAM,IAAI,GACR,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YACzF,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC;IAC5E,IAAI;SACD,OAAO,CAAC,aAAa,CAAC;SACtB,WAAW,CACV,wJAAwJ,CACzJ;SACA,MAAM,CAAC,OAAO,EAAE,4EAA4E,CAAC;SAC7F,MAAM,CAAC,QAAQ,EAAE,wCAAwC,CAAC;SAC1D,MAAM,CAAC,aAAa,EAAE,wBAAwB,CAAC;SAC/C,MAAM,CAAC,CAAC,IAAY,EAAE,IAAoD,EAAE,EAAE;QAC7E,OAAO,CACL,IAAI,EACJ,QAAQ,EACR,CAAC,CAAC,EAAE,EAAE;YACJ,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACnC,mEAAmE;YACnE,sEAAsE;YACtE,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,MAAM,IAAI,eAAe,CACvB,qBAAqB,IAAI,GAAG,EAC5B,IAAI,EACJ,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAChD,CAAC;YACJ,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC1B,CAAC,EACD,YAAY,CACb,CAAC;IACJ,CAAC,CAAC,CAAC;IACL,IAAI;SACD,OAAO,CAAC,YAAY,CAAC;SACrB,WAAW,CACV,mHAAmH,CACpH;SACA,MAAM,CAAC,QAAQ,EAAE,wCAAwC,CAAC;SAC1D,MAAM,CAAC,aAAa,EAAE,wBAAwB,CAAC;SAC/C,MAAM,CAAC,CAAC,GAAW,EAAE,IAAuD,EAAE,EAAE;QAC/E,UAAU,CACR,IAAI,EACJ,MAAM,EACN,CAAC,CAAC,EAAE,EAAE;YACJ,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACjC,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;gBACpB,MAAM,IAAI,UAAU,CAClB,IAAI,GAAG,UAAU,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,qCAAqC,CACjG,CAAC;YACJ,OAAO,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACpD,CAAC,EACD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CACvB,CAAC;IACJ,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { readFileSync } from "node:fs";
2
- import { addRepeatRuleFlags, repeatRuleFlagsFromOpts } from "./repeat-flags.js";
2
+ import { addRepeatCalendarFlags, addRepeatingRuleFieldsFromOpts, addRepeatRuleFlags, repeatRuleFlagsFromOpts, } from "./repeat-flags.js";
3
3
  import { aggregateExitCode, blockedCode, capabilitiesTable, ClockError, describeConfig, errorEnvelope, ExitCode, getConfigKey, mutationWireData, okEnvelope, openThings, OP_ID_RE, outcomeFailed, ReferenceResolutionError, saveConfigKey, splitWhenSugar, ThingsDbNotFoundError, ThingsDbOpenError, verifyFailedCode, } from "../../index.js";
4
4
  import { usageError } from "../read-driver.js";
5
5
  import { dim } from "../style.js";
@@ -8,6 +8,12 @@ function addWriteFlags(cmd) {
8
8
  .option("--json", "emit versioned JSON envelope on stdout")
9
9
  .option("--db <path>", "explicit database path")
10
10
  .option("--dry-run", "preview the planned change and its expected effect; nothing executes")
11
+ .option("--preserve-modified", "keep this change off the modification-date timeline: capture each pre-existing edited " +
12
+ "item's modification date and restore it (to the whole second) after the change, so a " +
13
+ "changes/watch query keyed on it does not surface the edit. A no-op on a pure create. " +
14
+ "Safe with Things Cloud: the restored date syncs to your other devices and stays put, so " +
15
+ "the item stays off the timeline everywhere — unless another device edits the same item " +
16
+ "at nearly the same time, which re-dates it.")
11
17
  .option("--vector <id>", "force how the change is delivered: url-scheme | applescript | shortcuts | ui")
12
18
  .option("--allow-disruptive", "permit changes that briefly steal window focus")
13
19
  .option("--allow-very-disruptive", "permit changes that visibly drive the Things UI")
@@ -88,6 +94,7 @@ function writeOptionsFrom(opts, extra = {}) {
88
94
  ...(opts.actor !== undefined && { actor: opts.actor }),
89
95
  ...(opts.dangerouslyDriveGui === true && { dangerouslyDriveGui: true }),
90
96
  ...(opts.opId !== undefined && { opId: opts.opId }),
97
+ ...(opts.preserveModified === true && { preserveModified: true }),
91
98
  ...extra,
92
99
  };
93
100
  }
@@ -234,6 +241,23 @@ async function runWrite(opts, fn, emitFn = emitResult) {
234
241
  client?.close();
235
242
  }
236
243
  }
244
+ /**
245
+ * TTY disclosure for `--preserve-modified` on a verified ok result: a dim line
246
+ * naming how many pre-existing rows' modification date was restored (kept off
247
+ * the `changes`/watch timeline), plus a stderr warning per row the best-effort
248
+ * restore could not neutralize. Silent when the flag did no work (the field is
249
+ * absent — a pure create, or an already-silent op).
250
+ */
251
+ function emitPreserveNote(result) {
252
+ if (result.preservedModified === undefined)
253
+ return;
254
+ const failed = result.preserveFailures ?? [];
255
+ const tail = failed.length > 0 ? `; ${failed.length} could not be restored (see warnings)` : "";
256
+ process.stdout.write(dim(` preserved modification date on ${result.preservedModified} item(s) — kept off the changes timeline${tail}\n`));
257
+ for (const f of failed) {
258
+ process.stderr.write(`warning: preserve-modified: ${f.uuid}: ${f.detail}\n`);
259
+ }
260
+ }
237
261
  function emitResult(result, opts, meta) {
238
262
  switch (result.kind) {
239
263
  case "bounce-aborted": {
@@ -265,6 +289,7 @@ function emitResult(result, opts, meta) {
265
289
  ? "already applied — matched op-id in the change history, not re-run"
266
290
  : `vector=${result.vector}, tier=${result.tier}, verified`;
267
291
  process.stdout.write(`ok ${result.op}${uuid} (${status})\n`);
292
+ emitPreserveNote(result);
268
293
  }
269
294
  process.exitCode = ExitCode.Ok;
270
295
  return;
@@ -680,6 +705,11 @@ function group(program, name, description) {
680
705
  return program.command(name).description(description);
681
706
  }
682
707
  const containerRef = (value) => value === undefined ? undefined : { uuid: value, title: value };
708
+ /** The clone-specific args (`--title` / `--preserve-created`) for todo/project clone. */
709
+ const cloneArgs = (opts) => ({
710
+ ...(opts["title"] !== undefined && { title: opts["title"] }),
711
+ ...(opts["preserveCreated"] === true && { preserveCreated: true }),
712
+ });
683
713
  export function registerWriteCommands(program) {
684
714
  const todo = group(program, "todo", "To-do–scoped operations");
685
715
  addCreateTagsFlag(addWriteFlags(todo
@@ -921,6 +951,21 @@ export function registerWriteCommands(program) {
921
951
  "available for repeating to-dos.")).action(async (uuid, opts) => {
922
952
  await runWrite(opts, (c) => c.write.duplicateTodo(uuid, writeOptionsFrom(opts)));
923
953
  });
954
+ addDriveGuiFlag(addWriteFlags(todo
955
+ .command("clone <uuid>")
956
+ .description("Clone a to-do — a faithful content copy (title, notes, tags, when, reminder, deadline, " +
957
+ "checklist items and their checked state, container, and completed/canceled state with " +
958
+ "the exact original timestamp). The clone is a new capture: a new uuid, created now, at " +
959
+ "its container's usual position; the clone's uuid is printed on success. Refuses a " +
960
+ "trashed source (restore it first). A repeating template is cloned as a NEW repeating " +
961
+ "series (its content plus the source's rule, minted fresh — no instances copied); this " +
962
+ "drives the app (requires --dangerously-drive-gui) and refuses a rule the Repeat dialog " +
963
+ "cannot express. Undo trashes the clone (a template clone trashes the new series).")
964
+ .option("--title <text>", "give the clone a different title (default: the source's title)")
965
+ .option("--preserve-created", "copy the source's creation date onto the clone (minute resolution) instead of now"))).action(async (uuid, opts) => {
966
+ const clone = cloneArgs(opts);
967
+ await runWrite(opts, (c) => c.write.cloneTodo(uuid, clone, writeOptionsFrom(opts)));
968
+ });
924
969
  addCreateTagsFlag(addWriteFlags(todo
925
970
  .command("tags <uuid>")
926
971
  .description("Set or extend a to-do's tags. --set REPLACES the full tag set (an empty value " +
@@ -1042,36 +1087,39 @@ export function registerWriteCommands(program) {
1042
1087
  // --- ui vector: GUI-driven transforms (two-key gated) --------------------
1043
1088
  const REPEAT_FREQ_HELP = "daily | weekly | monthly | yearly";
1044
1089
  const REPEAT_INTERVAL_HELP = "every N units (1–99)";
1045
- for (const [verb, op, desc] of [
1046
- [
1047
- "make-repeating",
1048
- "todo.make-repeating",
1049
- "Turn a plain to-do into a repeating one. This REPLACES the to-do with a new repeating " +
1050
- "series the original disappears and a fresh recurring item takes its place " +
1051
- "(cannot be undone). Set the rule with the flags below; see `things help repeating`.",
1052
- ],
1053
- [
1054
- "reschedule-repeat",
1055
- "todo.reschedule-repeat",
1056
- "Change an existing repeating to-do's rule in place (the item keeps its identity). Set the " +
1057
- "new rule with the flags below; see `things help repeating`. `things undo` restores the " +
1058
- "previous rule.",
1059
- ],
1060
- ]) {
1061
- addDriveGuiFlag(addRepeatRuleFlags(addWriteFlags(todo
1062
- .command(`${verb} <uuid>`)
1063
- .description(desc)
1064
- .requiredOption("--frequency <freq>", REPEAT_FREQ_HELP)
1065
- .requiredOption("--interval <n>", REPEAT_INTERVAL_HELP)))).action(async (uuid, opts) => {
1066
- const frequency = opts["frequency"];
1067
- await runWrite(opts, (c) => c.write.run(op, {
1068
- uuid,
1069
- frequency,
1070
- interval: Number(opts["interval"]),
1071
- ...repeatRuleFlagsFromOpts(opts, frequency),
1072
- }, writeOptionsFrom(opts)));
1073
- });
1074
- }
1090
+ // make-repeating is promote-via-clone: a disposable copy is promoted and the
1091
+ // ORIGINAL moves to the Trash, so `things undo` reverses it (removes the new
1092
+ // series and restores the original).
1093
+ addDriveGuiFlag(addRepeatRuleFlags(addWriteFlags(todo
1094
+ .command("make-repeating <uuid>")
1095
+ .description("Turn a plain to-do into a repeating one. A disposable copy is promoted and the " +
1096
+ "ORIGINAL is moved to the Trash so `things undo` reverses it (it removes the new " +
1097
+ "series and restores the original). The new template's uuid is printed. Set the rule " +
1098
+ "with the flags below; see `things help repeating`.")
1099
+ .requiredOption("--frequency <freq>", REPEAT_FREQ_HELP)
1100
+ .requiredOption("--interval <n>", REPEAT_INTERVAL_HELP)))).action(async (uuid, opts) => {
1101
+ const frequency = opts["frequency"];
1102
+ await runWrite(opts, (c) => c.write.makeRepeatingTodo(uuid, {
1103
+ frequency,
1104
+ interval: Number(opts["interval"]),
1105
+ ...repeatRuleFlagsFromOpts(opts, frequency),
1106
+ }, writeOptionsFrom(opts)));
1107
+ });
1108
+ addDriveGuiFlag(addRepeatRuleFlags(addWriteFlags(todo
1109
+ .command("reschedule-repeat <uuid>")
1110
+ .description("Change an existing repeating to-do's rule in place (the item keeps its identity). Set " +
1111
+ "the new rule with the flags below; see `things help repeating`. `things undo` " +
1112
+ "restores the previous rule.")
1113
+ .requiredOption("--frequency <freq>", REPEAT_FREQ_HELP)
1114
+ .requiredOption("--interval <n>", REPEAT_INTERVAL_HELP)))).action(async (uuid, opts) => {
1115
+ const frequency = opts["frequency"];
1116
+ await runWrite(opts, (c) => c.write.run("todo.reschedule-repeat", {
1117
+ uuid,
1118
+ frequency,
1119
+ interval: Number(opts["interval"]),
1120
+ ...repeatRuleFlagsFromOpts(opts, frequency),
1121
+ }, writeOptionsFrom(opts)));
1122
+ });
1075
1123
  for (const [verb, op, desc] of [
1076
1124
  [
1077
1125
  "pause-repeat",
@@ -1205,7 +1253,7 @@ export function registerWriteCommands(program) {
1205
1253
  return c.write.moveHeading({ uuid: proj.uuid }, headings, placement, writeOptionsFrom(opts));
1206
1254
  });
1207
1255
  });
1208
- addWriteFlags(project
1256
+ addDriveGuiFlag(addWriteFlags(project
1209
1257
  .command("move-heading-to-project <project> <heading>")
1210
1258
  .requiredOption("--to <project>", "destination project (uuid or unique title)")
1211
1259
  .description("Move a heading — WITH its to-dos — to a DIFFERENT project. This is the cross-project " +
@@ -1215,14 +1263,14 @@ export function registerWriteCommands(program) {
1215
1263
  "once with `things config set ui.enabled true` and pass --dangerously-drive-gui. Fails " +
1216
1264
  "closed when the heading title is shared by another heading in the project, or the " +
1217
1265
  "destination title by another project (the drive addresses both by title). No undo — " +
1218
- "move it back to reverse.")).action(async (projectRef, sel, opts) => {
1266
+ "move it back to reverse."))).action(async (projectRef, sel, opts) => {
1219
1267
  await runWrite(opts, (c) => {
1220
1268
  const proj = c.resolve.project(projectRef);
1221
1269
  const dest = c.resolve.project(String(opts["to"]));
1222
1270
  return c.write.moveHeadingToProject({ uuid: proj.uuid }, sel, { uuid: dest.uuid }, writeOptionsFrom(opts));
1223
1271
  });
1224
1272
  });
1225
- addWriteFlags(project
1273
+ addDriveGuiFlag(addWriteFlags(project
1226
1274
  .command("dissolve-heading <project> <heading>")
1227
1275
  .description("Dissolve a heading — remove it but KEEP its to-dos, which become direct children of the " +
1228
1276
  "project (in the same order), NOT trashed. This is the opposite of deleting: nothing is " +
@@ -1230,7 +1278,7 @@ export function registerWriteCommands(program) {
1230
1278
  "Shortcuts heading-delete cascade, which TRASH the children. GUI-ONLY (drives the " +
1231
1279
  "heading row's ellipsis Delete): enable it once with `things config set ui.enabled true` " +
1232
1280
  "and pass --dangerously-drive-gui. Fails closed when the heading title is shared by " +
1233
- "another heading in the project. No undo — re-create the heading and move the to-dos back.")).action(async (projectRef, sel, opts) => {
1281
+ "another heading in the project. No undo — re-create the heading and move the to-dos back."))).action(async (projectRef, sel, opts) => {
1234
1282
  await runWrite(opts, (c) => {
1235
1283
  const proj = c.resolve.project(projectRef);
1236
1284
  const h = c.resolve.heading(proj.uuid, sel);
@@ -1272,11 +1320,11 @@ export function registerWriteCommands(program) {
1272
1320
  }
1273
1321
  addDriveGuiFlag(addRepeatRuleFlags(addWriteFlags(project
1274
1322
  .command("make-repeating <ref>")
1275
- .description("Turn a project into a repeating one. This REPLACES the project with a new repeating " +
1276
- "series the original disappears and a fresh recurring project takes its place (its " +
1277
- "area is kept; cannot be undone). An Anytime project with no area is moved to Someday " +
1278
- "first (a cleanup-free intermediate step, shown in --dry-run). Set the rule with the " +
1279
- "flags below; see `things help repeating`.")
1323
+ .description("Turn a project into a repeating one. A disposable copy is promoted and the ORIGINAL is " +
1324
+ "moved to the Trash so `things undo` reverses it (it removes the new series and " +
1325
+ "restores the original). Refuses a project that holds a nested repeating template. An " +
1326
+ "Anytime project with no area is moved to Someday first (a cleanup-free intermediate " +
1327
+ "step, shown in --dry-run). Set the rule with the flags below; see `things help repeating`.")
1280
1328
  .requiredOption("--frequency <freq>", REPEAT_FREQ_HELP)
1281
1329
  .requiredOption("--interval <n>", REPEAT_INTERVAL_HELP)))).action(async (uuid, opts) => {
1282
1330
  const frequency = opts["frequency"];
@@ -1288,30 +1336,73 @@ export function registerWriteCommands(program) {
1288
1336
  ...repeatRuleFlagsFromOpts(opts, frequency),
1289
1337
  }, writeOptionsFrom(opts)));
1290
1338
  });
1291
- addDriveGuiFlag(addWriteFlags(project
1339
+ addDriveGuiFlag(addRepeatCalendarFlags(addWriteFlags(project
1292
1340
  .command("add-repeating <title>")
1293
- .description("Create a project and turn it into a repeating series in ONE call. Two operations: the " +
1294
- "project is created first and PERSISTS even if the make-repeating step refuses; then it " +
1295
- "is promoted (which drives the GUI). Give --area to place it, or omit it to create in " +
1296
- "Someday. The new repeating project's uuid is printed on success.")
1341
+ .description("Create a project and turn it into a repeating series in ONE call. Two legs: the project " +
1342
+ "is created (notes/area/when/deadline/child to-dos) and PERSISTS even if the promote " +
1343
+ "refuses; then it is promoted (which drives the GUI). Give --area to place it, or omit " +
1344
+ "it to create in Someday. The new repeating project's uuid is printed; `things undo` " +
1345
+ "removes the created series (trash-both).")
1297
1346
  .option("--notes <text>", "notes body")
1298
1347
  .option("--area <ref>", "destination area (uuid or unique name)")
1348
+ .option("--when <value>", "today | evening | anytime | someday | YYYY-MM-DD")
1299
1349
  .option("--deadline <date>", "YYYY-MM-DD")
1300
1350
  .option("--todo <title>", "initial child to-do (repeatable)", collect, [])
1301
1351
  .requiredOption("--frequency <freq>", REPEAT_FREQ_HELP)
1302
- .requiredOption("--interval <n>", REPEAT_INTERVAL_HELP))).action(async (title, opts) => {
1352
+ .requiredOption("--interval <n>", REPEAT_INTERVAL_HELP)))).action(async (title, opts) => {
1303
1353
  const todos = opts["todo"];
1304
1354
  const area = containerRef(opts["area"]);
1355
+ const frequency = opts["frequency"];
1305
1356
  if (opIdCompoundRefused(opts, "project add-repeating"))
1306
1357
  return;
1307
1358
  await runWrite(opts, (c) => c.write.addRepeatingProject({
1308
1359
  title,
1309
1360
  ...(opts["notes"] !== undefined && { notes: opts["notes"] }),
1310
1361
  ...(area !== undefined && { area }),
1362
+ ...(opts["when"] !== undefined && { when: opts["when"] }),
1311
1363
  ...(opts["deadline"] !== undefined && { deadline: opts["deadline"] }),
1312
1364
  ...(todos.length > 0 && { todos }),
1313
- frequency: opts["frequency"],
1314
- interval: Number(opts["interval"]),
1365
+ ...addRepeatingRuleFieldsFromOpts(opts, frequency, Number(opts["interval"])),
1366
+ }, writeOptionsFrom(opts)));
1367
+ });
1368
+ addDriveGuiFlag(addRepeatCalendarFlags(addWriteFlags(todo
1369
+ .command("add-repeating <title>")
1370
+ .description("Create a to-do and turn it into a repeating series in ONE call. Two legs: the to-do is " +
1371
+ "created (notes/tags/when/deadline/reminder/checklist/--created-at) and PERSISTS even " +
1372
+ "if the promote refuses; then it is promoted (which drives the GUI). The new repeating " +
1373
+ "template's uuid is printed; `things undo` removes the created series (trash-both).")
1374
+ .option("--notes <text>", "notes body")
1375
+ .option("--when <value>", "today | evening | anytime | someday | YYYY-MM-DD")
1376
+ .option("--reminder <time>", "HH:mm reminder (needs a schedulable --when)")
1377
+ .option("--deadline <date>", "YYYY-MM-DD")
1378
+ .option("--tag <name>", "tag to attach (repeatable)", collect, [])
1379
+ .option("--checklist-item <title>", "checklist item (repeatable)", collect, [])
1380
+ .option("--project <ref>", "destination project (uuid or unique name)")
1381
+ .option("--area <ref>", "destination area (uuid or unique name)")
1382
+ .option("--heading <title>", "existing heading inside the target project")
1383
+ .option("--created-at <iso>", "born with this creation timestamp (ISO date or datetime; a date is noon in the effective zone)")
1384
+ .requiredOption("--frequency <freq>", REPEAT_FREQ_HELP)
1385
+ .requiredOption("--interval <n>", REPEAT_INTERVAL_HELP)))).action(async (title, opts) => {
1386
+ const tags = opts["tag"];
1387
+ const checklistItems = opts["checklistItem"];
1388
+ const projectRef = containerRef(opts["project"]);
1389
+ const area = containerRef(opts["area"]);
1390
+ const frequency = opts["frequency"];
1391
+ if (opIdCompoundRefused(opts, "todo add-repeating"))
1392
+ return;
1393
+ await runWrite(opts, (c) => c.write.addRepeatingTodo({
1394
+ title,
1395
+ ...(opts["notes"] !== undefined && { notes: opts["notes"] }),
1396
+ ...(opts["when"] !== undefined && { when: opts["when"] }),
1397
+ ...(opts["reminder"] !== undefined && { reminder: opts["reminder"] }),
1398
+ ...(opts["deadline"] !== undefined && { deadline: opts["deadline"] }),
1399
+ ...(tags.length > 0 && { tags }),
1400
+ ...(checklistItems.length > 0 && { checklistItems }),
1401
+ ...(projectRef !== undefined && { project: projectRef }),
1402
+ ...(area !== undefined && { area }),
1403
+ ...(opts["heading"] !== undefined && { heading: opts["heading"] }),
1404
+ ...(opts["createdAt"] !== undefined && { createdAt: opts["createdAt"] }),
1405
+ ...addRepeatingRuleFieldsFromOpts(opts, frequency, Number(opts["interval"])),
1315
1406
  }, writeOptionsFrom(opts)));
1316
1407
  });
1317
1408
  addWriteFlags(project
@@ -1514,6 +1605,23 @@ export function registerWriteCommands(program) {
1514
1605
  "copy's uuid is printed on success. Not available for repeating projects.")).action(async (uuid, opts) => {
1515
1606
  await runWrite(opts, (c) => c.write.duplicateProject(uuid, writeOptionsFrom(opts)));
1516
1607
  });
1608
+ addDriveGuiFlag(addWriteFlags(project
1609
+ .command("clone <ref>")
1610
+ .description("Clone a project (target by uuid or unique name) — a faithful content copy: area, " +
1611
+ "headings, headed and root children (one import), notes and deadline, plus " +
1612
+ "logged/canceled children and the project's own completed/canceled state with the " +
1613
+ "exact original timestamps. The clone is a new capture (new uuid, created now); its " +
1614
+ "uuid is printed on success. Refuses a trashed source and a project holding a nested " +
1615
+ "repeating template (named in the refusal). A repeating-template project is cloned as " +
1616
+ "a NEW repeating series (its content plus the source's rule, minted fresh — no " +
1617
+ "instances copied); this drives the app (requires --dangerously-drive-gui) and refuses " +
1618
+ "a rule the Repeat dialog cannot express. Undo trashes the clone and every child it " +
1619
+ "minted (a template clone trashes the new series).")
1620
+ .option("--title <text>", "give the clone a different title (default: the source's title)")
1621
+ .option("--preserve-created", "copy the source's creation date onto the clone (minute resolution) instead of now"))).action(async (uuid, opts) => {
1622
+ const clone = cloneArgs(opts);
1623
+ await runWrite(opts, (c) => c.write.cloneProject(uuid, clone, writeOptionsFrom(opts)));
1624
+ });
1517
1625
  addWriteFlags(project
1518
1626
  .command("complete <ref>")
1519
1627
  .description("Complete a project (target by uuid or unique name). Completing also completes its " +
@@ -1676,28 +1784,52 @@ export function registerWriteCommands(program) {
1676
1784
  }),
1677
1785
  })));
1678
1786
  });
1787
+ addWriteFlags(program
1788
+ .command("log-now")
1789
+ .description("Move completed items to the Logbook now. Reports how many items were moved; when none " +
1790
+ "are waiting it logs nothing (a clean no-op, not an error). This cannot be undone.")).action(async (opts) => {
1791
+ await runWrite(opts, (c) => c.write.logNow(writeOptionsFrom(opts)), (result, o, m) => {
1792
+ // Human path discloses the count inline; --json carries it on
1793
+ // `observed.logged`. Everything else defers to the shared emitter.
1794
+ if (result.kind === "ok" && o.json !== true) {
1795
+ for (const warning of result.warnings ?? []) {
1796
+ process.stderr.write(`warning: ${warning}\n`);
1797
+ }
1798
+ const logged = result.observed?.logged ?? 0;
1799
+ process.stdout.write(`ok log-now (logged ${logged} item${logged === 1 ? "" : "s"}, ` +
1800
+ `vector=${result.vector}, tier=${result.tier}, verified)\n`);
1801
+ process.exitCode = ExitCode.Ok;
1802
+ return;
1803
+ }
1804
+ emitResult(result, o, m);
1805
+ });
1806
+ });
1679
1807
  program
1680
1808
  .command("batch [file]")
1681
1809
  .description("Run MANY mutations from JSONL (file, or stdin when omitted/'-'): one op per line, " +
1682
1810
  '{"op": "<kind>", "params": {...}, "options": {...}} — see `things capabilities` for ' +
1683
1811
  "op kinds and params. Ops run sequentially and independently — NO transactions; a " +
1684
- "failure does not roll back earlier ops. Per-op results stream as JSONL. Per-op " +
1812
+ "failure does not roll back earlier ops. A statically-invalid line (bad shape, unknown " +
1813
+ "op, a $ref to an undeclared/forward tempId, a duplicate tempId) refuses the WHOLE " +
1814
+ "batch before anything runs, naming every bad line. Otherwise ops run and per-op " +
1815
+ "results stream as JSONL. Per-op " +
1685
1816
  "options carry the confirmation flags (acknowledgeChecklistReset, " +
1686
1817
  "acknowledgeProjectReopen, dangerouslyPermanent, acknowledgeTagSubtree). " +
1687
1818
  "CHAINING: an op that creates something may carry a `tempId` (a handle like " +
1688
1819
  '"proj1"); a LATER op references that new uuid as "$proj1" in any id/container ' +
1689
1820
  'param (dotted "$proj1.instance"/"$proj1.replaced" reach a repeating op\'s spawned ' +
1690
1821
  "instance / replaced source). A tempId is valid only on a creating op (not tag.add — " +
1691
- "reference a tag by title) and unique per batch; an unresolved/forward $ref fails just " +
1692
- "that line. IDEMPOTENCY: a line's `opId` makes resubmission safe a matching earlier " +
1693
- "success is reported already-applied, not re-created. The trailing summary line adds " +
1694
- "`tempIdMapping` (handle → uuid) and `undoToken` — undo the WHOLE batch with " +
1695
- "`things undo --txn <undoToken>`. " +
1696
- "--dry-run plans everything without executing; --fail-fast skips the rest after " +
1697
- "the first failure. Exit (worst failure wins): 0 all ok · 3 any verify-failed/invalid " +
1698
- "· 4 any blocked · 5 any drift-blocked · 6 any unsupported.")
1822
+ "reference a tag by title) and unique per batch. IDEMPOTENCY: a line's `opId` makes " +
1823
+ "resubmission safe a matching earlier success is reported already-applied, not " +
1824
+ "re-created (put an opId on EVERY line so a stopped batch can be resubmitted verbatim " +
1825
+ "to resume). The trailing summary line adds `tempIdMapping` (handle → uuid) and " +
1826
+ "`undoToken` — undo the WHOLE batch with `things undo --txn <undoToken>`. " +
1827
+ "By DEFAULT a runtime failure STOPS the batch (later lines reported not-run, with resume " +
1828
+ "guidance in the summary); --continue-on-error runs past failures. --dry-run plans " +
1829
+ "everything without executing. Exit (worst failure wins): 0 all ok · 3 any " +
1830
+ "verify-failed/invalid · 4 any blocked · 5 any drift-blocked · 6 any unsupported.")
1699
1831
  .option("--dry-run", "plan every op; execute nothing")
1700
- .option("--fail-fast", "skip remaining ops after the first failure")
1832
+ .option("--continue-on-error", "run past a failed op instead of stopping (default: stop)")
1701
1833
  .option("--json", "JSONL results + summary on stdout (also the default)")
1702
1834
  .option("--db <path>", "explicit database path")
1703
1835
  .option("--actor <name>", "author name recorded for the whole batch")
@@ -1738,7 +1870,7 @@ export function registerWriteCommands(program) {
1738
1870
  client = openThings(opts.db ? { dbPath: opts.db } : {});
1739
1871
  const batchResult = await client.write.batch(ops, {
1740
1872
  ...(opts.dryRun !== undefined && { dryRun: opts.dryRun }),
1741
- ...(opts["failFast"] === true && { failFast: true }),
1873
+ ...(opts["continueOnError"] === true && { continueOnError: true }),
1742
1874
  ...(opts.actor !== undefined && { actor: opts.actor }),
1743
1875
  }, (r) => {
1744
1876
  const pre = preInvalid.find((p) => p.index === r.index);
@@ -1757,6 +1889,10 @@ export function registerWriteCommands(program) {
1757
1889
  // (undo the whole submission with `things undo --txn <token>`).
1758
1890
  ...(hasMapping && { tempIdMapping: batchResult.tempIdMapping }),
1759
1891
  ...(batchResult.undoToken !== undefined && { undoToken: batchResult.undoToken }),
1892
+ // ADDITIVE: resume guidance when a runtime failure stopped the batch
1893
+ // (Change 2) — how many lines did not run and whether a verbatim
1894
+ // resubmission is safe.
1895
+ ...(batchResult.resumption !== undefined && { resumption: batchResult.resumption }),
1760
1896
  },
1761
1897
  };
1762
1898
  process.stdout.write(`${JSON.stringify(summary)}\n`);
@@ -1777,8 +1913,13 @@ export function registerWriteCommands(program) {
1777
1913
  "author's changes (e.g. `--by mcp` to clean up after an agent), or --txn <token> " +
1778
1914
  "to undo one exact change by the `undoToken` its result returned (immune to any " +
1779
1915
  "changes made in between). Changes made directly in the Things app cannot be " +
1780
- "undone here. IRREVERSIBLE changes are reported, not guessed: permanent deletes " +
1781
- "and changes whose prior state is unknown. Partial restores carry notes (e.g. a " +
1916
+ "undone here. --last/--by select the N newest NOT-YET-undone changes: a change already " +
1917
+ "undone is SKIPPED, so repeating `undo --last N` walks progressively deeper into " +
1918
+ "history rather than re-selecting the same changes. An IRREVERSIBLE change is NOT " +
1919
+ "skipped — it still counts toward N and is reported every pass (so you always see it " +
1920
+ "rather than silently reaching past it). IRREVERSIBLE changes are reported, not " +
1921
+ "guessed: permanent deletes and changes whose prior state is unknown. Partial " +
1922
+ "restores carry notes (e.g. a " +
1782
1923
  "delete-undo lands in the Inbox de-scheduled). --dry-run shows every inverse plan " +
1783
1924
  "without executing. Undoing a CREATED area/tag deletes it permanently — requires " +
1784
1925
  "--dangerously-permanent. An undo is refused when the item changed outside things-api " +