things-api 0.19.3 → 0.20.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 (139) hide show
  1. package/README.md +1 -1
  2. package/deputy/prebuilt/Things API Helper.app/Contents/CodeResources +0 -0
  3. package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/MacOS/things-reader +0 -0
  4. package/deputy/prebuilt/Things API Helper.app/Contents/MacOS/things-deputy +0 -0
  5. package/dist/audit/schema.d.ts +42 -10
  6. package/dist/audit/schema.js +21 -0
  7. package/dist/audit/schema.js.map +1 -1
  8. package/dist/cli/commands/doctor.js +3 -1
  9. package/dist/cli/commands/doctor.js.map +1 -1
  10. package/dist/cli/commands/op-result.js +25 -7
  11. package/dist/cli/commands/op-result.js.map +1 -1
  12. package/dist/cli/commands/repeat-flags.d.ts +19 -1
  13. package/dist/cli/commands/repeat-flags.js +71 -0
  14. package/dist/cli/commands/repeat-flags.js.map +1 -1
  15. package/dist/cli/commands/rescue.d.ts +12 -0
  16. package/dist/cli/commands/rescue.js +83 -0
  17. package/dist/cli/commands/rescue.js.map +1 -0
  18. package/dist/cli/commands/writes.js +86 -32
  19. package/dist/cli/commands/writes.js.map +1 -1
  20. package/dist/cli/help.js +25 -25
  21. package/dist/cli/help.js.map +1 -1
  22. package/dist/cli/main.js +12 -2
  23. package/dist/cli/main.js.map +1 -1
  24. package/dist/cli/resolve-invocation.d.ts +9 -1
  25. package/dist/cli/resolve-invocation.js +71 -0
  26. package/dist/cli/resolve-invocation.js.map +1 -1
  27. package/dist/cli/unknown-command.d.ts +26 -0
  28. package/dist/cli/unknown-command.js +135 -0
  29. package/dist/cli/unknown-command.js.map +1 -0
  30. package/dist/contracts.d.ts +4 -3
  31. package/dist/contracts.js +1 -1
  32. package/dist/index.d.ts +4 -2
  33. package/dist/index.js +1 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/mcp/server.js +62 -10
  36. package/dist/mcp/server.js.map +1 -1
  37. package/dist/model/entities.d.ts +11 -0
  38. package/dist/model/entities.js.map +1 -1
  39. package/dist/model/mappers.js +15 -0
  40. package/dist/model/mappers.js.map +1 -1
  41. package/dist/op-result.d.ts +24 -2
  42. package/dist/op-result.js +77 -20
  43. package/dist/op-result.js.map +1 -1
  44. package/dist/process-instance.d.ts +41 -0
  45. package/dist/process-instance.js +82 -0
  46. package/dist/process-instance.js.map +1 -0
  47. package/dist/rescue.d.ts +247 -0
  48. package/dist/rescue.js +811 -0
  49. package/dist/rescue.js.map +1 -0
  50. package/dist/session-grant.js +4 -15
  51. package/dist/session-grant.js.map +1 -1
  52. package/dist/ui-state.d.ts +8 -4
  53. package/dist/ui-state.js +8 -4
  54. package/dist/ui-state.js.map +1 -1
  55. package/dist/write/batch.js +4 -1
  56. package/dist/write/batch.js.map +1 -1
  57. package/dist/write/clone.js +12 -6
  58. package/dist/write/clone.js.map +1 -1
  59. package/dist/write/commands.d.ts +9 -0
  60. package/dist/write/commands.js +135 -50
  61. package/dist/write/commands.js.map +1 -1
  62. package/dist/write/disclosures.d.ts +275 -0
  63. package/dist/write/disclosures.js +277 -0
  64. package/dist/write/disclosures.js.map +1 -0
  65. package/dist/write/experimental.d.ts +16 -0
  66. package/dist/write/experimental.js +19 -0
  67. package/dist/write/experimental.js.map +1 -1
  68. package/dist/write/failure-hints.js +3 -2
  69. package/dist/write/failure-hints.js.map +1 -1
  70. package/dist/write/guards.d.ts +1 -1
  71. package/dist/write/guards.js +102 -3
  72. package/dist/write/guards.js.map +1 -1
  73. package/dist/write/heading.js +4 -7
  74. package/dist/write/heading.js.map +1 -1
  75. package/dist/write/lock.d.ts +85 -1
  76. package/dist/write/lock.js +121 -6
  77. package/dist/write/lock.js.map +1 -1
  78. package/dist/write/make-repeating-project.js +2 -1
  79. package/dist/write/make-repeating-project.js.map +1 -1
  80. package/dist/write/operations.d.ts +52 -6
  81. package/dist/write/operations.js +22 -0
  82. package/dist/write/operations.js.map +1 -1
  83. package/dist/write/opid.d.ts +69 -8
  84. package/dist/write/opid.js +116 -20
  85. package/dist/write/opid.js.map +1 -1
  86. package/dist/write/param-schema.js +14 -2
  87. package/dist/write/param-schema.js.map +1 -1
  88. package/dist/write/pipeline.d.ts +82 -3
  89. package/dist/write/pipeline.js +204 -60
  90. package/dist/write/pipeline.js.map +1 -1
  91. package/dist/write/planner.d.ts +7 -0
  92. package/dist/write/planner.js +12 -0
  93. package/dist/write/planner.js.map +1 -1
  94. package/dist/write/preserve-modified.d.ts +17 -8
  95. package/dist/write/preserve-modified.js.map +1 -1
  96. package/dist/write/promote-clone.js +106 -53
  97. package/dist/write/promote-clone.js.map +1 -1
  98. package/dist/write/reorder.js +45 -39
  99. package/dist/write/reorder.js.map +1 -1
  100. package/dist/write/repeat-rule.d.ts +25 -1
  101. package/dist/write/repeat-rule.js +69 -0
  102. package/dist/write/repeat-rule.js.map +1 -1
  103. package/dist/write/resolution-timestamps.js +104 -78
  104. package/dist/write/resolution-timestamps.js.map +1 -1
  105. package/dist/write/reversibility.js +2 -2
  106. package/dist/write/reversibility.js.map +1 -1
  107. package/dist/write/spawn-expectation.d.ts +135 -0
  108. package/dist/write/spawn-expectation.js +237 -0
  109. package/dist/write/spawn-expectation.js.map +1 -0
  110. package/dist/write/template-mutation.js +34 -35
  111. package/dist/write/template-mutation.js.map +1 -1
  112. package/dist/write/undo.js +45 -5
  113. package/dist/write/undo.js.map +1 -1
  114. package/dist/write/vectors/simulator.js +48 -2
  115. package/dist/write/vectors/simulator.js.map +1 -1
  116. package/dist/write/vectors/types.d.ts +12 -0
  117. package/dist/write/vectors/ui-recipes.js +11 -1
  118. package/dist/write/vectors/ui-recipes.js.map +1 -1
  119. package/dist/write/vectors/ui-state.d.ts +64 -6
  120. package/dist/write/vectors/ui-state.js +91 -4
  121. package/dist/write/vectors/ui-state.js.map +1 -1
  122. package/dist/write/vectors/ui.d.ts +69 -3
  123. package/dist/write/vectors/ui.js +293 -146
  124. package/dist/write/vectors/ui.js.map +1 -1
  125. package/dist/write/vectors/url-scheme.js +26 -0
  126. package/dist/write/vectors/url-scheme.js.map +1 -1
  127. package/dist/write/verify/delta.d.ts +17 -2
  128. package/dist/write/verify/delta.js +46 -16
  129. package/dist/write/verify/delta.js.map +1 -1
  130. package/dist/write/verify/poller.d.ts +3 -2
  131. package/dist/write/verify/poller.js +3 -1
  132. package/dist/write/verify/poller.js.map +1 -1
  133. package/package.json +1 -1
  134. package/schema/envelope.schema.json +1 -2
  135. package/skills/things-cli/SKILL.md +4 -2
  136. package/skills/things-cli/references/repeating.md +1 -1
  137. package/dist/cli/commands/ui-state.d.ts +0 -11
  138. package/dist/cli/commands/ui-state.js +0 -27
  139. package/dist/cli/commands/ui-state.js.map +0 -1
