@otto-assistant/bridge 0.4.96 → 0.4.100

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 (44) hide show
  1. package/dist/agent-model.e2e.test.js +7 -1
  2. package/dist/anthropic-account-identity.js +62 -0
  3. package/dist/anthropic-account-identity.test.js +38 -0
  4. package/dist/anthropic-auth-plugin.js +72 -12
  5. package/dist/anthropic-auth-state.js +28 -3
  6. package/dist/{anthropic-auth-plugin.test.js → anthropic-auth-state.test.js} +21 -2
  7. package/dist/cli-parsing.test.js +12 -9
  8. package/dist/cli-send-thread.e2e.test.js +4 -7
  9. package/dist/cli.js +25 -12
  10. package/dist/commands/screenshare.js +1 -1
  11. package/dist/commands/screenshare.test.js +2 -2
  12. package/dist/commands/vscode.js +269 -0
  13. package/dist/db.js +1 -0
  14. package/dist/discord-command-registration.js +7 -2
  15. package/dist/gateway-proxy-reconnect.e2e.test.js +2 -2
  16. package/dist/interaction-handler.js +4 -0
  17. package/dist/system-message.js +24 -23
  18. package/dist/system-message.test.js +24 -23
  19. package/dist/system-prompt-drift-plugin.js +41 -11
  20. package/dist/utils.js +1 -1
  21. package/dist/worktrees.js +0 -33
  22. package/package.json +1 -1
  23. package/src/agent-model.e2e.test.ts +8 -1
  24. package/src/anthropic-account-identity.test.ts +52 -0
  25. package/src/anthropic-account-identity.ts +77 -0
  26. package/src/anthropic-auth-plugin.ts +82 -12
  27. package/src/{anthropic-auth-plugin.test.ts → anthropic-auth-state.test.ts} +23 -1
  28. package/src/anthropic-auth-state.ts +36 -3
  29. package/src/cli-parsing.test.ts +16 -9
  30. package/src/cli-send-thread.e2e.test.ts +6 -7
  31. package/src/cli.ts +31 -13
  32. package/src/commands/screenshare.test.ts +2 -2
  33. package/src/commands/screenshare.ts +1 -1
  34. package/src/commands/vscode.ts +342 -0
  35. package/src/db.ts +1 -0
  36. package/src/discord-command-registration.ts +9 -2
  37. package/src/gateway-proxy-reconnect.e2e.test.ts +2 -2
  38. package/src/interaction-handler.ts +5 -0
  39. package/src/system-message.test.ts +24 -23
  40. package/src/system-message.ts +24 -23
  41. package/src/system-prompt-drift-plugin.ts +48 -12
  42. package/src/utils.ts +1 -1
  43. package/src/worktrees.test.ts +1 -0
  44. package/src/worktrees.ts +1 -47
