things-api 0.16.0 → 0.18.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 (208) hide show
  1. package/README.md +1 -1
  2. package/deputy/VERSION +1 -0
  3. package/deputy/helpers-Info.plist +23 -0
  4. package/deputy/prebuilt/Things API Helper.app/Contents/CodeResources +0 -0
  5. package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/Info.plist +20 -0
  6. package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/MacOS/things-reader +0 -0
  7. package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/_CodeSignature/CodeResources +115 -0
  8. package/deputy/prebuilt/Things API Helper.app/Contents/Info.plist +23 -0
  9. package/deputy/prebuilt/Things API Helper.app/Contents/MacOS/things-deputy +0 -0
  10. package/deputy/prebuilt/Things API Helper.app/Contents/_CodeSignature/CodeResources +125 -0
  11. package/deputy/reader/Info.plist +20 -0
  12. package/deputy/reader/entitlements.plist +15 -0
  13. package/deputy/reader/main.swift +491 -0
  14. package/deputy/src/main.swift +133 -0
  15. package/deputy/src/osascript.swift +102 -0
  16. package/deputy/src/server.swift +350 -0
  17. package/deputy/src/sqlite.swift +121 -0
  18. package/dist/cli/commands/doctor.js +55 -2
  19. package/dist/cli/commands/doctor.js.map +1 -1
  20. package/dist/cli/commands/helpers.d.ts +17 -0
  21. package/dist/cli/commands/helpers.js +148 -0
  22. package/dist/cli/commands/helpers.js.map +1 -0
  23. package/dist/cli/commands/op-result.d.ts +15 -0
  24. package/dist/cli/commands/op-result.js +39 -0
  25. package/dist/cli/commands/op-result.js.map +1 -0
  26. package/dist/cli/commands/repeat-flags.d.ts +3 -3
  27. package/dist/cli/commands/repeat-flags.js +90 -33
  28. package/dist/cli/commands/repeat-flags.js.map +1 -1
  29. package/dist/cli/commands/writes.js +79 -6
  30. package/dist/cli/commands/writes.js.map +1 -1
  31. package/dist/cli/help.js +13 -2
  32. package/dist/cli/help.js.map +1 -1
  33. package/dist/cli/helpers-check.d.ts +10 -0
  34. package/dist/cli/helpers-check.js +59 -0
  35. package/dist/cli/helpers-check.js.map +1 -0
  36. package/dist/cli/interrupt.d.ts +42 -0
  37. package/dist/cli/interrupt.js +105 -0
  38. package/dist/cli/interrupt.js.map +1 -0
  39. package/dist/cli/main.js +27 -1
  40. package/dist/cli/main.js.map +1 -1
  41. package/dist/client.js +19 -2
  42. package/dist/client.js.map +1 -1
  43. package/dist/config.d.ts +70 -0
  44. package/dist/config.js +25 -1
  45. package/dist/config.js.map +1 -1
  46. package/dist/contracts.d.ts +9 -2
  47. package/dist/contracts.js +1 -1
  48. package/dist/contracts.js.map +1 -1
  49. package/dist/db/baselines/db-v26.js +8 -4
  50. package/dist/db/baselines/db-v26.js.map +1 -1
  51. package/dist/db/baselines/db-v27.d.ts +42 -0
  52. package/dist/db/baselines/db-v27.js +47 -0
  53. package/dist/db/baselines/db-v27.js.map +1 -0
  54. package/dist/db/baselines/index.js +2 -1
  55. package/dist/db/baselines/index.js.map +1 -1
  56. package/dist/db/locate.d.ts +4 -1
  57. package/dist/db/locate.js +3 -1
  58. package/dist/db/locate.js.map +1 -1
  59. package/dist/db/schema.d.ts +1 -1
  60. package/dist/db/schema.js +9 -0
  61. package/dist/db/schema.js.map +1 -1
  62. package/dist/deputy/bridge-worker.d.ts +1 -0
  63. package/dist/deputy/bridge-worker.js +79 -0
  64. package/dist/deputy/bridge-worker.js.map +1 -0
  65. package/dist/deputy/bridge.d.ts +14 -0
  66. package/dist/deputy/bridge.js +70 -0
  67. package/dist/deputy/bridge.js.map +1 -0
  68. package/dist/deputy/client.d.ts +19 -0
  69. package/dist/deputy/client.js +104 -0
  70. package/dist/deputy/client.js.map +1 -0
  71. package/dist/deputy/db-facade.d.ts +18 -0
  72. package/dist/deputy/db-facade.js +71 -0
  73. package/dist/deputy/db-facade.js.map +1 -0
  74. package/dist/deputy/files.d.ts +1 -0
  75. package/dist/deputy/files.js +16 -0
  76. package/dist/deputy/files.js.map +1 -0
  77. package/dist/deputy/install.d.ts +105 -0
  78. package/dist/deputy/install.js +467 -0
  79. package/dist/deputy/install.js.map +1 -0
  80. package/dist/deputy/notice.d.ts +6 -0
  81. package/dist/deputy/notice.js +28 -0
  82. package/dist/deputy/notice.js.map +1 -0
  83. package/dist/deputy/notices.d.ts +31 -0
  84. package/dist/deputy/notices.js +94 -0
  85. package/dist/deputy/notices.js.map +1 -0
  86. package/dist/deputy/osa.d.ts +21 -0
  87. package/dist/deputy/osa.js +72 -0
  88. package/dist/deputy/osa.js.map +1 -0
  89. package/dist/deputy/protocol.d.ts +86 -0
  90. package/dist/deputy/protocol.js +106 -0
  91. package/dist/deputy/protocol.js.map +1 -0
  92. package/dist/deputy/routing.d.ts +63 -0
  93. package/dist/deputy/routing.js +338 -0
  94. package/dist/deputy/routing.js.map +1 -0
  95. package/dist/deputy/shortcuts-exec.d.ts +10 -0
  96. package/dist/deputy/shortcuts-exec.js +50 -0
  97. package/dist/deputy/shortcuts-exec.js.map +1 -0
  98. package/dist/diagnose.d.ts +43 -1
  99. package/dist/diagnose.js +64 -2
  100. package/dist/diagnose.js.map +1 -1
  101. package/dist/index.d.ts +16 -2
  102. package/dist/index.js +17 -1
  103. package/dist/index.js.map +1 -1
  104. package/dist/mcp/server.js +25 -7
  105. package/dist/mcp/server.js.map +1 -1
  106. package/dist/model/mappers.d.ts +12 -3
  107. package/dist/model/mappers.js +6 -1
  108. package/dist/model/mappers.js.map +1 -1
  109. package/dist/model/occurrences.d.ts +14 -1
  110. package/dist/model/occurrences.js +20 -4
  111. package/dist/model/occurrences.js.map +1 -1
  112. package/dist/model/recurrence.d.ts +64 -9
  113. package/dist/model/recurrence.js +94 -19
  114. package/dist/model/recurrence.js.map +1 -1
  115. package/dist/model/template-projection.d.ts +26 -0
  116. package/dist/model/template-projection.js +113 -0
  117. package/dist/model/template-projection.js.map +1 -0
  118. package/dist/op-result.d.ts +46 -0
  119. package/dist/op-result.js +156 -0
  120. package/dist/op-result.js.map +1 -0
  121. package/dist/paths.d.ts +8 -0
  122. package/dist/paths.js +10 -0
  123. package/dist/paths.js.map +1 -1
  124. package/dist/read/detail.js +7 -1
  125. package/dist/read/detail.js.map +1 -1
  126. package/dist/read/queries.js +8 -1
  127. package/dist/read/queries.js.map +1 -1
  128. package/dist/read/views.d.ts +6 -4
  129. package/dist/read/views.js +60 -18
  130. package/dist/read/views.js.map +1 -1
  131. package/dist/sync-health.js +8 -3
  132. package/dist/sync-health.js.map +1 -1
  133. package/dist/trace/tracer.d.ts +87 -0
  134. package/dist/trace/tracer.js +167 -0
  135. package/dist/trace/tracer.js.map +1 -0
  136. package/dist/write/accessibility-probe.js +4 -2
  137. package/dist/write/accessibility-probe.js.map +1 -1
  138. package/dist/write/automation-probe.js +5 -1
  139. package/dist/write/automation-probe.js.map +1 -1
  140. package/dist/write/availability.js +7 -3
  141. package/dist/write/availability.js.map +1 -1
  142. package/dist/write/clear-reminder.js +24 -21
  143. package/dist/write/clear-reminder.js.map +1 -1
  144. package/dist/write/commands.js +95 -45
  145. package/dist/write/commands.js.map +1 -1
  146. package/dist/write/completion-context.d.ts +57 -0
  147. package/dist/write/completion-context.js +49 -0
  148. package/dist/write/completion-context.js.map +1 -0
  149. package/dist/write/experimental.d.ts +19 -0
  150. package/dist/write/experimental.js +50 -0
  151. package/dist/write/experimental.js.map +1 -1
  152. package/dist/write/failure-hints.d.ts +10 -1
  153. package/dist/write/failure-hints.js +8 -0
  154. package/dist/write/failure-hints.js.map +1 -1
  155. package/dist/write/guards.d.ts +1 -1
  156. package/dist/write/guards.js +10 -0
  157. package/dist/write/guards.js.map +1 -1
  158. package/dist/write/move.js +14 -6
  159. package/dist/write/move.js.map +1 -1
  160. package/dist/write/operations.d.ts +40 -7
  161. package/dist/write/pipeline.d.ts +41 -0
  162. package/dist/write/pipeline.js +289 -19
  163. package/dist/write/pipeline.js.map +1 -1
  164. package/dist/write/pre-state.d.ts +1 -1
  165. package/dist/write/pre-state.js +35 -17
  166. package/dist/write/pre-state.js.map +1 -1
  167. package/dist/write/promote-clone.js +434 -19
  168. package/dist/write/promote-clone.js.map +1 -1
  169. package/dist/write/reorder.js +76 -27
  170. package/dist/write/reorder.js.map +1 -1
  171. package/dist/write/repeat-anchor.d.ts +140 -0
  172. package/dist/write/repeat-anchor.js +403 -0
  173. package/dist/write/repeat-anchor.js.map +1 -0
  174. package/dist/write/repeat-asserts.d.ts +28 -0
  175. package/dist/write/repeat-asserts.js +169 -0
  176. package/dist/write/repeat-asserts.js.map +1 -0
  177. package/dist/write/repeat-rule.js +37 -12
  178. package/dist/write/repeat-rule.js.map +1 -1
  179. package/dist/write/vectors/applescript.js +7 -16
  180. package/dist/write/vectors/applescript.js.map +1 -1
  181. package/dist/write/vectors/session-reachability.d.ts +133 -0
  182. package/dist/write/vectors/session-reachability.js +151 -0
  183. package/dist/write/vectors/session-reachability.js.map +1 -0
  184. package/dist/write/vectors/shortcuts.js +5 -14
  185. package/dist/write/vectors/shortcuts.js.map +1 -1
  186. package/dist/write/vectors/simulator.d.ts +16 -1
  187. package/dist/write/vectors/simulator.js +122 -32
  188. package/dist/write/vectors/simulator.js.map +1 -1
  189. package/dist/write/vectors/types.d.ts +183 -10
  190. package/dist/write/vectors/ui-recipes.d.ts +8 -0
  191. package/dist/write/vectors/ui-recipes.js +246 -58
  192. package/dist/write/vectors/ui-recipes.js.map +1 -1
  193. package/dist/write/vectors/ui.d.ts +156 -14
  194. package/dist/write/vectors/ui.js +846 -84
  195. package/dist/write/vectors/ui.js.map +1 -1
  196. package/dist/write/verify/delta.d.ts +12 -14
  197. package/dist/write/verify/delta.js +35 -34
  198. package/dist/write/verify/delta.js.map +1 -1
  199. package/dist/write/verify/poller.d.ts +13 -0
  200. package/dist/write/verify/poller.js +38 -0
  201. package/dist/write/verify/poller.js.map +1 -1
  202. package/package.json +8 -1
  203. package/schema/envelope.schema.json +22 -0
  204. package/scripts/build-helpers.sh +104 -0
  205. package/scripts/deputy-cert-setup.sh +60 -0
  206. package/skills/things-cli/SKILL.md +11 -3
  207. package/skills/things-cli/references/errors.md +4 -0
  208. package/skills/things-cli/references/repeating.md +69 -0
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `things helpers` — lifecycle for the optional helper pair (one installed
3
+ * bundle housing both):
4
+ *
5
+ * things-deputy — app automation (mutations), unsandboxed.
6
+ * things-reader — database/file reads, sandboxed to a user-granted folder.
7
+ *
8
+ * Running them means macOS permission prompts and grants attach to two stable
9
+ * identities instead of every terminal or agent runtime that invokes
10
+ * `things`, so grants stop re-prompting when those runtimes update. Routing is
11
+ * tri-state (`helpers-enabled`: auto | true | false, default auto —
12
+ * installation is the intent signal), overridable per invocation with
13
+ * THINGS_API_HELPERS or the global `--helpers`/`--no-helpers` flags; the CLI
14
+ * always falls back to direct execution when a helper is unavailable.
15
+ */
16
+ import type { Command } from "commander";
17
+ export declare function registerHelpers(program: Command): void;
@@ -0,0 +1,148 @@
1
+ import { helpersStatus, ExitCode, grantReader, installHelpers, loadConfig, okEnvelope, restartHelpers, uninstallHelpers, } from "../../index.js";
2
+ function envelopeMeta(started) {
3
+ return { dbVersion: null, fingerprint: "unknown", elapsedMs: Date.now() - started };
4
+ }
5
+ /** One line describing what the configured mode means for this machine. */
6
+ function routingLine(status) {
7
+ switch (status.mode) {
8
+ case "true":
9
+ return "enabled — every process routes through the helpers, and reports when it cannot";
10
+ case "false":
11
+ return "disabled (things config set helpers-enabled auto)";
12
+ case "auto":
13
+ return status.bundleInstalled
14
+ ? "auto — the installed helpers are used while healthy; a failure is reported, never silent"
15
+ : "auto — nothing installed, so everything runs direct (things helpers install to change that)";
16
+ }
17
+ }
18
+ function renderStatus(status) {
19
+ const deputy = status.deputy;
20
+ const lines = [`deputy: ${deputy.running ? "running" : "does not appear to be running"}`];
21
+ // The plain no-socket case needs no elaboration; a socket that exists but
22
+ // misbehaves (not answering, handshake refused) is worth its own line.
23
+ if (!deputy.running && !deputy.detail.startsWith("not running")) {
24
+ lines.push(` detail: ${deputy.detail}`);
25
+ }
26
+ if (deputy.hungSocket) {
27
+ lines.push(" next: `things helpers restart` — the socket is present but no handshake answers");
28
+ }
29
+ lines.push(` routing: ${routingLine(status)}`, ` launchd: ${deputy.plistInstalled ? (deputy.loaded ? "installed + loaded" : "installed, NOT loaded") : "not installed (things helpers install)"}`, ` bundle: ${status.bundleInstalled
30
+ ? `installed${status.installedVersion !== null ? ` (v${status.installedVersion})` : ""}`
31
+ : "not installed"}`, ` socket: ${deputy.socketPath}`);
32
+ if (deputy.hello !== null) {
33
+ lines.push(` version: ${deputy.hello.deputyVersion} (protocol ${deputy.hello.protocol}, pid ${deputy.hello.pid})`);
34
+ }
35
+ if (deputy.signing !== null) {
36
+ lines.push(` signing: ${deputy.signing.state}${deputy.signing.authority !== null ? ` (${deputy.signing.authority})` : ""}`);
37
+ if (deputy.signing.state !== "signed") {
38
+ lines.push(" warning: an unsigned/ad-hoc helper loses its macOS grants on every rebuild — run scripts/deputy-cert-setup.sh once, rebuild, reinstall");
39
+ }
40
+ }
41
+ const reader = status.reader;
42
+ lines.push(`reader: ${reader.running ? (reader.granted ? "running, granted" : "running, NOT granted") : reader.installed ? reader.detail : "not installed"}`);
43
+ if (reader.running && !reader.granted) {
44
+ lines.push(" next: run `things helpers grant` and accept the panel (one time; the grant survives restarts, reboots, and rebuilds)");
45
+ }
46
+ if (reader.hungSocket) {
47
+ lines.push(" next: `things helpers restart` — the socket is present but no handshake answers");
48
+ }
49
+ return `${lines.join("\n")}\n`;
50
+ }
51
+ export function registerHelpers(program) {
52
+ const helpers = program
53
+ .command("helpers")
54
+ .description("Manage the optional helper pair that performs database reads and app automation " +
55
+ "on the CLI's behalf, so macOS permission grants attach to stable helpers instead " +
56
+ "of every terminal or agent runtime that runs `things`. Subcommands: status, " +
57
+ "install, grant, restart, uninstall.");
58
+ helpers
59
+ .command("status")
60
+ .description("Report both helpers' state: running or not, launchd installation, installed version, " +
61
+ "code-signing identity, the reader's grant, and the routing mode on this machine. " +
62
+ "Read-only.")
63
+ .option("--json", "emit versioned JSON envelope on stdout")
64
+ .action((opts) => {
65
+ const started = Date.now();
66
+ const status = helpersStatus(loadConfig().helpersMode);
67
+ if (opts.json === true) {
68
+ process.stdout.write(`${JSON.stringify(okEnvelope("helpers-status", status, envelopeMeta(started)))}\n`);
69
+ }
70
+ else {
71
+ process.stdout.write(renderStatus(status));
72
+ }
73
+ process.exitCode = ExitCode.Ok;
74
+ });
75
+ helpers
76
+ .command("install")
77
+ .description("Install the helper bundle: copy it to its stable location, register both helpers " +
78
+ "with launchd (starts now and on every login), and report signing + grant state. " +
79
+ "Under the default helpers-enabled auto, an installed and healthy helper is used from " +
80
+ "the next command on. Rerun after every rebuild.")
81
+ .option("--bundle <path>", "path to a built Things API Helper.app (default: deputy/build/Things API Helper.app in this package)")
82
+ .action((opts) => {
83
+ try {
84
+ const result = installHelpers(opts.bundle !== undefined ? { bundlePath: opts.bundle } : {});
85
+ process.stdout.write(`installed ${result.bundlePath}\nlaunchd agent: ${result.plistPath}\n`);
86
+ if (result.readerInstalled) {
87
+ process.stdout.write(result.readerGranted === true
88
+ ? "reader: granted — file reads ride the scoped reader\n"
89
+ : result.readerGranted === false
90
+ ? "reader: installed — run `things helpers grant` once to give it durable read access\n"
91
+ : "reader: installed, not answering yet — check `things helpers status`\n");
92
+ }
93
+ for (const warning of result.warnings) {
94
+ process.stderr.write(`warning: ${warning}\n`);
95
+ }
96
+ process.exitCode = ExitCode.Ok;
97
+ }
98
+ catch (err) {
99
+ process.stderr.write(`error: ${err instanceof Error ? err.message : String(err)}\n`);
100
+ process.exitCode = ExitCode.Environment;
101
+ }
102
+ });
103
+ helpers
104
+ .command("grant")
105
+ .description("One-time ceremony: opens a folder panel presented by the sandboxed reader helper, " +
106
+ "already showing the Things data folder. Accepting gives the reader durable read " +
107
+ "access to exactly that folder — it survives restarts, reboots, and updates, and " +
108
+ "macOS enforces that the reader can reach nothing else. Interactive: run this at " +
109
+ "the machine, not from an unattended session.")
110
+ .action(() => {
111
+ const result = grantReader();
112
+ if (result.granted) {
113
+ process.stdout.write("granted — file reads now ride the scoped reader\n");
114
+ process.exitCode = ExitCode.Ok;
115
+ }
116
+ else {
117
+ process.stderr.write(`not granted: ${result.detail}\n`);
118
+ process.exitCode = ExitCode.Environment;
119
+ }
120
+ });
121
+ helpers
122
+ .command("restart")
123
+ .description("Restart the launchd-managed helpers (picks up a rebuilt installed bundle).")
124
+ .action(() => {
125
+ try {
126
+ restartHelpers();
127
+ process.stdout.write("helpers restarted\n");
128
+ process.exitCode = ExitCode.Ok;
129
+ }
130
+ catch (err) {
131
+ process.stderr.write(`error: ${err instanceof Error ? err.message : String(err)}\n`);
132
+ process.exitCode = ExitCode.Environment;
133
+ }
134
+ });
135
+ helpers
136
+ .command("uninstall")
137
+ .description("Stop both helpers and remove their launchd registrations and the installed bundle. " +
138
+ "Local state (access token, logs, the reader's grant) is kept; routing config is " +
139
+ "untouched (commands fall back to direct execution).")
140
+ .action(() => {
141
+ const result = uninstallHelpers();
142
+ process.stdout.write(result.removed.length > 0
143
+ ? `removed:\n${result.removed.map((p) => ` ${p}`).join("\n")}\n`
144
+ : "nothing installed\n");
145
+ process.exitCode = ExitCode.Ok;
146
+ });
147
+ }
148
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/cli/commands/helpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `things op-result <op-id>` — a read-only CALLER-RECOVERY lookup over the local
3
+ * change history (RSPA1 deliverable 3). Thin surface over the library's opResult().
4
+ *
5
+ * When your environment kills long commands (a wall-time cap) before a write's
6
+ * result is printed — a `--dangerously-drive-gui` drive can run its full verify
7
+ * budget — dispatch the op with `--op-id <key>`, let it die, then run this in a
8
+ * fresh process to learn what ACTUALLY happened from the record the killed process
9
+ * already durably wrote. NEVER blind-retry a GUI drive.
10
+ *
11
+ * Always exit 0: this is a successful HISTORY READ regardless of the underlying
12
+ * op's outcome (which lives in `data.status` / `data.result`).
13
+ */
14
+ import type { Command } from "commander";
15
+ export declare function registerOpResult(program: Command): void;
@@ -0,0 +1,39 @@
1
+ import { okEnvelope, opResult } from "../../index.js";
2
+ export function registerOpResult(program) {
3
+ program
4
+ .command("op-result")
5
+ .argument("<op-id>", "the --op-id / op_id the original write carried")
6
+ .description("Look up what happened to a write you dispatched with --op-id, from the local change " +
7
+ "history alone (opens no database, drives nothing). Use it to recover the outcome when " +
8
+ "your environment killed the command before it printed its result: let the command die, " +
9
+ "then run this. Reports FOUND (the final result + target + observation), INTENT-ONLY (the " +
10
+ "op started but no outcome was written — still running or the process died mid-flight, " +
11
+ "outcome UNCERTAIN), or UNKNOWN (no such op-id in history). Always exit 0 (a history read).")
12
+ .option("--json", "emit versioned JSON envelope on stdout")
13
+ .action((opId, opts) => {
14
+ const started = Date.now();
15
+ const data = opResult(opId);
16
+ const meta = {
17
+ dbVersion: null,
18
+ fingerprint: "unknown",
19
+ elapsedMs: Date.now() - started,
20
+ };
21
+ if (opts.json) {
22
+ process.stdout.write(`${JSON.stringify(okEnvelope("op-result", data, meta))}\n`);
23
+ }
24
+ else {
25
+ const head = data.status === "found"
26
+ ? `${data.result} — ${data.op}${data.uuid !== null ? ` (${data.uuid})` : ""}`
27
+ : data.status === "intent-only"
28
+ ? `intent-only — ${data.op} (no final outcome recorded)`
29
+ : "unknown op-id";
30
+ process.stdout.write(`op-result ${opId}: ${head}\n`);
31
+ process.stdout.write(` ${data.note}\n`);
32
+ if (data.status === "found" && data.observed !== null) {
33
+ process.stdout.write(` observed: ${JSON.stringify(data.observed)}\n`);
34
+ }
35
+ }
36
+ process.exitCode = 0;
37
+ });
38
+ }
39
+ //# sourceMappingURL=op-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"op-result.js","sourceRoot":"","sources":["../../../src/cli/commands/op-result.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAqB,MAAM,gBAAgB,CAAC;AAEzE,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,OAAO;SACJ,OAAO,CAAC,WAAW,CAAC;SACpB,QAAQ,CAAC,SAAS,EAAE,gDAAgD,CAAC;SACrE,WAAW,CACV,sFAAsF;QACpF,wFAAwF;QACxF,yFAAyF;QACzF,2FAA2F;QAC3F,wFAAwF;QACxF,4FAA4F,CAC/F;SACA,MAAM,CAAC,QAAQ,EAAE,wCAAwC,CAAC;SAC1D,MAAM,CAAC,CAAC,IAAY,EAAE,IAAwB,EAAE,EAAE;QACjD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAiB;YACzB,SAAS,EAAE,IAAI;YACf,WAAW,EAAE,SAAS;YACtB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO;SAChC,CAAC;QACF,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QACnF,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GACR,IAAI,CAAC,MAAM,KAAK,OAAO;gBACrB,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,MAAM,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC7E,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,aAAa;oBAC7B,CAAC,CAAC,iBAAiB,IAAI,CAAC,EAAE,8BAA8B;oBACxD,CAAC,CAAC,eAAe,CAAC;YACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC;YACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;YACzC,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;gBACtD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -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":"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"}
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, HELPERS_MODES, installCliTrace, getConfigKey, parseHelpersMode, 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;
@@ -305,6 +354,7 @@ function emitResult(result, opts, meta) {
305
354
  ` invocation: ${result.plan.invocation}`,
306
355
  ` hazards checked: ${result.plan.hazardsChecked.join(", ") || "none"}`,
307
356
  ` expected delta: ${JSON.stringify(result.plan.expectedDelta)}`,
357
+ ...(result.plan.notes ?? []).map((n) => ` note: ${n}`),
308
358
  "",
309
359
  ].join("\n"));
