@doist/todoist-cli 1.1.2

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 (170) hide show
  1. package/README.md +76 -0
  2. package/dist/commands/activity.d.ts +3 -0
  3. package/dist/commands/activity.d.ts.map +1 -0
  4. package/dist/commands/activity.js +193 -0
  5. package/dist/commands/activity.js.map +1 -0
  6. package/dist/commands/add.d.ts +3 -0
  7. package/dist/commands/add.d.ts.map +1 -0
  8. package/dist/commands/add.js +28 -0
  9. package/dist/commands/add.js.map +1 -0
  10. package/dist/commands/auth.d.ts +3 -0
  11. package/dist/commands/auth.d.ts.map +1 -0
  12. package/dist/commands/auth.js +62 -0
  13. package/dist/commands/auth.js.map +1 -0
  14. package/dist/commands/comment.d.ts +3 -0
  15. package/dist/commands/comment.d.ts.map +1 -0
  16. package/dist/commands/comment.js +251 -0
  17. package/dist/commands/comment.js.map +1 -0
  18. package/dist/commands/completed.d.ts +3 -0
  19. package/dist/commands/completed.d.ts.map +1 -0
  20. package/dist/commands/completed.js +74 -0
  21. package/dist/commands/completed.js.map +1 -0
  22. package/dist/commands/filter.d.ts +3 -0
  23. package/dist/commands/filter.d.ts.map +1 -0
  24. package/dist/commands/filter.js +249 -0
  25. package/dist/commands/filter.js.map +1 -0
  26. package/dist/commands/inbox.d.ts +3 -0
  27. package/dist/commands/inbox.d.ts.map +1 -0
  28. package/dist/commands/inbox.js +23 -0
  29. package/dist/commands/inbox.js.map +1 -0
  30. package/dist/commands/label.d.ts +3 -0
  31. package/dist/commands/label.d.ts.map +1 -0
  32. package/dist/commands/label.js +196 -0
  33. package/dist/commands/label.js.map +1 -0
  34. package/dist/commands/notification.d.ts +3 -0
  35. package/dist/commands/notification.d.ts.map +1 -0
  36. package/dist/commands/notification.js +274 -0
  37. package/dist/commands/notification.js.map +1 -0
  38. package/dist/commands/project.d.ts +3 -0
  39. package/dist/commands/project.d.ts.map +1 -0
  40. package/dist/commands/project.js +364 -0
  41. package/dist/commands/project.js.map +1 -0
  42. package/dist/commands/reminder.d.ts +3 -0
  43. package/dist/commands/reminder.d.ts.map +1 -0
  44. package/dist/commands/reminder.js +225 -0
  45. package/dist/commands/reminder.js.map +1 -0
  46. package/dist/commands/section.d.ts +3 -0
  47. package/dist/commands/section.d.ts.map +1 -0
  48. package/dist/commands/section.js +138 -0
  49. package/dist/commands/section.js.map +1 -0
  50. package/dist/commands/settings.d.ts +3 -0
  51. package/dist/commands/settings.d.ts.map +1 -0
  52. package/dist/commands/settings.js +250 -0
  53. package/dist/commands/settings.js.map +1 -0
  54. package/dist/commands/stats.d.ts +3 -0
  55. package/dist/commands/stats.d.ts.map +1 -0
  56. package/dist/commands/stats.js +151 -0
  57. package/dist/commands/stats.js.map +1 -0
  58. package/dist/commands/task.d.ts +3 -0
  59. package/dist/commands/task.d.ts.map +1 -0
  60. package/dist/commands/task.js +363 -0
  61. package/dist/commands/task.js.map +1 -0
  62. package/dist/commands/today.d.ts +3 -0
  63. package/dist/commands/today.d.ts.map +1 -0
  64. package/dist/commands/today.js +97 -0
  65. package/dist/commands/today.js.map +1 -0
  66. package/dist/commands/upcoming.d.ts +3 -0
  67. package/dist/commands/upcoming.d.ts.map +1 -0
  68. package/dist/commands/upcoming.js +121 -0
  69. package/dist/commands/upcoming.js.map +1 -0
  70. package/dist/commands/workspace.d.ts +3 -0
  71. package/dist/commands/workspace.d.ts.map +1 -0
  72. package/dist/commands/workspace.js +289 -0
  73. package/dist/commands/workspace.js.map +1 -0
  74. package/dist/index.d.ts +3 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +53 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/lib/api/core.d.ts +40 -0
  79. package/dist/lib/api/core.d.ts.map +1 -0
  80. package/dist/lib/api/core.js +142 -0
  81. package/dist/lib/api/core.js.map +1 -0
  82. package/dist/lib/api/filters.d.ts +26 -0
  83. package/dist/lib/api/filters.d.ts.map +1 -0
  84. package/dist/lib/api/filters.js +86 -0
  85. package/dist/lib/api/filters.js.map +1 -0
  86. package/dist/lib/api/notifications.d.ts +33 -0
  87. package/dist/lib/api/notifications.d.ts.map +1 -0
  88. package/dist/lib/api/notifications.js +112 -0
  89. package/dist/lib/api/notifications.js.map +1 -0
  90. package/dist/lib/api/reminders.d.ts +30 -0
  91. package/dist/lib/api/reminders.d.ts.map +1 -0
  92. package/dist/lib/api/reminders.js +79 -0
  93. package/dist/lib/api/reminders.js.map +1 -0
  94. package/dist/lib/api/stats.d.ts +41 -0
  95. package/dist/lib/api/stats.d.ts.map +1 -0
  96. package/dist/lib/api/stats.js +79 -0
  97. package/dist/lib/api/stats.js.map +1 -0
  98. package/dist/lib/api/uploads.d.ts +10 -0
  99. package/dist/lib/api/uploads.d.ts.map +1 -0
  100. package/dist/lib/api/uploads.js +31 -0
  101. package/dist/lib/api/uploads.js.map +1 -0
  102. package/dist/lib/api/user-settings.d.ts +33 -0
  103. package/dist/lib/api/user-settings.d.ts.map +1 -0
  104. package/dist/lib/api/user-settings.js +90 -0
  105. package/dist/lib/api/user-settings.js.map +1 -0
  106. package/dist/lib/api/workspaces.d.ts +23 -0
  107. package/dist/lib/api/workspaces.d.ts.map +1 -0
  108. package/dist/lib/api/workspaces.js +67 -0
  109. package/dist/lib/api/workspaces.js.map +1 -0
  110. package/dist/lib/auth.d.ts +4 -0
  111. package/dist/lib/auth.d.ts.map +1 -0
  112. package/dist/lib/auth.js +66 -0
  113. package/dist/lib/auth.js.map +1 -0
  114. package/dist/lib/browser.d.ts +2 -0
  115. package/dist/lib/browser.d.ts.map +1 -0
  116. package/dist/lib/browser.js +6 -0
  117. package/dist/lib/browser.js.map +1 -0
  118. package/dist/lib/collaborators.d.ts +29 -0
  119. package/dist/lib/collaborators.d.ts.map +1 -0
  120. package/dist/lib/collaborators.js +184 -0
  121. package/dist/lib/collaborators.js.map +1 -0
  122. package/dist/lib/dates.d.ts +3 -0
  123. package/dist/lib/dates.d.ts.map +1 -0
  124. package/dist/lib/dates.js +20 -0
  125. package/dist/lib/dates.js.map +1 -0
  126. package/dist/lib/duration.d.ts +14 -0
  127. package/dist/lib/duration.d.ts.map +1 -0
  128. package/dist/lib/duration.js +58 -0
  129. package/dist/lib/duration.js.map +1 -0
  130. package/dist/lib/markdown.d.ts +2 -0
  131. package/dist/lib/markdown.d.ts.map +1 -0
  132. package/dist/lib/markdown.js +14 -0
  133. package/dist/lib/markdown.js.map +1 -0
  134. package/dist/lib/oauth-server.d.ts +6 -0
  135. package/dist/lib/oauth-server.d.ts.map +1 -0
  136. package/dist/lib/oauth-server.js +141 -0
  137. package/dist/lib/oauth-server.js.map +1 -0
  138. package/dist/lib/oauth.d.ts +3 -0
  139. package/dist/lib/oauth.d.ts.map +1 -0
  140. package/dist/lib/oauth.js +38 -0
  141. package/dist/lib/oauth.js.map +1 -0
  142. package/dist/lib/output.d.ts +36 -0
  143. package/dist/lib/output.d.ts.map +1 -0
  144. package/dist/lib/output.js +293 -0
  145. package/dist/lib/output.js.map +1 -0
  146. package/dist/lib/pagination.d.ts +23 -0
  147. package/dist/lib/pagination.d.ts.map +1 -0
  148. package/dist/lib/pagination.js +26 -0
  149. package/dist/lib/pagination.js.map +1 -0
  150. package/dist/lib/pkce.d.ts +4 -0
  151. package/dist/lib/pkce.d.ts.map +1 -0
  152. package/dist/lib/pkce.js +19 -0
  153. package/dist/lib/pkce.js.map +1 -0
  154. package/dist/lib/refs.d.ts +13 -0
  155. package/dist/lib/refs.d.ts.map +1 -0
  156. package/dist/lib/refs.js +117 -0
  157. package/dist/lib/refs.js.map +1 -0
  158. package/dist/lib/spinner.d.ts +20 -0
  159. package/dist/lib/spinner.d.ts.map +1 -0
  160. package/dist/lib/spinner.js +70 -0
  161. package/dist/lib/spinner.js.map +1 -0
  162. package/dist/lib/task-list.d.ts +28 -0
  163. package/dist/lib/task-list.d.ts.map +1 -0
  164. package/dist/lib/task-list.js +234 -0
  165. package/dist/lib/task-list.js.map +1 -0
  166. package/dist/lib/urls.d.ts +8 -0
  167. package/dist/lib/urls.d.ts.map +1 -0
  168. package/dist/lib/urls.js +26 -0
  169. package/dist/lib/urls.js.map +1 -0
  170. package/package.json +66 -0
