disunday 1.0.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 (83) hide show
  1. package/dist/ai-tool-to-genai.js +208 -0
  2. package/dist/ai-tool-to-genai.test.js +267 -0
  3. package/dist/channel-management.js +96 -0
  4. package/dist/cli.js +1674 -0
  5. package/dist/commands/abort.js +89 -0
  6. package/dist/commands/add-project.js +117 -0
  7. package/dist/commands/agent.js +250 -0
  8. package/dist/commands/ask-question.js +219 -0
  9. package/dist/commands/compact.js +126 -0
  10. package/dist/commands/context-menu.js +171 -0
  11. package/dist/commands/context.js +89 -0
  12. package/dist/commands/cost.js +93 -0
  13. package/dist/commands/create-new-project.js +111 -0
  14. package/dist/commands/diff.js +77 -0
  15. package/dist/commands/export.js +100 -0
  16. package/dist/commands/files.js +73 -0
  17. package/dist/commands/fork.js +199 -0
  18. package/dist/commands/help.js +54 -0
  19. package/dist/commands/login.js +488 -0
  20. package/dist/commands/merge-worktree.js +165 -0
  21. package/dist/commands/model.js +325 -0
  22. package/dist/commands/permissions.js +140 -0
  23. package/dist/commands/ping.js +13 -0
  24. package/dist/commands/queue.js +133 -0
  25. package/dist/commands/remove-project.js +119 -0
  26. package/dist/commands/rename.js +70 -0
  27. package/dist/commands/restart-opencode-server.js +77 -0
  28. package/dist/commands/resume.js +276 -0
  29. package/dist/commands/run-config.js +79 -0
  30. package/dist/commands/run.js +240 -0
  31. package/dist/commands/schedule.js +170 -0
  32. package/dist/commands/session-info.js +58 -0
  33. package/dist/commands/session.js +191 -0
  34. package/dist/commands/settings.js +84 -0
  35. package/dist/commands/share.js +89 -0
  36. package/dist/commands/status.js +79 -0
  37. package/dist/commands/sync.js +119 -0
  38. package/dist/commands/theme.js +53 -0
  39. package/dist/commands/types.js +2 -0
  40. package/dist/commands/undo-redo.js +170 -0
  41. package/dist/commands/user-command.js +135 -0
  42. package/dist/commands/verbosity.js +59 -0
  43. package/dist/commands/worktree-settings.js +50 -0
  44. package/dist/commands/worktree.js +288 -0
  45. package/dist/config.js +139 -0
  46. package/dist/database.js +585 -0
  47. package/dist/discord-bot.js +700 -0
  48. package/dist/discord-utils.js +336 -0
  49. package/dist/discord-utils.test.js +20 -0
  50. package/dist/errors.js +193 -0
  51. package/dist/escape-backticks.test.js +429 -0
  52. package/dist/format-tables.js +96 -0
  53. package/dist/format-tables.test.js +418 -0
  54. package/dist/genai-worker-wrapper.js +109 -0
  55. package/dist/genai-worker.js +299 -0
  56. package/dist/genai.js +230 -0
  57. package/dist/image-utils.js +107 -0
  58. package/dist/interaction-handler.js +289 -0
  59. package/dist/limit-heading-depth.js +25 -0
  60. package/dist/limit-heading-depth.test.js +105 -0
  61. package/dist/logger.js +111 -0
  62. package/dist/markdown.js +323 -0
  63. package/dist/markdown.test.js +269 -0
  64. package/dist/message-formatting.js +447 -0
  65. package/dist/message-formatting.test.js +73 -0
  66. package/dist/openai-realtime.js +226 -0
  67. package/dist/opencode.js +224 -0
  68. package/dist/reaction-handler.js +128 -0
  69. package/dist/scheduler.js +93 -0
  70. package/dist/security.js +200 -0
  71. package/dist/session-handler.js +1436 -0
  72. package/dist/system-message.js +138 -0
  73. package/dist/tools.js +354 -0
  74. package/dist/unnest-code-blocks.js +117 -0
  75. package/dist/unnest-code-blocks.test.js +432 -0
  76. package/dist/utils.js +95 -0
  77. package/dist/voice-handler.js +569 -0
  78. package/dist/voice.js +344 -0
  79. package/dist/worker-types.js +4 -0
  80. package/dist/worktree-utils.js +134 -0
  81. package/dist/xml.js +90 -0
  82. package/dist/xml.test.js +32 -0
  83. package/package.json +84 -0