310
360
  }
@@ -1374,7 +1424,12 @@ export function registerWriteCommands(program) {
1374
1424
  .option("--notes <text>", "notes body")
1375
1425
  .option("--when <value>", "today | evening | anytime | someday | YYYY-MM-DD")
1376
1426
  .option("--reminder <time>", "HH:mm reminder (needs a schedulable --when)")
1377
- .option("--deadline <date>", "YYYY-MM-DD")
1427
+ .option("--deadline <date>", "YYYY-MM-DD — deadlines EVERY occurrence: each starts (deadline − --when) days " +
1428
+ "before its own deadline (needs a concrete --when, on or before the deadline; " +
1429
+ "alternative spelling of --start-days-earlier — give one or the other, both must agree)")
1430
+ .option("--start-days-earlier <n>", "deadlines EVERY occurrence, naming the lead directly: each starts N days before " +
1431
+ "its own deadline (needs a concrete --when; alternative spelling of --deadline <date> — " +
1432
+ "give one or the other, both must agree)")
1378
1433
  .option("--tag <name>", "tag to attach (repeatable)", collect, [])
1379
1434
  .option("--checklist-item <title>", "checklist item (repeatable)", collect, [])
1380
1435
  .option("--project <ref>", "destination project (uuid or unique name)")
@@ -1396,6 +1451,9 @@ export function registerWriteCommands(program) {
1396
1451
  ...(opts["when"] !== undefined && { when: opts["when"] }),
1397
1452
  ...(opts["reminder"] !== undefined && { reminder: opts["reminder"] }),
1398
1453
  ...(opts["deadline"] !== undefined && { deadline: opts["deadline"] }),
1454
+ ...(opts["startDaysEarlier"] !== undefined && {
1455
+ startDaysEarlier: Number(opts["startDaysEarlier"]),
1456
+ }),
1399
1457
  ...(tags.length > 0 && { tags }),
1400
1458
  ...(checklistItems.length > 0 && { checklistItems }),
1401
1459
  ...(projectRef !== undefined && { project: projectRef }),
@@ -2106,7 +2164,8 @@ export function registerWriteCommands(program) {
2106
2164
  .command("set <key> <value>")
2107
2165
  .description("Persist a config key: profile | maxDisruption | actor | auditEnabled | " +
2108
2166
  "accepted-fingerprint | certified-app-version | allow-experimental | bounce-enabled | " +
2109
- "bounce-max-items | ui-enabled | scope")
2167
+ "bounce-max-items | auto-launch | helpers-enabled (auto | true | false) | ui-enabled | " +
2168
+ "ui-drive-budget-ms | trace | scope")
2110
2169
  .action((key, value, opts) => {
2111
2170
  const map = {
2112
2171
  profile: "profile",
@@ -2118,7 +2177,11 @@ export function registerWriteCommands(program) {
2118
2177
  "allow-experimental": "allowExperimental",
2119
2178
  "bounce-enabled": "bounceEnabled",
2120
2179
  "bounce-max-items": "bounceMaxItems",
2180
+ "auto-launch": "autoLaunch",
2181
+ "helpers-enabled": "helpersMode",
2121
2182
  "ui-enabled": "uiEnabled",
2183
+ "ui-drive-budget-ms": "uiDriveBudgetMs",
2184
+ trace: "traceEnabled",
2122
2185
  scope: "scope",
2123
2186
  };
2124
2187
  const target = map[key];
@@ -2127,12 +2190,22 @@ export function registerWriteCommands(program) {
2127
2190
  process.exitCode = ExitCode.Usage;
2128
2191
  return;
2129
2192
  }
2130
- const parsed = target === "maxDisruption" || target === "bounceMaxItems"
2193
+ // `helpers-enabled` is TRI-state (auto | true | false): anything else is
2194
+ // refused rather than silently coerced to a boolean, so a typo can never
2195
+ // read as "off" on a machine that meant "auto".
2196
+ if (target === "helpersMode" && parseHelpersMode(value) === undefined) {
2197
+ process.stderr.write(`error: helpers-enabled accepts ${HELPERS_MODES.join(" | ")} (got "${value}")\n`);
2198
+ process.exitCode = ExitCode.Usage;
2199
+ return;
2200
+ }
2201
+ const parsed = target === "maxDisruption" || target === "bounceMaxItems" || target === "uiDriveBudgetMs"
2131
2202
  ? Number(value)
2132
2203
  : target === "auditEnabled" ||
2133
2204
  target === "allowExperimental" ||
2134
2205
  target === "bounceEnabled" ||
2135
- target === "uiEnabled"
2206
+ target === "autoLaunch" ||
2207
+ target === "uiEnabled" ||
2208
+ target === "traceEnabled"
2136
2209
  ? value === "true"
2137
2210
  : value;
2138
2211
  // Universal `--dry-run` (../dry-run.ts): `config set` writes local config