@@ -0,0 +1,90 @@
1
+ import { getApiToken } from '../auth.js';
2
+ import { executeSyncCommand, generateUuid } from './core.js';
3
+ export async function fetchUserSettings() {
4
+ const token = await getApiToken();
5
+ const response = await fetch('https://api.todoist.com/api/v1/sync', {
6
+ method: 'POST',
7
+ headers: {
8
+ Authorization: `Bearer ${token}`,
9
+ 'Content-Type': 'application/x-www-form-urlencoded',
10
+ },
11
+ body: new URLSearchParams({
12
+ sync_token: '*',
13
+ resource_types: '["user","user_settings"]',
14
+ }),
15
+ });
16
+ if (!response.ok) {
17
+ throw new Error(`Failed to fetch user settings: ${response.status}`);
18
+ }
19
+ const data = await response.json();
20
+ if (data.error) {
21
+ throw new Error(`User settings API error: ${data.error}`);
22
+ }
23
+ const user = data.user ?? {};
24
+ const settings = data.user_settings ?? {};
25
+ return {
26
+ timezone: String(user.timezone ?? 'UTC'),
27
+ timeFormat: Number(user.time_format ?? 0),
28
+ dateFormat: Number(user.date_format ?? 0),
29
+ startDay: Number(user.start_day ?? 1),
30
+ theme: Number(user.theme_id ?? user.theme ?? 0),
31
+ autoReminder: Number(user.auto_reminder ?? 0),
32
+ nextWeek: Number(user.next_week ?? 1),
33
+ startPage: String(user.start_page ?? 'today'),
34
+ reminderPush: Boolean(settings.reminder_push ?? true),
35
+ reminderDesktop: Boolean(settings.reminder_desktop ?? true),
36
+ reminderEmail: Boolean(settings.reminder_email ?? false),
37
+ completedSoundDesktop: Boolean(settings.completed_sound_desktop ?? true),
38
+ completedSoundMobile: Boolean(settings.completed_sound_mobile ?? true),
39
+ };
40
+ }
41
+ export async function updateUserSettings(args) {
42
+ const commands = [];
43
+ const userArgs = {};
44
+ if (args.timezone !== undefined)
45
+ userArgs.timezone = args.timezone;
46
+ if (args.timeFormat !== undefined)
47
+ userArgs.time_format = args.timeFormat;
48
+ if (args.dateFormat !== undefined)
49
+ userArgs.date_format = args.dateFormat;
50
+ if (args.startDay !== undefined)
51
+ userArgs.start_day = args.startDay;
52
+ if (args.theme !== undefined)
53
+ userArgs.theme_id = String(args.theme);
54
+ if (args.autoReminder !== undefined)
55
+ userArgs.auto_reminder = args.autoReminder;
56
+ if (args.nextWeek !== undefined)
57
+ userArgs.next_week = args.nextWeek;
58
+ if (args.startPage !== undefined)
59
+ userArgs.start_page = args.startPage;
60
+ if (Object.keys(userArgs).length > 0) {
61
+ commands.push({
62
+ type: 'user_update',
63
+ uuid: generateUuid(),
64
+ args: userArgs,
65
+ });
66
+ }
67
+ const settingsArgs = {};
68
+ if (args.reminderPush !== undefined)
69
+ settingsArgs.reminder_push = args.reminderPush;
70
+ if (args.reminderDesktop !== undefined)
71
+ settingsArgs.reminder_desktop = args.reminderDesktop;
72
+ if (args.reminderEmail !== undefined)
73
+ settingsArgs.reminder_email = args.reminderEmail;
74
+ if (args.completedSoundDesktop !== undefined)
75
+ settingsArgs.completed_sound_desktop = args.completedSoundDesktop;
76
+ if (args.completedSoundMobile !== undefined)
77
+ settingsArgs.completed_sound_mobile = args.completedSoundMobile;
78
+ if (Object.keys(settingsArgs).length > 0) {
79
+ commands.push({
80
+ type: 'user_settings_update',
81
+ uuid: generateUuid(),
82
+ args: settingsArgs,
83
+ });
84
+ }
85
+ if (commands.length === 0) {
86
+ throw new Error('No settings to update');
87
+ }
88
+ await executeSyncCommand(commands);
89
+ }
90
+ //# sourceMappingURL=user-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-settings.js","sourceRoot":"","sources":["../../../src/lib/api/user-settings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ export interface Workspace {
2
+ id: string;
3
+ name: string;
4
+ role: 'ADMIN' | 'MEMBER' | 'GUEST';
5
+ plan: string;
6
+ domainName: string | null;
7
+ currentMemberCount: number;
8
+ currentActiveProjects: number;
9
+ memberCountByType: {
10
+ adminCount: number;
11
+ memberCount: number;
12
+ guestCount: number;
13
+ };
14
+ }
15
+ export interface WorkspaceFolder {
16
+ id: string;
17
+ name: string;
18
+ workspaceId: string;
19
+ }
20
+ export declare function fetchWorkspaces(): Promise<Workspace[]>;
21
+ export declare function fetchWorkspaceFolders(): Promise<WorkspaceFolder[]>;
22
+ export declare function clearWorkspaceCache(): void;
23
+ //# sourceMappingURL=workspaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../../src/lib/api/workspaces.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAA;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,qBAAqB,EAAE,MAAM,CAAA;IAC7B,iBAAiB,EAAE;QACf,UAAU,EAAE,MAAM,CAAA;QAClB,WAAW,EAAE,MAAM,CAAA;QACnB,UAAU,EAAE,MAAM,CAAA;KACrB,CAAA;CACJ;AAED,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACtB;AA6DD,wBAAsB,eAAe,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAG5D;AAED,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAOxE;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C"}
@@ -0,0 +1,67 @@
1
+ import { getApiToken } from '../auth.js';
2
+ let workspaceCache = null;
3
+ let folderCache = null;
4
+ async function fetchWorkspaceData() {
5
+ if (workspaceCache !== null && folderCache !== null) {
6
+ return { workspaces: workspaceCache, folders: folderCache };
7
+ }
8
+ const token = await getApiToken();
9
+ const response = await fetch('https://api.todoist.com/sync/v9/sync', {
10
+ method: 'POST',
11
+ headers: {
12
+ Authorization: `Bearer ${token}`,
13
+ 'Content-Type': 'application/x-www-form-urlencoded',
14
+ },
15
+ body: new URLSearchParams({
16
+ sync_token: '*',
17
+ resource_types: '["workspaces","folders"]',
18
+ }),
19
+ });
20
+ if (!response.ok) {
21
+ throw new Error(`Failed to fetch workspace data: ${response.status}`);
22
+ }
23
+ const data = await response.json();
24
+ if (data.error) {
25
+ throw new Error(`Workspace API error: ${data.error}`);
26
+ }
27
+ const workspaces = (data.workspaces ?? []).map((w) => ({
28
+ id: String(w.id),
29
+ name: w.name,
30
+ role: w.role,
31
+ plan: w.plan,
32
+ domainName: w.domain_name ?? null,
33
+ currentMemberCount: w.current_member_count ?? 0,
34
+ currentActiveProjects: w.current_active_projects ?? 0,
35
+ memberCountByType: {
36
+ adminCount: w.member_count_by_type?.admin_count ?? 0,
37
+ memberCount: w.member_count_by_type?.member_count ?? 0,
38
+ guestCount: w.member_count_by_type?.guest_count ?? 0,
39
+ },
40
+ }));
41
+ const folders = (data.folders ?? []).map((f) => ({
42
+ id: String(f.id),
43
+ name: String(f.name),
44
+ workspaceId: String(f.workspace_id),
45
+ }));
46
+ workspaceCache = workspaces;
47
+ folderCache = folders;
48
+ return { workspaces, folders };
49
+ }
50
+ export async function fetchWorkspaces() {
51
+ const { workspaces } = await fetchWorkspaceData();
52
+ return workspaces;
53
+ }
54
+ export async function fetchWorkspaceFolders() {
55
+ try {
56
+ const { folders } = await fetchWorkspaceData();
57
+ return folders;
58
+ }
59
+ catch {
60
+ return [];
61
+ }
62
+ }
63
+ export function clearWorkspaceCache() {
64
+ workspaceCache = null;
65
+ folderCache = null;
66
+ }
67
+ //# sourceMappingURL=workspaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspaces.js","sourceRoot":"","sources":["../../../src/lib/api/workspaces.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function getApiToken(): Promise<string>;
2
+ export declare function saveApiToken(token: string): Promise<void>;
3
+ export declare function clearApiToken(): Promise<void>;
4
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAUA,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAqBnD;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2B/D;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAgBnD"}
@@ -0,0 +1,66 @@
1
+ import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join } from 'node:path';
4
+ const CONFIG_PATH = join(homedir(), '.config', 'todoist-cli', 'config.json');
5
+ export async function getApiToken() {
6
+ // Priority 1: Environment variable
7
+ const envToken = process.env.TODOIST_API_TOKEN;
8
+ if (envToken) {
9
+ return envToken;
10
+ }
11
+ // Priority 2: Config file
12
+ try {
13
+ const content = await readFile(CONFIG_PATH, 'utf-8');
14
+ const config = JSON.parse(content);
15
+ if (config.api_token) {
16
+ return config.api_token;
17
+ }
18
+ }
19
+ catch {
20
+ // Config file doesn't exist or is invalid
21
+ }
22
+ throw new Error('No API token found. Set TODOIST_API_TOKEN environment variable or create ~/.config/todoist-cli/config.json with {"api_token": "your-token"}');
23
+ }
24
+ export async function saveApiToken(token) {
25
+ // Validate token (non-empty, reasonable length)
26
+ if (!token || token.trim().length < 10) {
27
+ throw new Error('Invalid token: Token must be at least 10 characters');
28
+ }
29
+ // Ensure config directory exists
30
+ const configDir = dirname(CONFIG_PATH);
31
+ await mkdir(configDir, { recursive: true });
32
+ // Read existing config to preserve other settings
33
+ let existingConfig = {};
34
+ try {
35
+ const content = await readFile(CONFIG_PATH, 'utf-8');
36
+ existingConfig = JSON.parse(content);
37
+ }
38
+ catch {
39
+ // Config doesn't exist - start fresh
40
+ }
41
+ // Update config with new token
42
+ const newConfig = {
43
+ ...existingConfig,
44
+ api_token: token.trim(),
45
+ };
46
+ // Write config file with proper formatting
47
+ await writeFile(CONFIG_PATH, `${JSON.stringify(newConfig, null, 2)}\n`);
48
+ }
49
+ export async function clearApiToken() {
50
+ let config = {};
51
+ try {
52
+ const content = await readFile(CONFIG_PATH, 'utf-8');
53
+ config = JSON.parse(content);
54
+ }
55
+ catch {
56
+ return; // Config doesn't exist, nothing to clear
57
+ }
58
+ delete config.api_token;
59
+ if (Object.keys(config).length === 0) {
60
+ await unlink(CONFIG_PATH);
61
+ }
62
+ else {
63
+ await writeFile(CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`);
64
+ }
65
+ }
66
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEzC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,CAAC,CAAA;AAM5E,MAAM,CAAC,KAAK,UAAU,WAAW;IAC7B,mCAAmC;IACnC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAA;IAC9C,IAAI,QAAQ,EAAE,CAAC;QACX,OAAO,QAAQ,CAAA;IACnB,CAAC;IAED,0BAA0B;IAC1B,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;QACpD,MAAM,MAAM,GAAW,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAC1C,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACnB,OAAO,MAAM,CAAC,SAAS,CAAA;QAC3B,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACL,0CAA0C;IAC9C,CAAC;IAED,MAAM,IAAI,KAAK,CACX,6IAA6I,CAChJ,CAAA;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa;IAC5C,gDAAgD;IAChD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;IAC1E,CAAC;IAED,iCAAiC;IACjC,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IACtC,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAE3C,kDAAkD;IAClD,IAAI,cAAc,GAAW,EAAE,CAAA;IAC/B,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;QACpD,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACxC,CAAC;IAAC,MAAM,CAAC;QACL,qCAAqC;IACzC,CAAC;IAED,+BAA+B;IAC/B,MAAM,SAAS,GAAW;QACtB,GAAG,cAAc;QACjB,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE;KAC1B,CAAA;IAED,2CAA2C;IAC3C,MAAM,SAAS,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;AAC3E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa;IAC/B,IAAI,MAAM,GAAW,EAAE,CAAA;IACvB,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;QACpD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAChC,CAAC;IAAC,MAAM,CAAC;QACL,OAAM,CAAC,yCAAyC;IACpD,CAAC;IAED,OAAO,MAAM,CAAC,SAAS,CAAA;IAEvB,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,MAAM,CAAC,WAAW,CAAC,CAAA;IAC7B,CAAC;SAAM,CAAC;QACJ,MAAM,SAAS,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;IACxE,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function openInBrowser(url: string): Promise<void>;
2
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/lib/browser.ts"],"names":[],"mappings":"AAEA,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9D"}
@@ -0,0 +1,6 @@
1
+ import open from 'open';
2
+ export async function openInBrowser(url) {
3
+ console.log(`Opening ${url}`);
4
+ await open(url);
5
+ }
6
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/lib/browser.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAA;AAEvB,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW;IAC3C,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,EAAE,CAAC,CAAA;IAC7B,MAAM,IAAI,CAAC,GAAG,CAAC,CAAA;AACnB,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { TodoistApi } from '@doist/todoist-api-typescript';
2
+ import { type Project, type Task } from './api/core.js';
3
+ export interface CollaboratorInfo {
4
+ id: string;
5
+ name: string;
6
+ email: string;
7
+ }
8
+ export declare class CollaboratorCache {
9
+ private workspaceUsers;
10
+ private projectCollaborators;
11
+ preload(api: TodoistApi, tasks: Task[], projects: Map<string, Project>): Promise<void>;
12
+ private fetchWorkspaceUsers;
13
+ private fetchProjectCollaborators;
14
+ getUserName({ userId, projectId, projects, }: {
15
+ userId: string;
16
+ projectId: string;
17
+ projects: Map<string, Project>;
18
+ }): string | null;
19
+ }
20
+ export declare function formatUserShortName(fullName: string): string;
21
+ export interface FormatAssigneeOptions {
22
+ userId: string | null;
23
+ projectId: string;
24
+ projects: Map<string, Project>;
25
+ cache: CollaboratorCache;
26
+ }
27
+ export declare function formatAssignee({ userId, projectId, projects, cache, }: FormatAssigneeOptions): string | null;
28
+ export declare function resolveAssigneeId(api: TodoistApi, ref: string, project: Project): Promise<string>;
29
+ //# sourceMappingURL=collaborators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collaborators.d.ts","sourceRoot":"","sources":["../../src/lib/collaborators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAwC,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,eAAe,CAAA;AAI7F,MAAM,WAAW,gBAAgB;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,qBAAa,iBAAiB;IAC1B,OAAO,CAAC,cAAc,CAAmD;IACzE,OAAO,CAAC,oBAAoB,CAAmD;IAEzE,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YAyC9E,mBAAmB;YA2BnB,yBAAyB;IAsBvC,WAAW,CAAC,EACR,MAAM,EACN,SAAS,EACT,QAAQ,GACX,EAAE;QACC,MAAM,EAAE,MAAM,CAAA;QACd,SAAS,EAAE,MAAM,CAAA;QACjB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACjC,GAAG,MAAM,GAAG,IAAI;CAcpB;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ5D;AAED,MAAM,WAAW,qBAAqB;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,KAAK,EAAE,iBAAiB,CAAA;CAC3B;AAED,wBAAgB,cAAc,CAAC,EAC3B,MAAM,EACN,SAAS,EACT,QAAQ,EACR,KAAK,GACR,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,CAQvC;AA0DD,wBAAsB,iBAAiB,CACnC,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,GACjB,OAAO,CAAC,MAAM,CAAC,CA+BjB"}
@@ -0,0 +1,184 @@
1
+ import { getCurrentUserId, isWorkspaceProject } from './api/core.js';
2
+ import { formatError } from './output.js';
3
+ import { extractId, isIdRef } from './refs.js';
4
+ export class CollaboratorCache {
5
+ workspaceUsers = new Map();
6
+ projectCollaborators = new Map();
7
+ async preload(api, tasks, projects) {
8
+ const projectsWithAssignees = new Set();
9
+ for (const task of tasks) {
10
+ if (task.responsibleUid) {
11
+ projectsWithAssignees.add(task.projectId);
12
+ }
13
+ }
14
+ if (projectsWithAssignees.size === 0)
15
+ return;
16
+ const workspaceIds = new Set();
17
+ const sharedPersonalProjectIds = [];
18
+ for (const projectId of projectsWithAssignees) {
19
+ const project = projects.get(projectId);
20
+ if (!project)
21
+ continue;
22
+ if (isWorkspaceProject(project)) {
23
+ workspaceIds.add(project.workspaceId);
24
+ }
25
+ else if (project.isShared) {
26
+ sharedPersonalProjectIds.push(projectId);
27
+ }
28
+ }
29
+ const fetches = [];
30
+ for (const workspaceId of workspaceIds) {
31
+ if (!this.workspaceUsers.has(workspaceId)) {
32
+ fetches.push(this.fetchWorkspaceUsers(api, workspaceId));
33
+ }
34
+ }
35
+ for (const projectId of sharedPersonalProjectIds) {
36
+ if (!this.projectCollaborators.has(projectId)) {
37
+ fetches.push(this.fetchProjectCollaborators(api, projectId));
38
+ }
39
+ }
40
+ await Promise.all(fetches);
41
+ }
42
+ async fetchWorkspaceUsers(api, workspaceId) {
43
+ const userMap = new Map();
44
+ let cursor;
45
+ const workspaceIdNum = parseInt(workspaceId, 10);
46
+ while (true) {
47
+ const response = await api.getWorkspaceUsers({
48
+ workspaceId: workspaceIdNum,
49
+ cursor,
50
+ limit: 200,
51
+ });
52
+ for (const user of response.workspaceUsers) {
53
+ userMap.set(user.userId, {
54
+ id: user.userId,
55
+ name: user.fullName,
56
+ email: user.userEmail,
57
+ });
58
+ }
59
+ if (!response.hasMore || !response.nextCursor)
60
+ break;
61
+ cursor = response.nextCursor;
62
+ }
63
+ this.workspaceUsers.set(workspaceId, userMap);
64
+ }
65
+ async fetchProjectCollaborators(api, projectId) {
66
+ const userMap = new Map();
67
+ let cursor;
68
+ while (true) {
69
+ const response = await api.getProjectCollaborators(projectId, { cursor });
70
+ for (const user of response.results) {
71
+ userMap.set(user.id, {
72
+ id: user.id,
73
+ name: user.name,
74
+ email: user.email,
75
+ });
76
+ }
77
+ if (!response.nextCursor)
78
+ break;
79
+ cursor = response.nextCursor;
80
+ }
81
+ this.projectCollaborators.set(projectId, userMap);
82
+ }
83
+ getUserName({ userId, projectId, projects, }) {
84
+ const project = projects.get(projectId);
85
+ if (!project)
86
+ return null;
87
+ if (isWorkspaceProject(project)) {
88
+ const workspaceMap = this.workspaceUsers.get(project.workspaceId);
89
+ const user = workspaceMap?.get(userId);
90
+ return user?.name ?? null;
91
+ }
92
+ const projectMap = this.projectCollaborators.get(projectId);
93
+ const user = projectMap?.get(userId);
94
+ return user?.name ?? null;
95
+ }
96
+ }
97
+ export function formatUserShortName(fullName) {
98
+ const parts = fullName.trim().split(/\s+/);
99
+ if (parts.length === 1) {
100
+ return parts[0];
101
+ }
102
+ const firstName = parts[0];
103
+ const lastInitial = parts[parts.length - 1][0];
104
+ return `${firstName} ${lastInitial}.`;
105
+ }
106
+ export function formatAssignee({ userId, projectId, projects, cache, }) {
107
+ if (!userId)
108
+ return null;
109
+ const name = cache.getUserName({ userId, projectId, projects });
110
+ if (name) {
111
+ return `+${formatUserShortName(name)}`;
112
+ }
113
+ return `+${userId}`;
114
+ }
115
+ async function fetchCollaboratorsForProject(api, project) {
116
+ if (isWorkspaceProject(project)) {
117
+ const users = [];
118
+ let cursor;
119
+ const workspaceIdNum = parseInt(project.workspaceId, 10);
120
+ while (true) {
121
+ const response = await api.getWorkspaceUsers({
122
+ workspaceId: workspaceIdNum,
123
+ cursor,
124
+ limit: 200,
125
+ });
126
+ for (const user of response.workspaceUsers) {
127
+ users.push({
128
+ id: user.userId,
129
+ name: user.fullName,
130
+ email: user.userEmail,
131
+ });
132
+ }
133
+ if (!response.hasMore || !response.nextCursor)
134
+ break;
135
+ cursor = response.nextCursor;
136
+ }
137
+ return users;
138
+ }
139
+ if (project.isShared) {
140
+ const users = [];
141
+ let cursor;
142
+ while (true) {
143
+ const response = await api.getProjectCollaborators(project.id, {
144
+ cursor,
145
+ });
146
+ for (const user of response.results) {
147
+ users.push({
148
+ id: user.id,
149
+ name: user.name,
150
+ email: user.email,
151
+ });
152
+ }
153
+ if (!response.nextCursor)
154
+ break;
155
+ cursor = response.nextCursor;
156
+ }
157
+ return users;
158
+ }
159
+ throw new Error(formatError('NOT_SHARED', 'Cannot assign tasks in non-shared projects.'));
160
+ }
161
+ export async function resolveAssigneeId(api, ref, project) {
162
+ if (ref.toLowerCase() === 'me') {
163
+ return getCurrentUserId();
164
+ }
165
+ if (isIdRef(ref)) {
166
+ return extractId(ref);
167
+ }
168
+ const collaborators = await fetchCollaboratorsForProject(api, project);
169
+ const lower = ref.toLowerCase();
170
+ const exactName = collaborators.find((c) => c.name.toLowerCase() === lower);
171
+ if (exactName)
172
+ return exactName.id;
173
+ const exactEmail = collaborators.find((c) => c.email.toLowerCase() === lower);
174
+ if (exactEmail)
175
+ return exactEmail.id;
176
+ const partialName = collaborators.filter((c) => c.name.toLowerCase().includes(lower));
177
+ if (partialName.length === 1)
178
+ return partialName[0].id;
179
+ if (partialName.length > 1) {
180
+ throw new Error(formatError('AMBIGUOUS_ASSIGNEE', `Multiple users match "${ref}":`, partialName.slice(0, 5).map((c) => `"${c.name}" (id:${c.id})`)));
181
+ }
182
+ throw new Error(formatError('ASSIGNEE_NOT_FOUND', `User "${ref}" not found.`));
183
+ }
184
+ //# sourceMappingURL=collaborators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collaborators.js","sourceRoot":"","sources":["../../src/lib/collaborators.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function getLocalDate(daysOffset?: number): string;
2
+ export declare function formatDateHeader(dateStr: string, today: string): string;
3
+ //# sourceMappingURL=dates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dates.d.ts","sourceRoot":"","sources":["../../src/lib/dates.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,UAAU,SAAI,GAAG,MAAM,CAInD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAUvE"}
@@ -0,0 +1,20 @@
1
+ export function getLocalDate(daysOffset = 0) {
2
+ const date = new Date();
3
+ date.setDate(date.getDate() + daysOffset);
4
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
5
+ }
6
+ export function formatDateHeader(dateStr, today) {
7
+ if (dateStr < today)
8
+ return 'Overdue';
9
+ if (dateStr === today)
10
+ return 'Today';
11
+ const tomorrow = getLocalDate(1);
12
+ if (dateStr === tomorrow)
13
+ return 'Tomorrow';
14
+ const [year, month, day] = dateStr.split('-').map(Number);
15
+ return new Date(year, month - 1, day).toLocaleDateString('en-US', {
16
+ month: 'short',
17
+ day: 'numeric',
18
+ });
19
+ }
20
+ //# sourceMappingURL=dates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dates.js","sourceRoot":"","sources":["../../src/lib/dates.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY,CAAC,UAAU,GAAG,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAA;IACvB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,CAAA;IACzC,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAA;AAC7H,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAe,EAAE,KAAa;IAC3D,IAAI,OAAO,GAAG,KAAK;QAAE,OAAO,SAAS,CAAA;IACrC,IAAI,OAAO,KAAK,KAAK;QAAE,OAAO,OAAO,CAAA;IACrC,MAAM,QAAQ,GAAG,YAAY,CAAC,CAAC,CAAC,CAAA;IAChC,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAA;IAC3C,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACzD,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE;QAC9D,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,SAAS;KACjB,CAAC,CAAA;AACN,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Parse a duration string into minutes.
3
+ * Supports formats like: "1h", "30m", "2h15m", "1 hour 30 minutes"
4
+ * @returns minutes or null if invalid
5
+ */
6
+ export declare function parseDuration(input: string): number | null;
7
+ /**
8
+ * Format a duration for display.
9
+ * @param amount - the duration amount
10
+ * @param unit - 'minute' or 'day'
11
+ * @returns formatted string like "1h30m" or "2d"
12
+ */
13
+ export declare function formatDuration(amount: number, unit?: 'minute' | 'day'): string;
14
+ //# sourceMappingURL=duration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../../src/lib/duration.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiC1D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,QAAQ,GAAG,KAAgB,GAAG,MAAM,CAaxF"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Parse a duration string into minutes.
3
+ * Supports formats like: "1h", "30m", "2h15m", "1 hour 30 minutes"
4
+ * @returns minutes or null if invalid
5
+ */
6
+ export function parseDuration(input) {
7
+ const normalized = input.trim().toLowerCase();
8
+ if (!normalized)
9
+ return null;
10
+ // Match all occurrences of number + unit
11
+ // Units: h, hr, hrs, hour, hours, m, min, mins, minute, minutes
12
+ const pattern = /(\d+)\s*(hours?|hrs?|h|minutes?|mins?|m)(?=\s|$|\d)/gi;
13
+ const matches = [...normalized.matchAll(pattern)];
14
+ if (matches.length === 0)
15
+ return null;
16
+ // Verify the entire input is consumed by valid duration parts
17
+ // Remove all matched parts and whitespace, should be empty
18
+ let remaining = normalized;
19
+ for (const match of matches) {
20
+ remaining = remaining.replace(match[0], '');
21
+ }
22
+ remaining = remaining.replace(/\s+/g, '');
23
+ if (remaining !== '')
24
+ return null;
25
+ let totalMinutes = 0;
26
+ for (const match of matches) {
27
+ const value = parseInt(match[1], 10);
28
+ const unit = match[2].toLowerCase();
29
+ if (unit.startsWith('h')) {
30
+ totalMinutes += value * 60;
31
+ }
32
+ else if (unit.startsWith('m')) {
33
+ totalMinutes += value;
34
+ }
35
+ }
36
+ return totalMinutes;
37
+ }
38
+ /**
39
+ * Format a duration for display.
40
+ * @param amount - the duration amount
41
+ * @param unit - 'minute' or 'day'
42
+ * @returns formatted string like "1h30m" or "2d"
43
+ */
44
+ export function formatDuration(amount, unit = 'minute') {
45
+ const totalMinutes = unit === 'day' ? amount * 1440 : amount;
46
+ const days = Math.floor(totalMinutes / 1440);
47
+ const hours = Math.floor((totalMinutes % 1440) / 60);
48
+ const minutes = totalMinutes % 60;
49
+ const parts = [];
50
+ if (days > 0)
51
+ parts.push(`${days}d`);
52
+ if (hours > 0)
53
+ parts.push(`${hours}h`);
54
+ if (minutes > 0)
55
+ parts.push(`${minutes}m`);
56
+ return parts.length > 0 ? parts.join('') : '0m';
57
+ }
58
+ //# sourceMappingURL=duration.js.map