@yakirmar/mcp-toggl 1.1.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/utils.js ADDED
@@ -0,0 +1,439 @@
1
+ // Convert seconds to hours with decimal precision
2
+ export function secondsToHours(seconds) {
3
+ return Math.round((seconds / 3600) * 100) / 100;
4
+ }
5
+ // Format duration for display
6
+ export function formatDuration(seconds) {
7
+ const hours = Math.floor(seconds / 3600);
8
+ const minutes = Math.floor((seconds % 3600) / 60);
9
+ const secs = seconds % 60;
10
+ if (hours > 0) {
11
+ return `${hours}h ${minutes}m`;
12
+ }
13
+ else if (minutes > 0) {
14
+ return `${minutes}m ${secs}s`;
15
+ }
16
+ else {
17
+ return `${secs}s`;
18
+ }
19
+ }
20
+ // Parse ISO date to local date string
21
+ export function formatDate(dateStr) {
22
+ const date = new Date(dateStr);
23
+ return date.toLocaleDateString('en-US', {
24
+ weekday: 'short',
25
+ year: 'numeric',
26
+ month: 'short',
27
+ day: 'numeric',
28
+ });
29
+ }
30
+ // Format a Date as YYYY-MM-DD in the host's local timezone.
31
+ export function toLocalYMD(date) {
32
+ const year = date.getFullYear();
33
+ const month = String(date.getMonth() + 1).padStart(2, '0');
34
+ const day = String(date.getDate()).padStart(2, '0');
35
+ return `${year}-${month}-${day}`;
36
+ }
37
+ // Parse a YYYY-MM-DD string into a Date at local midnight.
38
+ export function parseLocalYMD(value) {
39
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
40
+ if (!match) {
41
+ throw new Error(`Invalid date format: ${value}. Expected YYYY-MM-DD.`);
42
+ }
43
+ const [, yearStr, monthStr, dayStr] = match;
44
+ const year = Number(yearStr);
45
+ const month = Number(monthStr);
46
+ const day = Number(dayStr);
47
+ const date = new Date(year, month - 1, day, 0, 0, 0, 0);
48
+ if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
49
+ throw new Error(`Invalid calendar date: ${value}`);
50
+ }
51
+ return date;
52
+ }
53
+ function parseDateTime(value, field) {
54
+ if (typeof value !== 'string' || value.trim() === '') {
55
+ throw new Error(`${field} must be an ISO 8601 datetime string`);
56
+ }
57
+ const trimmed = value.trim();
58
+ // A bare YYYY-MM-DD is treated as local midnight (consistent with the rest of
59
+ // the codebase). `new Date('YYYY-MM-DD')` would parse it as UTC midnight, which
60
+ // shifts the entry a day in timezones behind/ahead of UTC.
61
+ if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
62
+ return parseLocalYMD(trimmed);
63
+ }
64
+ // Datetimes with an offset (Z or ±hh:mm) are absolute; datetimes without one
65
+ // are interpreted in the host's local timezone by the Date constructor.
66
+ const date = new Date(trimmed);
67
+ if (Number.isNaN(date.getTime())) {
68
+ throw new Error(`Invalid ${field}: ${value}. Expected an ISO 8601 datetime ` +
69
+ `(e.g. 2026-07-11T09:00:00Z or 2026-07-11T09:00:00+03:00).`);
70
+ }
71
+ return date;
72
+ }
73
+ // Build a completed time entry interval from a start plus exactly one of an end
74
+ // time or a length in minutes. Returns ISO 8601 start/stop and positive duration
75
+ // seconds, matching what Toggl's create time entry endpoint expects.
76
+ export function buildTimeEntryInterval(args) {
77
+ const start = parseDateTime(args.start, 'start');
78
+ const hasEnd = args.end !== undefined && args.end !== null && args.end !== '';
79
+ const hasDuration = args.duration_minutes !== undefined && args.duration_minutes !== null;
80
+ if (hasEnd === hasDuration) {
81
+ throw new Error('Provide exactly one of end or duration_minutes.');
82
+ }
83
+ let stop;
84
+ if (hasEnd) {
85
+ stop = parseDateTime(args.end, 'end');
86
+ }
87
+ else {
88
+ const minutes = args.duration_minutes;
89
+ if (typeof minutes !== 'number' || !Number.isFinite(minutes) || minutes <= 0) {
90
+ throw new Error('duration_minutes must be a positive number.');
91
+ }
92
+ stop = new Date(start.getTime() + minutes * 60_000);
93
+ }
94
+ const duration = Math.round((stop.getTime() - start.getTime()) / 1000);
95
+ if (duration <= 0) {
96
+ throw new Error('end must be after start.');
97
+ }
98
+ return {
99
+ start: start.toISOString(),
100
+ stop: stop.toISOString(),
101
+ duration,
102
+ };
103
+ }
104
+ function includesCI(haystack, needle) {
105
+ return (haystack ?? '').toLowerCase().includes(needle.toLowerCase());
106
+ }
107
+ function validateDurationBound(value, field) {
108
+ if (value === undefined)
109
+ return undefined;
110
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
111
+ throw new Error(`${field} must be a non-negative number.`);
112
+ }
113
+ return value * 60;
114
+ }
115
+ // Filter hydrated entries by the given criteria. Pure and side-effect free so
116
+ // the search logic can be unit tested independently of the Toggl API.
117
+ export function filterHydratedEntries(entries, criteria) {
118
+ const startAfter = criteria.start_after
119
+ ? parseDateTime(criteria.start_after, 'start_after').getTime()
120
+ : undefined;
121
+ const startBefore = criteria.start_before
122
+ ? parseDateTime(criteria.start_before, 'start_before').getTime()
123
+ : undefined;
124
+ const minSeconds = validateDurationBound(criteria.min_duration_minutes, 'min_duration_minutes');
125
+ const maxSeconds = validateDurationBound(criteria.max_duration_minutes, 'max_duration_minutes');
126
+ return entries.filter((entry) => {
127
+ if (criteria.description && !includesCI(entry.description, criteria.description))
128
+ return false;
129
+ if (criteria.project_name && !includesCI(entry.project_name, criteria.project_name)) {
130
+ return false;
131
+ }
132
+ if (criteria.client_name && !includesCI(entry.client_name, criteria.client_name))
133
+ return false;
134
+ if (criteria.project_id !== undefined && entry.project_id !== criteria.project_id)
135
+ return false;
136
+ if (criteria.client_id !== undefined && entry.client_id !== criteria.client_id)
137
+ return false;
138
+ if (criteria.workspace_id !== undefined && entry.workspace_id !== criteria.workspace_id) {
139
+ return false;
140
+ }
141
+ if (criteria.billable !== undefined && Boolean(entry.billable) !== criteria.billable) {
142
+ return false;
143
+ }
144
+ if (criteria.tag) {
145
+ const names = entry.tag_names ?? entry.tags ?? [];
146
+ if (!names.some((name) => includesCI(name, criteria.tag)))
147
+ return false;
148
+ }
149
+ if (startAfter !== undefined || startBefore !== undefined) {
150
+ const startMs = new Date(entry.start).getTime();
151
+ if (startAfter !== undefined && startMs < startAfter)
152
+ return false;
153
+ if (startBefore !== undefined && startMs > startBefore)
154
+ return false;
155
+ }
156
+ if (minSeconds !== undefined && entry.duration_seconds < minSeconds)
157
+ return false;
158
+ if (maxSeconds !== undefined && entry.duration_seconds > maxSeconds)
159
+ return false;
160
+ return true;
161
+ });
162
+ }
163
+ export function isDatePeriod(value) {
164
+ return (value === 'today' ||
165
+ value === 'yesterday' ||
166
+ value === 'week' ||
167
+ value === 'lastWeek' ||
168
+ value === 'month' ||
169
+ value === 'lastMonth');
170
+ }
171
+ export function localDateRangeFromArgs(args) {
172
+ if (args?.period !== undefined) {
173
+ if (!isDatePeriod(args.period)) {
174
+ throw new Error(`Invalid period: ${String(args.period)}. Must be one of: today, yesterday, week, lastWeek, month, lastMonth`);
175
+ }
176
+ return getDateRange(args.period);
177
+ }
178
+ const startValue = args?.start_date;
179
+ const endValue = args?.end_date;
180
+ if (startValue === undefined && endValue === undefined) {
181
+ return null;
182
+ }
183
+ if (startValue !== undefined && typeof startValue !== 'string') {
184
+ throw new Error('start_date must be a YYYY-MM-DD string');
185
+ }
186
+ if (endValue !== undefined && typeof endValue !== 'string') {
187
+ throw new Error('end_date must be a YYYY-MM-DD string');
188
+ }
189
+ const start = startValue ? parseLocalYMD(startValue) : null;
190
+ const end = endValue ? parseLocalYMD(endValue) : null;
191
+ if (end) {
192
+ end.setDate(end.getDate() + 1);
193
+ }
194
+ if (start && end && start >= end) {
195
+ throw new Error('start_date must be before or equal to end_date');
196
+ }
197
+ return { start, end };
198
+ }
199
+ // Get date range for various periods
200
+ export function getDateRange(period) {
201
+ const today = new Date();
202
+ today.setHours(0, 0, 0, 0);
203
+ switch (period) {
204
+ case 'today': {
205
+ const tomorrow = new Date(today);
206
+ tomorrow.setDate(tomorrow.getDate() + 1);
207
+ return { start: today, end: tomorrow };
208
+ }
209
+ case 'yesterday': {
210
+ const yesterday = new Date(today);
211
+ yesterday.setDate(yesterday.getDate() - 1);
212
+ return { start: yesterday, end: today };
213
+ }
214
+ case 'week': {
215
+ const dayOfWeek = today.getDay();
216
+ const diff = today.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1);
217
+ const monday = new Date(today);
218
+ monday.setDate(diff);
219
+ const nextMonday = new Date(monday);
220
+ nextMonday.setDate(nextMonday.getDate() + 7);
221
+ return { start: monday, end: nextMonday };
222
+ }
223
+ case 'lastWeek': {
224
+ const dayOfWeek = today.getDay();
225
+ const diff = today.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1) - 7;
226
+ const monday = new Date(today);
227
+ monday.setDate(diff);
228
+ const nextMonday = new Date(monday);
229
+ nextMonday.setDate(nextMonday.getDate() + 7);
230
+ return { start: monday, end: nextMonday };
231
+ }
232
+ case 'month': {
233
+ const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
234
+ const firstDayNextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
235
+ return { start: firstDay, end: firstDayNextMonth };
236
+ }
237
+ case 'lastMonth': {
238
+ const firstDay = new Date(today.getFullYear(), today.getMonth() - 1, 1);
239
+ const firstDayNextMonth = new Date(today.getFullYear(), today.getMonth(), 1);
240
+ return { start: firstDay, end: firstDayNextMonth };
241
+ }
242
+ }
243
+ }
244
+ // Group time entries by date
245
+ export function groupEntriesByDate(entries) {
246
+ const grouped = new Map();
247
+ entries.forEach((entry) => {
248
+ const date = toLocalYMD(new Date(entry.start));
249
+ if (!grouped.has(date)) {
250
+ grouped.set(date, []);
251
+ }
252
+ grouped.get(date).push(entry);
253
+ });
254
+ return grouped;
255
+ }
256
+ // Group time entries by project
257
+ export function groupEntriesByProject(entries) {
258
+ const grouped = new Map();
259
+ entries.forEach((entry) => {
260
+ const key = entry.project_name || 'No Project';
261
+ if (!grouped.has(key)) {
262
+ grouped.set(key, []);
263
+ }
264
+ grouped.get(key).push(entry);
265
+ });
266
+ return grouped;
267
+ }
268
+ // Group time entries by workspace
269
+ export function groupEntriesByWorkspace(entries) {
270
+ const grouped = new Map();
271
+ entries.forEach((entry) => {
272
+ const key = entry.workspace_name;
273
+ if (!grouped.has(key)) {
274
+ grouped.set(key, []);
275
+ }
276
+ grouped.get(key).push(entry);
277
+ });
278
+ return grouped;
279
+ }
280
+ function effectiveDurationSeconds(entry) {
281
+ if (entry.duration_seconds !== undefined)
282
+ return entry.duration_seconds;
283
+ if (entry.duration < 0) {
284
+ return Math.max(0, Math.floor((Date.now() - new Date(entry.start).getTime()) / 1000));
285
+ }
286
+ return entry.duration;
287
+ }
288
+ // Calculate total duration from entries
289
+ export function calculateTotalDuration(entries) {
290
+ return entries.reduce((total, entry) => total + effectiveDurationSeconds(entry), 0);
291
+ }
292
+ // Create a report entry from a hydrated time entry
293
+ export function createReportEntry(entry) {
294
+ const duration = effectiveDurationSeconds(entry);
295
+ const tagNames = entry.tag_names ?? [];
296
+ const tags = entry.tags ?? [];
297
+ return {
298
+ id: entry.id,
299
+ workspace: entry.workspace_name,
300
+ project: entry.project_name,
301
+ client: entry.client_name,
302
+ task: entry.task_name,
303
+ description: entry.description,
304
+ start: entry.start,
305
+ stop: entry.stop,
306
+ duration_hours: secondsToHours(duration),
307
+ duration_seconds: duration,
308
+ tags: tagNames.length > 0 ? tagNames : tags,
309
+ billable: entry.billable,
310
+ };
311
+ }
312
+ // Generate project summary
313
+ export function generateProjectSummary(projectName, entries) {
314
+ const totalSeconds = calculateTotalDuration(entries);
315
+ const billableSeconds = entries
316
+ .filter((e) => e.billable)
317
+ .reduce((total, e) => total + effectiveDurationSeconds(e), 0);
318
+ return {
319
+ project_id: entries[0]?.project_id,
320
+ project_name: projectName,
321
+ client_name: entries[0]?.client_name,
322
+ workspace_name: entries[0]?.workspace_name || 'Unknown',
323
+ total_hours: secondsToHours(totalSeconds),
324
+ total_seconds: totalSeconds,
325
+ billable_hours: secondsToHours(billableSeconds),
326
+ billable_seconds: billableSeconds,
327
+ entry_count: entries.length,
328
+ };
329
+ }
330
+ // Generate workspace summary
331
+ export function generateWorkspaceSummary(workspaceName, workspaceId, entries) {
332
+ const totalSeconds = calculateTotalDuration(entries);
333
+ const billableSeconds = entries
334
+ .filter((e) => e.billable)
335
+ .reduce((total, e) => total + effectiveDurationSeconds(e), 0);
336
+ const projectIds = new Set(entries.map((e) => e.project_id).filter(Boolean));
337
+ return {
338
+ workspace_id: workspaceId,
339
+ workspace_name: workspaceName,
340
+ total_hours: secondsToHours(totalSeconds),
341
+ total_seconds: totalSeconds,
342
+ billable_hours: secondsToHours(billableSeconds),
343
+ billable_seconds: billableSeconds,
344
+ project_count: projectIds.size,
345
+ entry_count: entries.length,
346
+ };
347
+ }
348
+ // Generate daily report
349
+ export function generateDailyReport(date, entries) {
350
+ const totalSeconds = calculateTotalDuration(entries);
351
+ const reportEntries = entries.map(createReportEntry);
352
+ // Group by project
353
+ const byProject = groupEntriesByProject(entries);
354
+ const projectSummaries = [];
355
+ byProject.forEach((projectEntries, projectName) => {
356
+ projectSummaries.push(generateProjectSummary(projectName, projectEntries));
357
+ });
358
+ // Group by workspace
359
+ const byWorkspace = groupEntriesByWorkspace(entries);
360
+ const workspaceSummaries = [];
361
+ byWorkspace.forEach((wsEntries, wsName) => {
362
+ const wsId = wsEntries[0]?.workspace_id || 0;
363
+ workspaceSummaries.push(generateWorkspaceSummary(wsName, wsId, wsEntries));
364
+ });
365
+ return {
366
+ date,
367
+ total_hours: secondsToHours(totalSeconds),
368
+ total_seconds: totalSeconds,
369
+ entries: reportEntries,
370
+ by_project: projectSummaries,
371
+ by_workspace: workspaceSummaries,
372
+ };
373
+ }
374
+ // Generate weekly report
375
+ export function generateWeeklyReport(weekStart, weekEnd, entries) {
376
+ const totalSeconds = calculateTotalDuration(entries);
377
+ // Group by date for daily breakdown
378
+ const byDate = groupEntriesByDate(entries);
379
+ const dailyBreakdown = [];
380
+ byDate.forEach((dateEntries, date) => {
381
+ dailyBreakdown.push(generateDailyReport(date, dateEntries));
382
+ });
383
+ // Sort daily reports
384
+ dailyBreakdown.sort((a, b) => a.date.localeCompare(b.date));
385
+ // Overall project summaries
386
+ const byProject = groupEntriesByProject(entries);
387
+ const projectSummaries = [];
388
+ byProject.forEach((projectEntries, projectName) => {
389
+ projectSummaries.push(generateProjectSummary(projectName, projectEntries));
390
+ });
391
+ // Overall workspace summaries
392
+ const byWorkspace = groupEntriesByWorkspace(entries);
393
+ const workspaceSummaries = [];
394
+ byWorkspace.forEach((wsEntries, wsName) => {
395
+ const wsId = wsEntries[0]?.workspace_id || 0;
396
+ workspaceSummaries.push(generateWorkspaceSummary(wsName, wsId, wsEntries));
397
+ });
398
+ return {
399
+ week_start: toLocalYMD(weekStart),
400
+ week_end: toLocalYMD(weekEnd),
401
+ total_hours: secondsToHours(totalSeconds),
402
+ total_seconds: totalSeconds,
403
+ daily_breakdown: dailyBreakdown,
404
+ by_project: projectSummaries,
405
+ by_workspace: workspaceSummaries,
406
+ };
407
+ }
408
+ // Format report for display
409
+ export function formatReportForDisplay(report) {
410
+ const lines = [];
411
+ if ('week_start' in report) {
412
+ // Weekly report
413
+ lines.push(`📊 Weekly Report (${report.week_start} to ${report.week_end})`);
414
+ lines.push(`Total: ${report.total_hours} hours`);
415
+ lines.push('');
416
+ lines.push('📅 Daily Breakdown:');
417
+ report.daily_breakdown.forEach((day) => {
418
+ lines.push(` ${day.date}: ${day.total_hours}h`);
419
+ });
420
+ }
421
+ else {
422
+ // Daily report
423
+ lines.push(`📊 Daily Report for ${report.date}`);
424
+ lines.push(`Total: ${report.total_hours} hours`);
425
+ }
426
+ lines.push('');
427
+ lines.push('🏢 By Workspace:');
428
+ report.by_workspace.forEach((ws) => {
429
+ lines.push(` ${ws.workspace_name}: ${ws.total_hours}h (${ws.project_count} projects)`);
430
+ });
431
+ lines.push('');
432
+ lines.push('📁 By Project:');
433
+ report.by_project.forEach((proj) => {
434
+ const client = proj.client_name ? ` (${proj.client_name})` : '';
435
+ lines.push(` ${proj.project_name}${client}: ${proj.total_hours}h`);
436
+ });
437
+ return lines.join('\n');
438
+ }
439
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { Project, Workspace } from './types.js';
2
+ export interface WorkspaceSummary {
3
+ id: number;
4
+ name: string;
5
+ }
6
+ export declare class WorkspaceResolutionError extends Error {
7
+ readonly code = "WORKSPACE_REQUIRED";
8
+ readonly tip: string;
9
+ readonly available_workspaces: WorkspaceSummary[];
10
+ constructor(action: string, workspaces: WorkspaceSummary[]);
11
+ }
12
+ interface ResolveWorkspaceOptions {
13
+ explicitWorkspaceId?: unknown;
14
+ defaultWorkspaceId?: number;
15
+ getWorkspaces: () => Promise<Workspace[]>;
16
+ action: string;
17
+ }
18
+ export declare function resolveWorkspaceId({ explicitWorkspaceId, defaultWorkspaceId, getWorkspaces, action, }: ResolveWorkspaceOptions): Promise<number>;
19
+ export declare function resolveProjectForClient(projects: Project[], clientId: number, projectId?: number): number;
20
+ export declare function parseWorkspaceId(value: unknown): number | undefined;
21
+ export {};
22
+ //# sourceMappingURL=workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,wBAAwB;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;gBAEtC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,EAAE;CAc3D;AAED,UAAU,uBAAuB;IAC/B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,kBAAkB,CAAC,EACvC,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,MAAM,GACP,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB3C;AAYD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,OAAO,EAAE,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA0BR;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAInE"}
@@ -0,0 +1,65 @@
1
+ export class WorkspaceResolutionError extends Error {
2
+ code = 'WORKSPACE_REQUIRED';
3
+ tip;
4
+ available_workspaces;
5
+ constructor(action, workspaces) {
6
+ const workspaceList = workspaces
7
+ .map((workspace) => `${workspace.id} (${workspace.name})`)
8
+ .join(', ');
9
+ super(workspaces.length > 0
10
+ ? `Workspace ID required for ${action}. Set TOGGL_DEFAULT_WORKSPACE_ID or provide workspace_id. Available workspaces: ${workspaceList}`
11
+ : `Workspace ID required for ${action}, but no Toggl workspaces were returned.`);
12
+ this.name = 'WorkspaceResolutionError';
13
+ this.available_workspaces = workspaces;
14
+ this.tip =
15
+ 'Pass workspace_id explicitly, or set TOGGL_DEFAULT_WORKSPACE_ID in your MCP server environment.';
16
+ }
17
+ }
18
+ export async function resolveWorkspaceId({ explicitWorkspaceId, defaultWorkspaceId, getWorkspaces, action, }) {
19
+ const explicit = parseWorkspaceId(explicitWorkspaceId);
20
+ if (explicit !== undefined)
21
+ return explicit;
22
+ if (defaultWorkspaceId !== undefined)
23
+ return defaultWorkspaceId;
24
+ const workspaces = await getWorkspaces();
25
+ if (workspaces.length === 1)
26
+ return workspaces[0].id;
27
+ throw new WorkspaceResolutionError(action, workspaces.map((workspace) => ({
28
+ id: workspace.id,
29
+ name: workspace.name,
30
+ })));
31
+ }
32
+ function formatProjectList(projects) {
33
+ return projects.map((project) => `${project.id} (${project.name})`).join(', ');
34
+ }
35
+ // Resolve which project a time entry should attach to when a client is given.
36
+ // Toggl entries attach to a project, not a client directly, so this maps the
37
+ // requested client to a concrete project:
38
+ // - project_id given → verify it belongs to the client
39
+ // - exactly one project for the client → use it
40
+ // - zero or many → throw with actionable guidance
41
+ export function resolveProjectForClient(projects, clientId, projectId) {
42
+ const clientProjects = projects.filter((project) => project.client_id === clientId);
43
+ if (projectId !== undefined) {
44
+ if (!clientProjects.some((project) => project.id === projectId)) {
45
+ throw new Error(`Project ${projectId} does not belong to client ${clientId}. ` +
46
+ `Projects for this client: ${clientProjects.length ? formatProjectList(clientProjects) : 'none'}.`);
47
+ }
48
+ return projectId;
49
+ }
50
+ if (clientProjects.length === 1)
51
+ return clientProjects[0].id;
52
+ if (clientProjects.length === 0) {
53
+ throw new Error(`No projects found for client ${clientId}. Provide project_id, ` +
54
+ `or create a project for this client first.`);
55
+ }
56
+ throw new Error(`Client ${clientId} has multiple projects; specify project_id. ` +
57
+ `Candidates: ${formatProjectList(clientProjects)}.`);
58
+ }
59
+ export function parseWorkspaceId(value) {
60
+ if (value === undefined || value === null || value === '')
61
+ return undefined;
62
+ const parsed = typeof value === 'number' ? value : Number(value);
63
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
64
+ }
65
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 320" role="img" aria-label="Comparison of tracked time versus actual app usage during a 1 hour 33 minute PR review session: 67 minutes on focus tools, 5 minutes on chat, 21 minutes unaccounted">
3
+ <title>Tracked vs reality — May 1 PR review session</title>
4
+ <desc>Two horizontal bars. Top shows the single tracked entry: 1 hour 33 minutes on PR review for Automation Hub. Bottom shows actual app activity in that same window: focus tools (Cursor, Chrome, Codex, Claude, iTerm, GitHub Desktop) covered 67 minutes, chat apps (Slack, WhatsApp, Discord) covered 5 minutes, and 21 minutes were unaccounted for due to idle time or events not in the timeline snapshot.</desc>
5
+ <style>
6
+ .title { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; font-size: 16px; font-weight: 500; fill: #2C2C2A; }
7
+ .subtitle { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; font-size: 12px; fill: #5F5E5A; }
8
+ .row-label { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; font-size: 13px; font-weight: 500; fill: #2C2C2A; }
9
+ .row-sub { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; font-size: 11px; fill: #5F5E5A; }
10
+ .seg-label { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; font-size: 11px; font-weight: 500; fill: #FFFFFF; }
11
+ .seg-label-dark { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; font-size: 11px; font-weight: 500; fill: #2C2C2A; }
12
+ .callout { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; font-size: 11px; fill: #993C1D; font-weight: 500; }
13
+ .legend-text { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; font-size: 11px; fill: #2C2C2A; }
14
+ .axis { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; font-size: 10px; fill: #888780; }
15
+ @media (prefers-color-scheme: dark) {
16
+ .title { fill: #F1EFE8; }
17
+ .subtitle { fill: #B4B2A9; }
18
+ .row-label { fill: #F1EFE8; }
19
+ .row-sub { fill: #B4B2A9; }
20
+ .seg-label-dark { fill: #F1EFE8; }
21
+ .callout { fill: #F0997B; }
22
+ .legend-text { fill: #F1EFE8; }
23
+ .axis { fill: #888780; }
24
+ }
25
+ </style>
26
+
27
+ <text x="20" y="28" class="title">Tracked vs reality — &quot;PR review&quot; entry on May 1</text>
28
+ <text x="20" y="48" class="subtitle">01:22 → 02:55 UTC · 1h 33m tracked on Automation Hub</text>
29
+
30
+ <text x="20" y="92" class="row-label">Tracked</text>
31
+ <text x="20" y="108" class="row-sub">1 entry</text>
32
+
33
+ <rect x="80" y="78" width="690" height="36" fill="#378ADD" rx="3" />
34
+ <text x="92" y="101" class="seg-label">PR review · Automation Hub</text>
35
+ <text x="758" y="101" class="seg-label" text-anchor="end">1h 33m</text>
36
+
37
+ <text x="20" y="162" class="row-label">Reality</text>
38
+ <text x="20" y="178" class="row-sub">9 apps</text>
39
+
40
+ <g>
41
+ <rect x="80" y="148" width="160.4" height="36" fill="#1D9E75" rx="3" />
42
+ <text x="160.2" y="171" class="seg-label" text-anchor="middle">Cursor 21m</text>
43
+
44
+ <rect x="241.4" y="148" width="157.0" height="36" fill="#0F6E56" rx="3" />
45
+ <text x="319.9" y="171" class="seg-label" text-anchor="middle">Chrome 21m</text>
46
+
47
+ <rect x="399.4" y="148" width="113.0" height="36" fill="#1D9E75" rx="3" />
48
+ <text x="455.9" y="171" class="seg-label" text-anchor="middle">Codex 15m</text>
49
+
50
+ <rect x="513.4" y="148" width="29.9" height="36" fill="#0F6E56" rx="3" />
51
+
52
+ <rect x="544.3" y="148" width="20.7" height="36" fill="#1D9E75" rx="3" />
53
+
54
+ <rect x="566.0" y="148" width="18.8" height="36" fill="#0F6E56" rx="3" />
55
+
56
+ <rect x="585.8" y="148" width="38.5" height="36" fill="#D85A30" rx="3" />
57
+ <text x="605.0" y="171" class="seg-label" text-anchor="middle">5m</text>
58
+
59
+ <rect x="625.3" y="148" width="152.7" height="36" fill="#B4B2A9" rx="3" />
60
+ <text x="701.6" y="171" class="seg-label-dark" text-anchor="middle">21m unaccounted</text>
61
+ </g>
62
+
63
+ <line x1="585.8" y1="184" x2="585.8" y2="208" stroke="#993C1D" stroke-width="1" stroke-dasharray="2 2" />
64
+ <line x1="624.3" y1="184" x2="624.3" y2="208" stroke="#993C1D" stroke-width="1" stroke-dasharray="2 2" />
65
+ <text x="605.0" y="222" class="callout" text-anchor="middle">drift: chat apps</text>
66
+
67
+ <line x1="80" y1="248" x2="770" y2="248" stroke="#888780" stroke-width="0.5" />
68
+ <text x="80" y="264" class="axis">01:22</text>
69
+ <text x="425" y="264" class="axis" text-anchor="middle">02:08</text>
70
+ <text x="770" y="264" class="axis" text-anchor="end">02:55</text>
71
+
72
+ <g transform="translate(20, 290)">
73
+ <rect x="0" y="0" width="10" height="10" fill="#1D9E75" rx="2" />
74
+ <text x="16" y="9" class="legend-text">Code/review tools</text>
75
+ <rect x="142" y="0" width="10" height="10" fill="#0F6E56" rx="2" />
76
+ <text x="158" y="9" class="legend-text">Browser/PR view</text>
77
+ <rect x="280" y="0" width="10" height="10" fill="#D85A30" rx="2" />
78
+ <text x="296" y="9" class="legend-text">Chat (Slack, WhatsApp, Discord)</text>
79
+ <rect x="496" y="0" width="10" height="10" fill="#B4B2A9" rx="2" />
80
+ <text x="512" y="9" class="legend-text">Idle / not in timeline snapshot</text>
81
+ </g>
82
+ </svg>