@@ -10,7 +10,7 @@ import { readFileSync } from "node:fs";
10
10
  import { armInterrupt, disarmInterrupt } from "../interrupt.js";
11
11
  import { isDevVersion } from "../skill.js";
12
12
  import { CLI_VERSION } from "../version.js";
13
- import { addRepeatCalendarFlags, addRepeatingRuleFieldsFromOpts, addRepeatRuleFlags, repeatRuleFlagsFromOpts, } from "./repeat-flags.js";
13
+ import { addRepeatCalendarFlags, addRepeatingRuleFieldsFromOpts, addRepeatRuleFlags, repeatRuleFlagsFromOpts, rescheduleParamsFromOpts, } from "./repeat-flags.js";
14
14
  import { aggregateExitCode, blockedCode, buildUpdatePatch, capabilitiesTable, CLI_UPDATE_LABELS, ClockError, closeCliTrace, describeConfig, errorEnvelope, ExitCode, HELPERS_MODES, installCliTrace, getConfigKey, parseHelpersMode, mutationWireData, NOTES_FORMAT, okEnvelope, openThings, OP_ID_RE, outcomeFailed, ParamSchemaError, ReferenceResolutionError, RESOLUTION_DATE_FORMAT, saveConfigKey, splitWhenSugar, trace, ThingsDbNotFoundError, ThingsDbOpenError, verifyFailedCode, } from "../../index.js";
