things-api 0.8.0 → 0.10.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 +31 -11
- package/dist/audit/log.js +56 -2
- package/dist/audit/log.js.map +1 -1
- package/dist/audit/schema.d.ts +26 -8
- package/dist/audit/schema.js +42 -1
- package/dist/audit/schema.js.map +1 -1
- package/dist/cli/commands/area.d.ts +34 -2
- package/dist/cli/commands/area.js +140 -28
- package/dist/cli/commands/area.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +1 -1
- package/dist/cli/commands/doctor.js +44 -5
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/mcp.js +23 -4
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/project.d.ts +23 -1
- package/dist/cli/commands/project.js +82 -15
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/reads.d.ts +6 -125
- package/dist/cli/commands/reads.js +649 -657
- package/dist/cli/commands/reads.js.map +1 -1
- package/dist/cli/commands/repeat-flags.d.ts +20 -0
- package/dist/cli/commands/repeat-flags.js +76 -0
- package/dist/cli/commands/repeat-flags.js.map +1 -0
- package/dist/cli/commands/setup.js +2 -3
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/show.d.ts +1 -1
- package/dist/cli/commands/show.js +169 -18
- package/dist/cli/commands/show.js.map +1 -1
- package/dist/cli/commands/snapshot.js +1 -1
- package/dist/cli/commands/snapshot.js.map +1 -1
- package/dist/cli/commands/todo.d.ts +1 -1
- package/dist/cli/commands/todo.js +21 -9
- package/dist/cli/commands/todo.js.map +1 -1
- package/dist/cli/commands/writes.js +378 -148
- package/dist/cli/commands/writes.js.map +1 -1
- package/dist/cli/did-you-mean.d.ts +29 -0
- package/dist/cli/did-you-mean.js +53 -0
- package/dist/cli/did-you-mean.js.map +1 -0
- package/dist/cli/excess-args.d.ts +15 -0
- package/dist/cli/excess-args.js +51 -0
- package/dist/cli/excess-args.js.map +1 -0
- package/dist/cli/glyphs.d.ts +67 -7
- package/dist/cli/glyphs.js +202 -19
- package/dist/cli/glyphs.js.map +1 -1
- package/dist/cli/help.d.ts +54 -0
- package/dist/cli/help.js +355 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/main.js +36 -21
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/period.d.ts +39 -0
- package/dist/cli/period.js +141 -0
- package/dist/cli/period.js.map +1 -0
- package/dist/cli/read-driver.d.ts +83 -0
- package/dist/cli/read-driver.js +215 -0
- package/dist/cli/read-driver.js.map +1 -0
- package/dist/cli/render.d.ts +246 -0
- package/dist/cli/render.js +806 -0
- package/dist/cli/render.js.map +1 -0
- package/dist/cli/resolve-invocation.d.ts +116 -0
- package/dist/cli/resolve-invocation.js +261 -0
- package/dist/cli/resolve-invocation.js.map +1 -0
- package/dist/cli/shell-quote.d.ts +9 -0
- package/dist/cli/shell-quote.js +12 -0
- package/dist/cli/shell-quote.js.map +1 -0
- package/dist/cli/tag-filters.d.ts +59 -0
- package/dist/cli/tag-filters.js +57 -0
- package/dist/cli/tag-filters.js.map +1 -0
- package/dist/cli/verb-hint.d.ts +25 -0
- package/dist/cli/verb-hint.js +138 -0
- package/dist/cli/verb-hint.js.map +1 -0
- package/dist/cli/width.d.ts +135 -0
- package/dist/cli/width.js +313 -0
- package/dist/cli/width.js.map +1 -0
- package/dist/client.d.ts +146 -24
- package/dist/client.js +106 -16
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/contracts.d.ts +136 -1
- package/dist/contracts.js +44 -1
- package/dist/contracts.js.map +1 -1
- package/dist/db/fingerprint.d.ts +12 -0
- package/dist/db/fingerprint.js +15 -1
- package/dist/db/fingerprint.js.map +1 -1
- package/dist/db/locate.js +1 -1
- package/dist/db/locate.js.map +1 -1
- package/dist/diagnose.d.ts +63 -0
- package/dist/diagnose.js +38 -1
- package/dist/diagnose.js.map +1 -1
- package/dist/index.d.ts +30 -4
- package/dist/index.js +35 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +9 -1
- package/dist/mcp/server.js +850 -128
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/entities.d.ts +40 -7
- package/dist/model/entities.js.map +1 -1
- package/dist/model/mappers.d.ts +7 -0
- package/dist/model/mappers.js +20 -3
- package/dist/model/mappers.js.map +1 -1
- package/dist/model/occurrences.d.ts +6 -1
- package/dist/model/occurrences.js +9 -5
- package/dist/model/occurrences.js.map +1 -1
- package/dist/model/recurrence.d.ts +17 -8
- package/dist/model/recurrence.js.map +1 -1
- package/dist/model/serialize.d.ts +28 -0
- package/dist/model/serialize.js +80 -0
- package/dist/model/serialize.js.map +1 -0
- package/dist/model/when-sugar.d.ts +47 -0
- package/dist/model/when-sugar.js +45 -0
- package/dist/model/when-sugar.js.map +1 -0
- package/dist/read/area-view.d.ts +8 -1
- package/dist/read/area-view.js +49 -13
- package/dist/read/area-view.js.map +1 -1
- package/dist/read/caps.d.ts +14 -0
- package/dist/read/caps.js +21 -0
- package/dist/read/caps.js.map +1 -0
- package/dist/read/filter-contract.d.ts +117 -0
- package/dist/read/filter-contract.js +78 -0
- package/dist/read/filter-contract.js.map +1 -0
- package/dist/read/log-boundary.d.ts +9 -4
- package/dist/read/log-boundary.js.map +1 -1
- package/dist/read/predicates.d.ts +61 -0
- package/dist/read/predicates.js +78 -0
- package/dist/read/predicates.js.map +1 -0
- package/dist/read/project-view.d.ts +12 -1
- package/dist/read/project-view.js +39 -8
- package/dist/read/project-view.js.map +1 -1
- package/dist/read/queries.d.ts +156 -8
- package/dist/read/queries.js +261 -38
- package/dist/read/queries.js.map +1 -1
- package/dist/read/search-rank.d.ts +36 -0
- package/dist/read/search-rank.js +31 -0
- package/dist/read/search-rank.js.map +1 -0
- package/dist/read/sections.d.ts +51 -0
- package/dist/read/sections.js +37 -0
- package/dist/read/sections.js.map +1 -0
- package/dist/read/show-target.d.ts +13 -4
- package/dist/read/show-target.js +11 -2
- package/dist/read/show-target.js.map +1 -1
- package/dist/read/sidebar-order.d.ts +19 -0
- package/dist/read/sidebar-order.js +93 -0
- package/dist/read/sidebar-order.js.map +1 -0
- package/dist/read/tags.d.ts +27 -3
- package/dist/read/tags.js +83 -12
- package/dist/read/tags.js.map +1 -1
- package/dist/read/truncation.d.ts +71 -0
- package/dist/read/truncation.js +217 -0
- package/dist/read/truncation.js.map +1 -0
- package/dist/read/views.d.ts +177 -19
- package/dist/read/views.js +390 -213
- package/dist/read/views.js.map +1 -1
- package/dist/surface-copy.d.ts +42 -0
- package/dist/surface-copy.js +55 -0
- package/dist/surface-copy.js.map +1 -1
- package/dist/sync-health.d.ts +78 -0
- package/dist/sync-health.js +312 -0
- package/dist/sync-health.js.map +1 -0
- package/dist/write/accessibility-probe.d.ts +12 -0
- package/dist/write/accessibility-probe.js +63 -0
- package/dist/write/accessibility-probe.js.map +1 -0
- package/dist/write/automation-probe.d.ts +7 -0
- package/dist/write/automation-probe.js +8 -1
- package/dist/write/automation-probe.js.map +1 -1
- package/dist/write/batch.js +4 -2
- package/dist/write/batch.js.map +1 -1
- package/dist/write/capabilities.d.ts +8 -0
- package/dist/write/capabilities.js +13 -5
- package/dist/write/capabilities.js.map +1 -1
- package/dist/write/commands.d.ts +2 -0
- package/dist/write/commands.js +562 -48
- package/dist/write/commands.js.map +1 -1
- package/dist/write/edit-checklist.js +3 -2
- package/dist/write/edit-checklist.js.map +1 -1
- package/dist/write/guards.d.ts +2 -7
- package/dist/write/guards.js +64 -6
- package/dist/write/guards.js.map +1 -1
- package/dist/write/heading.js +2 -0
- package/dist/write/heading.js.map +1 -1
- package/dist/write/lock.d.ts +40 -2
- package/dist/write/lock.js +91 -14
- package/dist/write/lock.js.map +1 -1
- package/dist/write/make-repeating-project.d.ts +4 -0
- package/dist/write/make-repeating-project.js +253 -0
- package/dist/write/make-repeating-project.js.map +1 -0
- package/dist/write/operations.d.ts +145 -1
- package/dist/write/operations.js +48 -0
- package/dist/write/operations.js.map +1 -1
- package/dist/write/pipeline.d.ts +19 -0
- package/dist/write/pipeline.js +109 -26
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/pre-state.d.ts +68 -3
- package/dist/write/pre-state.js +102 -3
- package/dist/write/pre-state.js.map +1 -1
- package/dist/write/reopen.js +3 -2
- package/dist/write/reopen.js.map +1 -1
- package/dist/write/reorder.js +27 -17
- package/dist/write/reorder.js.map +1 -1
- package/dist/write/repeat-rule.d.ts +22 -0
- package/dist/write/repeat-rule.js +258 -0
- package/dist/write/repeat-rule.js.map +1 -0
- package/dist/write/reversibility.js +49 -0
- package/dist/write/reversibility.js.map +1 -1
- package/dist/write/tag-refs.d.ts +47 -0
- package/dist/write/tag-refs.js +126 -0
- package/dist/write/tag-refs.js.map +1 -0
- package/dist/write/undo.d.ts +74 -5
- package/dist/write/undo.js +494 -80
- package/dist/write/undo.js.map +1 -1
- package/dist/write/vectors/registry.d.ts +17 -1
- package/dist/write/vectors/registry.js +18 -2
- package/dist/write/vectors/registry.js.map +1 -1
- package/dist/write/vectors/types.d.ts +119 -3
- package/dist/write/vectors/ui-certification.d.ts +48 -0
- package/dist/write/vectors/ui-certification.js +46 -0
- package/dist/write/vectors/ui-certification.js.map +1 -0
- package/dist/write/vectors/ui-drag.d.ts +188 -0
- package/dist/write/vectors/ui-drag.js +1095 -0
- package/dist/write/vectors/ui-drag.js.map +1 -0
- package/dist/write/vectors/ui-recipes.d.ts +70 -0
- package/dist/write/vectors/ui-recipes.js +558 -0
- package/dist/write/vectors/ui-recipes.js.map +1 -0
- package/dist/write/vectors/ui.d.ts +137 -0
- package/dist/write/vectors/ui.js +656 -0
- package/dist/write/vectors/ui.js.map +1 -0
- package/dist/write/verify/delta.d.ts +31 -3
- package/dist/write/verify/delta.js +22 -6
- package/dist/write/verify/delta.js.map +1 -1
- package/dist/write/verify/poller.js +1 -0
- package/dist/write/verify/poller.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/exit-codes.d.ts +0 -26
- package/dist/cli/exit-codes.js +0 -26
- package/dist/cli/exit-codes.js.map +0 -1
- package/dist/cli/output.d.ts +0 -42
- package/dist/cli/output.js +0 -16
- package/dist/cli/output.js.map +0 -1
package/dist/cli/help.js
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { ExitCode, PKG_VERSION } from "../index.js";
|
|
2
|
+
/** The index groups, in display order. Every top-level command lands in one. */
|
|
3
|
+
export const HELP_GROUPS = [
|
|
4
|
+
{
|
|
5
|
+
title: "Views — the app's built-in lists",
|
|
6
|
+
commands: ["inbox", "today", "upcoming", "anytime", "someday", "logbook", "trash"],
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
title: "Browse & search",
|
|
10
|
+
commands: ["show", "open", "search", "projects", "areas", "tags", "changes"],
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
title: "Write — create and change items",
|
|
14
|
+
commands: ["todo", "project", "area", "tag", "heading", "batch", "undo", "reorder"],
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
title: "Setup & diagnostics",
|
|
18
|
+
commands: ["config", "doctor", "capabilities", "setup", "mcp", "snapshot", "legend"],
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* The per-command index copy. Keyed by top-level command name. Kept ≤58 chars
|
|
23
|
+
* and behavior-only (surface-copy.md); the completeness test asserts this map's
|
|
24
|
+
* keys are exactly the registered top-level commands (minus `help`).
|
|
25
|
+
*/
|
|
26
|
+
export const INDEX = {
|
|
27
|
+
// Views
|
|
28
|
+
inbox: { args: "", desc: "captured, still-unsorted to-dos" },
|
|
29
|
+
today: { args: "", desc: "what's scheduled for today, plus This Evening" },
|
|
30
|
+
upcoming: { args: "", desc: "future-scheduled items, in date order" },
|
|
31
|
+
anytime: { args: "", desc: "all active items, grouped by area and project" },
|
|
32
|
+
someday: { args: "", desc: "incubating items kept without a date" },
|
|
33
|
+
logbook: { args: "", desc: "completed and canceled items, newest first" },
|
|
34
|
+
trash: { args: "", desc: "trashed items (empty it with `trash empty`)" },
|
|
35
|
+
// Browse & search
|
|
36
|
+
show: { args: "<ref>", desc: "show a to-do, project, or area by id or name" },
|
|
37
|
+
open: { args: "<ref>", desc: "reveal an item in the Things app on this Mac" },
|
|
38
|
+
search: { args: "<query>", desc: "find items by words in their title or notes" },
|
|
39
|
+
projects: { args: "[ref]", desc: "list projects, or show one" },
|
|
40
|
+
areas: { args: "[ref]", desc: "list areas, or show one" },
|
|
41
|
+
tags: { args: "", desc: "list the tag hierarchy" },
|
|
42
|
+
changes: { args: "", desc: "items created or changed since a moment (--since)" },
|
|
43
|
+
// Write
|
|
44
|
+
todo: { args: "<verb>", desc: "add, edit, schedule, complete, move to-dos" },
|
|
45
|
+
project: { args: "<verb>", desc: "add, edit, complete, move projects" },
|
|
46
|
+
area: { args: "<verb>", desc: "add, rename, retag, reorder, delete areas" },
|
|
47
|
+
tag: { args: "<verb>", desc: "add, rename, nest, delete tags" },
|
|
48
|
+
heading: { args: "<verb>", desc: "add, rename, archive headings in a project" },
|
|
49
|
+
batch: { args: "[file]", desc: "run many changes from a JSONL script" },
|
|
50
|
+
undo: { args: "", desc: "reverse recent changes made through this tool" },
|
|
51
|
+
reorder: { args: "<ids…>", desc: "reorder items within a list or container" },
|
|
52
|
+
// Setup & diagnostics
|
|
53
|
+
config: { args: "<verb>", desc: "show or set configuration keys" },
|
|
54
|
+
doctor: { args: "", desc: "check environment health and pending setup" },
|
|
55
|
+
capabilities: { args: "", desc: "what each write operation supports" },
|
|
56
|
+
setup: { args: "<verb>", desc: "one-time setup (install the Shortcuts)" },
|
|
57
|
+
mcp: { args: "", desc: "serve the Model Context Protocol server on stdio" },
|
|
58
|
+
snapshot: { args: "", desc: "full normalized dump of every record" },
|
|
59
|
+
legend: { args: "", desc: "the symbols and colors list output uses" },
|
|
60
|
+
};
|
|
61
|
+
/** Resolve the width to reflow help to: THINGS_WIDTH, else the TTY, else 100. */
|
|
62
|
+
export function helpWidth() {
|
|
63
|
+
const raw = process.env["THINGS_WIDTH"];
|
|
64
|
+
if (raw !== undefined) {
|
|
65
|
+
const n = Number(raw);
|
|
66
|
+
if (Number.isInteger(n) && n > 0)
|
|
67
|
+
return n;
|
|
68
|
+
}
|
|
69
|
+
const cols = process.stdout.columns;
|
|
70
|
+
if (typeof cols === "number" && cols > 0)
|
|
71
|
+
return cols;
|
|
72
|
+
return 100;
|
|
73
|
+
}
|
|
74
|
+
/** Wrap `text` to `width`, continuation lines indented to `hang`. */
|
|
75
|
+
function wrap(text, width, hang) {
|
|
76
|
+
const room = Math.max(24, width);
|
|
77
|
+
const out = [];
|
|
78
|
+
let cur = "";
|
|
79
|
+
for (const word of text.split(" ")) {
|
|
80
|
+
const candidate = cur === "" ? word : `${cur} ${word}`;
|
|
81
|
+
if (candidate.length <= room || cur === "")
|
|
82
|
+
cur = candidate;
|
|
83
|
+
else {
|
|
84
|
+
out.push(cur);
|
|
85
|
+
cur = " ".repeat(hang) + word;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (cur !== "")
|
|
89
|
+
out.push(cur);
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Lay out one index row: the (already padded) `head` column followed by the
|
|
94
|
+
* descriptor, wrapping the DESCRIPTOR only (so the head's alignment padding is
|
|
95
|
+
* never collapsed) with continuation lines hanging at the head column width.
|
|
96
|
+
*/
|
|
97
|
+
function layoutRow(head, desc, width, col) {
|
|
98
|
+
const first = head + desc;
|
|
99
|
+
if (first.length <= Math.max(24, width))
|
|
100
|
+
return [first];
|
|
101
|
+
const wrapped = wrap(desc, width - col, 0);
|
|
102
|
+
return wrapped.map((chunk, i) => (i === 0 ? head + chunk : " ".repeat(col) + chunk));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Render the top-level signpost index. Pure and width-parameterized so the
|
|
106
|
+
* contract test can assert its line budget at a fixed width. Returns the body
|
|
107
|
+
* WITHOUT a trailing newline.
|
|
108
|
+
*/
|
|
109
|
+
export function renderTopLevelHelp(program, width) {
|
|
110
|
+
void program;
|
|
111
|
+
const lines = [];
|
|
112
|
+
lines.push("things — a programmatic interface to Things 3 (Cultured Code).");
|
|
113
|
+
lines.push("Usage: things <command> [arguments] [options]");
|
|
114
|
+
lines.push("");
|
|
115
|
+
// One shared name column across every group, so descriptors align.
|
|
116
|
+
const headOf = (name) => {
|
|
117
|
+
const e = INDEX[name];
|
|
118
|
+
const args = e !== undefined && e.args !== "" ? ` ${e.args}` : "";
|
|
119
|
+
return ` ${name}${args}`;
|
|
120
|
+
};
|
|
121
|
+
let col = 0;
|
|
122
|
+
for (const g of HELP_GROUPS)
|
|
123
|
+
for (const name of g.commands) {
|
|
124
|
+
col = Math.max(col, headOf(name).length);
|
|
125
|
+
}
|
|
126
|
+
col += 2;
|
|
127
|
+
for (const g of HELP_GROUPS) {
|
|
128
|
+
lines.push(`${g.title}`);
|
|
129
|
+
for (const name of g.commands) {
|
|
130
|
+
const e = INDEX[name];
|
|
131
|
+
if (e === undefined)
|
|
132
|
+
continue;
|
|
133
|
+
const head = headOf(name).padEnd(col);
|
|
134
|
+
lines.push(...layoutRow(head, e.desc, width, col));
|
|
135
|
+
}
|
|
136
|
+
lines.push("");
|
|
137
|
+
}
|
|
138
|
+
lines.push("Global options (accepted by most commands)");
|
|
139
|
+
lines.push(`${" --json".padEnd(col)}emit a versioned JSON envelope on stdout`);
|
|
140
|
+
lines.push(`${" --db <path>".padEnd(col)}read from an explicit database path`);
|
|
141
|
+
lines.push(`${" -h, --help".padEnd(col)}help for things, or for any <command>`);
|
|
142
|
+
lines.push(`${" -V, --version".padEnd(col)}print the version (${PKG_VERSION})`);
|
|
143
|
+
lines.push("");
|
|
144
|
+
lines.push("Run `things <command> --help` for the behavior and options of any command.");
|
|
145
|
+
lines.push("Most commands take a ref (uuid, prefix, share link, or name) — see `things help ids`.");
|
|
146
|
+
lines.push("Guides: `things help <topic>` — agent, filters, ids, output, writes.");
|
|
147
|
+
lines.push("New to Things? Start with `things help agent`.");
|
|
148
|
+
return lines.join("\n");
|
|
149
|
+
}
|
|
150
|
+
// ── Topics ───────────────────────────────────────────────────────────────────
|
|
151
|
+
/**
|
|
152
|
+
* The AGENT NOTES, verbatim from the former top-level epilog. Authored one
|
|
153
|
+
* bullet per entry; reflowed to the terminal by {@link renderAgentTopic}.
|
|
154
|
+
*/
|
|
155
|
+
const AGENT_NOTE_BULLETS = [
|
|
156
|
+
"Every command supports --json: a versioned envelope on stdout, logs on stderr.",
|
|
157
|
+
"Uuid parameters accept unique PREFIXES (>= 6 chars); list output shows 8+-char prefixes, --json always carries full uuids. Ambiguous prefixes fail with the candidates listed.",
|
|
158
|
+
'A Things share link (Share > Copy Link, "things:///show?id=<uuid>") is accepted anywhere a uuid or name is expected — it is stripped to the id.',
|
|
159
|
+
"The word `show` may be omitted: `things <ref>` shows the referenced item whenever <ref> is not a command name (command names always win).",
|
|
160
|
+
"Exit codes are stable: 0 ok, 2 usage, 3 verify-failed, 4 blocked, 5 drift-blocked, 6 unsupported, 7 environment.",
|
|
161
|
+
"No command ever prompts interactively; operations with cascading or permanent effects require explicit flags documented in their --help.",
|
|
162
|
+
"Discover the full operation catalog with: things capabilities --json",
|
|
163
|
+
"Symbols & colors in list output: run `things legend` (add --json for the table).",
|
|
164
|
+
"Every write supports --dry-run: preview the planned change and its expected effect without executing anything.",
|
|
165
|
+
"Failures are loud: a change that does not take effect exits 3; refused changes exit 4 with machine-readable remediation.",
|
|
166
|
+
];
|
|
167
|
+
function renderAgentTopic(width) {
|
|
168
|
+
const room = Math.max(40, width);
|
|
169
|
+
const out = ["AGENT NOTES — orientation for an agent driving this tool", ""];
|
|
170
|
+
for (const bullet of AGENT_NOTE_BULLETS) {
|
|
171
|
+
const wrapped = wrap(`- ${bullet}`, room, 2);
|
|
172
|
+
out.push(...wrapped);
|
|
173
|
+
}
|
|
174
|
+
return out.join("\n");
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* The static topic bodies. Authored to fit ~90 columns and stay ≤40 lines;
|
|
178
|
+
* behavior-only (surface-copy.md) — the banned-vocabulary test scans them too.
|
|
179
|
+
*/
|
|
180
|
+
const STATIC_TOPICS = {
|
|
181
|
+
filters: `FILTERS & BOUNDS — the optional flags a read view accepts
|
|
182
|
+
|
|
183
|
+
Content scope (which items qualify):
|
|
184
|
+
--tag <ref> items with this tag: direct, container-inherited, or on a
|
|
185
|
+
descendant tag. Repeatable — several --tag AND together
|
|
186
|
+
--exact-tag with --tag: the named tag only, no descendants
|
|
187
|
+
--untagged only items with no tag at all (the app's "No Tag" filter)
|
|
188
|
+
--overdue only open items past their deadline, due today excluded
|
|
189
|
+
(today, inbox, anytime, someday, search; and the
|
|
190
|
+
containers projects, project show, area show — by each
|
|
191
|
+
entity's OWN deadline, no recursion; not the areas list)
|
|
192
|
+
--area <ref> restrict to one area (logbook, search)
|
|
193
|
+
--project <ref> restrict to one project (logbook, search)
|
|
194
|
+
--type <kind> search only: todo | project
|
|
195
|
+
|
|
196
|
+
--tag has two axes: CONTAINER inheritance (an item inherits its project/area/
|
|
197
|
+
heading tags) and DESCENDANT expansion (a parent tag matches child-tag items).
|
|
198
|
+
--exact-tag drops expansion. Inheritance is view-dependent: FLAT views (incl. the
|
|
199
|
+
projects LIST) are inheritance-inclusive; project show / area show match a tag
|
|
200
|
+
DIRECTLY on the row (a tag inherited from that one container is vacuous there).
|
|
201
|
+
--untagged mirrors it: whole-relation when flat, direct-only in a container.
|
|
202
|
+
|
|
203
|
+
Volume caps (how many rows):
|
|
204
|
+
--limit <n> maximum rows before truncating (flat views; default 50)
|
|
205
|
+
--area-limit <n> per-area-block cap on the grouped views (anytime, someday, areas)
|
|
206
|
+
--project-limit <n> per-project-block cap (anytime, area show)
|
|
207
|
+
--all lift this view's own caps and bounds — but never pull in a
|
|
208
|
+
different class: logged and trashed items stay behind their
|
|
209
|
+
own flags (--show-logged, --trashed)
|
|
210
|
+
|
|
211
|
+
Range bounds (the time window — keyed to each view's own timeline):
|
|
212
|
+
--since <when> logbook = logged date · upcoming = scheduled date
|
|
213
|
+
--until <when> changes = modified date · inbox = created date
|
|
214
|
+
Accepts \`2w\`/\`3m\`/\`1y\` back from today, or \`2024\`, \`2024-03\`, \`2024-03-05\`.
|
|
215
|
+
|
|
216
|
+
Defaults exist only for the bare invocation. Stating any explicit --limit or
|
|
217
|
+
--since/--until drops the remaining defaults of both classes; explicit values
|
|
218
|
+
always compose as an intersection. Content scopes and toggles never lift a
|
|
219
|
+
default. (\`things changes\` requires --since, so that required bound lifts nothing.)`,
|
|
220
|
+
ids: `REFERENCES & IDS — how to point at an item
|
|
221
|
+
|
|
222
|
+
A ref is: a things:/// URL, a UUID, a unique UUID prefix, or (areas/projects) a
|
|
223
|
+
case-insensitive name. Anywhere a ref is accepted you may pass any of these:
|
|
224
|
+
- a full uuid
|
|
225
|
+
- a unique uuid PREFIX (>= 6 characters)
|
|
226
|
+
- a things:/// share link (the app's Share > Copy Link) — stripped to its id
|
|
227
|
+
- a unique NAME, for projects and areas (case-, space-, and dash-insensitive)
|
|
228
|
+
Every item has a uuid; list views print a short prefix, and --json always carries
|
|
229
|
+
the full uuid. Ambiguous prefixes and names fail loudly and list the candidates.
|
|
230
|
+
|
|
231
|
+
Direct addressing — the word \`show\` may be omitted:
|
|
232
|
+
things <ref> show the referenced item (when <ref> is not a command name)
|
|
233
|
+
things area <ref> show that area (the verb is implied inside a type)
|
|
234
|
+
things project <ref> show that project
|
|
235
|
+
Command names always win: \`things today\` is the view, never an item called
|
|
236
|
+
"today". Reach a same-named item by its uuid, or by the typed form
|
|
237
|
+
(\`things area show Anytime\`).
|
|
238
|
+
|
|
239
|
+
A to-do takes a ref by uuid, prefix, or share link — never by title. A bare NAME
|
|
240
|
+
resolves against areas and projects only; on a tie, the area wins. When a name
|
|
241
|
+
does not resolve, the error offers close title matches you can copy.
|
|
242
|
+
|
|
243
|
+
WRITE targets: project, area, and tag verbs accept a uuid OR a unique name (a
|
|
244
|
+
duplicated project name is refused, listing the candidates to pick from by uuid);
|
|
245
|
+
to-do and heading writes take a uuid or prefix only.`,
|
|
246
|
+
output: `OUTPUT — human tables vs. --json
|
|
247
|
+
|
|
248
|
+
Human output (the default): compact aligned tables and detail cards, fit to the
|
|
249
|
+
terminal width on a TTY. On a TTY a list view also prints a title header and, for
|
|
250
|
+
a rewritten shorthand, a dim \`≡ <canonical command>\` echo. Neither the header nor
|
|
251
|
+
the echo rides piped output or --json, so \`things inbox | grep\` stays clean.
|
|
252
|
+
|
|
253
|
+
--json A versioned envelope on stdout: { apiVersion, ok, kind, data, meta }.
|
|
254
|
+
A failure becomes an error envelope (still exit-coded). Logs, warnings,
|
|
255
|
+
and errors go to stderr, so stdout stays one clean JSON line. Full uuids
|
|
256
|
+
always appear here. --json is byte-stable — it is never width-fitted.
|
|
257
|
+
Optional entity fields are omitted when empty: a to-do with no deadline
|
|
258
|
+
has no deadline key, one with no tags has no tags key, and so on. Read a
|
|
259
|
+
missing key as unset/empty/default — absent and empty are the same. The
|
|
260
|
+
identity keys (uuid, type, and the title) are always present.
|
|
261
|
+
|
|
262
|
+
Width: human tables fit the terminal on a TTY. Set THINGS_WIDTH=<cols> to force a
|
|
263
|
+
column width, or THINGS_WIDTH=0 to disable fitting. Piped output is never fitted,
|
|
264
|
+
so it stays stable for grep and diffs.
|
|
265
|
+
|
|
266
|
+
Symbols and colors used in the tables: \`things legend\` (or \`things legend --json\`).`,
|
|
267
|
+
writes: `WRITES — the gating model, undo, and setup
|
|
268
|
+
|
|
269
|
+
Every write behaves the same way:
|
|
270
|
+
- Success means the change happened; if it did not take effect the command exits
|
|
271
|
+
non-zero with a machine-readable reason.
|
|
272
|
+
- --dry-run previews the planned change and its expected effect; nothing runs.
|
|
273
|
+
- No command prompts interactively. A change that cascades, is permanent, or
|
|
274
|
+
disturbs the app requires an explicit flag, named for its consequence:
|
|
275
|
+
--acknowledge-* confirm a cascade or a reset (named per command)
|
|
276
|
+
--children <policy> decide what happens to a container's open to-dos
|
|
277
|
+
--dangerously-permanent accept a permanent, unrecoverable delete
|
|
278
|
+
--allow-disruptive permit a change that briefly steals window focus
|
|
279
|
+
--allow-very-disruptive permit a change that visibly drives the Things app
|
|
280
|
+
--dangerously-drive-gui for the few operations the app offers nowhere else
|
|
281
|
+
|
|
282
|
+
Undo — \`things undo\`:
|
|
283
|
+
Reverse recent changes made through this tool, newest first; each undo applies
|
|
284
|
+
the inverse change. --dry-run shows the plan. Irreversible changes (permanent
|
|
285
|
+
deletes, unknown prior state) are reported, never guessed. Changes made directly
|
|
286
|
+
in the Things app cannot be undone here.
|
|
287
|
+
|
|
288
|
+
Configuration that affects writes — \`things config set <key> <value>\`:
|
|
289
|
+
actor author name recorded on each change
|
|
290
|
+
maxDisruption ceiling for how disruptive a change may be
|
|
291
|
+
ui-enabled allow the GUI-driven operations
|
|
292
|
+
allow-experimental enable experimental strategies (e.g. native reorder)
|
|
293
|
+
|
|
294
|
+
Writes are NAMESPACED: a bare \`things <verb> …\` (update, add, delete, complete,
|
|
295
|
+
cancel, move, …) is not a command — it points you at the typed form
|
|
296
|
+
(\`things todo|project|area|tag|heading <verb> …\`), never running the change.
|
|
297
|
+
|
|
298
|
+
Discover every operation and the flags it needs: \`things capabilities\`.
|
|
299
|
+
A few operations need the bundled Shortcuts: \`things setup shortcuts\`.`,
|
|
300
|
+
};
|
|
301
|
+
/** The valid topic names, in the order the "unknown topic" hint lists them. */
|
|
302
|
+
export const TOPIC_NAMES = ["agent", "filters", "ids", "output", "writes"];
|
|
303
|
+
/** Render one topic to `width`, or null when `name` is not a topic. */
|
|
304
|
+
export function renderTopic(name, width) {
|
|
305
|
+
if (name === "agent")
|
|
306
|
+
return renderAgentTopic(width);
|
|
307
|
+
return STATIC_TOPICS[name] ?? null;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Install the custom top-level help and the `help [topic]` command. Replaces
|
|
311
|
+
* the root program's `helpInformation` with the signpost index (so both
|
|
312
|
+
* `things --help` and the help command render it) and disables commander's
|
|
313
|
+
* built-in help command in favor of one that also serves topics.
|
|
314
|
+
*/
|
|
315
|
+
export function registerHelp(program) {
|
|
316
|
+
// Root-only override: subcommands keep commander's default per-command help.
|
|
317
|
+
program.helpInformation = () => `${renderTopLevelHelp(program, helpWidth())}\n`;
|
|
318
|
+
// Replace commander's built-in `help [command]` with our topic-aware one.
|
|
319
|
+
program.helpCommand(false);
|
|
320
|
+
program
|
|
321
|
+
.command("help [command...]")
|
|
322
|
+
.description("show this index, a guide (`things help <topic>`), or a command's help")
|
|
323
|
+
.action((tokens = []) => {
|
|
324
|
+
const width = helpWidth();
|
|
325
|
+
if (tokens.length === 0) {
|
|
326
|
+
program.outputHelp();
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const first = (tokens[0] ?? "").toLowerCase();
|
|
330
|
+
// A single topic token renders the guide.
|
|
331
|
+
if (tokens.length === 1) {
|
|
332
|
+
const topic = renderTopic(first, width);
|
|
333
|
+
if (topic !== null) {
|
|
334
|
+
process.stdout.write(`${topic}\n`);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Otherwise resolve a command path and defer to its own --help.
|
|
339
|
+
let cmd = program;
|
|
340
|
+
for (const token of tokens) {
|
|
341
|
+
cmd = cmd.commands.find((c) => c.name() === token || c.aliases().includes(token));
|
|
342
|
+
if (cmd === undefined)
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
if (cmd !== undefined && cmd !== program) {
|
|
346
|
+
cmd.outputHelp();
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
process.stderr.write(`error: no help topic or command "${tokens.join(" ")}" — ` +
|
|
350
|
+
`topics: ${TOPIC_NAMES.join(", ")}; ` +
|
|
351
|
+
"commands: `things --help`\n");
|
|
352
|
+
process.exitCode = ExitCode.Usage;
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
//# sourceMappingURL=help.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/main.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
import { realpathSync } from "node:fs";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import { Command } from "commander";
|
|
10
|
+
import { registerHelp } from "./help.js";
|
|
11
|
+
import { installExcessArgsHelp } from "./excess-args.js";
|
|
10
12
|
import { registerDoctor } from "./commands/doctor.js";
|
|
11
13
|
import { registerMcp } from "./commands/mcp.js";
|
|
12
14
|
import { registerAreaCommands } from "./commands/area.js";
|
|
@@ -17,32 +19,20 @@ import { registerShowCommands } from "./commands/show.js";
|
|
|
17
19
|
import { registerSnapshot } from "./commands/snapshot.js";
|
|
18
20
|
import { registerTodoCommands } from "./commands/todo.js";
|
|
19
21
|
import { registerWriteCommands } from "./commands/writes.js";
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- Uuid parameters accept unique PREFIXES (>= 6 chars); list output shows
|
|
25
|
-
8+-char prefixes, --json always carries full uuids. Ambiguous prefixes
|
|
26
|
-
fail with the candidates listed.
|
|
27
|
-
- A Things share link (Share > Copy Link, "things:///show?id=<uuid>") is
|
|
28
|
-
accepted anywhere a uuid or name is expected — it is stripped to the id.
|
|
29
|
-
- Exit codes are stable: 0 ok, 2 usage, 3 verify-failed, 4 blocked,
|
|
30
|
-
5 drift-blocked, 6 unsupported, 7 environment.
|
|
31
|
-
- No command ever prompts interactively; operations with cascading or
|
|
32
|
-
permanent effects require explicit flags documented in their --help.
|
|
33
|
-
- Discover the full operation catalog with: things capabilities --json
|
|
34
|
-
- Every write supports --dry-run: preview the planned change and its
|
|
35
|
-
expected effect without executing anything.
|
|
36
|
-
- Failures are loud: a change that does not take effect exits 3; refused
|
|
37
|
-
changes exit 4 with machine-readable remediation.
|
|
38
|
-
`;
|
|
22
|
+
import { resolveInvocation } from "./resolve-invocation.js";
|
|
23
|
+
import { runVerbHint } from "./verb-hint.js";
|
|
24
|
+
import { resolveWidth, setFitWidth } from "./width.js";
|
|
25
|
+
import { ExitCode, PKG_VERSION } from "../index.js";
|
|
39
26
|
export function buildProgram() {
|
|
40
27
|
const program = new Command();
|
|
41
28
|
program
|
|
42
29
|
.name("things")
|
|
43
30
|
.description("Programmatic interface to Things 3 (Cultured Code)")
|
|
44
31
|
.version(PKG_VERSION)
|
|
45
|
-
|
|
32
|
+
// Unknown-command typos are answered with "did you mean …" (default on;
|
|
33
|
+
// stated for the record — most top-level typos route through the bare-noun
|
|
34
|
+
// did-you-mean instead, this covers the subcommand groups).
|
|
35
|
+
.showSuggestionAfterError(true);
|
|
46
36
|
registerDoctor(program);
|
|
47
37
|
registerReadCommands(program);
|
|
48
38
|
registerShowCommands(program);
|
|
@@ -53,14 +43,39 @@ export function buildProgram() {
|
|
|
53
43
|
registerSetup(program);
|
|
54
44
|
registerSnapshot(program);
|
|
55
45
|
registerMcp(program);
|
|
46
|
+
// The signpost index + `help [topic]` replace the multi-scroll epilog; the
|
|
47
|
+
// improved excess-argument message names the command and its usage line.
|
|
48
|
+
registerHelp(program);
|
|
49
|
+
installExcessArgsHelp(program);
|
|
56
50
|
return program;
|
|
57
51
|
}
|
|
58
52
|
export function runCli() {
|
|
53
|
+
// Resolve the width-aware row fit ONCE at startup (docs/design/width-aware-
|
|
54
|
+
// tty.md): THINGS_WIDTH override, else stdout.columns on a TTY, else null (no
|
|
55
|
+
// fitting — pipes/grep/--json byte-stable). Threaded to the renderers via the
|
|
56
|
+
// module-level fit width in ./width.ts, so every human list path inherits it
|
|
57
|
+
// and MCP/--json never touch it.
|
|
58
|
+
setFitWidth(resolveWidth({
|
|
59
|
+
env: process.env,
|
|
60
|
+
columns: process.stdout.columns,
|
|
61
|
+
isTTY: process.stdout.isTTY === true,
|
|
62
|
+
}));
|
|
59
63
|
const program = buildProgram();
|
|
60
64
|
program.exitOverride((err) => {
|
|
61
65
|
process.exit(err.exitCode === 0 ? ExitCode.Ok : ExitCode.Usage);
|
|
62
66
|
});
|
|
63
|
-
|
|
67
|
+
// The single router (docs/design/cli-grammar.md): classify the invocation,
|
|
68
|
+
// then dispatch its normalized argv. Sugar forms (bare noun, keyword-in-show)
|
|
69
|
+
// normalize into the canonical grammar here.
|
|
70
|
+
const resolved = resolveInvocation(program, process.argv.slice(2));
|
|
71
|
+
// A bare top-level mutation verb (`things update <ref>`) never reaches
|
|
72
|
+
// commander: it is answered with a namespaced-write suggestion instead of the
|
|
73
|
+
// show-sugar's confusing usage error (docs/design/cli-grammar.md).
|
|
74
|
+
if (resolved.form === "verb-hint") {
|
|
75
|
+
runVerbHint(program, resolved.argv);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
program.parse(resolved.argv, { from: "user" });
|
|
64
79
|
}
|
|
65
80
|
// Direct-run detection must survive the npm .bin symlink (argv[1] ends with
|
|
66
81
|
// "things", not "main.js") — resolve through realpath and compare to this
|
package/dist/cli/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AACA;;;;GAIG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Period-and-calendar grammar shared by the read views: the `--since`/`--until`
|
|
3
|
+
* whole-period parsers, the relative-period (`3d`/`2w`/`1m`/`1y`) grammar, and
|
|
4
|
+
* the Upcoming date-bucket labeller. Fully pure — no CLI or app imports — so
|
|
5
|
+
* the renderers and command registrations can both draw on it.
|
|
6
|
+
*/
|
|
7
|
+
declare const FULL_MONTHS: readonly ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
8
|
+
export { FULL_MONTHS };
|
|
9
|
+
/**
|
|
10
|
+
* Double a relative period for a "wider window" suggestion: `1m`→`2m`,
|
|
11
|
+
* `2w`→`4w`, `1y`→`2y`. A non-relative input (an absolute calendar period
|
|
12
|
+
* like `2026-09`) comes back unchanged — there is nothing sensible to double.
|
|
13
|
+
* Deliberately dumb: it only scales the count of a `\d+[dwmy]` period.
|
|
14
|
+
*/
|
|
15
|
+
export declare function doublePeriod(period: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* `--until` accepting whole periods: `2024` means through Dec 31 2024,
|
|
18
|
+
* `2024-03` through Mar 31, `2024-03-05` through end of that day; relative
|
|
19
|
+
* periods (`2w`, `1m`, `1y`) count FORWARD from now through the end of the
|
|
20
|
+
* landing day; anything else parses as an instant.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parsePeriodEnd(s: string, now?: Date): Date;
|
|
23
|
+
/**
|
|
24
|
+
* `--since` accepting the same vocabulary at the period's START: `2024` =
|
|
25
|
+
* Jan 1 2024 00:00, `2024-03` = Mar 1, `2024-03-05` = that midnight;
|
|
26
|
+
* relative periods (`2w`, `1m`) count BACKWARD from now to the landing
|
|
27
|
+
* day's midnight; anything else parses as an instant.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parsePeriodStart(s: string, now?: Date): Date;
|
|
30
|
+
/**
|
|
31
|
+
* GUI-style Upcoming bucket for a date, granularity decaying with distance:
|
|
32
|
+
* individual days for the next week ("Wed Jul 15"), the remainder of the
|
|
33
|
+
* current month ("Jul 19–31"), months through the end of NEXT year
|
|
34
|
+
* ("August", "January 2027"), then bare years ("2028").
|
|
35
|
+
*/
|
|
36
|
+
export declare function upcomingBucket(iso: string, todayIso: string): {
|
|
37
|
+
label: string;
|
|
38
|
+
isDay: boolean;
|
|
39
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Period-and-calendar grammar shared by the read views: the `--since`/`--until`
|
|
3
|
+
* whole-period parsers, the relative-period (`3d`/`2w`/`1m`/`1y`) grammar, and
|
|
4
|
+
* the Upcoming date-bucket labeller. Fully pure — no CLI or app imports — so
|
|
5
|
+
* the renderers and command registrations can both draw on it.
|
|
6
|
+
*/
|
|
7
|
+
const FULL_MONTHS = [
|
|
8
|
+
"January",
|
|
9
|
+
"February",
|
|
10
|
+
"March",
|
|
11
|
+
"April",
|
|
12
|
+
"May",
|
|
13
|
+
"June",
|
|
14
|
+
"July",
|
|
15
|
+
"August",
|
|
16
|
+
"September",
|
|
17
|
+
"October",
|
|
18
|
+
"November",
|
|
19
|
+
"December",
|
|
20
|
+
];
|
|
21
|
+
const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
22
|
+
const SHORT_MONTHS = [
|
|
23
|
+
"Jan",
|
|
24
|
+
"Feb",
|
|
25
|
+
"Mar",
|
|
26
|
+
"Apr",
|
|
27
|
+
"May",
|
|
28
|
+
"Jun",
|
|
29
|
+
"Jul",
|
|
30
|
+
"Aug",
|
|
31
|
+
"Sep",
|
|
32
|
+
"Oct",
|
|
33
|
+
"Nov",
|
|
34
|
+
"Dec",
|
|
35
|
+
];
|
|
36
|
+
export { FULL_MONTHS };
|
|
37
|
+
/**
|
|
38
|
+
* Relative period: `3d`/`2w`/`1m`/`1y` (days/weeks/calendar months/years),
|
|
39
|
+
* counted from `now` — FORWARD for an until-bound, BACKWARD for a since-
|
|
40
|
+
* bound (each command's natural direction; the flag name carries it).
|
|
41
|
+
*/
|
|
42
|
+
const RELATIVE_PERIOD = /^(\d+)([dwmy])$/i;
|
|
43
|
+
function relativePeriodDate(m, now, sign) {
|
|
44
|
+
const n = sign * Number(m[1]);
|
|
45
|
+
const unit = (m[2] ?? "").toLowerCase();
|
|
46
|
+
const d = new Date(now);
|
|
47
|
+
if (unit === "d")
|
|
48
|
+
d.setDate(d.getDate() + n);
|
|
49
|
+
else if (unit === "w")
|
|
50
|
+
d.setDate(d.getDate() + 7 * n);
|
|
51
|
+
else if (unit === "m")
|
|
52
|
+
d.setMonth(d.getMonth() + n);
|
|
53
|
+
else
|
|
54
|
+
d.setFullYear(d.getFullYear() + n);
|
|
55
|
+
return d;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Double a relative period for a "wider window" suggestion: `1m`→`2m`,
|
|
59
|
+
* `2w`→`4w`, `1y`→`2y`. A non-relative input (an absolute calendar period
|
|
60
|
+
* like `2026-09`) comes back unchanged — there is nothing sensible to double.
|
|
61
|
+
* Deliberately dumb: it only scales the count of a `\d+[dwmy]` period.
|
|
62
|
+
*/
|
|
63
|
+
export function doublePeriod(period) {
|
|
64
|
+
const m = RELATIVE_PERIOD.exec(period.trim());
|
|
65
|
+
if (m === null)
|
|
66
|
+
return period;
|
|
67
|
+
return `${Number(m[1]) * 2}${(m[2] ?? "").toLowerCase()}`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* `--until` accepting whole periods: `2024` means through Dec 31 2024,
|
|
71
|
+
* `2024-03` through Mar 31, `2024-03-05` through end of that day; relative
|
|
72
|
+
* periods (`2w`, `1m`, `1y`) count FORWARD from now through the end of the
|
|
73
|
+
* landing day; anything else parses as an instant.
|
|
74
|
+
*/
|
|
75
|
+
export function parsePeriodEnd(s, now = new Date()) {
|
|
76
|
+
const rel = RELATIVE_PERIOD.exec(s.trim());
|
|
77
|
+
if (rel !== null) {
|
|
78
|
+
const d = relativePeriodDate(rel, now, 1);
|
|
79
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);
|
|
80
|
+
}
|
|
81
|
+
const m = /^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?$/.exec(s.trim());
|
|
82
|
+
if (m === null)
|
|
83
|
+
return new Date(s);
|
|
84
|
+
const year = Number(m[1]);
|
|
85
|
+
// Day 0 of month n+1 = the last day of month n.
|
|
86
|
+
if (m[2] === undefined)
|
|
87
|
+
return new Date(year, 11, 31, 23, 59, 59, 999);
|
|
88
|
+
const month = Number(m[2]) - 1;
|
|
89
|
+
if (m[3] === undefined)
|
|
90
|
+
return new Date(year, month + 1, 0, 23, 59, 59, 999);
|
|
91
|
+
return new Date(year, month, Number(m[3]), 23, 59, 59, 999);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* `--since` accepting the same vocabulary at the period's START: `2024` =
|
|
95
|
+
* Jan 1 2024 00:00, `2024-03` = Mar 1, `2024-03-05` = that midnight;
|
|
96
|
+
* relative periods (`2w`, `1m`) count BACKWARD from now to the landing
|
|
97
|
+
* day's midnight; anything else parses as an instant.
|
|
98
|
+
*/
|
|
99
|
+
export function parsePeriodStart(s, now = new Date()) {
|
|
100
|
+
const rel = RELATIVE_PERIOD.exec(s.trim());
|
|
101
|
+
if (rel !== null) {
|
|
102
|
+
const d = relativePeriodDate(rel, now, -1);
|
|
103
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
104
|
+
}
|
|
105
|
+
const m = /^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?$/.exec(s.trim());
|
|
106
|
+
if (m === null)
|
|
107
|
+
return new Date(s);
|
|
108
|
+
const year = Number(m[1]);
|
|
109
|
+
if (m[2] === undefined)
|
|
110
|
+
return new Date(year, 0, 1);
|
|
111
|
+
const month = Number(m[2]) - 1;
|
|
112
|
+
if (m[3] === undefined)
|
|
113
|
+
return new Date(year, month, 1);
|
|
114
|
+
return new Date(year, month, Number(m[3]));
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* GUI-style Upcoming bucket for a date, granularity decaying with distance:
|
|
118
|
+
* individual days for the next week ("Wed Jul 15"), the remainder of the
|
|
119
|
+
* current month ("Jul 19–31"), months through the end of NEXT year
|
|
120
|
+
* ("August", "January 2027"), then bare years ("2028").
|
|
121
|
+
*/
|
|
122
|
+
export function upcomingBucket(iso, todayIso) {
|
|
123
|
+
const [y, m, d] = iso.split("-").map(Number);
|
|
124
|
+
const [y0, m0, d0] = todayIso.split("-").map(Number);
|
|
125
|
+
const diff = Math.round((Date.UTC(y ?? 0, (m ?? 1) - 1, d ?? 1) - Date.UTC(y0 ?? 0, (m0 ?? 1) - 1, d0 ?? 1)) /
|
|
126
|
+
86_400_000);
|
|
127
|
+
if (diff <= 7) {
|
|
128
|
+
const weekday = WEEKDAYS[new Date(Date.UTC(y ?? 0, (m ?? 1) - 1, d ?? 1)).getUTCDay()];
|
|
129
|
+
return { label: `${weekday} ${SHORT_MONTHS[(m ?? 1) - 1]} ${d}`, isDay: true };
|
|
130
|
+
}
|
|
131
|
+
if (y === y0 && m === m0) {
|
|
132
|
+
const lastDay = new Date(y ?? 0, m ?? 1, 0).getDate();
|
|
133
|
+
return { label: `${SHORT_MONTHS[(m ?? 1) - 1]} ${(d0 ?? 1) + 8}–${lastDay}`, isDay: false };
|
|
134
|
+
}
|
|
135
|
+
if ((y ?? 0) <= (y0 ?? 0) + 1) {
|
|
136
|
+
const month = FULL_MONTHS[(m ?? 1) - 1];
|
|
137
|
+
return { label: y === y0 ? `${month}` : `${month} ${y}`, isDay: false };
|
|
138
|
+
}
|
|
139
|
+
return { label: `${y}`, isDay: false };
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=period.js.map
|