gws-axi 0.20.1 → 0.22.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 (41) hide show
  1. package/dist/src/commands/calendar/dateish.d.ts +47 -0
  2. package/dist/src/commands/calendar/dateish.js +140 -0
  3. package/dist/src/commands/calendar/dateish.js.map +1 -1
  4. package/dist/src/commands/calendar/dateish.test.d.ts +1 -0
  5. package/dist/src/commands/calendar/dateish.test.js +223 -0
  6. package/dist/src/commands/calendar/dateish.test.js.map +1 -0
  7. package/dist/src/commands/calendar/events.d.ts +1 -1
  8. package/dist/src/commands/calendar/events.js +59 -16
  9. package/dist/src/commands/calendar/events.js.map +1 -1
  10. package/dist/src/commands/calendar/freebusy.d.ts +1 -1
  11. package/dist/src/commands/calendar/freebusy.js +46 -16
  12. package/dist/src/commands/calendar/freebusy.js.map +1 -1
  13. package/dist/src/commands/calendar/search.d.ts +1 -1
  14. package/dist/src/commands/calendar/search.js +44 -12
  15. package/dist/src/commands/calendar/search.js.map +1 -1
  16. package/dist/src/commands/calendar/week-start.d.ts +13 -0
  17. package/dist/src/commands/calendar/week-start.js +95 -0
  18. package/dist/src/commands/calendar/week-start.js.map +1 -0
  19. package/dist/src/commands/calendar/week-start.test.d.ts +1 -0
  20. package/dist/src/commands/calendar/week-start.test.js +89 -0
  21. package/dist/src/commands/calendar/week-start.test.js.map +1 -0
  22. package/dist/src/commands/calendar.js +8 -1
  23. package/dist/src/commands/calendar.js.map +1 -1
  24. package/dist/src/commands/drive/activity.d.ts +1 -1
  25. package/dist/src/commands/drive/activity.js +25 -7
  26. package/dist/src/commands/drive/activity.js.map +1 -1
  27. package/dist/src/commands/gmail/compose.d.ts +14 -1
  28. package/dist/src/commands/gmail/compose.js +36 -1
  29. package/dist/src/commands/gmail/compose.js.map +1 -1
  30. package/dist/src/commands/gmail/compose.test.js +51 -0
  31. package/dist/src/commands/gmail/compose.test.js.map +1 -1
  32. package/dist/src/commands/gmail/draft.d.ts +1 -1
  33. package/dist/src/commands/gmail/draft.js +10 -2
  34. package/dist/src/commands/gmail/draft.js.map +1 -1
  35. package/dist/src/config.d.ts +7 -0
  36. package/dist/src/config.js +9 -0
  37. package/dist/src/config.js.map +1 -1
  38. package/package.json +1 -1
  39. package/dist/src/commands/service-stub.d.ts +0 -8
  40. package/dist/src/commands/service-stub.js +0 -69
  41. package/dist/src/commands/service-stub.js.map +0 -1