15
15
  import { usageError } from "../read-driver.js";
16
16
  import { dim } from "../style.js";
@@ -25,6 +25,14 @@ const PRESERVE_MODIFIED_HELP = "keep this change off the modification-date timel
25
25
  "Safe with Things Cloud: the restored date syncs to your other devices and stays put, so " +
26
26
  "the item stays off the timeline everywhere — unless another device edits the same item " +
27
27
  "at nearly the same time, which re-dates it.";
28
+ /**
29
+ * `--verbose` help. The flag is the middle rung of the diagnostic ladder: a
30
+ * successful change reports what it did and what to consider doing next, and
31
+ * only says HOW it drove the app when asked. A failure prints the steps without
32
+ * the flag, and `things op-result <key>` prints them after the fact either way.
33
+ */
34
+ const VERBOSE_HELP = "add the step-by-step account of how the change was driven through the app; a failed change " +
35
+ "reports it anyway, and `things op-result` reports it for any change already made";
28
36
  function addWriteFlags(cmd, capability = {}) {
29
37
  const opId = new Option("--op-id <key>", "idempotency key: a resubmission with the same key is recognized as already applied " +
30
38
  "and not re-run (matches [A-Za-z0-9_-], 1-64 chars)");
@@ -40,6 +48,7 @@ function addWriteFlags(cmd, capability = {}) {
40
48
  .option("--allow-very-disruptive", "permit changes that visibly drive the Things UI")
41
49
  .option("--verify-timeout <ms>", "how long to wait for the change to take effect")
42
50
  .option("--actor <name>", "author name recorded for this change (default: from config)")
51
+ .option("--verbose", VERBOSE_HELP)
43
52
  .addOption(opId);
44
53
  }
45
54
  /**
@@ -126,6 +135,7 @@ function writeOptionsFrom(opts, extra = {}) {
126
135
  ...(maxDisruption !== undefined && { maxDisruption }),
127
136
  ...(opts.verifyTimeout !== undefined && { verifyTimeoutMs: Number(opts.verifyTimeout) }),
128
137
  ...(opts.actor !== undefined && { actor: opts.actor }),
138
+ ...(opts.verbose === true && { verbose: true }),
129
139
  ...(opts.dangerouslyDriveGui === true && { dangerouslyDriveGui: true }),
130
140
  ...(opts.opId !== undefined && { opId: opts.opId }),
131
141
  ...(opts.preserveModified === true && { preserveModified: true }),
@@ -396,6 +406,30 @@ function emitContextNote(result) {
396
406
  if (parts.length > 0)
397
407
  process.stdout.write(dim(` ${parts.join("; ")}\n`));
398
408
  }
409
+ /**
410
+ * The TWO TIERS on the human path (#632), plus the step account when it was
411
+ * asked for. Warnings are the actionable half and keep the `warning:` prefix on
412
+ * stderr, where an operator's eye already goes; notes are matter-of-fact and
413
+ * render DIM and unprefixed on stdout alongside the other disclosure lines, so
414
+ * "here is what happened" never wears the costume of "you should do something".
415
+ *
416
+ * The step account is dimmer still and indented under its own heading — present
417
+ * only when `--verbose` put `steps` on the result.
418
+ */
419
+ function emitDisclosures(result) {
420
+ for (const note of result.notes ?? [])
421
+ process.stdout.write(dim(` ${note}\n`));
422
+ for (const warning of result.warnings ?? []) {
423
+ process.stderr.write(`warning: ${warning}\n`);
424
+ }
425
+ const steps = result.steps ?? [];
426
+ if (steps.length === 0)
427
+ return;
428
+ process.stdout.write(dim(` drove ${steps.length} step(s):\n`));
429
+ for (const [i, step] of steps.entries()) {
430
+ process.stdout.write(dim(` ${i + 1}. ${step}\n`));
431
+ }
432
+ }
399
433
  function emitResult(result, opts, meta) {
400
434
  switch (result.kind) {
401
435
  case "bounce-aborted": {
@@ -415,10 +449,11 @@ function emitResult(result, opts, meta) {
415
449
  return;
416
450
  }
417
451
  case "ok": {
418
- for (const warning of result.warnings ?? []) {
419
- process.stderr.write(`warning: ${warning}\n`);
420
- }
421
452
  if (opts.json) {
453
+ // ONE CHANNEL (#632). Under --json the envelope is the WHOLE output:
454
+ // `warnings` and `notes` are already in it, so echoing them to stderr
455
+ // made every consumer pay for the same prose twice — once parsed, once
456
+ // as noise in its log. The human path below keeps the prose.
422
457
  process.stdout.write(`${JSON.stringify(okEnvelope("mutation-result", mutationWireData(result), meta))}\n`);
423
458
  }
424
459
  else {
@@ -430,6 +465,7 @@ function emitResult(result, opts, meta) {
430
465
  emitOccurrenceNote(result);
431
466
  emitPreserveNote(result);
432
467
  emitContextNote(result);
468
+ emitDisclosures(result);
433
469
  }
434
470
  process.exitCode = ExitCode.Ok;
435
471
  return;
@@ -459,7 +495,14 @@ function emitResult(result, opts, meta) {
459
495
  message: result.detail,
460
496
  ...(result.likelyCause !== undefined && { likelyCause: result.likelyCause }),
461
497
  ...(result.hint !== undefined && { remediation: result.hint }),
462
- detail: { expected: result.expected, observed: result.observed },
498
+ detail: {
499
+ expected: result.expected,
500
+ observed: result.observed,
501
+ // A FAILURE always carries the drive's play-by-play (#632) —
502
+ // no flag, no second command. It is what made the field bug
503
+ // reports actionable.
504
+ ...(result.steps !== undefined && { steps: result.steps }),
505
+ },
463
506
  }, meta))}\n`);
464
507
  }
465
508
  else {
@@ -467,6 +510,9 @@ function emitResult(result, opts, meta) {
467
510
  if (result.likelyCause !== undefined) {
468
511
  process.stderr.write(` likely cause: ${result.likelyCause}${result.hint !== undefined ? ` — ${result.hint}` : ""}\n`);
469
512
  }
513
+ for (const [i, step] of (result.steps ?? []).entries()) {
514
+ process.stderr.write(` step ${i + 1}: ${step}\n`);
515
+ }
470
516
  }
471
517
  process.exitCode = ExitCode.VerifyFailed;
472
518
  return;
@@ -1309,18 +1355,23 @@ export function registerWriteCommands(program) {
1309
1355
  });
1310
1356
  addDriveGuiFlag(addRepeatRuleFlags(addWriteFlags(todo
1311
1357
  .command("reschedule-repeat <uuid>")
1312
- .description("Change an existing repeating to-do's rule in place (the item keeps its identity). Set " +
1313
- "the new rule with the flags below; see `things help repeating`. `things undo` " +
1314
- "restores the previous rule.")
1315
- .requiredOption("--frequency <freq>", REPEAT_FREQ_HELP)
1316
- .requiredOption("--interval <n>", REPEAT_INTERVAL_HELP)))).action(async (uuid, opts) => {
1317
- const frequency = opts["frequency"];
1318
- await runWrite(opts, (c) => c.write.run("todo.reschedule-repeat", {
1319
- uuid,
1320
- frequency,
1321
- interval: Number(opts["interval"]),
1322
- ...repeatRuleFlagsFromOpts(opts, frequency),
1323
- }, writeOptionsFrom(opts)));
1358
+ .description("Change an existing repeating to-do in place (the item keeps its identity). Two " +
1359
+ "spellings: with --frequency and --interval it sets a new rule (see `things help " +
1360
+ "repeating`), driving the app's interface, and `things undo` restores the previous " +
1361
+ "rule. With --when <date> ALONE it MOVES the series to that date instead, keeping " +
1362
+ "the rule and needing no --dangerously-drive-gui the whole series moves (a weekly " +
1363
+ "item moved to a Thursday repeats on Thursdays), occurrences due before that date " +
1364
+ "never appear, and it cannot be undone here. Moving needs a date after today, a " +
1365
+ "series that is not paused, does not repeat after completion, and does not repeat " +
1366
+ "on several weekdays; Things 3.23 or later.")
1367
+ .option("--frequency <freq>", REPEAT_FREQ_HELP)
1368
+ .option("--interval <n>", REPEAT_INTERVAL_HELP)))).action(async (uuid, opts) => {
1369
+ const built = rescheduleParamsFromOpts(uuid, opts);
1370
+ if (built.kind === "error") {
1371
+ usageError(opts, built.message);
1372
+ return;
1373
+ }
1374
+ await runWrite(opts, (c) => c.write.run("todo.reschedule-repeat", built.params, writeOptionsFrom(opts)));
1324
1375
  });
