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.
- package/README.md +1 -1
- package/dist/cli/commands/repeat-flags.d.ts +3 -3
- package/dist/cli/commands/repeat-flags.js +90 -33
- package/dist/cli/commands/repeat-flags.js.map +1 -1
- package/dist/cli/commands/writes.js +59 -5
- package/dist/cli/commands/writes.js.map +1 -1
- package/dist/cli/help.js +9 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/interrupt.d.ts +42 -0
- package/dist/cli/interrupt.js +105 -0
- package/dist/cli/interrupt.js.map +1 -0
- package/dist/cli/main.js +5 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/client.js +3 -0
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +39 -0
- package/dist/config.js +16 -1
- package/dist/config.js.map +1 -1
- package/dist/contracts.d.ts +9 -2
- package/dist/contracts.js +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/model/occurrences.d.ts +14 -1
- package/dist/model/occurrences.js +20 -4
- package/dist/model/occurrences.js.map +1 -1
- package/dist/model/recurrence.d.ts +60 -9
- package/dist/model/recurrence.js +76 -19
- package/dist/model/recurrence.js.map +1 -1
- package/dist/paths.d.ts +8 -0
- package/dist/paths.js +10 -0
- package/dist/paths.js.map +1 -1
- package/dist/sync-health.js +2 -1
- package/dist/sync-health.js.map +1 -1
- package/dist/trace/tracer.d.ts +87 -0
- package/dist/trace/tracer.js +167 -0
- package/dist/trace/tracer.js.map +1 -0
- package/dist/write/clear-reminder.js +24 -21
- package/dist/write/clear-reminder.js.map +1 -1
- package/dist/write/commands.js +50 -45
- package/dist/write/commands.js.map +1 -1
- package/dist/write/completion-context.d.ts +57 -0
- package/dist/write/completion-context.js +49 -0
- package/dist/write/completion-context.js.map +1 -0
- package/dist/write/failure-hints.d.ts +10 -1
- package/dist/write/failure-hints.js +8 -0
- package/dist/write/failure-hints.js.map +1 -1
- package/dist/write/guards.d.ts +1 -1
- package/dist/write/guards.js +10 -0
- package/dist/write/guards.js.map +1 -1
- package/dist/write/operations.d.ts +12 -3
- package/dist/write/pipeline.d.ts +39 -0
- package/dist/write/pipeline.js +230 -17
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/pre-state.d.ts +1 -1
- package/dist/write/pre-state.js +2 -2
- package/dist/write/pre-state.js.map +1 -1
- package/dist/write/promote-clone.js +260 -13
- package/dist/write/promote-clone.js.map +1 -1
- package/dist/write/repeat-anchor.d.ts +110 -0
- package/dist/write/repeat-anchor.js +238 -0
- package/dist/write/repeat-anchor.js.map +1 -0
- package/dist/write/repeat-asserts.d.ts +28 -0
- package/dist/write/repeat-asserts.js +169 -0
- package/dist/write/repeat-asserts.js.map +1 -0
- package/dist/write/repeat-rule.js +27 -12
- package/dist/write/repeat-rule.js.map +1 -1
- package/dist/write/vectors/session-reachability.d.ts +78 -0
- package/dist/write/vectors/session-reachability.js +103 -0
- package/dist/write/vectors/session-reachability.js.map +1 -0
- package/dist/write/vectors/simulator.js +68 -29
- package/dist/write/vectors/simulator.js.map +1 -1
- package/dist/write/vectors/types.d.ts +117 -10
- package/dist/write/vectors/ui-recipes.d.ts +8 -0
- package/dist/write/vectors/ui-recipes.js +120 -22
- package/dist/write/vectors/ui-recipes.js.map +1 -1
- package/dist/write/vectors/ui.d.ts +79 -13
- package/dist/write/vectors/ui.js +415 -59
- package/dist/write/vectors/ui.js.map +1 -1
- package/dist/write/verify/delta.d.ts +12 -14
- package/dist/write/verify/delta.js +35 -34
- package/dist/write/verify/delta.js.map +1 -1
- package/package.json +1 -1
- package/schema/envelope.schema.json +22 -0
- 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.
|
|
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).
|
|
27
|
-
* (
|
|
28
|
-
*
|
|
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
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
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 === "
|
|
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
|