things-api 0.16.0 → 0.17.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 (86) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/commands/repeat-flags.d.ts +3 -3
  3. package/dist/cli/commands/repeat-flags.js +90 -33
  4. package/dist/cli/commands/repeat-flags.js.map +1 -1
  5. package/dist/cli/commands/writes.js +59 -5
  6. package/dist/cli/commands/writes.js.map +1 -1
  7. package/dist/cli/help.js +9 -2
  8. package/dist/cli/help.js.map +1 -1
  9. package/dist/cli/interrupt.d.ts +42 -0
  10. package/dist/cli/interrupt.js +105 -0
  11. package/dist/cli/interrupt.js.map +1 -0
  12. package/dist/cli/main.js +5 -0
  13. package/dist/cli/main.js.map +1 -1
  14. package/dist/client.js +3 -0
  15. package/dist/client.js.map +1 -1
  16. package/dist/config.d.ts +39 -0
  17. package/dist/config.js +16 -1
  18. package/dist/config.js.map +1 -1
  19. package/dist/contracts.d.ts +9 -2
  20. package/dist/contracts.js +1 -1
  21. package/dist/contracts.js.map +1 -1
  22. package/dist/index.d.ts +3 -0
  23. package/dist/index.js +7 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/model/occurrences.d.ts +14 -1
  26. package/dist/model/occurrences.js +20 -4
  27. package/dist/model/occurrences.js.map +1 -1
  28. package/dist/model/recurrence.d.ts +60 -9
  29. package/dist/model/recurrence.js +76 -19
  30. package/dist/model/recurrence.js.map +1 -1
  31. package/dist/paths.d.ts +8 -0
  32. package/dist/paths.js +10 -0
  33. package/dist/paths.js.map +1 -1
  34. package/dist/sync-health.js +2 -1
  35. package/dist/sync-health.js.map +1 -1
  36. package/dist/trace/tracer.d.ts +87 -0
  37. package/dist/trace/tracer.js +167 -0
  38. package/dist/trace/tracer.js.map +1 -0
  39. package/dist/write/clear-reminder.js +24 -21
  40. package/dist/write/clear-reminder.js.map +1 -1
  41. package/dist/write/commands.js +50 -45
  42. package/dist/write/commands.js.map +1 -1
  43. package/dist/write/completion-context.d.ts +57 -0
  44. package/dist/write/completion-context.js +49 -0
  45. package/dist/write/completion-context.js.map +1 -0
  46. package/dist/write/failure-hints.d.ts +10 -1
  47. package/dist/write/failure-hints.js +8 -0
  48. package/dist/write/failure-hints.js.map +1 -1
  49. package/dist/write/guards.d.ts +1 -1
  50. package/dist/write/guards.js +10 -0
  51. package/dist/write/guards.js.map +1 -1
  52. package/dist/write/operations.d.ts +12 -3
  53. package/dist/write/pipeline.d.ts +39 -0
  54. package/dist/write/pipeline.js +230 -17
  55. package/dist/write/pipeline.js.map +1 -1
  56. package/dist/write/pre-state.d.ts +1 -1
  57. package/dist/write/pre-state.js +2 -2
  58. package/dist/write/pre-state.js.map +1 -1
  59. package/dist/write/promote-clone.js +260 -13
  60. package/dist/write/promote-clone.js.map +1 -1
  61. package/dist/write/repeat-anchor.d.ts +110 -0
  62. package/dist/write/repeat-anchor.js +238 -0
  63. package/dist/write/repeat-anchor.js.map +1 -0
  64. package/dist/write/repeat-asserts.d.ts +28 -0
  65. package/dist/write/repeat-asserts.js +169 -0
  66. package/dist/write/repeat-asserts.js.map +1 -0
  67. package/dist/write/repeat-rule.js +27 -12
  68. package/dist/write/repeat-rule.js.map +1 -1
  69. package/dist/write/vectors/session-reachability.d.ts +78 -0
  70. package/dist/write/vectors/session-reachability.js +103 -0
  71. package/dist/write/vectors/session-reachability.js.map +1 -0
  72. package/dist/write/vectors/simulator.js +68 -29
  73. package/dist/write/vectors/simulator.js.map +1 -1
  74. package/dist/write/vectors/types.d.ts +117 -10
  75. package/dist/write/vectors/ui-recipes.d.ts +8 -0
  76. package/dist/write/vectors/ui-recipes.js +120 -22
  77. package/dist/write/vectors/ui-recipes.js.map +1 -1
  78. package/dist/write/vectors/ui.d.ts +79 -13
  79. package/dist/write/vectors/ui.js +415 -59
  80. package/dist/write/vectors/ui.js.map +1 -1
  81. package/dist/write/verify/delta.d.ts +12 -14
  82. package/dist/write/verify/delta.js +35 -34
  83. package/dist/write/verify/delta.js.map +1 -1
  84. package/package.json +1 -1
  85. package/schema/envelope.schema.json +22 -0
  86. package/skills/things-cli/SKILL.md +5 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A typed TypeScript library + CLI (`things`) for programmatic interaction with [Things 3](https://culturedcode.com/things/) by Cultured Code.
4
4
 
5
- **Status: read + write + MCP layers live and published to npm (v0.16.0 — see [CHANGELOG.md](CHANGELOG.md)).** Reads go straight to the local SQLite database (UI-exact Today ordering, sidebar-grouped Anytime/Someday with container-status cascade, decoded repeat rules, occurrence projections); writes run a verified pipeline over four write vectors — two lab-validated headless surfaces (the URL scheme + AppleScript) and two opt-in surfaces (Apple Shortcuts and an Accessibility-driven GUI vector) — with hazard guards, disruption-tier policy, a JSONL audit trail, batch mode, audit-replay undo (transactional across compound operations), full project lifecycle (complete/cancel/reopen/restore), heading rename/archive/unarchive with children policies, container detach, granular stateful checklists, tag hierarchy management incl. un-nesting, completion/creation backdating, Logbook imports, tiered fuzzy name resolution (uuid prefixes, `things:///show?id=` share links), and kind-neutral in-place reordering within any container or view (one `things reorder <refs…> [--start|--end|--before|--after] --in <target>` verb — Today/Evening, a project, an area, a heading's children, someday, inbox — plus `project move-heading` for the headings themselves and `area reorder` for the sidebar). The Apple Shortcuts surface is wired for the two headless capabilities nothing else can do: creating a heading in an existing project (`things project add-heading`) and clearing a reminder from a date-scheduled item (`things todo clear-reminder`). Both run through bundled proxy shortcuts (`things setup shortcuts`) and are gated on their presence. The fourth vector — the Accessibility-driven GUI vector — is off by default and drives the local Things app to reach transforms that exist on no scriptable surface (repeat-rule editing on existing to-dos and projects, convert-to-project, sidebar area reorder); see [Accessibility GUI vector](#accessibility-gui-vector-optional-off-by-default) below. Single-item permanent delete stays interactive-only (its macOS consent has no always-allow) and is out of the headless pipeline. See [docs/design/](docs/design/) for the architecture and VM-lab design, [docs/lab/](docs/lab/harness.md) for the probe harness and campaign results the write layer is grounded in, and [docs/atlas/](docs/atlas/schema-v26.md) for the database↔UI map.
5
+ **Status: read + write + MCP layers live and published to npm (v0.17.0 — see [CHANGELOG.md](CHANGELOG.md)).** Reads go straight to the local SQLite database (UI-exact Today ordering, sidebar-grouped Anytime/Someday with container-status cascade, decoded repeat rules, occurrence projections); writes run a verified pipeline over four write vectors — two lab-validated headless surfaces (the URL scheme + AppleScript) and two opt-in surfaces (Apple Shortcuts and an Accessibility-driven GUI vector) — with hazard guards, disruption-tier policy, a JSONL audit trail, batch mode, audit-replay undo (transactional across compound operations), full project lifecycle (complete/cancel/reopen/restore), heading rename/archive/unarchive with children policies, container detach, granular stateful checklists, tag hierarchy management incl. un-nesting, completion/creation backdating, Logbook imports, tiered fuzzy name resolution (uuid prefixes, `things:///show?id=` share links), and kind-neutral in-place reordering within any container or view (one `things reorder <refs…> [--start|--end|--before|--after] --in <target>` verb — Today/Evening, a project, an area, a heading's children, someday, inbox — plus `project move-heading` for the headings themselves and `area reorder` for the sidebar). The Apple Shortcuts surface is wired for the two headless capabilities nothing else can do: creating a heading in an existing project (`things project add-heading`) and clearing a reminder from a date-scheduled item (`things todo clear-reminder`). Both run through bundled proxy shortcuts (`things setup shortcuts`) and are gated on their presence. The fourth vector — the Accessibility-driven GUI vector — is off by default and drives the local Things app to reach transforms that exist on no scriptable surface (repeat-rule editing on existing to-dos and projects, convert-to-project, sidebar area reorder); see [Accessibility GUI vector](#accessibility-gui-vector-optional-off-by-default) below. Single-item permanent delete stays interactive-only (its macOS consent has no always-allow) and is out of the headless pipeline. See [docs/design/](docs/design/) for the architecture and VM-lab design, [docs/lab/](docs/lab/harness.md) for the probe harness and campaign results the write layer is grounded in, and [docs/atlas/](docs/atlas/schema-v26.md) for the database↔UI map.
6
6
 
7
7
  ```sh
8
8
  things today --json # read: your Today list, Evening split, UI order
@@ -23,8 +23,8 @@ export declare function addRepeatCalendarFlags(cmd: Command): Command;
23
23
  export declare function addRepeatingRuleFieldsFromOpts(opts: Record<string, unknown>, frequency: RepeatFrequency, interval: number): AddRepeatingRuleFields;
24
24
  /**
25
25
  * Build the extended rule fields from CLI options (present keys only —
26
- * exactOptionalPropertyTypes). Combination validity is enforced downstream
27
- * (the same refusals the library raises), so a wrong-frequency flag surfaces a
28
- * clear error rather than being silently applied.
26
+ * exactOptionalPropertyTypes). A wrong-frequency anchor flag is REFUSED here
27
+ * (UIC6-l), never silently dropped; other combination validity (e.g. weekdays on
28
+ * a non-weekly rule) is enforced downstream by assertRepeatRule.
29
29
  */
30
30
  export declare function repeatRuleFlagsFromOpts(opts: Record<string, unknown>, frequency: RepeatFrequency): RepeatRuleFlagFields;
@@ -9,6 +9,8 @@ export function addRepeatRuleFlags(cmd) {
9
9
  .option("--yearly-month <n>", "yearly only: the month (1–12)")
10
10
  .option("--ends-after <n>", "stop after N occurrences (1–999)")
11
11
  .option("--ends-on <date>", "YYYY-MM-DD — stop after this date")
12
+ .option("--when <date>", "YYYY-MM-DD — the first occurrence (drives the Repeat dialog's Next field); " +
13
+ "make-repeating defaults to the item's scheduled date")
12
14
  .option("--reminder <time>", "HH:mm — a reminder time on each occurrence")
13
15
  .option("--deadline", "give each occurrence a deadline")
14
16
  .option("--start-days-earlier <n>", "with --deadline: start each occurrence N days before its deadline");
@@ -55,45 +57,100 @@ function dayAnchor(opts) {
55
57
  return undefined;
56
58
  }
57
59
  /**
58
- * Build the extended rule fields from CLI options (present keys only
59
- * exactOptionalPropertyTypes). Combination validity is enforced downstream
60
- * (the same refusals the library raises), so a wrong-frequency flag surfaces a
61
- * clear error rather than being silently applied.
60
+ * The CALENDAR-ANCHOR flags that apply only to a SUBSET of frequencies, and the
61
+ * frequencies that consume each. A present anchor flag on any other frequency is
62
+ * a hard error never a silent drop (UIC6-l). Exhaustive over the anchor flag
63
+ * set; a new anchor flag must earn a row here (and the mapper below consumes it).
64
+ * `weekdays` is intentionally ABSENT: it is mapped unconditionally and refused
65
+ * downstream by assertRepeatRule ("weekdays apply only to a weekly rule"), the
66
+ * mapped-contradiction path — this table is only for the SILENTLY-dropped anchors.
62
67
  */
63
- export function repeatRuleFlagsFromOpts(opts, frequency) {
64
- const fields = {};
65
- if (opts["afterCompletion"] === true)
66
- fields.afterCompletion = true;
67
- if (typeof opts["weekdays"] === "string") {
68
- fields.weekdays = opts["weekdays"]
68
+ const ANCHOR_FLAG_FREQUENCIES = {
69
+ onDay: ["monthly", "yearly"],
70
+ onWeekday: ["monthly", "yearly"],
71
+ onOrdinal: ["monthly", "yearly"],
72
+ yearlyMonth: ["yearly"],
73
+ };
74
+ /** The user-facing flag spelling for a camelCase option key (error messages). */
75
+ const ANCHOR_FLAG_SPELLING = {
76
+ onDay: "--on-day",
77
+ onWeekday: "--on-weekday",
78
+ onOrdinal: "--on-ordinal",
79
+ yearlyMonth: "--yearly-month",
80
+ };
81
+ /**
82
+ * Refuse a calendar-anchor flag supplied on a frequency that does not consume it
83
+ * (UIC6-l): `--frequency weekly --on-day 15` errors instead of silently running a
84
+ * plain weekly rule. Behavioral message — names the flag + the frequency it
85
+ * belongs to, never a mechanism.
86
+ */
87
+ function assertAnchorFlagsMatchFrequency(opts, frequency) {
88
+ for (const [key, allowed] of Object.entries(ANCHOR_FLAG_FREQUENCIES)) {
89
+ if (opts[key] === undefined)
90
+ continue;
91
+ if (!allowed.includes(frequency)) {
92
+ const flag = ANCHOR_FLAG_SPELLING[key] ?? key;
93
+ throw new RangeError(`${flag} applies only to a ${allowed.join(" or ")} rule — this rule is ${frequency}`);
94
+ }
95
+ }
96
+ }
97
+ /**
98
+ * EXHAUSTIVE over every field of {@link RepeatRuleFlagFields}: each rule param is
99
+ * derived from the CLI options by exactly one entry, so a new param added to
100
+ * RepeatRuleParams breaks compilation here until it earns a flag mapping. This is
101
+ * what makes an accepted-but-dropped flag impossible for this vocabulary (the
102
+ * UIC6-l class) — the wrong-frequency guard above catches the inverse (a flag
103
+ * with no consuming frequency). Each entry returns the mapped value or
104
+ * `undefined` (absent — exactOptionalPropertyTypes).
105
+ */
106
+ const FLAG_MAP = {
107
+ afterCompletion: (opts) => (opts["afterCompletion"] === true ? true : undefined),
108
+ weekdays: (opts) => typeof opts["weekdays"] === "string"
109
+ ? opts["weekdays"]
69
110
  .split(",")
70
111
  .map((d) => d.trim().toLowerCase())
71
- .filter((d) => d.length > 0);
72
- }
73
- const anchor = dayAnchor(opts);
74
- if (frequency === "monthly" && anchor !== undefined) {
75
- fields.monthly = anchor;
76
- }
77
- if (frequency === "yearly") {
112
+ .filter((d) => d.length > 0)
113
+ : undefined,
114
+ monthly: (_opts, frequency, anchor) => frequency === "monthly" && anchor !== undefined ? anchor : undefined,
115
+ yearly: (opts, frequency, anchor) => {
116
+ if (frequency !== "yearly")
117
+ return undefined;
78
118
  const month = opts["yearlyMonth"];
79
- if (month !== undefined || anchor !== undefined) {
80
- const base = { month: Number(month) };
81
- fields.yearly = (anchor === undefined ? base : { ...base, ...anchor });
119
+ if (month === undefined && anchor === undefined)
120
+ return undefined;
121
+ const base = { month: Number(month) };
122
+ return (anchor === undefined ? base : { ...base, ...anchor });
123
+ },
124
+ ends: (opts) => {
125
+ if (opts["endsAfter"] !== undefined)
126
+ return { kind: "after", count: Number(opts["endsAfter"]) };
127
+ if (typeof opts["endsOn"] === "string")
128
+ return { kind: "on-date", date: opts["endsOn"] };
129
+ return undefined;
130
+ },
131
+ reminder: (opts) => (typeof opts["reminder"] === "string" ? opts["reminder"] : undefined),
132
+ next: (opts) => (typeof opts["when"] === "string" ? opts["when"] : undefined),
133
+ deadline: (opts) => (opts["deadline"] === true ? true : undefined),
134
+ startDaysEarlier: (opts) => opts["startDaysEarlier"] !== undefined ? Number(opts["startDaysEarlier"]) : undefined,
135
+ };
136
+ /**
137
+ * Build the extended rule fields from CLI options (present keys only —
138
+ * exactOptionalPropertyTypes). A wrong-frequency anchor flag is REFUSED here
139
+ * (UIC6-l), never silently dropped; other combination validity (e.g. weekdays on
140
+ * a non-weekly rule) is enforced downstream by assertRepeatRule.
141
+ */
142
+ export function repeatRuleFlagsFromOpts(opts, frequency) {
143
+ assertAnchorFlagsMatchFrequency(opts, frequency);
144
+ const anchor = dayAnchor(opts);
145
+ const fields = {};
146
+ for (const key of Object.keys(FLAG_MAP)) {
147
+ const value = FLAG_MAP[key](opts, frequency, anchor);
148
+ if (value !== undefined) {
149
+ // The map's per-key return type is exactly RepeatRuleFlagFields[K]; the
150
+ // index write is safe but TS cannot narrow K across the loop.
151
+ fields[key] = value;
82
152
  }
83
153
  }
84
- if (opts["endsAfter"] !== undefined) {
85
- fields.ends = { kind: "after", count: Number(opts["endsAfter"]) };
86
- }
87
- else if (typeof opts["endsOn"] === "string") {
88
- fields.ends = { kind: "on-date", date: opts["endsOn"] };
89
- }
90
- if (typeof opts["reminder"] === "string")
91
- fields.reminder = opts["reminder"];
92
- if (opts["deadline"] === true)
93
- fields.deadline = true;
94
- if (opts["startDaysEarlier"] !== undefined) {
95
- fields.startDaysEarlier = Number(opts["startDaysEarlier"]);
96
- }
97
154
  return fields;
98
155
  }
99
156
  //# sourceMappingURL=repeat-flags.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"repeat-flags.js","sourceRoot":"","sources":["../../../src/cli/commands/repeat-flags.ts"],"names":[],"mappings":"AAsBA,8DAA8D;AAC9D,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC7C,OAAO,GAAG;SACP,MAAM,CACL,oBAAoB,EACpB,6EAA6E,CAC9E;SACA,MAAM,CACL,mBAAmB,EACnB,qEAAqE,CACtE;SACA,MAAM,CAAC,gBAAgB,EAAE,2DAA2D,CAAC;SACrF,MAAM,CACL,wBAAwB,EACxB,4EAA4E,CAC7E;SACA,MAAM,CACL,kBAAkB,EAClB,uEAAuE,CACxE;SACA,MAAM,CAAC,oBAAoB,EAAE,+BAA+B,CAAC;SAC7D,MAAM,CAAC,kBAAkB,EAAE,kCAAkC,CAAC;SAC9D,MAAM,CAAC,kBAAkB,EAAE,mCAAmC,CAAC;SAC/D,MAAM,CAAC,mBAAmB,EAAE,4CAA4C,CAAC;SACzE,MAAM,CAAC,YAAY,EAAE,iCAAiC,CAAC;SACvD,MAAM,CACL,0BAA0B,EAC1B,mEAAmE,CACpE,CAAC;AACN,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAY;IACjD,OAAO,GAAG;SACP,MAAM,CACL,oBAAoB,EACpB,6EAA6E,CAC9E;SACA,MAAM,CACL,mBAAmB,EACnB,qEAAqE,CACtE;SACA,MAAM,CAAC,gBAAgB,EAAE,2DAA2D,CAAC;SACrF,MAAM,CACL,wBAAwB,EACxB,4EAA4E,CAC7E;SACA,MAAM,CACL,kBAAkB,EAClB,uEAAuE,CACxE;SACA,MAAM,CAAC,oBAAoB,EAAE,+BAA+B,CAAC;SAC7D,MAAM,CAAC,kBAAkB,EAAE,kCAAkC,CAAC;SAC9D,MAAM,CAAC,kBAAkB,EAAE,mCAAmC,CAAC,CAAC;AACrE,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,8BAA8B,CAC5C,IAA6B,EAC7B,SAA0B,EAC1B,QAAgB;IAEhB,MAAM,EACJ,QAAQ,EAAE,EAAE,EACZ,QAAQ,EAAE,EAAE,EACZ,gBAAgB,EAAE,EAAE,EACpB,GAAG,IAAI,EACR,GAAG,uBAAuB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC7C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED,iFAAiF;AACjF,6EAA6E;AAC7E,gEAAgE;AAChE,SAAS,SAAS,CAAC,IAA6B;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAuB,CAAC;IAClD,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAuB,CAAC;IAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAuB,CAAC;IAC1D,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,EAAE,GAAG,EAAE,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAC5D,CAAC;IACD,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QACvD,OAAO;YACL,OAAO,EAAE,SAAoB;YAC7B,OAAO,EAAE,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAmB;SAC/E,CAAC;IACJ,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,IAA6B,EAC7B,SAA0B;IAE1B,MAAM,MAAM,GAAyB,EAAE,CAAC;IAExC,IAAI,IAAI,CAAC,iBAAiB,CAAC,KAAK,IAAI;QAAE,MAAM,CAAC,eAAe,GAAG,IAAI,CAAC;IAEpE,IAAI,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,QAAQ,EAAE,CAAC;QACzC,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC;aAC/B,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;aAClC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAc,CAAC;IAC9C,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,SAAS,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACpD,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC;IAC1B,CAAC;IACD,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;QAClC,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAChD,MAAM,IAAI,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACtC,MAAM,CAAC,MAAM,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,EAAE,CAAiB,CAAC;QACzF,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,SAAS,EAAE,CAAC;QACpC,MAAM,CAAC,IAAI,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;IACpE,CAAC;SAAM,IAAI,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC9C,MAAM,CAAC,IAAI,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC1D,CAAC;IAED,IAAI,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,QAAQ;QAAE,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;IAC7E,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI;QAAE,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC;IACtD,IAAI,IAAI,CAAC,kBAAkB,CAAC,KAAK,SAAS,EAAE,CAAC;QAC3C,MAAM,CAAC,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"repeat-flags.js","sourceRoot":"","sources":["../../../src/cli/commands/repeat-flags.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,9 @@
1
1
  import { readFileSync } from "node:fs";
2
+ import { armInterrupt, disarmInterrupt } from "../interrupt.js";
3
+ import { isDevVersion } from "../skill.js";
4
+ import { CLI_VERSION } from "../version.js";
2
5
  import { addRepeatCalendarFlags, addRepeatingRuleFieldsFromOpts, addRepeatRuleFlags, repeatRuleFlagsFromOpts, } from "./repeat-flags.js";
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";
6
+ import { aggregateExitCode, blockedCode, capabilitiesTable, ClockError, closeCliTrace, describeConfig, errorEnvelope, ExitCode, installCliTrace, getConfigKey, mutationWireData, okEnvelope, openThings, OP_ID_RE, outcomeFailed, ReferenceResolutionError, saveConfigKey, splitWhenSugar, trace, ThingsDbNotFoundError, ThingsDbOpenError, verifyFailedCode, } from "../../index.js";
4
7
  import { usageError } from "../read-driver.js";
5
8
  import { dim } from "../style.js";
6
9
  function addWriteFlags(cmd) {
@@ -145,7 +148,12 @@ function headingPlacement(c, projectUuid, opts) {
145
148
  /** Add the mandatory GUI-drive acknowledgement to a ui-vector command. */
146
149
  function addDriveGuiFlag(cmd) {
147
150
  return cmd.option("--dangerously-drive-gui", "required: visibly drives the Things app to make a change it offers nowhere else; " +
148
- "also needs `things config set ui-enabled true`");
151
+ "also needs `things config set ui-enabled true`. Driving the app can take over a " +
152
+ "minute on a large database, so allow a generous timeout (180s or more) — if your own " +
153
+ "timeout stops the command first you get empty output, not a result. A timeout or empty " +
154
+ "output is not a success: the change may still have landed, so re-check with " +
155
+ "`things show <ref>` before retrying, and never fire an identical retry (it can duplicate " +
156
+ "the change) — stop and report if the state is wrong or unclear");
149
157
  }
150
158
  function collect(value, previous) {
151
159
  return [...previous, value];
@@ -186,6 +194,16 @@ async function runWrite(opts, fn, emitFn = emitResult) {
186
194
  if (!opIdOk(opts))
187
195
  return;
188
196
  const started = Date.now();
197
+ // Dev-mode step-timeline trace + signal-safe interrupt guard (TRACE1, #487).
198
+ // Scoped to the write driver — the invocations where a per-step timeline earns
199
+ // its keep, and where a mid-drive kill would otherwise leave empty stdout. The
200
+ // sink is a no-op unless tracing is on (a `-dev` build, or config/env forced).
201
+ installCliTrace({
202
+ argv: process.argv.slice(1),
203
+ version: CLI_VERSION,
204
+ isDev: isDevVersion(CLI_VERSION),
205
+ });
206
+ armInterrupt(opts.json === true);
189
207
  let client = null;
190
208
  const meta = (client_) => {
191
209
  let dbVersion = null;
@@ -239,6 +257,16 @@ async function runWrite(opts, fn, emitFn = emitResult) {
239
257
  }
240
258
  finally {
241
259
  client?.close();
260
+ // Close the trace with a final result line (the exit code the caller sees),
261
+ // then disarm the interrupt guard so a later signal during teardown/reads
262
+ // emits nothing.
263
+ trace(() => ({
264
+ phase: "invocation-end",
265
+ exitCode: process.exitCode ?? 0,
266
+ elapsedMs: Date.now() - started,
267
+ }));
268
+ disarmInterrupt();
269
+ closeCliTrace();
242
270
  }
243
271
  }
244
272
  /**
@@ -258,6 +286,26 @@ function emitPreserveNote(result) {
258
286
  process.stderr.write(`warning: preserve-modified: ${f.uuid}: ${f.detail}\n`);
259
287
  }
260
288
  }
289
+ /**
290
+ * TTY disclosure for the HINTS1 completion-context on a verified complete/cancel:
291
+ * a dim line naming the OPEN work remaining in the to-do's project and/or Today,
292
+ * so an operator (or an agent reading the human render) sees an emptied container
293
+ * at a glance. Silent when neither arm applies (the field is absent).
294
+ */
295
+ function emitContextNote(result) {
296
+ const context = result.context;
297
+ if (context === undefined)
298
+ return;
299
+ const parts = [];
300
+ if (context.project !== undefined) {
301
+ parts.push(`project "${context.project.title}": ${context.project.remainingOpen} open remaining`);
302
+ }
303
+ if (context.today !== undefined) {
304
+ parts.push(`today: ${context.today.remainingOpen} open remaining`);
305
+ }
306
+ if (parts.length > 0)
307
+ process.stdout.write(dim(` ${parts.join("; ")}\n`));
308
+ }
261
309
  function emitResult(result, opts, meta) {
262
310
  switch (result.kind) {
263
311
  case "bounce-aborted": {
@@ -290,6 +338,7 @@ function emitResult(result, opts, meta) {
290
338
  : `vector=${result.vector}, tier=${result.tier}, verified`;
291
339
  process.stdout.write(`ok ${result.op}${uuid} (${status})\n`);
292
340
  emitPreserveNote(result);
341
+ emitContextNote(result);
293
342
  }
294
343
  process.exitCode = ExitCode.Ok;
295
344
  return;
@@ -2106,7 +2155,7 @@ export function registerWriteCommands(program) {
2106
2155
  .command("set <key> <value>")
2107
2156
  .description("Persist a config key: profile | maxDisruption | actor | auditEnabled | " +
2108
2157
  "accepted-fingerprint | certified-app-version | allow-experimental | bounce-enabled | " +
2109
- "bounce-max-items | ui-enabled | scope")
2158
+ "bounce-max-items | auto-launch | ui-enabled | ui-drive-budget-ms | trace | scope")
2110
2159
  .action((key, value, opts) => {
2111
2160
  const map = {
2112
2161
  profile: "profile",
@@ -2118,7 +2167,10 @@ export function registerWriteCommands(program) {
2118
2167
  "allow-experimental": "allowExperimental",
2119
2168
  "bounce-enabled": "bounceEnabled",
2120
2169
  "bounce-max-items": "bounceMaxItems",
2170
+ "auto-launch": "autoLaunch",
2121
2171
  "ui-enabled": "uiEnabled",
2172
+ "ui-drive-budget-ms": "uiDriveBudgetMs",
2173
+ trace: "traceEnabled",
2122
2174
  scope: "scope",
2123
2175
  };
2124
2176
  const target = map[key];
@@ -2127,12 +2179,14 @@ export function registerWriteCommands(program) {
2127
2179
  process.exitCode = ExitCode.Usage;
2128
2180
  return;
2129
2181
  }
2130
- const parsed = target === "maxDisruption" || target === "bounceMaxItems"
2182
+ const parsed = target === "maxDisruption" || target === "bounceMaxItems" || target === "uiDriveBudgetMs"
2131
2183
  ? Number(value)
2132
2184
  : target === "auditEnabled" ||
2133
2185
  target === "allowExperimental" ||
2134
2186
  target === "bounceEnabled" ||
2135
- target === "uiEnabled"
2187
+ target === "autoLaunch" ||
2188
+ target === "uiEnabled" ||
2189
+ target === "traceEnabled"
2136
2190
  ? value === "true"
2137
2191
  : value;
2138
2192
  // Universal `--dry-run` (../dry-run.ts): `config set` writes local config