1325
1376
  for (const [verb, op, desc] of [
1326
1377
  [
@@ -1495,18 +1546,23 @@ export function registerWriteCommands(program) {
1495
1546
  // --- ui vector: repeating-project transforms (two-key gated) -------------
1496
1547
  addDriveGuiFlag(addRepeatRuleFlags(addWriteFlags(project
1497
1548
  .command("reschedule-repeat <ref>")
1498
- .description("Change an existing repeating project's rule in place (target by uuid or unique name; " +
1499
- "the project keeps its identity). Set the new rule with the flags below; see " +
1500
- "`things help repeating`. `things undo` restores the previous rule.")
1501
- .requiredOption("--frequency <freq>", REPEAT_FREQ_HELP)
1502
- .requiredOption("--interval <n>", REPEAT_INTERVAL_HELP)))).action(async (uuid, opts) => {
1503
- const frequency = opts["frequency"];
1504
- await runWrite(opts, (c) => c.write.run("project.reschedule-repeat", {
1505
- uuid,
1506
- frequency,
1507
- interval: Number(opts["interval"]),
1508
- ...repeatRuleFlagsFromOpts(opts, frequency),
1509
- }, writeOptionsFrom(opts)));
1549
+ .description("Change an existing repeating project in place (target by uuid or unique name; the " +
1550
+ "project keeps its identity). With --frequency and --interval it sets a new rule " +
1551
+ "(see `things help repeating`), driving the app's interface, and `things undo` " +
1552
+ "restores the previous rule. With --when <date> ALONE it MOVES the series to that " +
1553
+ "date instead, keeping the rule and needing no --dangerously-drive-gui — the whole " +
1554
+ "series moves, occurrences due before that date never appear, and it cannot be " +
1555
+ "undone here. Same conditions as the to-do verb: a date after today, a series that " +
1556
+ "is not paused, does not repeat after completion, and does not repeat on several " +
1557
+ "weekdays; Things 3.23 or later.")
1558
+ .option("--frequency <freq>", REPEAT_FREQ_HELP)
1559
+ .option("--interval <n>", REPEAT_INTERVAL_HELP)))).action(async (uuid, opts) => {
1560
+ const built = rescheduleParamsFromOpts(uuid, opts);
1561
+ if (built.kind === "error") {
1562
+ usageError(opts, built.message);
1563
+ return;
1564
+ }
1565
+ await runWrite(opts, (c) => c.write.run("project.reschedule-repeat", built.params, writeOptionsFrom(opts)));
1510
1566
  });
1511
1567
  for (const [verb, op, desc] of [
1512
1568
  [
@@ -1999,12 +2055,10 @@ export function registerWriteCommands(program) {
1999
2055
  // Human path discloses the count inline; --json carries it on
2000
2056
  // `observed.logged`. Everything else defers to the shared emitter.
2001
2057
  if (result.kind === "ok" && o.json !== true) {
2002
- for (const warning of result.warnings ?? []) {
2003
- process.stderr.write(`warning: ${warning}\n`);
2004
- }
2005
2058
  const logged = result.observed?.logged ?? 0;
2006
2059
  process.stdout.write(`ok log-now (logged ${logged} item${logged === 1 ? "" : "s"}, ` +
2007
2060
  `vector=${result.vector}, tier=${result.tier}, verified)\n`);
2061
+ emitDisclosures(result);
2008
2062
  process.exitCode = ExitCode.Ok;
2009
2063
  return;
2010
2064
  }