@robinthues/rt-claude-coach 0.1.2 → 0.3.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/dist/backup/dump.d.ts +17 -0
- package/dist/backup/dump.js +118 -0
- package/dist/backup/index.d.ts +11 -0
- package/dist/backup/index.js +202 -0
- package/dist/backup/lock.d.ts +25 -0
- package/dist/backup/lock.js +187 -0
- package/dist/backup/repo.d.ts +20 -0
- package/dist/backup/repo.js +70 -0
- package/dist/backup/restore.d.ts +33 -0
- package/dist/backup/restore.js +102 -0
- package/dist/calendar/dates.d.ts +24 -0
- package/dist/calendar/dates.js +57 -0
- package/dist/calendar/index.d.ts +3 -0
- package/dist/calendar/index.js +2 -0
- package/dist/calendar/parse.d.ts +19 -0
- package/dist/calendar/parse.js +74 -0
- package/dist/calendar/types.d.ts +70 -0
- package/dist/calendar/types.js +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +272 -5
- package/dist/db/client.js +4 -0
- package/dist/db/schema.sql +64 -0
- package/dist/db/sql.d.ts +4 -0
- package/dist/db/sql.js +10 -0
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.js +23 -17
- package/dist/recovery/dates.d.ts +12 -0
- package/dist/recovery/dates.js +31 -0
- package/dist/recovery/index.d.ts +4 -0
- package/dist/recovery/index.js +3 -0
- package/dist/recovery/parse.d.ts +21 -0
- package/dist/recovery/parse.js +128 -0
- package/dist/recovery/store.d.ts +14 -0
- package/dist/recovery/store.js +108 -0
- package/dist/recovery/types.d.ts +50 -0
- package/dist/recovery/types.js +1 -0
- package/dist/strava/store.d.ts +2 -1
- package/dist/strava/store.js +2 -5
- package/dist/viewer/lib/completion.d.ts +5 -0
- package/dist/viewer/lib/completion.js +15 -0
- package/dist/viewer/stores/plan.d.ts +0 -1
- package/dist/viewer/stores/plan.js +6 -7
- package/package.json +1 -1
- package/templates/plan-viewer.html +20 -20
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rebuild a database from `schemaSql` plus a dump produced by `dumpDatabase`.
|
|
3
|
+
*
|
|
4
|
+
* Refuses to touch an existing file: restoring over live data would be the
|
|
5
|
+
* exact accident this whole feature exists to protect against.
|
|
6
|
+
*
|
|
7
|
+
* A restore happens once every few years, at the worst possible moment: the
|
|
8
|
+
* original machine is gone and the user is rebuilding from nothing. If schema
|
|
9
|
+
* creation or dump replay fails partway through, we must not leave a partial
|
|
10
|
+
* file behind — a half-built database at `dbPath` would make the *next*
|
|
11
|
+
* attempt fail with "already exists" and send the user hunting for data they
|
|
12
|
+
* never had, instead of just letting them retry. So on any failure after the
|
|
13
|
+
* file is created, we close it, delete it (this call is the only thing that
|
|
14
|
+
* could have created it, since the existsSync guard above already refused to
|
|
15
|
+
* run otherwise), and rethrow the original error — never a cleanup error.
|
|
16
|
+
*/
|
|
17
|
+
export declare function restoreDatabase(dbPath: string, dumpSql: string, schemaSql: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Move `dbPath` to `<dbPath>.bak`, replacing an older .bak, and return the
|
|
20
|
+
* new path. Its journal and WAL files move with it under the matching .bak
|
|
21
|
+
* names: left behind, SQLite could apply a stale journal to the restored
|
|
22
|
+
* database, while next to the .bak it still belongs to the data it came
|
|
23
|
+
* from. Sidecars of the older .bak are removed so they cannot be applied to
|
|
24
|
+
* the wrong file.
|
|
25
|
+
*/
|
|
26
|
+
export declare function moveAsideDatabase(dbPath: string): string;
|
|
27
|
+
/** The `restore-db` command. Returns the process exit code. */
|
|
28
|
+
export declare function runRestore(options: {
|
|
29
|
+
dbPath: string;
|
|
30
|
+
dumpPath: string;
|
|
31
|
+
schemaPath: string;
|
|
32
|
+
force: boolean;
|
|
33
|
+
}): number;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { existsSync, readFileSync, renameSync, rmSync, unlinkSync } from "fs";
|
|
3
|
+
import { log } from "../lib/logging.js";
|
|
4
|
+
/**
|
|
5
|
+
* Rebuild a database from `schemaSql` plus a dump produced by `dumpDatabase`.
|
|
6
|
+
*
|
|
7
|
+
* Refuses to touch an existing file: restoring over live data would be the
|
|
8
|
+
* exact accident this whole feature exists to protect against.
|
|
9
|
+
*
|
|
10
|
+
* A restore happens once every few years, at the worst possible moment: the
|
|
11
|
+
* original machine is gone and the user is rebuilding from nothing. If schema
|
|
12
|
+
* creation or dump replay fails partway through, we must not leave a partial
|
|
13
|
+
* file behind — a half-built database at `dbPath` would make the *next*
|
|
14
|
+
* attempt fail with "already exists" and send the user hunting for data they
|
|
15
|
+
* never had, instead of just letting them retry. So on any failure after the
|
|
16
|
+
* file is created, we close it, delete it (this call is the only thing that
|
|
17
|
+
* could have created it, since the existsSync guard above already refused to
|
|
18
|
+
* run otherwise), and rethrow the original error — never a cleanup error.
|
|
19
|
+
*/
|
|
20
|
+
export function restoreDatabase(dbPath, dumpSql, schemaSql) {
|
|
21
|
+
if (existsSync(dbPath)) {
|
|
22
|
+
throw new Error(`${dbPath} already exists. Move it aside or pass --force.`);
|
|
23
|
+
}
|
|
24
|
+
const db = new DatabaseSync(dbPath);
|
|
25
|
+
try {
|
|
26
|
+
db.exec(schemaSql);
|
|
27
|
+
db.exec("BEGIN");
|
|
28
|
+
try {
|
|
29
|
+
if (dumpSql.trim().length > 0)
|
|
30
|
+
db.exec(dumpSql);
|
|
31
|
+
db.exec("COMMIT");
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
db.exec("ROLLBACK");
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
try {
|
|
40
|
+
db.close();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Original error is what the caller needs to see; ignore close failures here.
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
unlinkSync(dbPath);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Original error still takes priority over any cleanup failure.
|
|
50
|
+
}
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
db.close();
|
|
54
|
+
}
|
|
55
|
+
/** Files SQLite keeps next to a database, named `<db>-journal` and so on. */
|
|
56
|
+
const SIDECARS = ["-journal", "-wal", "-shm"];
|
|
57
|
+
/**
|
|
58
|
+
* Move `dbPath` to `<dbPath>.bak`, replacing an older .bak, and return the
|
|
59
|
+
* new path. Its journal and WAL files move with it under the matching .bak
|
|
60
|
+
* names: left behind, SQLite could apply a stale journal to the restored
|
|
61
|
+
* database, while next to the .bak it still belongs to the data it came
|
|
62
|
+
* from. Sidecars of the older .bak are removed so they cannot be applied to
|
|
63
|
+
* the wrong file.
|
|
64
|
+
*/
|
|
65
|
+
export function moveAsideDatabase(dbPath) {
|
|
66
|
+
const bakPath = `${dbPath}.bak`;
|
|
67
|
+
for (const suffix of SIDECARS)
|
|
68
|
+
rmSync(`${bakPath}${suffix}`, { force: true });
|
|
69
|
+
renameSync(dbPath, bakPath);
|
|
70
|
+
for (const suffix of SIDECARS) {
|
|
71
|
+
if (existsSync(`${dbPath}${suffix}`))
|
|
72
|
+
renameSync(`${dbPath}${suffix}`, `${bakPath}${suffix}`);
|
|
73
|
+
}
|
|
74
|
+
return bakPath;
|
|
75
|
+
}
|
|
76
|
+
/** The `restore-db` command. Returns the process exit code. */
|
|
77
|
+
export function runRestore(options) {
|
|
78
|
+
const { dbPath, dumpPath, schemaPath } = options;
|
|
79
|
+
if (!existsSync(dumpPath)) {
|
|
80
|
+
log.error(`No dump at ${dumpPath}`);
|
|
81
|
+
return 1;
|
|
82
|
+
}
|
|
83
|
+
if (existsSync(dbPath)) {
|
|
84
|
+
if (!options.force) {
|
|
85
|
+
log.error(`${dbPath} already exists. Move it aside or pass --force.`);
|
|
86
|
+
return 1;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const backupPath = existsSync(dbPath) ? moveAsideDatabase(dbPath) : undefined;
|
|
90
|
+
if (backupPath)
|
|
91
|
+
log.info(`Moved the existing database to ${backupPath}`);
|
|
92
|
+
try {
|
|
93
|
+
restoreDatabase(dbPath, readFileSync(dumpPath, "utf-8"), readFileSync(schemaPath, "utf-8"));
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
const kept = backupPath ? ` Your previous database is at ${backupPath}.` : "";
|
|
97
|
+
log.error(`Restore failed: ${error.message}.${kept}`);
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
100
|
+
log.success(`Restored ${dbPath} from ${dumpPath}`);
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { RawEvent } from "./types.js";
|
|
2
|
+
/** True when an event is all-day (uses `date`, not `dateTime`). */
|
|
3
|
+
export declare function isAllDay(event: RawEvent): boolean;
|
|
4
|
+
/** The YYYY-MM-DD portion of an all-day time value. */
|
|
5
|
+
export declare function allDayDate(value: string): string;
|
|
6
|
+
/** Advance a YYYY-MM-DD string by one day, UTC-safe (no DST drift). */
|
|
7
|
+
export declare function addDay(date: string): string;
|
|
8
|
+
/** True when a string is a YYYY-MM-DD calendar date. */
|
|
9
|
+
export declare function isIsoDate(value: string): boolean;
|
|
10
|
+
/** Enumerate dates from start (inclusive) to end (exclusive). */
|
|
11
|
+
export declare function datesInRange(startInclusive: string, endExclusive: string): string[];
|
|
12
|
+
/** The weekday name for a YYYY-MM-DD date. */
|
|
13
|
+
export declare function weekdayOf(date: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Some ICU builds render midnight on a 24-hour clock as "24"; normalise it to
|
|
16
|
+
* "00" so a wall-clock hour is always in [00, 23]. Defensive against ICU
|
|
17
|
+
* variation across Node runtimes — inert where the clock is already h23.
|
|
18
|
+
*/
|
|
19
|
+
export declare function normalizeHour(hour: string): string;
|
|
20
|
+
/** Resolve an RFC3339 timed value to wall-clock date + HH:MM in the target zone. */
|
|
21
|
+
export declare function resolveWallClock(dateTime: string, timeZone: string): {
|
|
22
|
+
date: string;
|
|
23
|
+
time: string;
|
|
24
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** True when an event is all-day (uses `date`, not `dateTime`). */
|
|
2
|
+
export function isAllDay(event) {
|
|
3
|
+
return Boolean(event.start?.date) && !event.start?.dateTime;
|
|
4
|
+
}
|
|
5
|
+
/** The YYYY-MM-DD portion of an all-day time value. */
|
|
6
|
+
export function allDayDate(value) {
|
|
7
|
+
return value.slice(0, 10);
|
|
8
|
+
}
|
|
9
|
+
/** Advance a YYYY-MM-DD string by one day, UTC-safe (no DST drift). */
|
|
10
|
+
export function addDay(date) {
|
|
11
|
+
const [y, m, d] = date.split("-").map(Number);
|
|
12
|
+
return new Date(Date.UTC(y, m - 1, d + 1)).toISOString().slice(0, 10);
|
|
13
|
+
}
|
|
14
|
+
/** True when a string is a YYYY-MM-DD calendar date. */
|
|
15
|
+
export function isIsoDate(value) {
|
|
16
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(value);
|
|
17
|
+
}
|
|
18
|
+
/** Enumerate dates from start (inclusive) to end (exclusive). */
|
|
19
|
+
export function datesInRange(startInclusive, endExclusive) {
|
|
20
|
+
const out = [];
|
|
21
|
+
let cur = startInclusive;
|
|
22
|
+
while (cur < endExclusive) {
|
|
23
|
+
out.push(cur);
|
|
24
|
+
cur = addDay(cur);
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
const WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
29
|
+
/** The weekday name for a YYYY-MM-DD date. */
|
|
30
|
+
export function weekdayOf(date) {
|
|
31
|
+
const [y, m, d] = date.split("-").map(Number);
|
|
32
|
+
return WEEKDAYS[new Date(Date.UTC(y, m - 1, d)).getUTCDay()];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Some ICU builds render midnight on a 24-hour clock as "24"; normalise it to
|
|
36
|
+
* "00" so a wall-clock hour is always in [00, 23]. Defensive against ICU
|
|
37
|
+
* variation across Node runtimes — inert where the clock is already h23.
|
|
38
|
+
*/
|
|
39
|
+
export function normalizeHour(hour) {
|
|
40
|
+
return hour === "24" ? "00" : hour;
|
|
41
|
+
}
|
|
42
|
+
/** Resolve an RFC3339 timed value to wall-clock date + HH:MM in the target zone. */
|
|
43
|
+
export function resolveWallClock(dateTime, timeZone) {
|
|
44
|
+
const dt = new Date(dateTime);
|
|
45
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
46
|
+
timeZone,
|
|
47
|
+
year: "numeric",
|
|
48
|
+
month: "2-digit",
|
|
49
|
+
day: "2-digit",
|
|
50
|
+
hour: "2-digit",
|
|
51
|
+
minute: "2-digit",
|
|
52
|
+
hour12: false,
|
|
53
|
+
}).formatToParts(dt);
|
|
54
|
+
const get = (type) => parts.find((p) => p.type === type).value;
|
|
55
|
+
const hour = normalizeHour(get("hour"));
|
|
56
|
+
return { date: `${get("year")}-${get("month")}-${get("day")}`, time: `${hour}:${get("minute")}` };
|
|
57
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { isCancelled, isFree, isNonBlockingType, isDeclined, isBusy, eventTitle, buildAvailability, } from "./parse.js";
|
|
2
|
+
export { isAllDay, allDayDate, addDay, isIsoDate, datesInRange, weekdayOf, resolveWallClock, } from "./dates.js";
|
|
3
|
+
export type { RawEvent, RawEventTime, RawAttendee, CalendarEventsResponse, CalendarInput, AvailabilityInput, BusyBlock, AllDayEvent, DayAvailability, AvailabilityOutput, } from "./types.js";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RawEvent, AvailabilityInput, AvailabilityOutput } from "./types.js";
|
|
2
|
+
/** Google marks deleted/cancelled instances with status "cancelled". */
|
|
3
|
+
export declare function isCancelled(e: RawEvent): boolean;
|
|
4
|
+
/** The athlete set themselves free during this event, so it does not block training. */
|
|
5
|
+
export declare function isFree(e: RawEvent): boolean;
|
|
6
|
+
/** Event types that never represent a real time commitment. */
|
|
7
|
+
export declare function isNonBlockingType(e: RawEvent): boolean;
|
|
8
|
+
/** The athlete declined this invitation. */
|
|
9
|
+
export declare function isDeclined(e: RawEvent): boolean;
|
|
10
|
+
/** Whether this event should count against the athlete's availability. */
|
|
11
|
+
export declare function isBusy(e: RawEvent): boolean;
|
|
12
|
+
/** A display title, with a placeholder for untitled events. */
|
|
13
|
+
export declare function eventTitle(e: RawEvent): string;
|
|
14
|
+
/**
|
|
15
|
+
* Normalise verbatim `list_events` responses into a clean per-day picture of
|
|
16
|
+
* busy blocks and all-day events. Every date in the window appears, so a clear
|
|
17
|
+
* day reads as clear rather than missing. All-day `end` dates are exclusive.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildAvailability(input: AvailabilityInput): AvailabilityOutput;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { isAllDay, allDayDate, addDay, datesInRange, weekdayOf, resolveWallClock, } from "./dates.js";
|
|
2
|
+
/** Google marks deleted/cancelled instances with status "cancelled". */
|
|
3
|
+
export function isCancelled(e) {
|
|
4
|
+
return e.status === "cancelled";
|
|
5
|
+
}
|
|
6
|
+
/** The athlete set themselves free during this event, so it does not block training. */
|
|
7
|
+
export function isFree(e) {
|
|
8
|
+
return e.transparency === "transparent" || e.availability === "AVAILABILITY_FREE";
|
|
9
|
+
}
|
|
10
|
+
const NON_BLOCKING_TYPES = new Set(["BIRTHDAY", "WORKING_LOCATION"]);
|
|
11
|
+
/** Event types that never represent a real time commitment. */
|
|
12
|
+
export function isNonBlockingType(e) {
|
|
13
|
+
return e.eventType !== undefined && NON_BLOCKING_TYPES.has(e.eventType);
|
|
14
|
+
}
|
|
15
|
+
/** The athlete declined this invitation. */
|
|
16
|
+
export function isDeclined(e) {
|
|
17
|
+
const self = e.attendees?.find((a) => a.self);
|
|
18
|
+
return self?.responseStatus === "declined";
|
|
19
|
+
}
|
|
20
|
+
/** Whether this event should count against the athlete's availability. */
|
|
21
|
+
export function isBusy(e) {
|
|
22
|
+
return !isCancelled(e) && !isFree(e) && !isNonBlockingType(e) && !isDeclined(e);
|
|
23
|
+
}
|
|
24
|
+
/** A display title, with a placeholder for untitled events. */
|
|
25
|
+
export function eventTitle(e) {
|
|
26
|
+
const title = (e.summary ?? "").trim();
|
|
27
|
+
return title.length > 0 ? title : "(no title)";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Normalise verbatim `list_events` responses into a clean per-day picture of
|
|
31
|
+
* busy blocks and all-day events. Every date in the window appears, so a clear
|
|
32
|
+
* day reads as clear rather than missing. All-day `end` dates are exclusive.
|
|
33
|
+
*/
|
|
34
|
+
export function buildAvailability(input) {
|
|
35
|
+
const { timeZone, window } = input;
|
|
36
|
+
const days = new Map();
|
|
37
|
+
for (const date of datesInRange(window.start, window.end)) {
|
|
38
|
+
days.set(date, { date, weekday: weekdayOf(date), allDayEvents: [], busyBlocks: [] });
|
|
39
|
+
}
|
|
40
|
+
for (const cal of input.calendars) {
|
|
41
|
+
const events = cal.responses.flatMap((r) => r.events ?? []);
|
|
42
|
+
for (const event of events) {
|
|
43
|
+
if (!isBusy(event))
|
|
44
|
+
continue;
|
|
45
|
+
const title = eventTitle(event);
|
|
46
|
+
if (isAllDay(event)) {
|
|
47
|
+
const startDate = allDayDate(event.start.date);
|
|
48
|
+
const endExclusive = event.end?.date ? allDayDate(event.end.date) : addDay(startDate);
|
|
49
|
+
for (const date of datesInRange(startDate, endExclusive)) {
|
|
50
|
+
days.get(date)?.allDayEvents.push({ title, calendar: cal.summary });
|
|
51
|
+
}
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (event.start?.dateTime && event.end?.dateTime) {
|
|
55
|
+
const start = resolveWallClock(event.start.dateTime, timeZone);
|
|
56
|
+
const end = resolveWallClock(event.end.dateTime, timeZone);
|
|
57
|
+
// A timed block is attributed to its start day; multi-day timed events are vanishingly rare.
|
|
58
|
+
days.get(start.date)?.busyBlocks.push({
|
|
59
|
+
start: start.time,
|
|
60
|
+
end: end.time,
|
|
61
|
+
title,
|
|
62
|
+
calendar: cal.summary,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const day of days.values()) {
|
|
68
|
+
day.busyBlocks.sort((a, b) => a.start.localeCompare(b.start) || a.end.localeCompare(b.end));
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
window: { start: window.start, end: window.end, timeZone },
|
|
72
|
+
days: [...days.values()],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** One time endpoint of a calendar event: all-day (`date`) or timed (`dateTime`). */
|
|
2
|
+
export interface RawEventTime {
|
|
3
|
+
/** All-day marker, e.g. "2026-07-25T00:00:00Z" or "2026-07-25". */
|
|
4
|
+
date?: string;
|
|
5
|
+
/** Timed RFC3339 value with offset, e.g. "2026-08-04T18:00:00+02:00". */
|
|
6
|
+
dateTime?: string;
|
|
7
|
+
timeZone?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface RawAttendee {
|
|
10
|
+
email?: string;
|
|
11
|
+
self?: boolean;
|
|
12
|
+
responseStatus?: string;
|
|
13
|
+
}
|
|
14
|
+
/** A single event from a `list_events` response. All fields optional. */
|
|
15
|
+
export interface RawEvent {
|
|
16
|
+
summary?: string;
|
|
17
|
+
status?: string;
|
|
18
|
+
transparency?: string;
|
|
19
|
+
availability?: string;
|
|
20
|
+
eventType?: string;
|
|
21
|
+
start?: RawEventTime;
|
|
22
|
+
end?: RawEventTime;
|
|
23
|
+
attendees?: RawAttendee[];
|
|
24
|
+
}
|
|
25
|
+
/** One verbatim `list_events` page response. */
|
|
26
|
+
export interface CalendarEventsResponse {
|
|
27
|
+
events?: RawEvent[];
|
|
28
|
+
summary?: string;
|
|
29
|
+
timeZone?: string;
|
|
30
|
+
nextPageToken?: string;
|
|
31
|
+
}
|
|
32
|
+
/** One selected calendar's verbatim page responses. */
|
|
33
|
+
export interface CalendarInput {
|
|
34
|
+
summary: string;
|
|
35
|
+
responses: CalendarEventsResponse[];
|
|
36
|
+
}
|
|
37
|
+
/** The wrapper file the skill assembles and passes to the CLI. */
|
|
38
|
+
export interface AvailabilityInput {
|
|
39
|
+
timeZone: string;
|
|
40
|
+
/** Window covered; `end` is exclusive, both YYYY-MM-DD. */
|
|
41
|
+
window: {
|
|
42
|
+
start: string;
|
|
43
|
+
end: string;
|
|
44
|
+
};
|
|
45
|
+
calendars: CalendarInput[];
|
|
46
|
+
}
|
|
47
|
+
export interface BusyBlock {
|
|
48
|
+
start: string;
|
|
49
|
+
end: string;
|
|
50
|
+
title: string;
|
|
51
|
+
calendar: string;
|
|
52
|
+
}
|
|
53
|
+
export interface AllDayEvent {
|
|
54
|
+
title: string;
|
|
55
|
+
calendar: string;
|
|
56
|
+
}
|
|
57
|
+
export interface DayAvailability {
|
|
58
|
+
date: string;
|
|
59
|
+
weekday: string;
|
|
60
|
+
allDayEvents: AllDayEvent[];
|
|
61
|
+
busyBlocks: BusyBlock[];
|
|
62
|
+
}
|
|
63
|
+
export interface AvailabilityOutput {
|
|
64
|
+
window: {
|
|
65
|
+
start: string;
|
|
66
|
+
end: string;
|
|
67
|
+
timeZone: string;
|
|
68
|
+
};
|
|
69
|
+
days: DayAvailability[];
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/cli.d.ts
CHANGED
|
@@ -1 +1,10 @@
|
|
|
1
|
+
interface BackupArgs {
|
|
2
|
+
command: "backup";
|
|
3
|
+
init: boolean;
|
|
4
|
+
push: boolean;
|
|
5
|
+
quiet: boolean;
|
|
6
|
+
repo: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const DEFAULT_BACKUP_REPO = "RobinThues/claude-coach-data";
|
|
9
|
+
export declare function parseBackupArgs(args: string[]): BackupArgs;
|
|
1
10
|
export {};
|