kimaki 0.22.0 → 0.23.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/anthropic-auth-plugin.js +78 -33
- package/dist/anthropic-auth-state.js +62 -27
- package/dist/anthropic-auth-state.test.js +55 -2
- package/dist/channel-management.js +29 -6
- package/dist/cli-commands/project.js +6 -1
- package/dist/commands/login.js +1 -1
- package/dist/commands/new-worktree.js +9 -3
- package/dist/commands/queue.js +12 -2
- package/dist/database.js +7 -8
- package/dist/db.js +1 -0
- package/dist/discord-bot.js +9 -0
- package/dist/hrana-server.js +19 -0
- package/dist/oauth-rotation-shared.js +21 -0
- package/dist/opencode.js +73 -4
- package/dist/schema.js +3 -0
- package/dist/session-handler/global-event-listener.js +38 -6
- package/dist/session-handler/thread-runtime-state.js +2 -0
- package/dist/session-handler/thread-session-runtime.js +19 -3
- package/dist/system-message.js +34 -20
- package/dist/system-message.test.js +34 -20
- package/dist/task-runner.js +8 -4
- package/dist/worktree-lifecycle.e2e.test.js +16 -2
- package/package.json +6 -6
- package/skills/holocron/SKILL.md +11 -8
- package/src/anthropic-auth-plugin.ts +82 -35
- package/src/anthropic-auth-state.test.ts +73 -1
- package/src/anthropic-auth-state.ts +85 -25
- package/src/channel-management.ts +35 -6
- package/src/cli-commands/project.ts +6 -1
- package/src/commands/login.ts +1 -1
- package/src/commands/new-worktree.ts +14 -3
- package/src/commands/queue.ts +13 -2
- package/src/database.ts +8 -9
- package/src/db.ts +1 -0
- package/src/discord-bot.ts +12 -0
- package/src/hrana-server.ts +19 -0
- package/src/oauth-rotation-shared.ts +22 -0
- package/src/opencode.ts +90 -6
- package/src/schema.sql +1 -0
- package/src/schema.ts +3 -0
- package/src/session-handler/global-event-listener.ts +38 -6
- package/src/session-handler/thread-runtime-state.ts +4 -1
- package/src/session-handler/thread-session-runtime.ts +25 -3
- package/src/system-message.test.ts +34 -20
- package/src/system-message.ts +34 -20
- package/src/task-runner.ts +8 -4
- package/src/worktree-lifecycle.e2e.test.ts +18 -2
package/src/system-message.ts
CHANGED
|
@@ -309,7 +309,7 @@ ${escapePromptText(repliedMessage.text)}
|
|
|
309
309
|
: []),
|
|
310
310
|
...(worktree && worktreeChanged
|
|
311
311
|
? [
|
|
312
|
-
`<system-reminder>\nThis session is running inside a git worktree. The working directory (cwd / pwd) has changed. The user expects you to edit files in the new cwd. You MUST operate inside the new worktree from now on.\n- New worktree path (new cwd / pwd, edit files here): ${worktree.worktreeDirectory}\n- Branch: ${worktree.branch}\n- Main repo path (previous folder, DO NOT TOUCH): ${worktree.mainRepoDirectory}\n- To find the base branch (the branch this worktree was created from): \`git -C ${worktree.mainRepoDirectory} symbolic-ref --short HEAD\`\n- To find the base commit (the commit this worktree diverged from): \`git merge-base <base-branch> HEAD\`\nYou MUST read, write, and edit files only under the new worktree path ${worktree.worktreeDirectory}. You MUST NOT read, write, or edit any files under the main repo path ${worktree.mainRepoDirectory} — even though it is the same project, that folder is a separate checkout and the user or another agent may be actively working there, so writing to it would override their unrelated changes. Run all checks (tests, builds, lint) inside the new worktree. Do not create another worktree by default.
|
|
312
|
+
`<system-reminder>\nThis session is running inside a git worktree. The working directory (cwd / pwd) has changed. The user expects you to edit files in the new cwd. You MUST operate inside the new worktree from now on.\n- New worktree path (new cwd / pwd, edit files here): ${worktree.worktreeDirectory}\n- Branch: ${worktree.branch}\n- Main repo path (previous folder, DO NOT TOUCH): ${worktree.mainRepoDirectory}\n- To find the base branch (the branch this worktree was created from): \`git -C ${worktree.mainRepoDirectory} symbolic-ref --short HEAD\`\n- To find the base commit (the commit this worktree diverged from): \`git merge-base <base-branch> HEAD\`\nYou MUST read, write, and edit files only under the new worktree path ${worktree.worktreeDirectory}. You MUST NOT read, write, or edit any files under the main repo path ${worktree.mainRepoDirectory} — even though it is the same project, that folder is a separate checkout and the user or another agent may be actively working there, so writing to it would override their unrelated changes. Run all checks (tests, builds, lint) inside the new worktree. Do not create another worktree by default. To merge this worktree into the main branch, run \`kimaki merge-worktree\`. If it reports rebase conflicts, resolve them and rerun until it succeeds.\n</system-reminder>`,
|
|
313
313
|
]
|
|
314
314
|
: []),
|
|
315
315
|
]
|
|
@@ -575,18 +575,30 @@ When the user specifies a time without a timezone, ask them to confirm their tim
|
|
|
575
575
|
|
|
576
576
|
\`--wait\` is incompatible with \`--send-at\` because scheduled tasks run in the future.
|
|
577
577
|
|
|
578
|
-
|
|
578
|
+
Keep scheduled task prompts **short**. The prompt text becomes the first message in the Discord thread, so long prompts clutter the channel. Instead of inlining the full task description in \`--prompt\`, write a markdown file in the project's \`tasks/\` folder and reference it:
|
|
579
579
|
|
|
580
|
-
|
|
580
|
+
\`\`\`bash
|
|
581
|
+
kimaki send --channel ${channelId} --prompt 'Read tasks/weekly-test-suite.md and follow instructions' --send-at '0 9 * * 1' --agent <current_agent>${parentSessionArg}${userArg}
|
|
582
|
+
\`\`\`
|
|
583
|
+
|
|
584
|
+
The task file should contain all the detail: goal, constraints, expected output, completion criteria. Use this frontmatter format:
|
|
585
|
+
|
|
586
|
+
\`\`\`yaml
|
|
587
|
+
---
|
|
588
|
+
title: Weekly test suite
|
|
589
|
+
description: >
|
|
590
|
+
Managed by kimaki scheduled task. Do not move or delete this file
|
|
591
|
+
without also updating the kimaki task (kimaki task list / kimaki task edit).
|
|
592
|
+
---
|
|
593
|
+
\`\`\`
|
|
594
|
+
|
|
595
|
+
For simple reminders and notifications (\`--notify-only\`), inline the prompt directly since there is no AI session to read files.
|
|
581
596
|
|
|
582
|
-
Notification strategy
|
|
597
|
+
Notification strategy:
|
|
583
598
|
- 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.
|
|
584
|
-
-
|
|
585
|
-
-
|
|
586
|
-
-
|
|
587
|
-
- With \`--user\`, the user is added to the thread and may receive more frequent thread-level notifications.
|
|
588
|
-
- If a scheduled task completes with no actionable result and no user-visible change, prefer archiving the session after the final message so Discord does not keep a no-op thread highlighted.
|
|
589
|
-
- Example no-op cleanup command: \`kimaki session archive ${archiveTarget}\`
|
|
599
|
+
- If a task needs user attention, add "mention the user via Discord user ID when task requires user review" in the task md file.
|
|
600
|
+
- With \`--user\`, the user is added to the thread and receives thread-level notifications.
|
|
601
|
+
- If a scheduled task completes with no actionable result, archive the session: \`kimaki session archive ${archiveTarget}\`
|
|
590
602
|
|
|
591
603
|
Manage scheduled tasks with:
|
|
592
604
|
|
|
@@ -596,20 +608,22 @@ kimaki task delete <id>
|
|
|
596
608
|
|
|
597
609
|
\`kimaki session list\` also shows if a session was started by a scheduled \`delay\` or \`cron\` task, including task ID when available.
|
|
598
610
|
|
|
611
|
+
**Never duplicate tasks to run more frequently.** If a task should run twice a day (morning and evening), edit the existing task's cron expression instead of creating a second task. Cron supports comma-separated hours:
|
|
612
|
+
|
|
613
|
+
\`\`\`bash
|
|
614
|
+
# runs at 9:00 UTC and 18:00 UTC every day
|
|
615
|
+
kimaki task edit <id> --send-at '0 9,18 * * *'
|
|
616
|
+
\`\`\`
|
|
617
|
+
|
|
599
618
|
Use case patterns:
|
|
600
|
-
- Reminder flows: create deadline reminders
|
|
601
|
-
- Proactive reminders: when you encounter time-sensitive information
|
|
602
|
-
- Weekly QA:
|
|
603
|
-
-
|
|
604
|
-
- Recurring maintenance: use cron \`--send-at\` for repetitive tasks like rotating secrets, checking dependency updates, running security audits, or cleaning up stale branches. Example: \`--send-at "0 9 1 * *"\` to run on the 1st of every month.
|
|
605
|
-
- Quiet no-op checks: if a recurring task checks something and finds nothing to report, let it post a brief final summary and then archive the session with \`kimaki session archive ${archiveTarget}\`. Example: a scheduled email triage run that finds no new emails should archive itself so it does not add noise to Discord.
|
|
606
|
-
- Thread reminders: when the user says "remind me about this in 2 hours" (or any duration), use \`--send-at\` with \`--thread\` to resurface the current thread. Compute the future UTC time and send a mention so Discord shows a notification:
|
|
619
|
+
- Reminder flows: create deadline reminders with one-time \`--send-at\` and \`--notify-only\`; mention only if action is required.
|
|
620
|
+
- Proactive reminders: when you encounter time-sensitive information (API key expiration, certificate renewal, trial ending), schedule a \`--notify-only\` reminder before the deadline. Always tell the user you scheduled the reminder so they know.
|
|
621
|
+
- Weekly QA / recurring maintenance: write the full task spec in \`tasks/\` and schedule a short prompt pointing to it.
|
|
622
|
+
- Thread reminders: when the user says "remind me about this in 2 hours", use \`--send-at\` with \`--thread\` to resurface the current thread:
|
|
607
623
|
|
|
608
624
|
kimaki send ${sendToSelfTarget} --prompt 'Reminder: <@USER_ID> you asked to be reminded about this thread.' --send-at '<future_UTC_time>' --notify-only --agent <current_agent>
|
|
609
625
|
|
|
610
|
-
Replace \`<future_UTC_time>\` with the computed UTC ISO timestamp.
|
|
611
|
-
|
|
612
|
-
Scheduled tasks can maintain project memory by reading and updating an md file in the repository (for example \`docs/automation-notes.md\`) on each run.
|
|
626
|
+
Replace \`<future_UTC_time>\` with the computed UTC ISO timestamp.
|
|
613
627
|
|
|
614
628
|
Worktrees are useful for handing off parallel tasks that need to be isolated from each other (each session works on its own branch).
|
|
615
629
|
|
package/src/task-runner.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
markScheduledTaskCronRescheduled,
|
|
11
11
|
markScheduledTaskCronRetry,
|
|
12
12
|
markScheduledTaskFailed,
|
|
13
|
-
|
|
13
|
+
deleteScheduledTask,
|
|
14
14
|
recoverStaleRunningScheduledTasks,
|
|
15
15
|
type ScheduledTask,
|
|
16
16
|
} from './database.js'
|
|
@@ -59,7 +59,10 @@ async function executeThreadScheduledTask({
|
|
|
59
59
|
const marker: ThreadStartMarker = {
|
|
60
60
|
start: true,
|
|
61
61
|
scheduledKind: task.schedule_kind,
|
|
62
|
-
scheduledTaskId
|
|
62
|
+
// Only include scheduledTaskId for cron tasks. One-shot tasks are deleted
|
|
63
|
+
// after execution, so the ID would be stale by the time the bot processes
|
|
64
|
+
// the Discord event and tries to insert a session_start_sources row.
|
|
65
|
+
...(task.schedule_kind === 'cron' ? { scheduledTaskId: task.id } : {}),
|
|
63
66
|
...(payload.agent ? { agent: payload.agent } : {}),
|
|
64
67
|
...(payload.model ? { model: payload.model } : {}),
|
|
65
68
|
...(payload.username ? { username: payload.username } : {}),
|
|
@@ -105,7 +108,8 @@ async function executeChannelScheduledTask({
|
|
|
105
108
|
: {
|
|
106
109
|
start: true,
|
|
107
110
|
scheduledKind: task.schedule_kind,
|
|
108
|
-
scheduledTaskId
|
|
111
|
+
// Only include scheduledTaskId for cron tasks (see thread variant comment)
|
|
112
|
+
...(task.schedule_kind === 'cron' ? { scheduledTaskId: task.id } : {}),
|
|
109
113
|
...(payload.worktreeName ? { worktree: payload.worktreeName } : {}),
|
|
110
114
|
...(payload.cwd ? { cwd: payload.cwd } : {}),
|
|
111
115
|
...(payload.agent ? { agent: payload.agent } : {}),
|
|
@@ -222,7 +226,7 @@ async function finalizeSuccessfulTask({
|
|
|
222
226
|
completedAt: Date
|
|
223
227
|
}): Promise<void> {
|
|
224
228
|
if (task.schedule_kind === 'at') {
|
|
225
|
-
await
|
|
229
|
+
await deleteScheduledTask(task.id)
|
|
226
230
|
return
|
|
227
231
|
}
|
|
228
232
|
|
|
@@ -61,6 +61,10 @@ function normalizeWorktreeLifecycleText(text: string): string {
|
|
|
61
61
|
.replaceAll(CHANNEL_WORKTREE_NAME, 'CHANNEL_WORKTREE_NAME')
|
|
62
62
|
.replaceAll(AUTO_WORKTREE_SUFFIX, 'AUTO_WORKTREE_NAME')
|
|
63
63
|
.replaceAll(WORKTREE_NAME, 'WORKTREE_NAME')
|
|
64
|
+
.replace(
|
|
65
|
+
/opencode\/kimaki-rply-wth-exctly-snd-at-wt-[a-z0-9]+/g,
|
|
66
|
+
'AUTO_WORKTREE_BRANCH',
|
|
67
|
+
)
|
|
64
68
|
.replaceAll(WORKTREE_SUFFIX, 'SUFFIX')
|
|
65
69
|
.replace(/ses_[a-zA-Z0-9]+/g, 'ses_TEST')
|
|
66
70
|
.replace(/<#\d+>/g, '<#THREAD_ID>')
|
|
@@ -444,11 +448,23 @@ describe('worktree lifecycle', () => {
|
|
|
444
448
|
expect(sourceSessionAfterFork).toBe(sessionBefore)
|
|
445
449
|
const worktreeSession = await getThreadSession(worktreeThread.id)
|
|
446
450
|
expect(worktreeSession).toBeTruthy()
|
|
451
|
+
if (!worktreeSession) throw new Error('Worktree session was not persisted')
|
|
447
452
|
expect(worktreeSession).not.toBe(sessionBefore)
|
|
448
453
|
await expect(getThreadWorktreeOrWorkspace(thread.id)).resolves.toBeUndefined()
|
|
449
454
|
const worktreeInfo = await getThreadWorktreeOrWorkspace(worktreeThread.id)
|
|
450
455
|
expect(worktreeInfo?.status).toBe('ready')
|
|
451
456
|
expect(worktreeInfo?.workspace_directory).toContain(WORKTREE_NAME)
|
|
457
|
+
const worktreeDirectory = worktreeInfo?.workspace_directory
|
|
458
|
+
if (!worktreeDirectory) throw new Error('Worktree directory was not persisted')
|
|
459
|
+
const getWorktreeClient = await initializeOpencodeForDirectory(
|
|
460
|
+
worktreeDirectory,
|
|
461
|
+
)
|
|
462
|
+
if (getWorktreeClient instanceof Error) throw getWorktreeClient
|
|
463
|
+
const worktreeSessionResponse = await getWorktreeClient().session.get({
|
|
464
|
+
sessionID: worktreeSession,
|
|
465
|
+
directory: worktreeDirectory,
|
|
466
|
+
})
|
|
467
|
+
expect(worktreeSessionResponse.data?.directory).toBe(worktreeDirectory)
|
|
452
468
|
|
|
453
469
|
const runtimeAfter = getRuntime(thread.id)
|
|
454
470
|
expect(runtimeAfter).toBe(runtimeBefore)
|
|
@@ -841,9 +857,9 @@ describe('worktree lifecycle', () => {
|
|
|
841
857
|
» **kimaki-cli:**
|
|
842
858
|
Reply with exactly: send-auto-wt-SUFFIX
|
|
843
859
|
[embed]
|
|
844
|
-
🌳 **Worktree:
|
|
860
|
+
🌳 **Worktree: AUTO_WORKTREE_BRANCH**
|
|
845
861
|
📁 \`/tmp/worktrees/WORKTREE_NAME\`
|
|
846
|
-
🌿 Branch: \`
|
|
862
|
+
🌿 Branch: \`AUTO_WORKTREE_BRANCH\`
|
|
847
863
|
*using deterministic-provider/deterministic-v2*
|
|
848
864
|
⬥ ok"
|
|
849
865
|
`)
|