kimaki 0.28.0 → 0.30.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/agent-model.e2e.test.js +33 -33
- package/dist/anthropic-account-identity.js +1 -1
- package/dist/anthropic-auth-plugin.js +5 -5
- package/dist/anthropic-auth-plugin.test.js +30 -2
- package/dist/anthropic-auth-state.js +1 -1
- package/dist/bash-tool-schema-plugin.js +18 -0
- package/dist/bash-tool-schema-plugin.test.js +20 -1
- package/dist/bin.js +7 -3
- package/dist/bot-lifecycle.test.js +206 -0
- package/dist/cache-drift-plugin.js +25 -18
- package/dist/cache-rewrite.js +39 -0
- package/dist/cache-rewrite.test.js +41 -0
- package/dist/channel-management.js +113 -51
- package/dist/cli-commands/misc.js +4 -1
- package/dist/cli-commands/project.js +7 -4
- package/dist/cli-commands/session.js +118 -36
- package/dist/cli-commands/thread.js +63 -0
- package/dist/cli-parsing.test.js +3 -3
- package/dist/cli-runner.js +11 -1
- package/dist/cli-send-thread.e2e.test.js +2 -2
- package/dist/cli.js +6 -4
- package/dist/commands/ask-question.js +72 -23
- package/dist/commands/queue.js +84 -36
- package/dist/commands/queue.test.js +15 -0
- package/dist/commands/restart-opencode-server.js +7 -1
- package/dist/commands/session-id.js +5 -5
- package/dist/database.js +149 -6
- package/dist/db.js +34 -0
- package/dist/db.test.js +199 -1
- package/dist/default-channel-provisioning.test.js +2 -1
- package/dist/discord-bot.js +220 -146
- package/dist/discord-command-registration.js +1 -1
- package/dist/external-directory-permissions.e2e.test.js +3 -3
- package/dist/external-directory-project-deny.e2e.test.js +1 -1
- package/dist/forum-sync/watchers.js +15 -0
- package/dist/gateway-proxy.e2e.test.js +9 -9
- package/dist/guild-categories.test.js +225 -0
- package/dist/hrana-server.js +68 -14
- package/dist/interaction-handler.js +5 -1
- package/dist/kimaki-opencode-plugin.js +2 -0
- package/dist/kimaki-opencode-plugin.test.js +28 -0
- package/dist/list-channel-threads.js +163 -0
- package/dist/list-channel-threads.test.js +80 -0
- package/dist/markdown.js +164 -102
- package/dist/markdown.test.js +88 -20
- package/dist/message-formatting.js +29 -18
- package/dist/message-formatting.test.js +111 -3
- package/dist/opencode.js +27 -12
- package/dist/question-abort-clears-dropdown.e2e.test.js +1 -1
- package/dist/question-abort-resume.e2e.test.js +100 -0
- package/dist/question-abort-retry-text-before-dropdown.e2e.test.js +1 -1
- package/dist/question-text-before-dropdown.e2e.test.js +1 -1
- package/dist/queue-advanced-abort.e2e.test.js +3 -3
- package/dist/queue-advanced-action-buttons.e2e.test.js +5 -5
- package/dist/queue-advanced-e2e-setup.js +63 -0
- package/dist/queue-advanced-footer.e2e.test.js +18 -18
- package/dist/queue-advanced-model-switch.e2e.test.js +3 -4
- package/dist/queue-advanced-permissions-typing.e2e.test.js +2 -2
- package/dist/queue-advanced-typing-interrupt.e2e.test.js +3 -3
- package/dist/queue-delete-message.e2e.test.js +4 -4
- package/dist/queue-drain-after-interactive-ui.e2e.test.js +3 -3
- package/dist/queue-interrupt-drain.e2e.test.js +10 -10
- package/dist/queue-question-select-drain.e2e.test.js +5 -5
- package/dist/runtime-lifecycle.e2e.test.js +95 -11
- package/dist/schema.js +21 -0
- package/dist/session-handler/event-stream-state.js +240 -6
- package/dist/session-handler/event-stream-state.test.js +1116 -130
- package/dist/session-handler/thread-runtime-state.js +4 -0
- package/dist/session-handler/thread-session-runtime.js +499 -89
- package/dist/sleep.e2e.test.js +7 -7
- package/dist/store.js +1 -1
- package/dist/system-message.js +64 -23
- package/dist/system-message.test.js +69 -23
- package/dist/task-id-plugin.js +16 -0
- package/dist/task-runner.js +7 -0
- package/dist/task-schedule.test.js +1 -1
- package/dist/test-utils.js +5 -3
- package/dist/thread-message-queue.e2e.test.js +30 -31
- package/dist/undo-redo.e2e.test.js +3 -3
- package/dist/voice-message.e2e.test.js +64 -26
- package/dist/voice-routing-input.e2e.test.js +4 -4
- package/dist/voice-routing-question.e2e.test.js +4 -4
- package/dist/voice.js +107 -21
- package/dist/voice.test.js +52 -1
- package/dist/wait-session.js +24 -2
- package/dist/worktree-lifecycle.e2e.test.js +8 -8
- package/package.json +7 -7
- package/skills/egaki/SKILL.md +14 -0
- package/skills/holocron/SKILL.md +125 -42
- package/skills/opensrc/SKILL.md +20 -3
- package/skills/spiceflow/SKILL.md +53 -172
- package/skills/strada/SKILL.md +61 -232
- package/src/agent-model.e2e.test.ts +33 -33
- package/src/anthropic-account-identity.ts +1 -1
- package/src/anthropic-auth-plugin.test.ts +37 -2
- package/src/anthropic-auth-plugin.ts +9 -7
- package/src/anthropic-auth-state.ts +1 -0
- package/src/bash-tool-schema-plugin.test.ts +29 -0
- package/src/bash-tool-schema-plugin.ts +31 -0
- package/src/bin.ts +7 -3
- package/src/bot-lifecycle.test.ts +252 -0
- package/src/cache-drift-plugin.ts +34 -24
- package/src/cache-rewrite.test.ts +49 -0
- package/src/cache-rewrite.ts +96 -0
- package/src/channel-management.ts +144 -65
- package/src/cli-commands/misc.ts +4 -1
- package/src/cli-commands/project.ts +9 -4
- package/src/cli-commands/session.ts +167 -38
- package/src/cli-commands/thread.ts +86 -0
- package/src/cli-parsing.test.ts +3 -3
- package/src/cli-runner.ts +11 -0
- package/src/cli-send-thread.e2e.test.ts +2 -2
- package/src/cli.ts +8 -6
- package/src/commands/ask-question.ts +88 -25
- package/src/commands/queue.test.ts +19 -0
- package/src/commands/queue.ts +111 -45
- package/src/commands/restart-opencode-server.ts +12 -1
- package/src/commands/session-id.ts +5 -5
- package/src/database.ts +219 -6
- package/src/db.test.ts +236 -0
- package/src/db.ts +38 -0
- package/src/default-channel-provisioning.test.ts +2 -1
- package/src/discord-bot.ts +272 -185
- package/src/discord-command-registration.ts +3 -1
- package/src/external-directory-permissions.e2e.test.ts +3 -3
- package/src/external-directory-project-deny.e2e.test.ts +1 -1
- package/src/forum-sync/watchers.ts +20 -0
- package/src/gateway-proxy.e2e.test.ts +9 -9
- package/src/guild-categories.test.ts +260 -0
- package/src/hrana-server.ts +96 -16
- package/src/interaction-handler.ts +7 -0
- package/src/kimaki-opencode-plugin.test.ts +34 -0
- package/src/kimaki-opencode-plugin.ts +2 -0
- package/src/list-channel-threads.test.ts +90 -0
- package/src/list-channel-threads.ts +252 -0
- package/src/markdown.test.ts +108 -22
- package/src/markdown.ts +216 -103
- package/src/message-formatting.test.ts +129 -2
- package/src/message-formatting.ts +28 -14
- package/src/opencode.ts +28 -13
- package/src/question-abort-clears-dropdown.e2e.test.ts +1 -1
- package/src/question-abort-resume.e2e.test.ts +121 -0
- package/src/question-abort-retry-text-before-dropdown.e2e.test.ts +1 -1
- package/src/question-text-before-dropdown.e2e.test.ts +1 -1
- package/src/queue-advanced-abort.e2e.test.ts +3 -3
- package/src/queue-advanced-action-buttons.e2e.test.ts +5 -5
- package/src/queue-advanced-e2e-setup.ts +65 -0
- package/src/queue-advanced-footer.e2e.test.ts +18 -18
- package/src/queue-advanced-model-switch.e2e.test.ts +3 -4
- package/src/queue-advanced-permissions-typing.e2e.test.ts +2 -2
- package/src/queue-advanced-typing-interrupt.e2e.test.ts +3 -3
- package/src/queue-delete-message.e2e.test.ts +4 -4
- package/src/queue-drain-after-interactive-ui.e2e.test.ts +3 -3
- package/src/queue-interrupt-drain.e2e.test.ts +10 -10
- package/src/queue-question-select-drain.e2e.test.ts +5 -5
- package/src/runtime-lifecycle.e2e.test.ts +104 -10
- package/src/schema.sql +16 -0
- package/src/schema.ts +23 -0
- package/src/session-handler/event-stream-state.test.ts +1184 -133
- package/src/session-handler/event-stream-state.ts +322 -8
- package/src/session-handler/thread-runtime-state.ts +5 -0
- package/src/session-handler/thread-session-runtime.ts +597 -95
- package/src/sleep.e2e.test.ts +7 -7
- package/src/store.ts +2 -2
- package/src/system-message.test.ts +70 -23
- package/src/system-message.ts +67 -22
- package/src/task-id-plugin.ts +17 -0
- package/src/task-runner.ts +8 -0
- package/src/task-schedule.test.ts +1 -1
- package/src/test-utils.ts +5 -3
- package/src/thread-message-queue.e2e.test.ts +30 -31
- package/src/undo-redo.e2e.test.ts +3 -3
- package/src/voice-message.e2e.test.ts +70 -26
- package/src/voice-routing-input.e2e.test.ts +4 -4
- package/src/voice-routing-question.e2e.test.ts +4 -4
- package/src/voice.test.ts +56 -0
- package/src/voice.ts +135 -25
- package/src/wait-session.ts +25 -2
- package/src/worktree-lifecycle.e2e.test.ts +8 -8
package/src/sleep.e2e.test.ts
CHANGED
|
@@ -344,19 +344,19 @@ describe('kimaki_sleep', () => {
|
|
|
344
344
|
"--- from: user (sleep-tester)
|
|
345
345
|
SLEEP_TOOL_MARKER wait for the deploy
|
|
346
346
|
--- from: assistant (TestBot)
|
|
347
|
-
|
|
347
|
+
-# *using deterministic-provider/deterministic-v2*
|
|
348
348
|
> waiting for the deploy
|
|
349
349
|
|
|
350
350
|
┣ kimaki_sleep until 2030-01-01T09:00:00Z _waiting for the deploy_
|
|
351
351
|
|
|
352
352
|
> sleep-started
|
|
353
|
-
|
|
354
|
-
|
|
353
|
+
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
354
|
+
-# Woke after sleeping until 2030-01-01 09:00 UTC
|
|
355
355
|
Reason: waiting for the deploy
|
|
356
356
|
Continue the work you were waiting for.
|
|
357
357
|
[embed]
|
|
358
358
|
> sleep-wake-done
|
|
359
|
-
|
|
359
|
+
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
360
360
|
`)
|
|
361
361
|
|
|
362
362
|
// The wake must continue the SAME OpenCode session, not start a new one:
|
|
@@ -465,16 +465,16 @@ describe('kimaki_sleep', () => {
|
|
|
465
465
|
"--- from: user (sleep-tester)
|
|
466
466
|
SLEEP_CANCEL_MARKER wait for something
|
|
467
467
|
--- from: assistant (TestBot)
|
|
468
|
-
|
|
468
|
+
-# *using deterministic-provider/deterministic-v2*
|
|
469
469
|
┣ kimaki_sleep for 2h _cancel me_
|
|
470
470
|
|
|
471
471
|
> cancel-sleep-started
|
|
472
|
-
|
|
472
|
+
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
|
|
473
473
|
--- from: user (sleep-tester)
|
|
474
474
|
SLEEP_CANCEL_FOLLOWUP never mind, keep going
|
|
475
475
|
--- from: assistant (TestBot)
|
|
476
476
|
> cancel-followup-done
|
|
477
|
-
|
|
477
|
+
-# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
|
|
478
478
|
`)
|
|
479
479
|
},
|
|
480
480
|
20_000,
|
package/src/store.ts
CHANGED
|
@@ -69,7 +69,7 @@ export type KimakiState = {
|
|
|
69
69
|
critiqueEnabled: boolean
|
|
70
70
|
|
|
71
71
|
// Whether final session footers mention the thread creator.
|
|
72
|
-
// Changes: set once at startup from --
|
|
72
|
+
// Changes: set once at startup from --enable-footer-mentions (default off).
|
|
73
73
|
// Read by: ThreadSessionRuntime.emitFooter().
|
|
74
74
|
footerMentionsEnabled: boolean
|
|
75
75
|
|
|
@@ -204,7 +204,7 @@ export const store = createStore<KimakiState>(() => ({
|
|
|
204
204
|
defaultVerbosity: 'text_and_essential_tools',
|
|
205
205
|
defaultMentionMode: false,
|
|
206
206
|
critiqueEnabled: true,
|
|
207
|
-
footerMentionsEnabled:
|
|
207
|
+
footerMentionsEnabled: false,
|
|
208
208
|
enabledSkills: [],
|
|
209
209
|
disabledSkills: [],
|
|
210
210
|
allowedMentions: ['users'],
|
|
@@ -143,6 +143,17 @@ describe('system-message', () => {
|
|
|
143
143
|
).rejects.toMatchObject({ code: 'EISDIR' })
|
|
144
144
|
})
|
|
145
145
|
|
|
146
|
+
test('tells agents to read compressed session transcripts under 100 KB', () => {
|
|
147
|
+
const message = getOpencodeSystemMessage({
|
|
148
|
+
sessionId: 'ses_123',
|
|
149
|
+
channelId: 'chan_123',
|
|
150
|
+
threadId: 'thread_123',
|
|
151
|
+
})
|
|
152
|
+
expect(message).toContain('kimaki session read <sessionId> > ./tmp/session.md 2>/dev/null')
|
|
153
|
+
expect(message).toContain('If it is under 100 KB, read the whole file. Do not grep first.')
|
|
154
|
+
expect(message).not.toContain('rg ')
|
|
155
|
+
})
|
|
156
|
+
|
|
146
157
|
test('includes all-projects session search example', () => {
|
|
147
158
|
const message = getOpencodeSystemMessage({
|
|
148
159
|
sessionId: 'ses_123',
|
|
@@ -165,7 +176,7 @@ describe('system-message', () => {
|
|
|
165
176
|
"kimaki session title 'Short title' --session ses_123",
|
|
166
177
|
)
|
|
167
178
|
expect(message).toContain(
|
|
168
|
-
'
|
|
179
|
+
'Skip the first turn. OpenCode already auto-generates the title from the first message.',
|
|
169
180
|
)
|
|
170
181
|
expect(message).toContain('thread-name="..."')
|
|
171
182
|
expect(message).toContain('Do not retitle every turn')
|
|
@@ -415,14 +426,14 @@ describe('system-message', () => {
|
|
|
415
426
|
|
|
416
427
|
## updating the session title
|
|
417
428
|
|
|
418
|
-
|
|
429
|
+
Skip the first turn. OpenCode already auto-generates the title from the first message.
|
|
430
|
+
On later turns, if the scope or goal changed, run:
|
|
419
431
|
|
|
420
432
|
kimaki session title 'Short title' --session ses_123
|
|
421
433
|
|
|
422
|
-
|
|
423
|
-
This updates the OpenCode title. Discord follows automatically.
|
|
434
|
+
Current Discord title is in \`<discord-user thread-name="..." />\`. Discord follows the OpenCode title.
|
|
424
435
|
Do not retitle every turn. Discord rate-limits thread renames.
|
|
425
|
-
Keep titles short.
|
|
436
|
+
Keep titles short. No emoji. No ⬦, btw:, or Fork: prefixes.
|
|
426
437
|
|
|
427
438
|
## discord user mentions
|
|
428
439
|
|
|
@@ -456,7 +467,17 @@ describe('system-message', () => {
|
|
|
456
467
|
|
|
457
468
|
kimaki send --thread <thread_id> --prompt 'follow-up prompt' --agent <current_agent>
|
|
458
469
|
|
|
459
|
-
Use this when you already have the Discord thread ID. Prefer \`--thread\` over \`--session
|
|
470
|
+
Use this when you already have the Discord thread ID. Prefer \`--thread\` over \`--session\`. Discord thread IDs work on every computer. Session IDs do not.
|
|
471
|
+
|
|
472
|
+
A session ID only exists in the local SQLite database and OpenCode server on the computer that created it. \`kimaki send --session\` and \`kimaki session read\` fail on another computer.
|
|
473
|
+
|
|
474
|
+
To continue a remote thread, find the Discord channel, list its threads, then send with \`--thread\`:
|
|
475
|
+
|
|
476
|
+
\`\`\`bash
|
|
477
|
+
kimaki project list --all --json
|
|
478
|
+
kimaki thread list --channel <channel_id> --json
|
|
479
|
+
kimaki send --thread <thread_id> --prompt 'continue the work' --agent <current_agent>
|
|
480
|
+
\`\`\`
|
|
460
481
|
|
|
461
482
|
To send to the thread associated with a known session (same machine only):
|
|
462
483
|
|
|
@@ -530,7 +551,7 @@ describe('system-message', () => {
|
|
|
530
551
|
|
|
531
552
|
Scheduled tasks do not overlap by default. Add \`--allow-concurrency\` only when concurrent sessions from the same task are safe.
|
|
532
553
|
|
|
533
|
-
|
|
554
|
+
For autonomous scheduled tasks, omit \`--user\` by default so each run stays out of the user's Discord sidebar. Put the user's Discord ID in the task instructions instead, and tell the agent to mention that ID only when it completed work worth reviewing, found an issue, or needs a decision. Do not mention the user when the run found no work or made no changes. Reserve \`--user\` for reminders that must surface every time they fire.
|
|
534
555
|
|
|
535
556
|
ALL scheduling is in UTC. Dates must be UTC ISO format ending with \`Z\`. Cron expressions also fire in UTC (e.g. \`0 9 * * 1\` means 9:00 UTC every Monday).
|
|
536
557
|
When the user specifies a time without a timezone, ask them to confirm their timezone or the UTC equivalent. Never guess the user's timezone.
|
|
@@ -542,7 +563,7 @@ describe('system-message', () => {
|
|
|
542
563
|
- \`--pre-run\` to start only when a project command exits with code 0
|
|
543
564
|
- \`--allow-concurrency\` to permit overlapping runs from the same task
|
|
544
565
|
- \`--parent-session\` to pass this session as parent of the scheduled child
|
|
545
|
-
- \`--user\` to add a specific user to
|
|
566
|
+
- \`--user\` to add a specific user to every scheduled thread. Use this for reminders, not autonomous tasks
|
|
546
567
|
|
|
547
568
|
\`--wait\` is incompatible with \`--send-at\` because scheduled tasks run in the future.
|
|
548
569
|
|
|
@@ -567,9 +588,9 @@ describe('system-message', () => {
|
|
|
567
588
|
|
|
568
589
|
Notification strategy:
|
|
569
590
|
- NEVER use \`@username\` (e.g. \`@Tommy\`) directly in task prompts. The prompt text becomes the first message in the thread, so a raw \`@\` mention triggers an actual Discord ping every time the task fires. Instead, wrap it in inline code like \`\\\`@Tommy\\\`\`, or use Discord user ID mentions like \`<@USER_ID>\` only in the body of the prompt where the agent will process it, not in the opening line.
|
|
570
|
-
-
|
|
571
|
-
-
|
|
572
|
-
-
|
|
591
|
+
- For autonomous tasks, include the user's Discord ID in the task md file and instruct the agent to mention it only after completing work the user should review, when reporting an issue, or when asking for a decision.
|
|
592
|
+
- Do not mention the user if the task found no work, made no changes, or has nothing actionable to report. Archive that session instead: \`kimaki session archive thread_123 (or --session ses_123)\`
|
|
593
|
+
- Do not pass \`--user\` for autonomous tasks. It adds the user to every task thread before the result is known. Use it only when every occurrence must appear in the user's sidebar, such as an explicit reminder.
|
|
573
594
|
|
|
574
595
|
Manage scheduled tasks with:
|
|
575
596
|
|
|
@@ -577,7 +598,7 @@ describe('system-message', () => {
|
|
|
577
598
|
kimaki task edit <id> --prompt "new prompt" [--send-at "new schedule"] [--pre-run "command"] [--allow-concurrency true|false] [--user "<discord-user-id>"] [--model "provider/model"] [--agent "<agent>"]
|
|
578
599
|
kimaki task delete <id>
|
|
579
600
|
|
|
580
|
-
\`kimaki task list\` prints \`userId\`, \`agent\`, and \`model\` columns.
|
|
601
|
+
\`kimaki task list\` prints \`userId\`, \`agent\`, and \`model\` columns. For autonomous tasks, \`userId\` should normally be \`-\`; put the Discord ID and conditional mention rule in the task instructions instead. Clear a stored user with \`kimaki task edit <id> --user ''\`. Add \`--user '<discord-user-id>'\` only for tasks whose every occurrence must surface. Change model or agent in place with \`--model\` / \`--agent\` (empty string clears the override). Do not read SQLite or recreate the task just to swap model.
|
|
581
602
|
|
|
582
603
|
\`kimaki session list\` also shows if a session was started by a scheduled \`delay\` or \`cron\` task, including task ID when available.
|
|
583
604
|
|
|
@@ -656,10 +677,15 @@ describe('system-message', () => {
|
|
|
656
677
|
kimaki session list --json # machine-readable output
|
|
657
678
|
kimaki session list --project /path/to/project # specific project
|
|
658
679
|
|
|
680
|
+
# List sessions across every locally registered project
|
|
681
|
+
kimaki session list --all
|
|
682
|
+
|
|
659
683
|
# List only in-progress sessions. Exit status is 1 when none remain.
|
|
660
684
|
kimaki session list --active
|
|
661
685
|
\`\`\`
|
|
662
686
|
|
|
687
|
+
Each row shows the session title, project directory, \`status: working\` or \`status: idle\`, and \`tokens: N\` (total token footprint) when available. Kimaki-started sessions also show their Discord \`thread\` ID.
|
|
688
|
+
|
|
663
689
|
Titles prefixed with \`btw:\` are side sessions that answer a related user question in parallel. They are not duplicate sessions of the main task.
|
|
664
690
|
|
|
665
691
|
To search past sessions (supports plain text or /regex/flags). Defaults to this project and the last 14 days. Use \`--days 0\` for all time. Use \`--all\` to search every locally registered project:
|
|
@@ -673,14 +699,13 @@ describe('system-message', () => {
|
|
|
673
699
|
kimaki session search "auth timeout" --all
|
|
674
700
|
\`\`\`
|
|
675
701
|
|
|
676
|
-
To read a session
|
|
677
|
-
Logs go to stderr, so redirect stderr to hide them:
|
|
702
|
+
To read a session as markdown, pipe to a file. Logs go to stderr:
|
|
678
703
|
|
|
679
704
|
\`\`\`bash
|
|
680
705
|
kimaki session read <sessionId> > ./tmp/session.md 2>/dev/null
|
|
681
706
|
\`\`\`
|
|
682
707
|
|
|
683
|
-
|
|
708
|
+
The dump is already compressed (no thinking, truncated tool inputs). If it is under 100 KB, read the whole file. Do not grep first. Use \`--thinking\` / \`--verbose\` only when you need the full dump.
|
|
684
709
|
|
|
685
710
|
### who edited a file
|
|
686
711
|
|
|
@@ -709,18 +734,25 @@ describe('system-message', () => {
|
|
|
709
734
|
|
|
710
735
|
When the user references another project by name, run \`kimaki project list\` to find its directory path and channel ID. Then read files, search code, or run commands directly in that directory. If the project is not listed, use \`kimaki project add /path/to/repo\` to register it and create a Discord channel for it. Do not add subfolders of an existing project — only add root project directories.
|
|
711
736
|
|
|
712
|
-
When the user uses \`#project-name\` syntax, they usually mean a Kimaki project channel. Use \`kimaki project list --json\` to resolve the \`channel_name\` to its repo working directory. The JSON output includes \`guild_id\` and \`guild_name\`
|
|
737
|
+
When the user uses \`#project-name\` syntax, they usually mean a Kimaki project channel. Use \`kimaki project list --json\` to resolve the \`channel_name\` to its repo working directory. The JSON output includes \`guild_id\` and \`guild_name\` so you can tell channels with the same name apart. Prefer \`guild_id\` when there are duplicates. It is stable. \`guild_name\` can change.
|
|
713
738
|
|
|
714
|
-
|
|
739
|
+
If the local list has no match, the channel may live on another computer. Scan Discord with \`kimaki project list --all --json\` and send by \`channel_id\`. Remote rows have \`is_local: false\` and \`directory: null\`. Do not use \`--project\` or \`--session\` for those channels.
|
|
715
740
|
|
|
716
|
-
|
|
717
|
-
# 1. Find the session ID by searching thread titles across all projects
|
|
718
|
-
kimaki session list --project /path/to/project --json | jq -r '.[] | select(.title | test("Thread Title"; "i")) | .id + " | " + .title'
|
|
741
|
+
When the user uses \`#Some Thread Title\` with spaces, they mean a **thread title**, not a project channel. On this computer, search local sessions, then read the markdown:
|
|
719
742
|
|
|
720
|
-
|
|
743
|
+
\`\`\`bash
|
|
744
|
+
kimaki session list --project /path/to/project --json
|
|
721
745
|
kimaki session read <sessionId> > ./tmp/session.md 2>/dev/null
|
|
722
746
|
\`\`\`
|
|
723
747
|
|
|
748
|
+
If that local search has no match, list Discord threads instead of using a session ID:
|
|
749
|
+
|
|
750
|
+
\`\`\`bash
|
|
751
|
+
kimaki project list --all --json
|
|
752
|
+
kimaki thread list --channel <channel_id> --json
|
|
753
|
+
kimaki send --thread <thread_id> --prompt 'continue the work' --agent <current_agent>
|
|
754
|
+
\`\`\`
|
|
755
|
+
|
|
724
756
|
If you don't know which project the thread belongs to, try each project from \`kimaki project list --json\`.
|
|
725
757
|
|
|
726
758
|
\`\`\`bash
|
|
@@ -728,10 +760,18 @@ describe('system-message', () => {
|
|
|
728
760
|
kimaki project list
|
|
729
761
|
kimaki project list --json # machine-readable output with guild_id, guild_name, is_local
|
|
730
762
|
|
|
731
|
-
# Include projects from other machines (scans Kimaki
|
|
763
|
+
# Include projects from other machines (scans Kimaki groups in Discord)
|
|
732
764
|
kimaki project list --all
|
|
733
765
|
kimaki project list --all --json # remote projects have is_local: false and directory: null
|
|
734
766
|
|
|
767
|
+
# Find a channel on another computer, then send a new prompt there
|
|
768
|
+
kimaki project list --all --json
|
|
769
|
+
kimaki send --channel <channel_id> --prompt 'Plan how to update the API client to v2' --agent <current_agent>
|
|
770
|
+
|
|
771
|
+
# Continue an existing remote thread. Session IDs do not work across computers.
|
|
772
|
+
kimaki thread list --channel <channel_id> --json
|
|
773
|
+
kimaki send --thread <thread_id> --prompt 'continue the work' --agent <current_agent>
|
|
774
|
+
|
|
735
775
|
# Resolve by channel name (prefer adding guild_name filter if duplicates exist)
|
|
736
776
|
kimaki project list --json | jq -r '.[] | select(.channel_name == "project-name") | .channel_id + " " + .guild_name + " " + .directory'
|
|
737
777
|
|
|
@@ -793,7 +833,7 @@ describe('system-message', () => {
|
|
|
793
833
|
|
|
794
834
|
\`session list --active\` exits with status 0 while it finds active sessions and status 1 when none remain. Exclude the current session in a wait loop so the loop does not wait for itself.
|
|
795
835
|
|
|
796
|
-
\`session wait\` exits with the session markdown on stdout once the model finishes responding.
|
|
836
|
+
\`session wait\` exits with the session markdown on stdout once the model finishes responding, or when the session pauses to show the user a question (it will not finish on its own until answered).
|
|
797
837
|
|
|
798
838
|
Use \`--wait\` when you need to:
|
|
799
839
|
- **Fix a bug in another project** before continuing here (e.g. fix a dependency, then resume)
|
|
@@ -1084,6 +1124,8 @@ describe('system-message', () => {
|
|
|
1084
1124
|
test('moves per-turn discord metadata into synthetic prompt context', () => {
|
|
1085
1125
|
expect(
|
|
1086
1126
|
getOpencodePromptContext({
|
|
1127
|
+
sessionId: 'ses_123',
|
|
1128
|
+
threadId: 'thread_123',
|
|
1087
1129
|
username: 'Tommy',
|
|
1088
1130
|
userId: 'user_123',
|
|
1089
1131
|
sourceMessageId: 'msg_123',
|
|
@@ -1104,6 +1146,11 @@ describe('system-message', () => {
|
|
|
1104
1146
|
).toMatchInlineSnapshot(`
|
|
1105
1147
|
"<discord-user name="Tommy" user-id="user_123" message-id="msg_123" thread-id="thread_123" thread-name="Fix auth bug" />
|
|
1106
1148
|
|
|
1149
|
+
<system-reminder>
|
|
1150
|
+
Your current OpenCode session ID is: ses_123
|
|
1151
|
+
Your current Discord thread ID is: thread_123
|
|
1152
|
+
</system-reminder>
|
|
1153
|
+
|
|
1107
1154
|
This message was a reply to message
|
|
1108
1155
|
|
|
1109
1156
|
<replied-message author="alice">
|
package/src/system-message.ts
CHANGED
|
@@ -392,6 +392,8 @@ function escapePromptText(value: string): string {
|
|
|
392
392
|
}
|
|
393
393
|
|
|
394
394
|
export function getOpencodePromptContext({
|
|
395
|
+
sessionId,
|
|
396
|
+
threadId,
|
|
395
397
|
username,
|
|
396
398
|
userId,
|
|
397
399
|
sourceMessageId,
|
|
@@ -402,6 +404,8 @@ export function getOpencodePromptContext({
|
|
|
402
404
|
currentAgent,
|
|
403
405
|
worktreeChanged,
|
|
404
406
|
}: {
|
|
407
|
+
sessionId?: string
|
|
408
|
+
threadId?: string
|
|
405
409
|
username?: string
|
|
406
410
|
userId?: string
|
|
407
411
|
sourceMessageId?: string
|
|
@@ -429,6 +433,17 @@ export function getOpencodePromptContext({
|
|
|
429
433
|
? [` thread-name="${escapePromptAttribute(threadName)}"`]
|
|
430
434
|
: []),
|
|
431
435
|
].join('')
|
|
436
|
+
const identityLines = [
|
|
437
|
+
...(sessionId
|
|
438
|
+
? [`Your current OpenCode session ID is: ${sessionId}`]
|
|
439
|
+
: []),
|
|
440
|
+
...(threadId
|
|
441
|
+
? [`Your current Discord thread ID is: ${threadId}`]
|
|
442
|
+
: []),
|
|
443
|
+
]
|
|
444
|
+
const identityReminder = identityLines.length > 0
|
|
445
|
+
? `<system-reminder>\n${identityLines.join('\n')}\n</system-reminder>`
|
|
446
|
+
: undefined
|
|
432
447
|
const repliedMessageXml = repliedMessage
|
|
433
448
|
? `This message was a reply to message
|
|
434
449
|
|
|
@@ -438,6 +453,7 @@ ${escapePromptText(repliedMessage.text)}
|
|
|
438
453
|
: undefined
|
|
439
454
|
const sections = [
|
|
440
455
|
...(userAttrs ? [`<discord-user${userAttrs} />`] : []),
|
|
456
|
+
...(identityReminder ? [identityReminder] : []),
|
|
441
457
|
...(repliedMessageXml ? [repliedMessageXml] : []),
|
|
442
458
|
...(currentAgent
|
|
443
459
|
? [`<system-reminder>\nCurrent agent: ${currentAgent}\n</system-reminder>`]
|
|
@@ -625,14 +641,14 @@ Different from \`kimaki session archive\` which hides the thread.
|
|
|
625
641
|
|
|
626
642
|
## updating the session title
|
|
627
643
|
|
|
628
|
-
|
|
644
|
+
Skip the first turn. OpenCode already auto-generates the title from the first message.
|
|
645
|
+
On later turns, if the scope or goal changed, run:
|
|
629
646
|
|
|
630
647
|
kimaki session title 'Short title' --session ${sessionId}
|
|
631
648
|
|
|
632
|
-
|
|
633
|
-
This updates the OpenCode title. Discord follows automatically.
|
|
649
|
+
Current Discord title is in \`<discord-user thread-name="..." />\`. Discord follows the OpenCode title.
|
|
634
650
|
Do not retitle every turn. Discord rate-limits thread renames.
|
|
635
|
-
Keep titles short.
|
|
651
|
+
Keep titles short. No emoji. No ⬦, btw:, or Fork: prefixes.
|
|
636
652
|
|
|
637
653
|
## discord user mentions
|
|
638
654
|
|
|
@@ -668,7 +684,17 @@ To send a prompt to an existing thread instead of creating a new one:
|
|
|
668
684
|
|
|
669
685
|
kimaki send --thread <thread_id> --prompt 'follow-up prompt' --agent <current_agent>
|
|
670
686
|
|
|
671
|
-
Use this when you already have the Discord thread ID. Prefer \`--thread\` over \`--session
|
|
687
|
+
Use this when you already have the Discord thread ID. Prefer \`--thread\` over \`--session\`. Discord thread IDs work on every computer. Session IDs do not.
|
|
688
|
+
|
|
689
|
+
A session ID only exists in the local SQLite database and OpenCode server on the computer that created it. \`kimaki send --session\` and \`kimaki session read\` fail on another computer.
|
|
690
|
+
|
|
691
|
+
To continue a remote thread, find the Discord channel, list its threads, then send with \`--thread\`:
|
|
692
|
+
|
|
693
|
+
\`\`\`bash
|
|
694
|
+
kimaki project list --all --json
|
|
695
|
+
kimaki thread list --channel <channel_id> --json
|
|
696
|
+
kimaki send --thread <thread_id> --prompt 'continue the work' --agent <current_agent>
|
|
697
|
+
\`\`\`
|
|
672
698
|
|
|
673
699
|
To send to the thread associated with a known session (same machine only):
|
|
674
700
|
|
|
@@ -738,7 +764,7 @@ Use \`--pre-run '<command>'\` to check whether a scheduled task should start. Ki
|
|
|
738
764
|
|
|
739
765
|
Scheduled tasks do not overlap by default. Add \`--allow-concurrency\` only when concurrent sessions from the same task are safe.
|
|
740
766
|
|
|
741
|
-
|
|
767
|
+
For autonomous scheduled tasks, omit \`--user\` by default so each run stays out of the user's Discord sidebar. Put the user's Discord ID in the task instructions instead, and tell the agent to mention that ID only when it completed work worth reviewing, found an issue, or needs a decision. Do not mention the user when the run found no work or made no changes. Reserve \`--user\` for reminders that must surface every time they fire.
|
|
742
768
|
|
|
743
769
|
ALL scheduling is in UTC. Dates must be UTC ISO format ending with \`Z\`. Cron expressions also fire in UTC (e.g. \`0 9 * * 1\` means 9:00 UTC every Monday).
|
|
744
770
|
When the user specifies a time without a timezone, ask them to confirm their timezone or the UTC equivalent. Never guess the user's timezone.
|
|
@@ -750,7 +776,7 @@ When the user specifies a time without a timezone, ask them to confirm their tim
|
|
|
750
776
|
- \`--pre-run\` to start only when a project command exits with code 0
|
|
751
777
|
- \`--allow-concurrency\` to permit overlapping runs from the same task
|
|
752
778
|
- \`--parent-session\` to pass this session as parent of the scheduled child
|
|
753
|
-
- \`--user\` to add a specific user to
|
|
779
|
+
- \`--user\` to add a specific user to every scheduled thread. Use this for reminders, not autonomous tasks
|
|
754
780
|
|
|
755
781
|
\`--wait\` is incompatible with \`--send-at\` because scheduled tasks run in the future.
|
|
756
782
|
|
|
@@ -775,9 +801,9 @@ For simple reminders and notifications (\`--notify-only\`), inline the prompt di
|
|
|
775
801
|
|
|
776
802
|
Notification strategy:
|
|
777
803
|
- NEVER use \`@username\` (e.g. \`@Tommy\`) directly in task prompts. The prompt text becomes the first message in the thread, so a raw \`@\` mention triggers an actual Discord ping every time the task fires. Instead, wrap it in inline code like \`\\\`@Tommy\\\`\`, or use Discord user ID mentions like \`<@USER_ID>\` only in the body of the prompt where the agent will process it, not in the opening line.
|
|
778
|
-
-
|
|
779
|
-
-
|
|
780
|
-
-
|
|
804
|
+
- For autonomous tasks, include the user's Discord ID in the task md file and instruct the agent to mention it only after completing work the user should review, when reporting an issue, or when asking for a decision.
|
|
805
|
+
- Do not mention the user if the task found no work, made no changes, or has nothing actionable to report. Archive that session instead: \`kimaki session archive ${archiveTarget}\`
|
|
806
|
+
- Do not pass \`--user\` for autonomous tasks. It adds the user to every task thread before the result is known. Use it only when every occurrence must appear in the user's sidebar, such as an explicit reminder.
|
|
781
807
|
|
|
782
808
|
Manage scheduled tasks with:
|
|
783
809
|
|
|
@@ -785,7 +811,7 @@ kimaki task list
|
|
|
785
811
|
kimaki task edit <id> --prompt "new prompt" [--send-at "new schedule"] [--pre-run "command"] [--allow-concurrency true|false] [--user "<discord-user-id>"] [--model "provider/model"] [--agent "<agent>"]
|
|
786
812
|
kimaki task delete <id>
|
|
787
813
|
|
|
788
|
-
\`kimaki task list\` prints \`userId\`, \`agent\`, and \`model\` columns.
|
|
814
|
+
\`kimaki task list\` prints \`userId\`, \`agent\`, and \`model\` columns. For autonomous tasks, \`userId\` should normally be \`-\`; put the Discord ID and conditional mention rule in the task instructions instead. Clear a stored user with \`kimaki task edit <id> --user ''\`. Add \`--user '<discord-user-id>'\` only for tasks whose every occurrence must surface. Change model or agent in place with \`--model\` / \`--agent\` (empty string clears the override). Do not read SQLite or recreate the task just to swap model.
|
|
789
815
|
|
|
790
816
|
\`kimaki session list\` also shows if a session was started by a scheduled \`delay\` or \`cron\` task, including task ID when available.
|
|
791
817
|
|
|
@@ -864,10 +890,15 @@ kimaki session list
|
|
|
864
890
|
kimaki session list --json # machine-readable output
|
|
865
891
|
kimaki session list --project /path/to/project # specific project
|
|
866
892
|
|
|
893
|
+
# List sessions across every locally registered project
|
|
894
|
+
kimaki session list --all
|
|
895
|
+
|
|
867
896
|
# List only in-progress sessions. Exit status is 1 when none remain.
|
|
868
897
|
kimaki session list --active
|
|
869
898
|
\`\`\`
|
|
870
899
|
|
|
900
|
+
Each row shows the session title, project directory, \`status: working\` or \`status: idle\`, and \`tokens: N\` (total token footprint) when available. Kimaki-started sessions also show their Discord \`thread\` ID.
|
|
901
|
+
|
|
871
902
|
Titles prefixed with \`btw:\` are side sessions that answer a related user question in parallel. They are not duplicate sessions of the main task.
|
|
872
903
|
|
|
873
904
|
To search past sessions (supports plain text or /regex/flags). Defaults to this project and the last ${SESSION_SEARCH_DEFAULT_DAYS} days. Use \`--days 0\` for all time. Use \`--all\` to search every locally registered project:
|
|
@@ -881,14 +912,13 @@ kimaki session search "/panic|crash/i" --channel <channel_id>
|
|
|
881
912
|
kimaki session search "auth timeout" --all
|
|
882
913
|
\`\`\`
|
|
883
914
|
|
|
884
|
-
To read a session
|
|
885
|
-
Logs go to stderr, so redirect stderr to hide them:
|
|
915
|
+
To read a session as markdown, pipe to a file. Logs go to stderr:
|
|
886
916
|
|
|
887
917
|
\`\`\`bash
|
|
888
918
|
kimaki session read <sessionId> > ./tmp/session.md 2>/dev/null
|
|
889
919
|
\`\`\`
|
|
890
920
|
|
|
891
|
-
|
|
921
|
+
The dump is already compressed (no thinking, truncated tool inputs). If it is under 100 KB, read the whole file. Do not grep first. Use \`--thinking\` / \`--verbose\` only when you need the full dump.
|
|
892
922
|
|
|
893
923
|
### who edited a file
|
|
894
924
|
|
|
@@ -917,18 +947,25 @@ If this session edited the file, use this session ID instead. If several files c
|
|
|
917
947
|
|
|
918
948
|
When the user references another project by name, run \`kimaki project list\` to find its directory path and channel ID. Then read files, search code, or run commands directly in that directory. If the project is not listed, use \`kimaki project add /path/to/repo\` to register it and create a Discord channel for it. Do not add subfolders of an existing project — only add root project directories.
|
|
919
949
|
|
|
920
|
-
When the user uses \`#project-name\` syntax, they usually mean a Kimaki project channel. Use \`kimaki project list --json\` to resolve the \`channel_name\` to its repo working directory. The JSON output includes \`guild_id\` and \`guild_name\`
|
|
950
|
+
When the user uses \`#project-name\` syntax, they usually mean a Kimaki project channel. Use \`kimaki project list --json\` to resolve the \`channel_name\` to its repo working directory. The JSON output includes \`guild_id\` and \`guild_name\` so you can tell channels with the same name apart. Prefer \`guild_id\` when there are duplicates. It is stable. \`guild_name\` can change.
|
|
921
951
|
|
|
922
|
-
|
|
952
|
+
If the local list has no match, the channel may live on another computer. Scan Discord with \`kimaki project list --all --json\` and send by \`channel_id\`. Remote rows have \`is_local: false\` and \`directory: null\`. Do not use \`--project\` or \`--session\` for those channels.
|
|
923
953
|
|
|
924
|
-
|
|
925
|
-
# 1. Find the session ID by searching thread titles across all projects
|
|
926
|
-
kimaki session list --project /path/to/project --json | jq -r '.[] | select(.title | test("Thread Title"; "i")) | .id + " | " + .title'
|
|
954
|
+
When the user uses \`#Some Thread Title\` with spaces, they mean a **thread title**, not a project channel. On this computer, search local sessions, then read the markdown:
|
|
927
955
|
|
|
928
|
-
|
|
956
|
+
\`\`\`bash
|
|
957
|
+
kimaki session list --project /path/to/project --json
|
|
929
958
|
kimaki session read <sessionId> > ./tmp/session.md 2>/dev/null
|
|
930
959
|
\`\`\`
|
|
931
960
|
|
|
961
|
+
If that local search has no match, list Discord threads instead of using a session ID:
|
|
962
|
+
|
|
963
|
+
\`\`\`bash
|
|
964
|
+
kimaki project list --all --json
|
|
965
|
+
kimaki thread list --channel <channel_id> --json
|
|
966
|
+
kimaki send --thread <thread_id> --prompt 'continue the work' --agent <current_agent>
|
|
967
|
+
\`\`\`
|
|
968
|
+
|
|
932
969
|
If you don't know which project the thread belongs to, try each project from \`kimaki project list --json\`.
|
|
933
970
|
|
|
934
971
|
\`\`\`bash
|
|
@@ -936,10 +973,18 @@ If you don't know which project the thread belongs to, try each project from \`k
|
|
|
936
973
|
kimaki project list
|
|
937
974
|
kimaki project list --json # machine-readable output with guild_id, guild_name, is_local
|
|
938
975
|
|
|
939
|
-
# Include projects from other machines (scans Kimaki
|
|
976
|
+
# Include projects from other machines (scans Kimaki groups in Discord)
|
|
940
977
|
kimaki project list --all
|
|
941
978
|
kimaki project list --all --json # remote projects have is_local: false and directory: null
|
|
942
979
|
|
|
980
|
+
# Find a channel on another computer, then send a new prompt there
|
|
981
|
+
kimaki project list --all --json
|
|
982
|
+
kimaki send --channel <channel_id> --prompt 'Plan how to update the API client to v2' --agent <current_agent>
|
|
983
|
+
|
|
984
|
+
# Continue an existing remote thread. Session IDs do not work across computers.
|
|
985
|
+
kimaki thread list --channel <channel_id> --json
|
|
986
|
+
kimaki send --thread <thread_id> --prompt 'continue the work' --agent <current_agent>
|
|
987
|
+
|
|
943
988
|
# Resolve by channel name (prefer adding guild_name filter if duplicates exist)
|
|
944
989
|
kimaki project list --json | jq -r '.[] | select(.channel_name == "project-name") | .channel_id + " " + .guild_name + " " + .directory'
|
|
945
990
|
|
|
@@ -1001,7 +1046,7 @@ while kimaki session list --active --exclude ${sessionId}; do sleep 5; done
|
|
|
1001
1046
|
|
|
1002
1047
|
\`session list --active\` exits with status 0 while it finds active sessions and status 1 when none remain. Exclude the current session in a wait loop so the loop does not wait for itself.
|
|
1003
1048
|
|
|
1004
|
-
\`session wait\` exits with the session markdown on stdout once the model finishes responding.
|
|
1049
|
+
\`session wait\` exits with the session markdown on stdout once the model finishes responding, or when the session pauses to show the user a question (it will not finish on its own until answered).
|
|
1005
1050
|
|
|
1006
1051
|
Use \`--wait\` when you need to:
|
|
1007
1052
|
- **Fix a bug in another project** before continuing here (e.g. fix a dependency, then resume)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Plugin } from '@opencode-ai/plugin'
|
|
2
|
+
|
|
3
|
+
// Work around https://github.com/anomalyco/opencode/issues/49599 until invalid task IDs are ignored upstream.
|
|
4
|
+
export function sanitizeTaskArguments(args: Record<string, unknown>) {
|
|
5
|
+
const taskId = args.task_id
|
|
6
|
+
if (typeof taskId !== 'string' || taskId.startsWith('ses')) return
|
|
7
|
+
delete args.task_id
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const taskIdPlugin: Plugin = async () => {
|
|
11
|
+
return {
|
|
12
|
+
'tool.execute.before': async (input, output) => {
|
|
13
|
+
if (input.tool !== 'task') return
|
|
14
|
+
sanitizeTaskArguments(output.args)
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/task-runner.ts
CHANGED
|
@@ -455,6 +455,9 @@ async function executeScheduledTask({
|
|
|
455
455
|
task: ScheduledTask
|
|
456
456
|
runId?: number
|
|
457
457
|
}): Promise<string | null | Error | { kind: 'condition-not-met' }> {
|
|
458
|
+
const claimedTask = await getScheduledTask(task.id)
|
|
459
|
+
if (claimedTask?.status !== 'running') return { kind: 'condition-not-met' }
|
|
460
|
+
|
|
458
461
|
const payloadResult = parseScheduledTaskPayload(task.payload_json)
|
|
459
462
|
if (payloadResult instanceof Error) {
|
|
460
463
|
return new Error(`Task ${task.id} has invalid payload`, {
|
|
@@ -466,6 +469,11 @@ async function executeScheduledTask({
|
|
|
466
469
|
if (commandResult instanceof Error) return commandResult
|
|
467
470
|
if (commandResult.kind === 'skip') return { kind: 'condition-not-met' }
|
|
468
471
|
|
|
472
|
+
const taskAfterCommand = await getScheduledTask(task.id)
|
|
473
|
+
if (taskAfterCommand?.status !== 'running') {
|
|
474
|
+
return { kind: 'condition-not-met' }
|
|
475
|
+
}
|
|
476
|
+
|
|
469
477
|
if (payloadResult.kind === 'thread') {
|
|
470
478
|
return executeThreadScheduledTask({
|
|
471
479
|
rest,
|
|
@@ -210,7 +210,7 @@ describe('formatSessionSleepToolOutput', () => {
|
|
|
210
210
|
reason: 'Wait 1 day, then check mail',
|
|
211
211
|
}),
|
|
212
212
|
).toBe(
|
|
213
|
-
'
|
|
213
|
+
'-# Woke after sleeping until 2026-09-07 13:21 UTC\nReason: Wait 1 day, then check mail\nContinue the work you were waiting for.',
|
|
214
214
|
)
|
|
215
215
|
})
|
|
216
216
|
})
|
package/src/test-utils.ts
CHANGED
|
@@ -354,9 +354,11 @@ export function isFooterMessage({
|
|
|
354
354
|
if (message.author.id !== botUserId) {
|
|
355
355
|
return false
|
|
356
356
|
}
|
|
357
|
-
const content = message.content.startsWith('
|
|
358
|
-
? message.content.slice(
|
|
359
|
-
: message.content
|
|
357
|
+
const content = message.content.startsWith('-# ')
|
|
358
|
+
? message.content.slice(3)
|
|
359
|
+
: message.content.startsWith('> ')
|
|
360
|
+
? message.content.slice(2)
|
|
361
|
+
: message.content
|
|
360
362
|
if (!content.startsWith('*')) {
|
|
361
363
|
return false
|
|
362
364
|
}
|