@@ -0,0 +1,432 @@
1
+ import { test, expect } from 'vitest';
2
+ import { unnestCodeBlocksFromLists } from './unnest-code-blocks.js';
3
+ test('basic - single item with code block', () => {
4
+ const input = `- Item 1
5
+ \`\`\`js
6
+ const x = 1
7
+ \`\`\``;
8
+ const result = unnestCodeBlocksFromLists(input);
9
+ expect(result).toMatchInlineSnapshot(`
10
+ "- Item 1
11
+
12
+ \`\`\`js
13
+ const x = 1
14
+ \`\`\`"
15
+ `);
16
+ });
17
+ test('multiple items - code in middle item only', () => {
18
+ const input = `- Item 1
19
+ - Item 2
20
+ \`\`\`js
21
+ const x = 1
22
+ \`\`\`
23
+ - Item 3`;
24
+ const result = unnestCodeBlocksFromLists(input);
25
+ expect(result).toMatchInlineSnapshot(`
26
+ "- Item 1
27
+ - Item 2
28
+
29
+ \`\`\`js
30
+ const x = 1
31
+ \`\`\`
32
+ - Item 3"
33
+ `);
34
+ });
35
+ test('multiple code blocks in one item', () => {
36
+ const input = `- Item with two code blocks
37
+ \`\`\`js
38
+ const a = 1
39
+ \`\`\`
40
+ \`\`\`python
41
+ b = 2
42
+ \`\`\``;
43
+ const result = unnestCodeBlocksFromLists(input);
44
+ expect(result).toMatchInlineSnapshot(`
45
+ "- Item with two code blocks
46
+
47
+ \`\`\`js
48
+ const a = 1
49
+ \`\`\`
50
+ \`\`\`python
51
+ b = 2
52
+ \`\`\`"
53
+ `);
54
+ });
55
+ test('nested list with code', () => {
56
+ const input = `- Item 1
57
+ - Nested item
58
+ \`\`\`js
59
+ const x = 1
60
+ \`\`\`
61
+ - Item 2`;
62
+ const result = unnestCodeBlocksFromLists(input);
63
+ expect(result).toMatchInlineSnapshot(`
64
+ "- Item 1
65
+ - Nested item
66
+
67
+ \`\`\`js
68
+ const x = 1
69
+ \`\`\`
70
+ - Item 2"
71
+ `);
72
+ });
73
+ test('ordered list preserves numbering', () => {
74
+ const input = `1. First item
75
+ \`\`\`js
76
+ const a = 1
77
+ \`\`\`
78
+ 2. Second item
79
+ 3. Third item`;
80
+ const result = unnestCodeBlocksFromLists(input);
81
+ expect(result).toMatchInlineSnapshot(`
82
+ "1. First item
83
+
84
+ \`\`\`js
85
+ const a = 1
86
+ \`\`\`
87
+ 2. Second item
88
+ 3. Third item"
89
+ `);
90
+ });
91
+ test('list without code blocks unchanged', () => {
92
+ const input = `- Item 1
93
+ - Item 2
94
+ - Item 3`;
95
+ const result = unnestCodeBlocksFromLists(input);
96
+ expect(result).toMatchInlineSnapshot(`
97
+ "- Item 1
98
+ - Item 2
99
+ - Item 3"
100
+ `);
101
+ });
102
+ test('mixed - some items have code, some dont', () => {
103
+ const input = `- Normal item
104
+ - Item with code
105
+ \`\`\`js
106
+ const x = 1
107
+ \`\`\`
108
+ - Another normal item
109
+ - Another with code
110
+ \`\`\`python
111
+ y = 2
112
+ \`\`\``;
113
+ const result = unnestCodeBlocksFromLists(input);
114
+ expect(result).toMatchInlineSnapshot(`
115
+ "- Normal item
116
+ - Item with code
117
+
118
+ \`\`\`js
119
+ const x = 1
120
+ \`\`\`
121
+ - Another normal item
122
+ - Another with code
123
+
124
+ \`\`\`python
125
+ y = 2
126
+ \`\`\`"
127
+ `);
128
+ });
129
+ test('text before and after code in same item', () => {
130
+ const input = `- Start text
131
+ \`\`\`js
132
+ const x = 1
133
+ \`\`\`
134
+ End text`;
135
+ const result = unnestCodeBlocksFromLists(input);
136
+ expect(result).toMatchInlineSnapshot(`
137
+ "- Start text
138
+
139
+ \`\`\`js
140
+ const x = 1
141
+ \`\`\`
142
+ - End text"
143
+ `);
144
+ });
145
+ test('preserves content outside lists', () => {
146
+ const input = `# Heading
147
+
148
+ Some paragraph text.
149
+
150
+ - List item
151
+ \`\`\`js
152
+ const x = 1
153
+ \`\`\`
154
+
155
+ More text after.`;
156
+ const result = unnestCodeBlocksFromLists(input);
157
+ expect(result).toMatchInlineSnapshot(`
158
+ "# Heading
159
+
160
+ Some paragraph text.
161
+
162
+ - List item
163
+
164
+ \`\`\`js
165
+ const x = 1
166
+ \`\`\`
167
+
168
+ More text after."
169
+ `);
170
+ });
171
+ test('code block at root level unchanged', () => {
172
+ const input = `\`\`\`js
173
+ const x = 1
174
+ \`\`\``;
175
+ const result = unnestCodeBlocksFromLists(input);
176
+ expect(result).toMatchInlineSnapshot(`
177
+ "\`\`\`js
178
+ const x = 1
179
+ \`\`\`"
180
+ `);
181
+ });
182
+ test('handles code block without language', () => {
183
+ const input = `- Item
184
+ \`\`\`
185
+ plain code
186
+ \`\`\``;
187
+ const result = unnestCodeBlocksFromLists(input);
188
+ expect(result).toMatchInlineSnapshot(`
189
+ "- Item
190
+
191
+ \`\`\`
192
+ plain code
193
+ \`\`\`"
194
+ `);
195
+ });
196
+ test('handles empty list item with code', () => {
197
+ const input = `- \`\`\`js
198
+ const x = 1
199
+ \`\`\``;
200
+ const result = unnestCodeBlocksFromLists(input);
201
+ expect(result).toMatchInlineSnapshot(`
202
+ "\`\`\`js
203
+ const x = 1
204
+ \`\`\`"
205
+ `);
206
+ });
207
+ test('numbered list with text after code block', () => {
208
+ const input = `1. First item
209
+ \`\`\`js
210
+ const a = 1
211
+ \`\`\`
212
+ Text after the code
213
+ 2. Second item`;
214
+ const result = unnestCodeBlocksFromLists(input);
215
+ expect(result).toMatchInlineSnapshot(`
216
+ "1. First item
217
+
218
+ \`\`\`js
219
+ const a = 1
220
+ \`\`\`
221
+ - Text after the code
222
+ 2. Second item"
223
+ `);
224
+ });
225
+ test('numbered list with multiple code blocks and text between', () => {
226
+ const input = `1. First item
227
+ \`\`\`js
228
+ const a = 1
229
+ \`\`\`
230
+ Middle text
231
+ \`\`\`python
232
+ b = 2
233
+ \`\`\`
234
+ Final text
235
+ 2. Second item`;
236
+ const result = unnestCodeBlocksFromLists(input);
237
+ expect(result).toMatchInlineSnapshot(`
238
+ "1. First item
239
+
240
+ \`\`\`js
241
+ const a = 1
242
+ \`\`\`
243
+ - Middle text
244
+
245
+ \`\`\`python
246
+ b = 2
247
+ \`\`\`
248
+ - Final text
249
+ 2. Second item"
250
+ `);
251
+ });
252
+ test('unordered list with multiple code blocks and text between', () => {
253
+ const input = `- First item
254
+ \`\`\`js
255
+ const a = 1
256
+ \`\`\`
257
+ Middle text
258
+ \`\`\`python
259
+ b = 2
260
+ \`\`\`
261
+ Final text
262
+ - Second item`;
263
+ const result = unnestCodeBlocksFromLists(input);
264
+ expect(result).toMatchInlineSnapshot(`
265
+ "- First item
266
+
267
+ \`\`\`js
268
+ const a = 1
269
+ \`\`\`
270
+ - Middle text
271
+
272
+ \`\`\`python
273
+ b = 2
274
+ \`\`\`
275
+ - Final text
276
+ - Second item"
277
+ `);
278
+ });
279
+ test('numbered list starting from 5', () => {
280
+ const input = `5. Fifth item
281
+ \`\`\`js
282
+ code
283
+ \`\`\`
284
+ Text after
285
+ 6. Sixth item`;
286
+ const result = unnestCodeBlocksFromLists(input);
287
+ expect(result).toMatchInlineSnapshot(`
288
+ "5. Fifth item
289
+
290
+ \`\`\`js
291
+ code
292
+ \`\`\`
293
+ - Text after
294
+ 6. Sixth item"
295
+ `);
296
+ });
297
+ test('deeply nested list with code', () => {
298
+ const input = `- Level 1
299
+ - Level 2
300
+ - Level 3
301
+ \`\`\`js
302
+ deep code
303
+ \`\`\`
304
+ Text after deep code
305
+ - Another level 3
306
+ - Back to level 2`;
307
+ const result = unnestCodeBlocksFromLists(input);
308
+ expect(result).toMatchInlineSnapshot(`
309
+ "- Level 1
310
+ - Level 2
311
+ - Level 3
312
+
313
+ \`\`\`js
314
+ deep code
315
+ \`\`\`
316
+ - Text after deep code
317
+ - Another level 3
318
+ - Back to level 2"
319
+ `);
320
+ });
321
+ test('nested numbered list inside unordered with code', () => {
322
+ const input = `- Unordered item
323
+ 1. Nested numbered
324
+ \`\`\`js
325
+ code
326
+ \`\`\`
327
+ Text after
328
+ 2. Second nested
329
+ - Another unordered`;
330
+ const result = unnestCodeBlocksFromLists(input);
331
+ expect(result).toMatchInlineSnapshot(`
332
+ "- Unordered item
333
+ 1. Nested numbered
334
+
335
+ \`\`\`js
336
+ code
337
+ \`\`\`
338
+ - Text after
339
+ 2. Second nested
340
+ - Another unordered"
341
+ `);
342
+ });
343
+ test('code block at end of numbered item no text after', () => {
344
+ const input = `1. First with text
345
+ \`\`\`js
346
+ code here
347
+ \`\`\`
348
+ 2. Second item
349
+ 3. Third item`;
350
+ const result = unnestCodeBlocksFromLists(input);
351
+ expect(result).toMatchInlineSnapshot(`
352
+ "1. First with text
353
+
354
+ \`\`\`js
355
+ code here
356
+ \`\`\`
357
+ 2. Second item
358
+ 3. Third item"
359
+ `);
360
+ });
361
+ test('multiple items each with code and text after', () => {
362
+ const input = `1. First
363
+ \`\`\`js
364
+ code1
365
+ \`\`\`
366
+ After first
367
+ 2. Second
368
+ \`\`\`python
369
+ code2
370
+ \`\`\`
371
+ After second
372
+ 3. Third no code`;
373
+ const result = unnestCodeBlocksFromLists(input);
374
+ expect(result).toMatchInlineSnapshot(`
375
+ "1. First
376
+
377
+ \`\`\`js
378
+ code1
379
+ \`\`\`
380
+ - After first
381
+ 2. Second
382
+
383
+ \`\`\`python
384
+ code2
385
+ \`\`\`
386
+ - After second
387
+ 3. Third no code"
388
+ `);
389
+ });
390
+ test('code block immediately after list marker', () => {
391
+ const input = `1. \`\`\`js
392
+ immediate code
393
+ \`\`\`
394
+ 2. Normal item`;
395
+ const result = unnestCodeBlocksFromLists(input);
396
+ expect(result).toMatchInlineSnapshot(`
397
+ "\`\`\`js
398
+ immediate code
399
+ \`\`\`
400
+ 2. Normal item"
401
+ `);
402
+ });
403
+ test('code block with filename metadata', () => {
404
+ const input = `- Item with code
405
+ \`\`\`tsx filename=example.tsx
406
+ const x = 1
407
+ \`\`\``;
408
+ const result = unnestCodeBlocksFromLists(input);
409
+ expect(result).toMatchInlineSnapshot(`
410
+ "- Item with code
411
+
412
+ \`\`\`tsx filename=example.tsx
413
+ const x = 1
414
+ \`\`\`"
415
+ `);
416
+ });
417
+ test('numbered list with filename metadata code block', () => {
418
+ const input = `1. First item
419
+ \`\`\`tsx filename=app.tsx
420
+ export default function App() {}
421
+ \`\`\`
422
+ 2. Second item`;
423
+ const result = unnestCodeBlocksFromLists(input);
424
+ expect(result).toMatchInlineSnapshot(`
425
+ "1. First item
426
+
427
+ \`\`\`tsx filename=app.tsx
428
+ export default function App() {}
429
+ \`\`\`
430
+ 2. Second item"
431
+ `);
432
+ });
package/dist/utils.js ADDED
@@ -0,0 +1,95 @@
1
+ // General utility functions for the bot.
2
+ // Includes Discord OAuth URL generation, array deduplication,
3
+ // abort error detection, and date/time formatting helpers.
4
+ import os from 'node:os';
5
+ import { PermissionsBitField } from 'discord.js';
6
+ export function generateBotInstallUrl({ clientId, permissions = [
7
+ PermissionsBitField.Flags.ViewChannel,
8
+ PermissionsBitField.Flags.ManageChannels,
9
+ PermissionsBitField.Flags.SendMessages,
10
+ PermissionsBitField.Flags.SendMessagesInThreads,
11
+ PermissionsBitField.Flags.CreatePublicThreads,
12
+ PermissionsBitField.Flags.ManageThreads,
13
+ PermissionsBitField.Flags.ReadMessageHistory,
14
+ PermissionsBitField.Flags.AddReactions,
15
+ PermissionsBitField.Flags.ManageMessages,
16
+ PermissionsBitField.Flags.UseExternalEmojis,
17
+ PermissionsBitField.Flags.AttachFiles,
18
+ PermissionsBitField.Flags.Connect,
19
+ PermissionsBitField.Flags.Speak,
20
+ PermissionsBitField.Flags.ManageRoles,
21
+ ], scopes = ['bot'], guildId, disableGuildSelect = false, }) {
22
+ const permissionsBitField = new PermissionsBitField(permissions);
23
+ const permissionsValue = permissionsBitField.bitfield.toString();
24
+ const url = new URL('https://discord.com/api/oauth2/authorize');
25
+ url.searchParams.set('client_id', clientId);
26
+ url.searchParams.set('permissions', permissionsValue);
27
+ url.searchParams.set('scope', scopes.join(' '));
28
+ if (guildId) {
29
+ url.searchParams.set('guild_id', guildId);
30
+ }
31
+ if (disableGuildSelect) {
32
+ url.searchParams.set('disable_guild_select', 'true');
33
+ }
34
+ return url.toString();
35
+ }
36
+ export function deduplicateByKey(arr, keyFn) {
37
+ const seen = new Set();
38
+ return arr.filter((item) => {
39
+ const key = keyFn(item);
40
+ if (seen.has(key)) {
41
+ return false;
42
+ }
43
+ seen.add(key);
44
+ return true;
45
+ });
46
+ }
47
+ export function isAbortError(error, signal) {
48
+ return ((error instanceof Error &&
49
+ (error.name === 'AbortError' ||
50
+ error.name === 'Aborterror' ||
51
+ error.name === 'aborterror' ||
52
+ error.name.toLowerCase() === 'aborterror' ||
53
+ error.name === 'MessageAbortedError' ||
54
+ error.message?.includes('aborted') ||
55
+ (signal?.aborted ?? false))) ||
56
+ (error instanceof DOMException && error.name === 'AbortError'));
57
+ }
58
+ const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
59
+ const TIME_DIVISIONS = [
60
+ { amount: 60, name: 'seconds' },
61
+ { amount: 60, name: 'minutes' },
62
+ { amount: 24, name: 'hours' },
63
+ { amount: 7, name: 'days' },
64
+ { amount: 4.34524, name: 'weeks' },
65
+ { amount: 12, name: 'months' },
66
+ { amount: Number.POSITIVE_INFINITY, name: 'years' },
67
+ ];
68
+ export function formatDistanceToNow(date) {
69
+ let duration = (date.getTime() - Date.now()) / 1000;
70
+ for (const division of TIME_DIVISIONS) {
71
+ if (Math.abs(duration) < division.amount) {
72
+ return rtf.format(Math.round(duration), division.name);
73
+ }
74
+ duration /= division.amount;
75
+ }
76
+ return rtf.format(Math.round(duration), 'years');
77
+ }
78
+ const dtf = new Intl.DateTimeFormat('en-US', {
79
+ month: 'short',
80
+ day: 'numeric',
81
+ year: 'numeric',
82
+ hour: 'numeric',
83
+ minute: '2-digit',
84
+ hour12: true,
85
+ });
86
+ export function formatDateTime(date) {
87
+ return dtf.format(date);
88
+ }
89
+ export function abbreviatePath(fullPath) {
90
+ const home = os.homedir();
91
+ if (fullPath.startsWith(home)) {
92
+ return '~' + fullPath.slice(home.length);
93
+ }
94
+ return fullPath;
95
+ }