@@ -182,7 +182,7 @@ export async function registerCommands({
182
182
  new SlashCommandBuilder()
183
183
  .setName('new-worktree')
184
184
  .setDescription(
185
- truncateCommandDescription('Create a git worktree branch from origin/HEAD (or main). Optionally pick a base branch.'),
185
+ truncateCommandDescription('Create a git worktree branch from HEAD by default. Optionally pick a base branch.'),
186
186
  )
187
187
  .addStringOption((option) => {
188
188
  option
@@ -198,7 +198,7 @@ export async function registerCommands({
198
198
  option
199
199
  .setName('base-branch')
200
200
  .setDescription(
201
- truncateCommandDescription('Branch to create the worktree from (default: origin/HEAD or main)'),
201
+ truncateCommandDescription('Branch to create the worktree from (default: HEAD)'),
202
202
  )
203
203
  .setRequired(false)
204
204
  .setAutocomplete(true)
@@ -488,6 +488,13 @@ export async function registerCommands({
488
488
  .setDescription(truncateCommandDescription('Stop screen sharing'))
489
489
  .setDMPermission(false)
490
490
  .toJSON(),
491
+ new SlashCommandBuilder()
492
+ .setName('vscode')
493
+ .setDescription(
494
+ truncateCommandDescription('Open VS Code in the browser for this project or worktree (auto-stops after 30 minutes)'),
495
+ )
496
+ .setDMPermission(false)
497
+ .toJSON(),
491
498
  ]
492
499
 
493
500
  // Dynamic commands are registered in priority order: agents → user commands → skills → MCP prompts.
@@ -6,7 +6,7 @@
6
6
  // Starts a digital-twin + local gateway-proxy binary, kills and restarts the proxy.
7
7
  //
8
8
  // Production mode (env vars):
9
- // GATEWAY_TEST_URL - production gateway WS+REST URL (e.g. wss://discord-gateway.kimaki.xyz)
9
+ // GATEWAY_TEST_URL - production gateway WS+REST URL (e.g. wss://discord-gateway.kimaki.dev)
10
10
  // GATEWAY_TEST_TOKEN - client token (clientId:secret)
11
11
  // GATEWAY_TEST_REDEPLOY - if "1", runs `fly deploy` between kill/restart instead of local binary
12
12
  //
@@ -15,7 +15,7 @@
15
15
  // pnpm test --run src/gateway-proxy-reconnect.e2e.test.ts
16
16
  //
17
17
  // # Against production (just connect + kill WS + wait for reconnect):
18
- // GATEWAY_TEST_URL=wss://discord-gateway.kimaki.xyz \
18
+ // GATEWAY_TEST_URL=wss://discord-gateway.kimaki.dev \
19
19
  // GATEWAY_TEST_TOKEN=myclientid:mysecret \
20
20
  // KIMAKI_TEST_LOGS=1 \
21
21
  // pnpm test --run src/gateway-proxy-reconnect.e2e.test.ts -t "production"
@@ -101,6 +101,7 @@ import {
101
101
  handleScreenshareCommand,
102
102
  handleScreenshareStopCommand,
103
103
  } from './commands/screenshare.js'
104
+ import { handleVscodeCommand } from './commands/vscode.js'
104
105
  import { handleModelVariantSelectMenu } from './commands/model.js'
105
106
  import {
106
107
  handleModelVariantCommand,
@@ -356,6 +357,10 @@ export function registerInteractionHandler({
356
357
  appId,
357
358
  })
358
359
  return
360
+
361
+ case 'vscode':
362
+ await handleVscodeCommand({ command: interaction, appId })
363
+ return
359
364
  }
360
365
 
361
366
  // Handle quick agent commands (ending with -agent suffix, but not the base /agent command)
@@ -23,7 +23,7 @@ describe('system-message', () => {
23
23
  }).replace(/`[^`]*\/kimaki\.log`/, '`<data-dir>/kimaki.log`'),
24
24
  ).toMatchInlineSnapshot(`
25
25
  "
26
- The user is reading your messages from inside Discord, via kimaki.xyz
26
+ The user is reading your messages from inside Discord, via kimaki.dev
27
27
 
28
28
  ## bash tool
29
29
 
@@ -92,39 +92,40 @@ describe('system-message', () => {
92
92
 
93
93
  To start a new thread/session in this channel pro-grammatically, run:
94
94
 
95
- kimaki send --channel chan_123 --prompt "your prompt here" --user "Tommy"
95
+ kimaki send --channel chan_123 --prompt "your prompt here" --agent <current_agent> --user "Tommy"
96
96
 
97
97
  You can use this to "spawn" parallel helper sessions like teammates: start new threads with focused prompts, then come back and collect the results.
98
+ Prefer passing the current agent with \`--agent <current_agent>\` so spawned or scheduled sessions keep the same agent unless you are intentionally switching. Replace \`<current_agent>\` with the value from the per-turn \`Current agent\` reminder.
98
99
 
99
100
  IMPORTANT: NEVER use \`--worktree\` unless the user explicitly asks for a worktree. Default to creating normal threads without worktrees.
100
101
 
101
102
  To send a prompt to an existing thread instead of creating a new one:
102
103
 
103
- kimaki send --thread <thread_id> --prompt "follow-up prompt"
104
+ kimaki send --thread <thread_id> --prompt "follow-up prompt" --agent <current_agent>
104
105
 
105
106
  Use this when you already have the Discord thread ID.
106
107
 
107
108
  To send to the thread associated with a known session:
108
109
 
109
- kimaki send --session <session_id> --prompt "follow-up prompt"
110
+ kimaki send --session <session_id> --prompt "follow-up prompt" --agent <current_agent>
110
111
 
111
112
  Use this when you have the OpenCode session ID.
112
113
 
113
114
  Use --notify-only to create a notification thread without starting an AI session:
114
115
 
115
- kimaki send --channel chan_123 --prompt "User cancelled subscription" --notify-only --user "Tommy"
116
+ kimaki send --channel chan_123 --prompt "User cancelled subscription" --notify-only --agent <current_agent> --user "Tommy"
116
117
 
117
118
  Use --user to add a specific Discord user to the new thread:
118
119
 
119
- kimaki send --channel chan_123 --prompt "Review the latest CI failure" --user "Tommy"
120
+ kimaki send --channel chan_123 --prompt "Review the latest CI failure" --agent <current_agent> --user "Tommy"
120
121
 
121
122
  Use --worktree to create a git worktree for the session (ONLY when the user explicitly asks for a worktree):
122
123
 
123
- kimaki send --channel chan_123 --prompt "Add dark mode support" --worktree dark-mode --user "Tommy"
124
+ kimaki send --channel chan_123 --prompt "Add dark mode support" --worktree dark-mode --agent <current_agent> --user "Tommy"
124
125
 
125
126
  Use --cwd to start a session in an existing git worktree directory (must be a worktree of the project):
126
127
 
127
- kimaki send --channel chan_123 --prompt "Continue work on feature" --cwd /path/to/existing-worktree --user "Tommy"
128
+ kimaki send --channel chan_123 --prompt "Continue work on feature" --cwd /path/to/existing-worktree --agent <current_agent> --user "Tommy"
128
129
 
129
130
  Important:
130
131
  - NEVER use \`--worktree\` unless the user explicitly requests a worktree. Most tasks should use normal threads without worktrees.
@@ -146,8 +147,8 @@ describe('system-message', () => {
146
147
 
147
148
  You can trigger registered opencode commands (slash commands, skills, MCP prompts) by starting the \`--prompt\` with \`/commandname\`:
148
149
 
149
- kimaki send --thread <thread_id> --prompt "/review fix the auth module"
150
- kimaki send --channel chan_123 --prompt "/build-cmd update dependencies" --user "Tommy"
150
+ kimaki send --thread <thread_id> --prompt "/review fix the auth module" --agent <current_agent>
151
+ kimaki send --channel chan_123 --prompt "/build-cmd update dependencies" --agent <current_agent> --user "Tommy"
151
152
 
152
153
  The command name must match a registered opencode command. If the command is not recognized, the prompt is sent as plain text to the model. This works for both new threads (\`--channel\`) and existing threads (\`--thread\`/\`--session\`).
153
154
 
@@ -157,14 +158,14 @@ describe('system-message', () => {
157
158
 
158
159
  You can also switch agents via \`kimaki send\`:
159
160
 
160
- kimaki send --thread <thread_id> --prompt "/<agentname>-agent"
161
+ kimaki send --thread <thread_id> --prompt "/<agentname>-agent" --agent <current_agent>
161
162
 
162
163
  ## scheduled sends and task management
163
164
 
164
165
  Use \`--send-at\` to schedule a one-time or recurring task:
165
166
 
166
- kimaki send --channel chan_123 --prompt "Reminder: review open PRs" --send-at "2026-03-01T09:00:00Z" --user "Tommy"
167
- kimaki send --channel chan_123 --prompt "Run weekly test suite and summarize failures" --send-at "0 9 * * 1" --user "Tommy"
167
+ kimaki send --channel chan_123 --prompt "Reminder: review open PRs" --send-at "2026-03-01T09:00:00Z" --agent <current_agent> --user "Tommy"
168
+ kimaki send --channel chan_123 --prompt "Run weekly test suite and summarize failures" --send-at "0 9 * * 1" --agent <current_agent> --user "Tommy"
168
169
 
169
170
  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).
170
171
  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.
@@ -198,13 +199,13 @@ describe('system-message', () => {
198
199
 
199
200
  Use case patterns:
200
201
  - Reminder flows: create deadline reminders in this channel with one-time \`--send-at\`; mention only if action is required.
201
- - Proactive reminders: when you encounter time-sensitive information during your work (e.g. creating an API key that expires in 90 days, a certificate with an expiration date, a trial period ending, a deadline mentioned in code comments), proactively schedule a \`--notify-only\` reminder before the expiration so the user gets notified in time. For example, if you generate an API key expiring on 2026-06-01, schedule a reminder a few days before: \`kimaki send --channel chan_123 --prompt "Reminder: <@USER_ID> the API key created on 2026-03-01 expires on 2026-06-01. Renew it before it breaks production." --send-at "2026-05-28T09:00:00Z" --notify-only\`. Always tell the user you scheduled the reminder so they know.
202
+ - Proactive reminders: when you encounter time-sensitive information during your work (e.g. creating an API key that expires in 90 days, a certificate with an expiration date, a trial period ending, a deadline mentioned in code comments), proactively schedule a \`--notify-only\` reminder before the expiration so the user gets notified in time. For example, if you generate an API key expiring on 2026-06-01, schedule a reminder a few days before: \`kimaki send --channel chan_123 --prompt "Reminder: <@USER_ID> the API key created on 2026-03-01 expires on 2026-06-01. Renew it before it breaks production." --send-at "2026-05-28T09:00:00Z" --notify-only --agent <current_agent>\`. Always tell the user you scheduled the reminder so they know.
202
203
  - Weekly QA: schedule "run full test suite, inspect failures, post summary, and mention @username only when failures require review".
203
204
  - Weekly benchmark automation: schedule a benchmark prompt that runs model evals, writes JSON outputs in the repo, commits results, and mentions only for regressions.
204
205
  - 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.
205
206
  - 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:
206
207
 
207
- kimaki send --session ses_123 --prompt "Reminder: <@USER_ID> you asked to be reminded about this thread." --send-at "<future_UTC_time>" --notify-only
208
+ kimaki send --session ses_123 --prompt "Reminder: <@USER_ID> you asked to be reminded about this thread." --send-at "<future_UTC_time>" --notify-only --agent <current_agent>
208
209
 
209
210
  Replace \`<future_UTC_time>\` with the computed UTC ISO timestamp. The \`--notify-only\` flag creates just a notification message without starting a new AI session. The \`<@userId>\` mention ensures the user gets a Discord notification.
210
211
 
@@ -219,12 +220,12 @@ describe('system-message', () => {
219
220
  When the user asks to "create a worktree" or "make a worktree", they mean you should use the kimaki CLI to create it. Do NOT use raw \`git worktree add\` commands. Instead use:
220
221
 
221
222
  \`\`\`bash
222
- kimaki send --channel chan_123 --prompt "your task description" --worktree worktree-name --user "Tommy"
223
+ kimaki send --channel chan_123 --prompt "your task description" --worktree worktree-name --agent <current_agent> --user "Tommy"
223
224
  \`\`\`
224
225
 
225
226
  This creates a new Discord thread with an isolated git worktree and starts a session in it. The worktree name should be kebab-case and descriptive of the task.
226
227
 
227
- By default, worktrees are created from \`origin/HEAD\` (the remote's default branch). To change the base branch for a project, the user can run \`git remote set-head origin <branch>\` in the project directory. For example, \`git remote set-head origin dev\` makes all new worktrees branch off \`origin/dev\` instead of \`origin/main\`.
228
+ By default, worktrees are created from \`HEAD\`, which means whatever commit or branch the current checkout is on. If you want a different base, pass \`--base-branch\` or use the slash command option explicitly.
228
229
 
229
230
  Critical recursion guard:
230
231
  - If you already are in a worktree thread, do not create another worktree unless the user explicitly asks for a nested worktree.
@@ -235,7 +236,7 @@ describe('system-message', () => {
235
236
  Use \`--cwd\` to start a session in an existing git worktree directory instead of creating a new one:
236
237
 
237
238
  \`\`\`bash
238
- kimaki send --channel chan_123 --prompt "Continue work on feature X" --cwd /path/to/existing-worktree --user "Tommy"
239
+ kimaki send --channel chan_123 --prompt "Continue work on feature X" --cwd /path/to/existing-worktree --agent <current_agent> --user "Tommy"
239
240
  \`\`\`
240
241
 
241
242
  The path must be a git worktree of the project (validated via \`git worktree list\`). The session resolves to the correct project channel but uses the worktree as its working directory. Use \`--worktree\` to create a new worktree, \`--cwd\` to reuse an existing one.
@@ -249,7 +250,7 @@ describe('system-message', () => {
249
250
  When you are approaching the **context window limit** or the user explicitly asks to **handoff to a new thread**, use the \`kimaki send\` command to start a fresh session with context:
250
251
 
251
252
  \`\`\`bash
252
- kimaki send --channel chan_123 --prompt "Continuing from previous session: <summary of current task and state>" --user "Tommy"
253
+ kimaki send --channel chan_123 --prompt "Continuing from previous session: <summary of current task and state>" --agent <current_agent> --user "Tommy"
253
254
  \`\`\`
254
255
 
255
256
  The command automatically handles long prompts (over 2000 chars) by sending them as file attachments.
@@ -307,10 +308,10 @@ describe('system-message', () => {
307
308
 
308
309
  \`\`\`bash
309
310
  # Send to a specific channel
310
- kimaki send --channel <channel_id> --prompt "Plan how to update the API client to v2"
311
+ kimaki send --channel <channel_id> --prompt "Plan how to update the API client to v2" --agent <current_agent>
311
312
 
312
313
  # Or use --project to resolve from directory
313
- kimaki send --project /path/to/other-repo --prompt "Plan how to bump version to 1.2.0"
314
+ kimaki send --project /path/to/other-repo --prompt "Plan how to bump version to 1.2.0" --agent <current_agent>
314
315
  \`\`\`
315
316
 
316
317
  When sending prompts to other projects, always ask the agent to plan first, never build upfront. The prompt should start with "Plan how to ..." so the user can review before greenlighting implementation.
@@ -333,10 +334,10 @@ describe('system-message', () => {
333
334
 
334
335
  \`\`\`bash
335
336
  # Start a session and wait for it to finish
336
- kimaki send --channel <channel_id> --prompt "Fix the auth bug" --wait
337
+ kimaki send --channel <channel_id> --prompt "Fix the auth bug" --wait --agent <current_agent>
337
338
 
338
339
  # Send to an existing thread and wait
339
- kimaki send --thread <thread_id> --prompt "Run the tests" --wait
340
+ kimaki send --thread <thread_id> --prompt "Run the tests" --wait --agent <current_agent>
340
341
  \`\`\`
341
342
 
342
343
  The command exits with the session markdown on stdout once the model finishes responding.
@@ -363,7 +363,7 @@ export function getOpencodeSystemMessage({
363
363
  .join('\n')}`
364
364
  : ''
365
365
  return `
366
- The user is reading your messages from inside Discord, via kimaki.xyz
366
+ The user is reading your messages from inside Discord, via kimaki.dev
367
367
 
368
368
  ## bash tool
369
369
 
@@ -431,39 +431,40 @@ ${
431
431
 
432
432
  To start a new thread/session in this channel pro-grammatically, run:
433
433
 
434
- kimaki send --channel ${channelId} --prompt "your prompt here"${userArg}
434
+ kimaki send --channel ${channelId} --prompt "your prompt here" --agent <current_agent>${userArg}
435
435
 
436
436
  You can use this to "spawn" parallel helper sessions like teammates: start new threads with focused prompts, then come back and collect the results.
437
+ Prefer passing the current agent with \`--agent <current_agent>\` so spawned or scheduled sessions keep the same agent unless you are intentionally switching. Replace \`<current_agent>\` with the value from the per-turn \`Current agent\` reminder.
437
438
 
438
439
  IMPORTANT: NEVER use \`--worktree\` unless the user explicitly asks for a worktree. Default to creating normal threads without worktrees.
439
440
 
440
441
  To send a prompt to an existing thread instead of creating a new one:
441
442
 
442
- kimaki send --thread <thread_id> --prompt "follow-up prompt"
443
+ kimaki send --thread <thread_id> --prompt "follow-up prompt" --agent <current_agent>
443
444
 
444
445
  Use this when you already have the Discord thread ID.
445
446
 
446
447
  To send to the thread associated with a known session:
447
448
 
448
- kimaki send --session <session_id> --prompt "follow-up prompt"
449
+ kimaki send --session <session_id> --prompt "follow-up prompt" --agent <current_agent>
449
450
 
450
451
  Use this when you have the OpenCode session ID.
451
452
 
452
453
  Use --notify-only to create a notification thread without starting an AI session:
453
454
 
454
- kimaki send --channel ${channelId} --prompt "User cancelled subscription" --notify-only${userArg}
455
+ kimaki send --channel ${channelId} --prompt "User cancelled subscription" --notify-only --agent <current_agent>${userArg}
455
456
 
456
457
  Use --user to add a specific Discord user to the new thread:
457
458
 
458
- kimaki send --channel ${channelId} --prompt "Review the latest CI failure"${userArg}
459
+ kimaki send --channel ${channelId} --prompt "Review the latest CI failure" --agent <current_agent>${userArg}
459
460
 
460
461
  Use --worktree to create a git worktree for the session (ONLY when the user explicitly asks for a worktree):
461
462
 
462
- kimaki send --channel ${channelId} --prompt "Add dark mode support" --worktree dark-mode${userArg}
463
+ kimaki send --channel ${channelId} --prompt "Add dark mode support" --worktree dark-mode --agent <current_agent>${userArg}
463
464
 
464
465
  Use --cwd to start a session in an existing git worktree directory (must be a worktree of the project):
465
466
 
466
- kimaki send --channel ${channelId} --prompt "Continue work on feature" --cwd /path/to/existing-worktree${userArg}
467
+ kimaki send --channel ${channelId} --prompt "Continue work on feature" --cwd /path/to/existing-worktree --agent <current_agent>${userArg}
467
468
 
468
469
  Important:
469
470
  - NEVER use \`--worktree\` unless the user explicitly requests a worktree. Most tasks should use normal threads without worktrees.
@@ -481,8 +482,8 @@ ${availableAgentsContext}
481
482
 
482
483
  You can trigger registered opencode commands (slash commands, skills, MCP prompts) by starting the \`--prompt\` with \`/commandname\`:
483
484
 
484
- kimaki send --thread <thread_id> --prompt "/review fix the auth module"
485
- kimaki send --channel ${channelId} --prompt "/build-cmd update dependencies"${userArg}
485
+ kimaki send --thread <thread_id> --prompt "/review fix the auth module" --agent <current_agent>
486
+ kimaki send --channel ${channelId} --prompt "/build-cmd update dependencies" --agent <current_agent>${userArg}
486
487
 
487
488
  The command name must match a registered opencode command. If the command is not recognized, the prompt is sent as plain text to the model. This works for both new threads (\`--channel\`) and existing threads (\`--thread\`/\`--session\`).
488
489
 
@@ -492,14 +493,14 @@ The user can switch the active agent mid-session using the Discord slash command
492
493
 
493
494
  You can also switch agents via \`kimaki send\`:
494
495
 
495
- kimaki send --thread <thread_id> --prompt "/<agentname>-agent"
496
+ kimaki send --thread <thread_id> --prompt "/<agentname>-agent" --agent <current_agent>
496
497
 
497
498
  ## scheduled sends and task management
498
499
 
499
500
  Use \`--send-at\` to schedule a one-time or recurring task:
500
501
 
501
- kimaki send --channel ${channelId} --prompt "Reminder: review open PRs" --send-at "2026-03-01T09:00:00Z"${userArg}
502
- kimaki send --channel ${channelId} --prompt "Run weekly test suite and summarize failures" --send-at "0 9 * * 1"${userArg}
502
+ kimaki send --channel ${channelId} --prompt "Reminder: review open PRs" --send-at "2026-03-01T09:00:00Z" --agent <current_agent>${userArg}
503
+ kimaki send --channel ${channelId} --prompt "Run weekly test suite and summarize failures" --send-at "0 9 * * 1" --agent <current_agent>${userArg}
503
504
 
504
505
  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).
505
506
  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.
@@ -533,13 +534,13 @@ kimaki task delete <id>
533
534
 
534
535
  Use case patterns:
535
536
  - Reminder flows: create deadline reminders in this channel with one-time \`--send-at\`; mention only if action is required.
536
- - Proactive reminders: when you encounter time-sensitive information during your work (e.g. creating an API key that expires in 90 days, a certificate with an expiration date, a trial period ending, a deadline mentioned in code comments), proactively schedule a \`--notify-only\` reminder before the expiration so the user gets notified in time. For example, if you generate an API key expiring on 2026-06-01, schedule a reminder a few days before: \`kimaki send --channel ${channelId} --prompt "Reminder: <@USER_ID> the API key created on 2026-03-01 expires on 2026-06-01. Renew it before it breaks production." --send-at "2026-05-28T09:00:00Z" --notify-only\`. Always tell the user you scheduled the reminder so they know.
537
+ - Proactive reminders: when you encounter time-sensitive information during your work (e.g. creating an API key that expires in 90 days, a certificate with an expiration date, a trial period ending, a deadline mentioned in code comments), proactively schedule a \`--notify-only\` reminder before the expiration so the user gets notified in time. For example, if you generate an API key expiring on 2026-06-01, schedule a reminder a few days before: \`kimaki send --channel ${channelId} --prompt "Reminder: <@USER_ID> the API key created on 2026-03-01 expires on 2026-06-01. Renew it before it breaks production." --send-at "2026-05-28T09:00:00Z" --notify-only --agent <current_agent>\`. Always tell the user you scheduled the reminder so they know.
537
538
  - Weekly QA: schedule "run full test suite, inspect failures, post summary, and mention @username only when failures require review".
538
539
  - Weekly benchmark automation: schedule a benchmark prompt that runs model evals, writes JSON outputs in the repo, commits results, and mentions only for regressions.
539
540
  - 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.
540
541
  - 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:
541
542
 
542
- kimaki send --session ${sessionId} --prompt "Reminder: <@USER_ID> you asked to be reminded about this thread." --send-at "<future_UTC_time>" --notify-only
543
+ kimaki send --session ${sessionId} --prompt "Reminder: <@USER_ID> you asked to be reminded about this thread." --send-at "<future_UTC_time>" --notify-only --agent <current_agent>
543
544
 
544
545
  Replace \`<future_UTC_time>\` with the computed UTC ISO timestamp. The \`--notify-only\` flag creates just a notification message without starting a new AI session. The \`<@userId>\` mention ensures the user gets a Discord notification.
545
546
 
@@ -554,12 +555,12 @@ ONLY create worktrees when the user explicitly asks for one. Never proactively u
554
555
  When the user asks to "create a worktree" or "make a worktree", they mean you should use the kimaki CLI to create it. Do NOT use raw \`git worktree add\` commands. Instead use:
555
556
 
556
557
  \`\`\`bash
557
- kimaki send --channel ${channelId} --prompt "your task description" --worktree worktree-name${userArg}
558
+ kimaki send --channel ${channelId} --prompt "your task description" --worktree worktree-name --agent <current_agent>${userArg}
558
559
  \`\`\`
559
560
 
560
561
  This creates a new Discord thread with an isolated git worktree and starts a session in it. The worktree name should be kebab-case and descriptive of the task.
561
562
 
562
- By default, worktrees are created from \`origin/HEAD\` (the remote's default branch). To change the base branch for a project, the user can run \`git remote set-head origin <branch>\` in the project directory. For example, \`git remote set-head origin dev\` makes all new worktrees branch off \`origin/dev\` instead of \`origin/main\`.
563
+ By default, worktrees are created from \`HEAD\`, which means whatever commit or branch the current checkout is on. If you want a different base, pass \`--base-branch\` or use the slash command option explicitly.
563
564
 
564
565
  Critical recursion guard:
565
566
  - If you already are in a worktree thread, do not create another worktree unless the user explicitly asks for a nested worktree.
@@ -570,7 +571,7 @@ Critical recursion guard:
570
571
  Use \`--cwd\` to start a session in an existing git worktree directory instead of creating a new one:
571
572
 
572
573
  \`\`\`bash
573
- kimaki send --channel ${channelId} --prompt "Continue work on feature X" --cwd /path/to/existing-worktree${userArg}
574
+ kimaki send --channel ${channelId} --prompt "Continue work on feature X" --cwd /path/to/existing-worktree --agent <current_agent>${userArg}
574
575
  \`\`\`
575
576
 
576
577
  The path must be a git worktree of the project (validated via \`git worktree list\`). The session resolves to the correct project channel but uses the worktree as its working directory. Use \`--worktree\` to create a new worktree, \`--cwd\` to reuse an existing one.
@@ -584,7 +585,7 @@ This is useful for automation (cron jobs, GitHub webhooks, n8n, etc.)
584
585
  When you are approaching the **context window limit** or the user explicitly asks to **handoff to a new thread**, use the \`kimaki send\` command to start a fresh session with context:
585
586
 
586
587
  \`\`\`bash
587
- kimaki send --channel ${channelId} --prompt "Continuing from previous session: <summary of current task and state>"${userArg}
588
+ kimaki send --channel ${channelId} --prompt "Continuing from previous session: <summary of current task and state>" --agent <current_agent>${userArg}
588
589
  \`\`\`
589
590
 
590
591
  The command automatically handles long prompts (over 2000 chars) by sending them as file attachments.
@@ -642,10 +643,10 @@ To send a task to another project:
642
643
 
643
644
  \`\`\`bash
644
645
  # Send to a specific channel
645
- kimaki send --channel <channel_id> --prompt "Plan how to update the API client to v2"
646
+ kimaki send --channel <channel_id> --prompt "Plan how to update the API client to v2" --agent <current_agent>
646
647
 
647
648
  # Or use --project to resolve from directory
648
- kimaki send --project /path/to/other-repo --prompt "Plan how to bump version to 1.2.0"
649
+ kimaki send --project /path/to/other-repo --prompt "Plan how to bump version to 1.2.0" --agent <current_agent>
649
650
  \`\`\`
650
651
 
651
652
  When sending prompts to other projects, always ask the agent to plan first, never build upfront. The prompt should start with "Plan how to ..." so the user can review before greenlighting implementation.
@@ -668,10 +669,10 @@ If your Bash tool timeout triggers anyway, fall back to reading the session outp
668
669
 
669
670
  \`\`\`bash
670
671
  # Start a session and wait for it to finish
671
- kimaki send --channel <channel_id> --prompt "Fix the auth bug" --wait
672
+ kimaki send --channel <channel_id> --prompt "Fix the auth bug" --wait --agent <current_agent>
672
673
 
673
674
  # Send to an existing thread and wait
674
- kimaki send --thread <thread_id> --prompt "Run the tests" --wait
675
+ kimaki send --thread <thread_id> --prompt "Run the tests" --wait --agent <current_agent>
675
676
  \`\`\`
676
677
 
677
678
  The command exits with the session markdown on stdout once the model finishes responding.
@@ -33,6 +33,7 @@ type SessionState = {
33
33
  comparedTurn: number
34
34
  previousTurnContext: TurnContext | undefined
35
35
  currentTurnContext: TurnContext | undefined
36
+ pendingCompareTimeout: ReturnType<typeof setTimeout> | undefined
36
37
  }
37
38
 
38
39
  type SystemPromptDiff = {
@@ -146,7 +147,6 @@ function writeSystemPromptDiffFile({
146
147
  additions: number
147
148
  deletions: number
148
149
  filePath: string
149
- latestPromptPath: string
150
150
  } {
151
151
  const diff = buildPatch({
152
152
  beforeText: beforePrompt,
@@ -157,7 +157,7 @@ function writeSystemPromptDiffFile({
157
157
  const timestamp = new Date().toISOString().replaceAll(':', '-')
158
158
  const sessionDir = path.join(getSystemPromptDiffDir({ dataDir }), sessionId)
159
159
  const filePath = path.join(sessionDir, `${timestamp}.diff`)
160
- const latestPromptPath = path.join(sessionDir, `${timestamp}.md`)
160
+
161
161
  const fileContent = [
162
162
  `Session: ${sessionId}`,
163
163
  `Created: ${new Date().toISOString()}`,
@@ -176,7 +176,6 @@ function writeSystemPromptDiffFile({
176
176
  additions: diff.additions,
177
177
  deletions: diff.deletions,
178
178
  filePath,
179
- latestPromptPath,
180
179
  }
181
180
  },
182
181
  catch: (error) => {
@@ -204,6 +203,7 @@ function getOrCreateSessionState({
204
203
  comparedTurn: 0,
205
204
  previousTurnContext: undefined,
206
205
  currentTurnContext: undefined,
206
+ pendingCompareTimeout: undefined,
207
207
  }
208
208
  sessions.set(sessionId, state)
209
209
  return state
@@ -278,8 +278,7 @@ async function handleSystemTransform({
278
278
  sessionId,
279
279
  message:
280
280
  `system prompt changed since the previous message (+${diffFileResult.additions} / -${diffFileResult.deletions}). ` +
281
- `Diff: \`${abbreviatePath(diffFileResult.filePath)}\`. ` +
282
- `Latest prompt: \`${abbreviatePath(diffFileResult.latestPromptPath)}\``,
281
+ `Diff: \`${abbreviatePath(diffFileResult.filePath)}\`. `
283
282
  }),
284
283
  },
285
284
  })
@@ -315,13 +314,46 @@ const systemPromptDriftPlugin: Plugin = async ({ client, directory }) => {
315
314
  'experimental.chat.system.transform': async (input, output) => {
316
315
  const result = await errore.tryAsync({
317
316
  try: async () => {
318
- await handleSystemTransform({
319
- input,
320
- output,
321
- sessions,
322
- dataDir,
323
- client,
324
- })
317
+ const sessionId = input.sessionID
318
+ if (!sessionId) {
319
+ return
320
+ }
321
+ const state = getOrCreateSessionState({ sessions, sessionId })
322
+ if (state.pendingCompareTimeout) {
323
+ clearTimeout(state.pendingCompareTimeout)
324
+ }
325
+ // Delay one tick so other system-transform hooks can finish mutating
326
+ // output.system before we snapshot it for drift detection.
327
+ state.pendingCompareTimeout = setTimeout(() => {
328
+ state.pendingCompareTimeout = undefined
329
+ void errore.tryAsync({
330
+ try: async () => {
331
+ await handleSystemTransform({
332
+ input,
333
+ output,
334
+ sessions,
335
+ dataDir,
336
+ client,
337
+ })
338
+ },
339
+ catch: (error) => {
340
+ return new Error('system prompt drift transform hook failed', {
341
+ cause: error,
342
+ })
343
+ },
344
+ }).then((delayedResult) => {
345
+ if (!(delayedResult instanceof Error)) {
346
+ return
347
+ }
348
+ logger.warn(
349
+ `[system-prompt-drift-plugin] ${formatPluginErrorWithStack(delayedResult)}`,
350
+ )
351
+ void notifyError(
352
+ delayedResult,
353
+ 'system prompt drift plugin transform hook failed',
354
+ )
355
+ })
356
+ }, 0)
325
357
  },
326
358
  catch: (error) => {
327
359
  return new Error('system prompt drift transform hook failed', {
@@ -346,6 +378,10 @@ const systemPromptDriftPlugin: Plugin = async ({ client, directory }) => {
346
378
  if (!deletedSessionId) {
347
379
  return
348
380
  }
381
+ const state = sessions.get(deletedSessionId)
382
+ if (state?.pendingCompareTimeout) {
383
+ clearTimeout(state.pendingCompareTimeout)
384
+ }
349
385
  sessions.delete(deletedSessionId)
350
386
  },
351
387
  catch: (error) => {
package/src/utils.ts CHANGED
@@ -82,7 +82,7 @@ export function generateBotInstallUrl({
82
82
 
83
83
  export const KIMAKI_GATEWAY_APP_ID =
84
84
  process.env.KIMAKI_GATEWAY_APP_ID || '1477605701202481173'
85
- export const KIMAKI_WEBSITE_URL = process.env.KIMAKI_WEBSITE_URL || 'https://kimaki.xyz'
85
+ export const KIMAKI_WEBSITE_URL = process.env.KIMAKI_WEBSITE_URL || 'https://kimaki.dev'
86
86
 
87
87
  export function generateDiscordInstallUrlForBot({
88
88
  appId,
@@ -220,4 +220,5 @@ describe('worktrees', () => {
220
220
  fs.rmSync(sandbox, { recursive: true, force: true })
221
221
  }
222
222
  })
223
+
223
224
  })
package/src/worktrees.ts CHANGED
@@ -527,52 +527,6 @@ type WorktreeResult = {
527
527
  async function resolveDefaultWorktreeTarget(
528
528
  directory: string,
529
529
  ): Promise<string> {
530
- const remoteHead = await execAsync(
531
- 'git symbolic-ref refs/remotes/origin/HEAD',
532
- {
533
- cwd: directory,
534
- },
535
- ).catch(() => {
536
- return null
537
- })
538
-
539
- const remoteRef = remoteHead?.stdout.trim()
540
- if (remoteRef?.startsWith('refs/remotes/')) {
541
- return remoteRef.replace('refs/remotes/', '')
542
- }
543
-
544
- const hasMain = await execAsync(
545
- 'git show-ref --verify --quiet refs/heads/main',
546
- {
547
- cwd: directory,
548
- },
549
- )
550
- .then(() => {
551
- return true
552
- })
553
- .catch(() => {
554
- return false
555
- })
556
- if (hasMain) {
557
- return 'main'
558
- }
559
-
560
- const hasMaster = await execAsync(
561
- 'git show-ref --verify --quiet refs/heads/master',
562
- {
563
- cwd: directory,
564
- },
565
- )
566
- .then(() => {
567
- return true
568
- })
569
- .catch(() => {
570
- return false
571
- })
572
- if (hasMaster) {
573
- return 'master'
574
- }
575
-
576
530
  return 'HEAD'
577
531
  }
578
532
 
@@ -608,7 +562,7 @@ export async function createWorktreeWithSubmodules({
608
562
  }: {
609
563
  directory: string
610
564
  name: string
611
- /** Override the base branch to create the worktree from. Defaults to origin/HEAD → main → master → HEAD. */
565
+ /** Override the base branch to create the worktree from. Defaults to HEAD. */
612
566
  baseBranch?: string
613
567
  /** Called with a short phase label so callers can update UI (e.g. Discord status message). */
614
568
  onProgress?: (phase: string) => void