@@ -40,3 +40,50 @@ export declare function formatEventTime(value: {
40
40
  * Invalid input returns the original string unchanged.
41
41
  */
42
42
  export declare function toLocalOffsetISO(input: string): string;
43
+ export type RangeEdge = "from" | "to";
44
+ /**
45
+ * Parse one range-flag value into an ISO instant, honoring per-edge day
46
+ * expansion and relative tokens. Anything that isn't a token or a bare date
47
+ * falls through to `parseDateishFlag` unchanged (instant semantics, existing
48
+ * error messages).
49
+ */
50
+ export declare function parseRangeFlag(value: string, edge: RangeEdge, now?: Date): string;
51
+ /** Local midnight of the most recent `weekStartDay` (0=Sun … 6=Sat) on or before `now`. */
52
+ export declare function startOfWeek(now: Date, weekStartDay: number): Date;
53
+ export interface WindowInput {
54
+ /** Raw, unresolved flag values — resolution happens here so errors can quote what was typed. */
55
+ from?: string;
56
+ to?: string;
57
+ today?: boolean;
58
+ thisWeek?: boolean;
59
+ }
60
+ export interface WindowOptions {
61
+ /** Already-ISO fallbacks used when the corresponding flag is absent. */
62
+ defaults?: {
63
+ from?: string;
64
+ to?: string;
65
+ };
66
+ /** Flag spellings for error messages — `drive activity` uses --since/--until. */
67
+ flagNames?: {
68
+ from: string;
69
+ to: string;
70
+ };
71
+ /** 0=Sun … 6=Sat. Callers resolve this from the account before calling. */
72
+ weekStartDay?: number;
73
+ /**
74
+ * Whether the calling command actually offers --today/--this-week. False for
75
+ * `drive activity`, whose errors must not suggest flags it doesn't have
76
+ * (principles.md#no-dead-end-surfaces).
77
+ */
78
+ shortcuts?: boolean;
79
+ now?: Date;
80
+ }
81
+ /**
82
+ * Resolve `--today` / `--this-week` / `--from` / `--to` into one validated
83
+ * window. Single source of truth for the shortcut-conflict and empty-window
84
+ * rules so no call site re-implements them.
85
+ */
86
+ export declare function resolveWindow(input: WindowInput, options?: WindowOptions): {
87
+ from: string | undefined;
88
+ to: string | undefined;
89
+ };
@@ -87,4 +87,144 @@ export function toLocalOffsetISO(input) {
87
87
  `T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}` +
88
88
  offsetStr);
89
89
  }
90
+ /**
91
+ * Local midnight of a day, optionally shifted by whole days.
92
+ *
93
+ * The local `Date` constructor with an out-of-range day component is what makes
94
+ * this DST-, month- and year-rollover-correct in one stroke. Never do this with
95
+ * millisecond addition: `+ 86_400_000` across a DST boundary lands on 23:00 or
96
+ * 01:00, not midnight.
97
+ */
98
+ function localMidnight(parts, plusDays = 0) {
99
+ return new Date(parts.y, parts.m - 1, parts.d + plusDays, 0, 0, 0, 0);
100
+ }
101
+ function partsOf(date) {
102
+ return { y: date.getFullYear(), m: date.getMonth() + 1, d: date.getDate() };
103
+ }
104
+ function dayOffsetFrom(now, days) {
105
+ return { kind: "day", parts: partsOf(localMidnight(partsOf(now), days)) };
106
+ }
107
+ const RELATIVE_RE = /^([+-])(\d+)([dwh])$/;
108
+ const DATE_ONLY_RE = /^\d{4}-\d{2}-\d{2}$/;
109
+ /**
110
+ * Resolve a relative/named token. Returns undefined when `value` isn't one, so
111
+ * the caller can fall through to literal parsing.
112
+ */
113
+ function resolveToken(value, now) {
114
+ const v = value.trim().toLowerCase();
115
+ switch (v) {
116
+ case "now":
117
+ return { kind: "instant", date: new Date(now.getTime()) };
118
+ case "today":
119
+ return dayOffsetFrom(now, 0);
120
+ case "tomorrow":
121
+ return dayOffsetFrom(now, 1);
122
+ case "yesterday":
123
+ return dayOffsetFrom(now, -1);
124
+ }
125
+ const rel = RELATIVE_RE.exec(v);
126
+ if (!rel)
127
+ return undefined;
128
+ const sign = rel[1] === "-" ? -1 : 1;
129
+ const n = Number(rel[2]);
130
+ const unit = rel[3];
131
+ // Hours are instant-precision (no day expansion); days/weeks are day-precision.
132
+ if (unit === "h") {
133
+ return { kind: "instant", date: new Date(now.getTime() + sign * n * 3600 * 1000) };
134
+ }
135
+ return dayOffsetFrom(now, sign * n * (unit === "w" ? 7 : 1));
136
+ }
137
+ function materialize(resolved, edge) {
138
+ if (resolved.kind === "instant")
139
+ return resolved.date.toISOString();
140
+ // Day precision: the `from` edge opens at that day's midnight, the `to` edge
141
+ // closes at the NEXT midnight so the named day is included (ranges are
142
+ // half-open). This is the whole point — `--from D --to D` is all of day D.
143
+ return localMidnight(resolved.parts, edge === "to" ? 1 : 0).toISOString();
144
+ }
145
+ /**
146
+ * Parse one range-flag value into an ISO instant, honoring per-edge day
147
+ * expansion and relative tokens. Anything that isn't a token or a bare date
148
+ * falls through to `parseDateishFlag` unchanged (instant semantics, existing
149
+ * error messages).
150
+ */
151
+ export function parseRangeFlag(value, edge, now = new Date()) {
152
+ if (!value) {
153
+ throw new AxiError("Missing date/time value", "VALIDATION_ERROR", [
154
+ "Use a date (2026-04-20), a datetime (2026-04-20T14:00), or a token (today, tomorrow, +7d)",
155
+ ]);
156
+ }
157
+ const token = resolveToken(value, now);
158
+ if (token)
159
+ return materialize(token, edge);
160
+ if (DATE_ONLY_RE.test(value)) {
161
+ const [y, m, d] = value.split("-").map(Number);
162
+ const probe = localMidnight({ y, m, d });
163
+ if (Number.isNaN(probe.getTime())) {
164
+ throw new AxiError(`Cannot parse date: ${value}`, "VALIDATION_ERROR", [
165
+ "Use YYYY-MM-DD (e.g. 2026-04-20)",
166
+ ]);
167
+ }
168
+ return materialize({ kind: "day", parts: { y, m, d } }, edge);
169
+ }
170
+ return parseDateishFlag(value);
171
+ }
172
+ /** Local midnight of the most recent `weekStartDay` (0=Sun … 6=Sat) on or before `now`. */
173
+ export function startOfWeek(now, weekStartDay) {
174
+ const back = (now.getDay() - weekStartDay + 7) % 7;
175
+ return localMidnight(partsOf(now), -back);
176
+ }
177
+ /**
178
+ * Resolve `--today` / `--this-week` / `--from` / `--to` into one validated
179
+ * window. Single source of truth for the shortcut-conflict and empty-window
180
+ * rules so no call site re-implements them.
181
+ */
182
+ export function resolveWindow(input, options = {}) {
183
+ const now = options.now ?? new Date();
184
+ const names = options.flagNames ?? { from: "--from", to: "--to" };
185
+ const shortcuts = [];
186
+ if (input.today)
187
+ shortcuts.push("--today");
188
+ if (input.thisWeek)
189
+ shortcuts.push("--this-week");
190
+ if (shortcuts.length > 1) {
191
+ throw new AxiError(`Cannot combine ${shortcuts.join(" and ")}`, "VALIDATION_ERROR", [
192
+ "Pass one window shortcut, or an explicit range with " + `${names.from} / ${names.to}`,
193
+ ]);
194
+ }
195
+ if (shortcuts.length === 1) {
196
+ const explicit = [
197
+ input.from !== undefined ? names.from : undefined,
198
+ input.to !== undefined ? names.to : undefined,
199
+ ].filter(Boolean);
200
+ if (explicit.length > 0) {
201
+ throw new AxiError(`Cannot combine ${shortcuts[0]} with ${explicit.join(" / ")}`, "VALIDATION_ERROR", [
202
+ `Use ${shortcuts[0]} on its own for that window`,
203
+ `Or drop ${shortcuts[0]} and set the range explicitly with ${names.from} / ${names.to}`,
204
+ ]);
205
+ }
206
+ const start = input.today
207
+ ? localMidnight(partsOf(now))
208
+ : startOfWeek(now, options.weekStartDay ?? 1);
209
+ const span = input.today ? 1 : 7;
210
+ return {
211
+ from: start.toISOString(),
212
+ to: localMidnight(partsOf(start), span).toISOString(),
213
+ };
214
+ }
215
+ const from = input.from !== undefined ? parseRangeFlag(input.from, "from", now) : options.defaults?.from;
216
+ const to = input.to !== undefined ? parseRangeFlag(input.to, "to", now) : options.defaults?.to;
217
+ // A window that cannot match anything must never render as an empty list —
218
+ // "no events found in the given time range" reads as "nothing is scheduled".
219
+ if (from !== undefined && to !== undefined && new Date(from) >= new Date(to)) {
220
+ throw new AxiError(`Empty time range: ${names.from} ${toLocalOffsetISO(from)} is not before ${names.to} ${toLocalOffsetISO(to)}`, "VALIDATION_ERROR", [
221
+ `A date-only ${names.to} already covers that whole day — ${names.from} 2026-04-20 ${names.to} 2026-04-20 is all of April 20`,
222
+ ...(options.shortcuts === false
223
+ ? []
224
+ : ["Use --today for today, or --this-week for the current week"]),
225
+ `Otherwise make ${names.to} later than ${names.from}`,
226
+ ]);
227
+ }
228
+ return { from, to };
229
+ }
90
230
  //# sourceMappingURL=dateish.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dateish.js","sourceRoot":"","sources":["../../../../src/commands/calendar/dateish.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dateish.js","sourceRoot":"","sources":["../../../../src/commands/calendar/dateish.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,223 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { AxiError } from "axi-sdk-js";
3
+ import { parseDateishFlag, parseRangeFlag, resolveWindow, startOfWeek } from "./dateish.js";
4
+ // These tests assume the repo's configured local zone (America/New_York) for the
5
+ // DST cases; every other case is zone-agnostic. `now` is always injected, so no
6
+ // test depends on the wall clock.
7
+ /** Local wall-clock rendering of an instant, for zone-independent assertions. */
8
+ function local(iso) {
9
+ const d = new Date(iso);
10
+ const pad = (n) => String(n).padStart(2, "0");
11
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
12
+ }
13
+ /** Whole days spanned by a window, by local midnight count. */
14
+ function spanDays(from, to) {
15
+ return Math.round((new Date(to).getTime() - new Date(from).getTime()) / (24 * 3600 * 1000));
16
+ }
17
+ const NOW = new Date(2026, 7, 26, 14, 30, 0); // Wed 2026-08-26 14:30 local
18
+ describe("parseRangeFlag — per-edge precision", () => {
19
+ it("opens a date-only value at that day's local midnight on the from edge", () => {
20
+ expect(local(parseRangeFlag("2026-08-26", "from", NOW))).toBe("2026-08-26 00:00");
21
+ });
22
+ it("closes a date-only value at the NEXT local midnight on the to edge", () => {
23
+ expect(local(parseRangeFlag("2026-08-26", "to", NOW))).toBe("2026-08-27 00:00");
24
+ });
25
+ it("makes --from D --to D cover the whole of day D", () => {
26
+ const from = parseRangeFlag("2026-08-26", "from", NOW);
27
+ const to = parseRangeFlag("2026-08-26", "to", NOW);
28
+ expect(spanDays(from, to)).toBe(1);
29
+ });
30
+ it("keeps each edge independently monotonic", () => {
31
+ // The rejected design (expand only when from === to) would make these two
32
+ // windows identical. They must differ by exactly one day.
33
+ const from = parseRangeFlag("2026-08-26", "from", NOW);
34
+ const sameDay = parseRangeFlag("2026-08-26", "to", NOW);
35
+ const nextDay = parseRangeFlag("2026-08-27", "to", NOW);
36
+ expect(spanDays(from, sameDay)).toBe(1);
37
+ expect(spanDays(from, nextDay)).toBe(2);
38
+ });
39
+ it("leaves instant values untouched on both edges", () => {
40
+ expect(parseRangeFlag("2026-08-26T14:00:00-04:00", "from", NOW)).toBe(new Date("2026-08-26T14:00:00-04:00").toISOString());
41
+ expect(parseRangeFlag("2026-08-26T14:00:00-04:00", "to", NOW)).toBe(new Date("2026-08-26T14:00:00-04:00").toISOString());
42
+ });
43
+ it("treats a local datetime as local on both edges (no day expansion)", () => {
44
+ expect(local(parseRangeFlag("2026-08-26T00:00", "to", NOW))).toBe("2026-08-26 00:00");
45
+ });
46
+ it("rejects garbage through the existing parseDateishFlag error", () => {
47
+ expect(() => parseRangeFlag("next thursday", "from", NOW)).toThrow(AxiError);
48
+ expect(() => parseRangeFlag("", "from", NOW)).toThrow(/Missing date\/time value/);
49
+ });
50
+ });
51
+ describe("parseRangeFlag — tokens", () => {
52
+ it("resolves now to the current instant", () => {
53
+ expect(parseRangeFlag("now", "to", NOW)).toBe(NOW.toISOString());
54
+ });
55
+ it("resolves day tokens with per-edge expansion", () => {
56
+ expect(local(parseRangeFlag("today", "from", NOW))).toBe("2026-08-26 00:00");
57
+ expect(local(parseRangeFlag("today", "to", NOW))).toBe("2026-08-27 00:00");
58
+ expect(local(parseRangeFlag("tomorrow", "from", NOW))).toBe("2026-08-27 00:00");
59
+ expect(local(parseRangeFlag("yesterday", "from", NOW))).toBe("2026-08-25 00:00");
60
+ });
61
+ it("resolves day and week offsets", () => {
62
+ expect(local(parseRangeFlag("+6d", "to", NOW))).toBe("2026-09-02 00:00");
63
+ expect(local(parseRangeFlag("-2d", "from", NOW))).toBe("2026-08-24 00:00");
64
+ expect(local(parseRangeFlag("+1w", "from", NOW))).toBe("2026-09-02 00:00");
65
+ expect(local(parseRangeFlag("-1w", "from", NOW))).toBe("2026-08-19 00:00");
66
+ });
67
+ it("treats hour offsets as instants, not days", () => {
68
+ expect(parseRangeFlag("+4h", "to", NOW)).toBe(new Date(NOW.getTime() + 4 * 3600 * 1000).toISOString());
69
+ });
70
+ it("composes into the rest of today and the next seven days", () => {
71
+ const restOfToday = {
72
+ from: parseRangeFlag("now", "from", NOW),
73
+ to: parseRangeFlag("today", "to", NOW),
74
+ };
75
+ expect(local(restOfToday.from)).toBe("2026-08-26 14:30");
76
+ expect(local(restOfToday.to)).toBe("2026-08-27 00:00");
77
+ const nextSeven = {
78
+ from: parseRangeFlag("today", "from", NOW),
79
+ to: parseRangeFlag("+6d", "to", NOW),
80
+ };
81
+ expect(spanDays(nextSeven.from, nextSeven.to)).toBe(7);
82
+ });
83
+ it("is case- and whitespace-insensitive", () => {
84
+ expect(parseRangeFlag(" Today ", "from", NOW)).toBe(parseRangeFlag("today", "from", NOW));
85
+ });
86
+ });
87
+ describe("local-calendar arithmetic", () => {
88
+ it("rolls over a month boundary", () => {
89
+ const eom = new Date(2026, 7, 31, 9, 0, 0); // Aug 31
90
+ expect(local(parseRangeFlag("+1d", "from", eom))).toBe("2026-09-01 00:00");
91
+ });
92
+ it("rolls over a year boundary", () => {
93
+ const eoy = new Date(2026, 11, 31, 9, 0, 0); // Dec 31
94
+ expect(local(parseRangeFlag("+1d", "from", eoy))).toBe("2027-01-01 00:00");
95
+ });
96
+ it("lands on local midnight across a spring-forward DST transition", () => {
97
+ // 2026-03-08 is the US spring-forward date; that day is only 23h long, so
98
+ // millisecond addition would land on 01:00 rather than midnight.
99
+ const beforeDst = new Date(2026, 2, 7, 12, 0, 0);
100
+ expect(local(parseRangeFlag("+1d", "from", beforeDst))).toBe("2026-03-08 00:00");
101
+ expect(local(parseRangeFlag("+2d", "from", beforeDst))).toBe("2026-03-09 00:00");
102
+ });
103
+ it("lands on local midnight across a fall-back DST transition", () => {
104
+ // 2026-11-01 is the US fall-back date (25h long).
105
+ const beforeDst = new Date(2026, 9, 31, 12, 0, 0);
106
+ expect(local(parseRangeFlag("+1d", "from", beforeDst))).toBe("2026-11-01 00:00");
107
+ expect(local(parseRangeFlag("+2d", "from", beforeDst))).toBe("2026-11-02 00:00");
108
+ });
109
+ it("closes a date-only to edge at midnight even when that day is 23h long", () => {
110
+ expect(local(parseRangeFlag("2026-03-08", "to", NOW))).toBe("2026-03-09 00:00");
111
+ });
112
+ });
113
+ describe("startOfWeek", () => {
114
+ const wednesday = new Date(2026, 7, 26, 14, 30, 0); // Wed
115
+ const sunday = new Date(2026, 7, 30, 9, 0, 0);
116
+ const monday = new Date(2026, 7, 24, 9, 0, 0);
117
+ it("walks back to the configured start day (Monday)", () => {
118
+ expect(local(startOfWeek(wednesday, 1).toISOString())).toBe("2026-08-24 00:00");
119
+ expect(local(startOfWeek(sunday, 1).toISOString())).toBe("2026-08-24 00:00");
120
+ expect(local(startOfWeek(monday, 1).toISOString())).toBe("2026-08-24 00:00");
121
+ });
122
+ it("walks back to the configured start day (Sunday)", () => {
123
+ expect(local(startOfWeek(wednesday, 0).toISOString())).toBe("2026-08-23 00:00");
124
+ expect(local(startOfWeek(sunday, 0).toISOString())).toBe("2026-08-30 00:00");
125
+ expect(local(startOfWeek(monday, 0).toISOString())).toBe("2026-08-23 00:00");
126
+ });
127
+ it("walks back to the configured start day (Saturday)", () => {
128
+ expect(local(startOfWeek(wednesday, 6).toISOString())).toBe("2026-08-22 00:00");
129
+ expect(local(startOfWeek(sunday, 6).toISOString())).toBe("2026-08-29 00:00");
130
+ });
131
+ });
132
+ describe("resolveWindow — shortcuts", () => {
133
+ it("--today covers exactly today", () => {
134
+ const w = resolveWindow({ today: true }, { now: NOW });
135
+ expect(local(w.from)).toBe("2026-08-26 00:00");
136
+ expect(local(w.to)).toBe("2026-08-27 00:00");
137
+ });
138
+ it("--this-week spans seven days from the configured start day", () => {
139
+ for (const day of [0, 1, 6]) {
140
+ const w = resolveWindow({ thisWeek: true }, { now: NOW, weekStartDay: day });
141
+ expect(local(w.from)).toBe(local(startOfWeek(NOW, day).toISOString()));
142
+ expect(spanDays(w.from, w.to)).toBe(7);
143
+ }
144
+ });
145
+ it("--this-week defaults to Monday when no week start is supplied", () => {
146
+ const w = resolveWindow({ thisWeek: true }, { now: NOW });
147
+ expect(local(w.from)).toBe("2026-08-24 00:00");
148
+ expect(local(w.to)).toBe("2026-08-31 00:00");
149
+ });
150
+ it("--this-week still spans seven local days across a DST transition", () => {
151
+ const dstWeek = new Date(2026, 2, 10, 12, 0, 0); // week containing spring-forward
152
+ const w = resolveWindow({ thisWeek: true }, { now: dstWeek, weekStartDay: 1 });
153
+ expect(local(w.from)).toBe("2026-03-09 00:00");
154
+ expect(local(w.to)).toBe("2026-03-16 00:00");
155
+ });
156
+ it("rejects two shortcuts at once", () => {
157
+ expect(() => resolveWindow({ today: true, thisWeek: true }, { now: NOW })).toThrow(/Cannot combine --today and --this-week/);
158
+ });
159
+ it("rejects a shortcut combined with an explicit edge", () => {
160
+ expect(() => resolveWindow({ today: true, from: "2026-08-26" }, { now: NOW })).toThrow(/Cannot combine --today with --from/);
161
+ expect(() => resolveWindow({ thisWeek: true, to: "2026-08-26" }, { now: NOW })).toThrow(/Cannot combine --this-week with --to/);
162
+ });
163
+ });
164
+ describe("resolveWindow — empty windows and defaults", () => {
165
+ it("rejects a zero-width window rather than returning nothing", () => {
166
+ // The originating bug: this used to silently produce `count: 0`.
167
+ expect(() => resolveWindow({ from: "2026-08-26T00:00", to: "2026-08-26T00:00" }, { now: NOW })).toThrow(/Empty time range/);
168
+ });
169
+ it("rejects an inverted window", () => {
170
+ expect(() => resolveWindow({ from: "2026-08-26T10:00", to: "2026-08-26T09:00" }, { now: NOW })).toThrow(/is not before/);
171
+ });
172
+ it("accepts the date-only same-day window that used to be empty", () => {
173
+ const w = resolveWindow({ from: "2026-08-26", to: "2026-08-26" }, { now: NOW });
174
+ expect(spanDays(w.from, w.to)).toBe(1);
175
+ });
176
+ it("names the caller's own flag spellings in the error", () => {
177
+ expect(() => resolveWindow({ from: "2026-08-26T10:00", to: "2026-08-26T09:00" }, { now: NOW, flagNames: { from: "--since", to: "--until" } })).toThrow(/--since .* is not before --until/);
178
+ });
179
+ it("falls back to defaults for absent edges", () => {
180
+ const w = resolveWindow({ to: "2026-08-30" }, { now: NOW, defaults: { from: "2026-08-01T00:00:00.000Z", to: "2026-09-01T00:00:00.000Z" } });
181
+ expect(w.from).toBe("2026-08-01T00:00:00.000Z");
182
+ expect(local(w.to)).toBe("2026-08-31 00:00");
183
+ });
184
+ it("leaves both edges undefined when neither flag nor default is present", () => {
185
+ expect(resolveWindow({}, { now: NOW })).toEqual({ from: undefined, to: undefined });
186
+ });
187
+ it("skips the empty-window check when only one edge is set", () => {
188
+ const w = resolveWindow({ from: "2026-08-26" }, { now: NOW });
189
+ expect(w.to).toBeUndefined();
190
+ });
191
+ });
192
+ describe("parseDateishFlag is unchanged by the range layer", () => {
193
+ it("still resolves a date-only value to that day's local midnight", () => {
194
+ // `calendar create --start 2026-08-26` means midnight, NOT end of day.
195
+ expect(local(parseDateishFlag("2026-08-26"))).toBe("2026-08-26 00:00");
196
+ });
197
+ it("does not accept range tokens", () => {
198
+ expect(() => parseDateishFlag("today")).toThrow(AxiError);
199
+ });
200
+ });
201
+ describe("resolveWindow — suggestion targeting", () => {
202
+ it("suggests the shortcuts when the caller offers them", () => {
203
+ try {
204
+ resolveWindow({ from: "2026-08-26T10:00", to: "2026-08-26T09:00" }, { now: NOW });
205
+ expect.unreachable();
206
+ }
207
+ catch (err) {
208
+ expect(err.suggestions.join(" ")).toMatch(/--today/);
209
+ }
210
+ });
211
+ it("omits them for a command that has no such flags", () => {
212
+ // `drive activity` takes --since/--until only; suggesting --today there
213
+ // would be a dead-end (principles.md#no-dead-end-surfaces).
214
+ try {
215
+ resolveWindow({ from: "2026-08-26T10:00", to: "2026-08-26T09:00" }, { now: NOW, shortcuts: false, flagNames: { from: "--since", to: "--until" } });
216
+ expect.unreachable();
217
+ }
218
+ catch (err) {
219
+ expect(err.suggestions.join(" ")).not.toMatch(/--today/);
220
+ }
221
+ });
222
+ });
223
+ //# sourceMappingURL=dateish.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dateish.test.js","sourceRoot":"","sources":["../../../../src/commands/calendar/dateish.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export declare const EVENTS_HELP = "usage: gws-axi calendar events [flags]\nflags[8]:\n --calendar <id> Calendar to query (default: \"primary\")\n --from <iso> Earliest event start (default: now)\n --to <iso> Latest event start (default: 7 days from now)\n --limit <n> Max events to return (default: 100, max: 2500)\n --query <text> Full-text search across summary/description/location/attendees\n --single-events Expand recurring events into individual instances (default: true)\n --fields <list> Extra columns: status, organizer, location, attendees, description, htmlLink, hangoutLink, join_url, conference, conference_source\n --account <email> Account override when 2+ are configured\nexamples:\n gws-axi calendar events\n gws-axi calendar events --from 2026-04-20T00:00 --to 2026-04-21T00:00\n gws-axi calendar events --calendar team@jarv.us --limit 50\n gws-axi calendar events --query standup\n gws-axi calendar events --fields status,attendees,location\n gws-axi calendar events --fields join_url,conference\ntime formats:\n Timed events: ISO 8601 with offset \u2014 2026-04-20T14:00:00-04:00\n Local time: 2026-04-20T14:00 (interpreted as local tz)\n Date-only: 2026-04-20 (midnight local)\noutput note:\n The start/end columns use datetime (with offset) for timed events\n and date-only strings for all-day events. Agents parsing these\n columns must handle both formats.\ndefault columns:\n id, summary, start, end, my_response (your responseStatus if you're\n an attendee; blank for self-organized events with no attendee list),\n attachments (compact count; \"(N Docs)\" suffix when meeting notes /\n Gemini Notes Docs are attached \u2014 chase any with `calendar get <id>`\n to see file_ids and hand off to `docs read`).\n status column suppressed by default (most events are \"confirmed\" \u2014\n add --fields status to see cancelled/tentative).\nconferencing:\n --fields join_url provider-uniform join link \u2014 resolves from\n structured conferenceData (Meet + Calendar add-on\n Zoom/Teams/Webex), then hangoutLink, then a\n provider-anchored URL scraped from the description\n or location (externally-organized Teams/Zoom/Webex\n meetings carry no conferenceData).\n --fields conference provider label (Google Meet / Zoom / Microsoft\n Teams / Webex).\n --fields conference_source where join_url came from: conferenceData |\n hangoutLink | description | location. A\n description/location value means a best-effort\n scrape \u2014 verify before use.\n";
1
+ export declare const EVENTS_HELP = "usage: gws-axi calendar events [flags]\nflags[10]:\n --calendar <id> Calendar to query (default: \"primary\")\n --today Just today (local calendar day)\n --this-week The current week (starts on your Google Calendar's\n configured first day of the week)\n --from <when> Earliest event start (default: now)\n --to <when> Latest event start (default: 7 days from now)\n --limit <n> Max events to return (default: 100, max: 2500)\n --query <text> Full-text search across summary/description/location/attendees\n --single-events Expand recurring events into individual instances (default: true)\n --fields <list> Extra columns: status, organizer, location, attendees, description, htmlLink, hangoutLink, join_url, conference, conference_source\n --account <email> Account override when 2+ are configured\nexamples:\n gws-axi calendar events --today\n gws-axi calendar events --this-week\n gws-axi calendar events --from 2026-04-20 --to 2026-04-20 (all of Apr 20)\n gws-axi calendar events --from 2026-04-20T00:00 --to 2026-04-21T00:00\n gws-axi calendar events --calendar team@jarv.us --limit 50\n gws-axi calendar events --query standup\n gws-axi calendar events --fields status,attendees,location\n gws-axi calendar events --fields join_url,conference\ntime formats / ranges:\n Ranges are half-open: [--from, --to).\n Date-only: 2026-04-20 denotes the DAY \u2014 --from opens at its\n midnight, --to closes at the END of it. So\n --from 2026-04-20 --to 2026-04-20 is all of Apr 20.\n (--to 2026-04-20 and --to 2026-04-20T00:00 therefore\n differ by 24h \u2014 day granularity in, day boundary out.)\n Local time: 2026-04-20T14:00 (interpreted as local tz)\n With offset: 2026-04-20T14:00:00-04:00 (preserved as written)\n Tokens: now, today, tomorrow, yesterday, +Nd/-Nd, +Nw/-Nw\n (day precision), +Nh/-Nh (instant). Compose them:\n --from now --to today (rest of today)\n --from today --to +6d (next seven days)\noutput note:\n The start/end columns use datetime (with offset) for timed events\n and date-only strings for all-day events. Agents parsing these\n columns must handle both formats.\ndefault columns:\n id, summary, start, end, my_response (your responseStatus if you're\n an attendee; blank for self-organized events with no attendee list),\n attachments (compact count; \"(N Docs)\" suffix when meeting notes /\n Gemini Notes Docs are attached \u2014 chase any with `calendar get <id>`\n to see file_ids and hand off to `docs read`).\n status column suppressed by default (most events are \"confirmed\" \u2014\n add --fields status to see cancelled/tentative).\nconferencing:\n --fields join_url provider-uniform join link \u2014 resolves from\n structured conferenceData (Meet + Calendar add-on\n Zoom/Teams/Webex), then hangoutLink, then a\n provider-anchored URL scraped from the description\n or location (externally-organized Teams/Zoom/Webex\n meetings carry no conferenceData).\n --fields conference provider label (Google Meet / Zoom / Microsoft\n Teams / Webex).\n --fields conference_source where join_url came from: conferenceData |\n hangoutLink | description | location. A\n description/location value means a best-effort\n scrape \u2014 verify before use.\n";
2
2
  export declare function calendarEventsCommand(account: string, args: string[]): Promise<string>;
@@ -2,28 +2,43 @@ import { AxiError } from "axi-sdk-js";
2
2
  import { calendarClient, translateGoogleError } from "../../google/client.js";
3
3
  import { computed, field, mapEnum, pluck, renderListResponse, truncated, } from "../../output/index.js";
4
4
  import { extractConference, resolveJoinUrl } from "./conference.js";
5
- import { parseDateishFlag } from "./dateish.js";
5
+ import { resolveWindow, toLocalOffsetISO } from "./dateish.js";
6
+ import { resolveWeekStart } from "./week-start.js";
6
7
  export const EVENTS_HELP = `usage: gws-axi calendar events [flags]
7
- flags[8]:
8
+ flags[10]:
8
9
  --calendar <id> Calendar to query (default: "primary")
9
- --from <iso> Earliest event start (default: now)
10
- --to <iso> Latest event start (default: 7 days from now)
10
+ --today Just today (local calendar day)
11
+ --this-week The current week (starts on your Google Calendar's
12
+ configured first day of the week)
13
+ --from <when> Earliest event start (default: now)
14
+ --to <when> Latest event start (default: 7 days from now)
11
15
  --limit <n> Max events to return (default: 100, max: 2500)
12
16
  --query <text> Full-text search across summary/description/location/attendees
13
17
  --single-events Expand recurring events into individual instances (default: true)
14
18
  --fields <list> Extra columns: status, organizer, location, attendees, description, htmlLink, hangoutLink, join_url, conference, conference_source
15
19
  --account <email> Account override when 2+ are configured
16
20
  examples:
17
- gws-axi calendar events
21
+ gws-axi calendar events --today
22
+ gws-axi calendar events --this-week
23
+ gws-axi calendar events --from 2026-04-20 --to 2026-04-20 (all of Apr 20)
18
24
  gws-axi calendar events --from 2026-04-20T00:00 --to 2026-04-21T00:00
19
25
  gws-axi calendar events --calendar team@jarv.us --limit 50
20
26
  gws-axi calendar events --query standup
21
27
  gws-axi calendar events --fields status,attendees,location
22
28
  gws-axi calendar events --fields join_url,conference
23
- time formats:
24
- Timed events: ISO 8601 with offset — 2026-04-20T14:00:00-04:00
29
+ time formats / ranges:
30
+ Ranges are half-open: [--from, --to).
31
+ Date-only: 2026-04-20 denotes the DAY — --from opens at its
32
+ midnight, --to closes at the END of it. So
33
+ --from 2026-04-20 --to 2026-04-20 is all of Apr 20.
34
+ (--to 2026-04-20 and --to 2026-04-20T00:00 therefore
35
+ differ by 24h — day granularity in, day boundary out.)
25
36
  Local time: 2026-04-20T14:00 (interpreted as local tz)
26
- Date-only: 2026-04-20 (midnight local)
37
+ With offset: 2026-04-20T14:00:00-04:00 (preserved as written)
38
+ Tokens: now, today, tomorrow, yesterday, +Nd/-Nd, +Nw/-Nw
39
+ (day precision), +Nh/-Nh (instant). Compose them:
40
+ --from now --to today (rest of today)
41
+ --from today --to +6d (next seven days)
27
42
  output note:
28
43
  The start/end columns use datetime (with offset) for timed events
29
44
  and date-only strings for all-day events. Agents parsing these
@@ -53,8 +68,10 @@ conferencing:
53
68
  function parseEventsFlags(args) {
54
69
  const flags = {
55
70
  calendar: "primary",
56
- from: new Date().toISOString(),
57
- to: new Date(Date.now() + 7 * 24 * 3600 * 1000).toISOString(),
71
+ from: undefined,
72
+ to: undefined,
73
+ today: false,
74
+ thisWeek: false,
58
75
  limit: 100,
59
76
  query: undefined,
60
77
  singleEvents: true,
@@ -69,13 +86,19 @@ function parseEventsFlags(args) {
69
86
  i++;
70
87
  break;
71
88
  case "--from":
72
- flags.from = parseDateishFlag(next);
89
+ flags.from = next;
73
90
  i++;
74
91
  break;
75
92
  case "--to":
76
- flags.to = parseDateishFlag(next);
93
+ flags.to = next;
77
94
  i++;
78
95
  break;
96
+ case "--today":
97
+ flags.today = true;
98
+ break;
99
+ case "--this-week":
100
+ flags.thisWeek = true;
101
+ break;
79
102
  case "--limit":
80
103
  flags.limit = Math.min(2500, Math.max(1, parseInt(next, 10) || 100));
81
104
  i++;
@@ -216,11 +239,25 @@ function schemaWithExtras(extras) {
216
239
  }
217
240
  export async function calendarEventsCommand(account, args) {
218
241
  const flags = parseEventsFlags(args);
242
+ // Only a week shortcut needs the account's weekStart preference — keep every
243
+ // other invocation to a single round trip.
244
+ const weekStart = flags.thisWeek ? await resolveWeekStart(account) : undefined;
245
+ const now = new Date();
246
+ const window = resolveWindow(flags, {
247
+ defaults: {
248
+ from: now.toISOString(),
249
+ to: new Date(now.getTime() + 7 * 24 * 3600 * 1000).toISOString(),
250
+ },
251
+ weekStartDay: weekStart?.day,
252
+ now,
253
+ });
254
+ const from = window.from;
255
+ const to = window.to;
219
256
  const api = await calendarClient(account);
220
257
  const requestParams = {
221
258
  calendarId: flags.calendar,
222
- timeMin: flags.from,
223
- timeMax: flags.to,
259
+ timeMin: from,
260
+ timeMax: to,
224
261
  maxResults: flags.limit,
225
262
  singleEvents: flags.singleEvents,
226
263
  orderBy: flags.singleEvents ? "startTime" : undefined,
@@ -254,8 +291,11 @@ export async function calendarEventsCommand(account, args) {
254
291
  }
255
292
  const summary = {
256
293
  count: items.length,
257
- range: `${flags.from} → ${flags.to}`,
294
+ range: `${toLocalOffsetISO(from)} → ${toLocalOffsetISO(to)}`,
258
295
  };
296
+ if (weekStart) {
297
+ summary.week_start = `${weekStart.label} (${weekStart.source})`;
298
+ }
259
299
  if (flags.query) {
260
300
  summary.query = flags.query;
261
301
  }
@@ -290,7 +330,10 @@ export async function calendarEventsCommand(account, args) {
290
330
  if (flags.query) {
291
331
  suggestions.push(`Remove --query to show all events in the range, or try a broader search term`);
292
332
  }
293
- suggestions.push(`Broaden the time range with --from / --to (current: ${flags.from} → ${flags.to})`);
333
+ suggestions.push(`Broaden the time range with --from / --to (current: ${toLocalOffsetISO(from)} → ${toLocalOffsetISO(to)})`);
334
+ if (!flags.thisWeek) {
335
+ suggestions.push(`Try \`--this-week\` for the current week, or \`--today\` for just today`);
336
+ }
294
337
  if (flags.calendar === "primary") {
295
338
  suggestions.push(`Try a different calendar with \`--calendar <id>\` (list them with \`gws-axi calendar calendars\`)`);
296
339
  }