kimaki 0.4.42 → 0.4.44

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.
@@ -2,12 +2,23 @@
2
2
  // Creates the system message injected into every OpenCode session,
3
3
  // including Discord-specific formatting rules, diff commands, and permissions info.
4
4
 
5
+ export type WorktreeInfo = {
6
+ /** The worktree directory path */
7
+ worktreeDirectory: string
8
+ /** The branch name (e.g., opencode/kimaki-feature) */
9
+ branch: string
10
+ /** The main repository directory */
11
+ mainRepoDirectory: string
12
+ }
13
+
5
14
  export function getOpencodeSystemMessage({
6
15
  sessionId,
7
16
  channelId,
17
+ worktree,
8
18
  }: {
9
19
  sessionId: string
10
20
  channelId?: string
21
+ worktree?: WorktreeInfo
11
22
  }) {
12
23
  return `
13
24
  The user is reading your messages from inside Discord, via kimaki.xyz
@@ -43,6 +54,43 @@ Use --notify-only to create a notification thread without starting an AI session
43
54
  npx -y kimaki send --channel ${channelId} --prompt "User cancelled subscription" --notify-only
44
55
 
45
56
  This is useful for automation (cron jobs, GitHub webhooks, n8n, etc.)
57
+
58
+ ### Session handoff
59
+
60
+ 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:
61
+
62
+ \`\`\`bash
63
+ npx -y kimaki send --channel ${channelId} --prompt "Continuing from previous session: <summary of current task and state>"
64
+ \`\`\`
65
+
66
+ The command automatically handles long prompts (over 2000 chars) by sending them as file attachments.
67
+
68
+ Use this for handoff when:
69
+ - User asks to "handoff", "continue in new thread", or "start fresh session"
70
+ - You detect you're running low on context window space
71
+ - A complex task would benefit from a clean slate with summarized context
72
+ `
73
+ : ''
74
+ }${
75
+ worktree
76
+ ? `
77
+ ## worktree
78
+
79
+ This session is running inside a git worktree.
80
+ - **Worktree path:** \`${worktree.worktreeDirectory}\`
81
+ - **Branch:** \`${worktree.branch}\`
82
+ - **Main repo:** \`${worktree.mainRepoDirectory}\`
83
+
84
+ Before finishing a task, ask the user if they want to merge changes back to the main branch.
85
+
86
+ To merge (without leaving the worktree):
87
+ \`\`\`bash
88
+ # Get the default branch name
89
+ DEFAULT_BRANCH=$(git -C ${worktree.mainRepoDirectory} symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
90
+
91
+ # Merge worktree branch into main
92
+ git -C ${worktree.mainRepoDirectory} checkout $DEFAULT_BRANCH && git -C ${worktree.mainRepoDirectory} merge ${worktree.branch}
93
+ \`\`\`
46
94
  `
47
95
  : ''
48
96
  }
@@ -52,20 +100,24 @@ IMPORTANT: After editing any files, you MUST execute the critique command using
52
100
 
53
101
  Execute this after making changes:
54
102
 
55
- bunx critique web --title "Add user authentication flow"
103
+ bunx critique --web "Add user authentication flow"
56
104
 
57
105
  If there are other unrelated changes in the working directory, filter to only show the files you edited:
58
106
 
59
- bunx critique web --title "Fix database connection retry" -- path/to/file1.ts path/to/file2.ts
107
+ bunx critique --web "Fix database connection retry" --filter "path/to/file1.ts" --filter "path/to/file2.ts"
60
108
 
61
109
  You can also show latest commit changes using:
62
110
 
63
- bunx critique web --title "Refactor API endpoints" HEAD
111
+ bunx critique HEAD --web "Refactor API endpoints"
64
112
 
65
- bunx critique web --title "Update dependencies" HEAD~1 to get the one before last
113
+ bunx critique HEAD~1 --web "Update dependencies"
66
114
 
67
115
  Do this in case you committed the changes yourself (only if the user asks so, never commit otherwise).
68
116
 
117
+ To compare two branches:
118
+
119
+ bunx critique main feature-branch --web "Compare branches"
120
+
69
121
  The command outputs a URL - share that URL with the user so they can see the diff.
70
122
 
71
123
  ## markdown
@@ -506,6 +506,7 @@ export async function processVoiceAttachment({
506
506
  EmptyTranscriptionError: (e) => e.message,
507
507
  NoResponseContentError: (e) => e.message,
508
508
  NoToolResponseError: (e) => e.message,
509
+ Error: (e) => e.message,
509
510
  })
510
511
  voiceLogger.error(`Transcription failed:`, transcription)
511
512
  await sendThreadMessage(thread, `⚠️ Transcription failed: ${errMsg}`)
@@ -0,0 +1,78 @@
1
+ // Worktree utility functions.
2
+ // Wrapper for OpenCode worktree creation that also initializes git submodules.
3
+
4
+ import { exec } from 'node:child_process'
5
+ import { promisify } from 'node:util'
6
+ import { createLogger } from './logger.js'
7
+ import type { getOpencodeClientV2 } from './opencode.js'
8
+
9
+ export const execAsync = promisify(exec)
10
+
11
+ const logger = createLogger('WORKTREE-UTILS')
12
+
13
+ type OpencodeClientV2 = NonNullable<ReturnType<typeof getOpencodeClientV2>>
14
+
15
+ type WorktreeResult = {
16
+ directory: string
17
+ branch: string
18
+ }
19
+
20
+ /**
21
+ * Create a worktree using OpenCode SDK and initialize git submodules.
22
+ * This wrapper ensures submodules are properly set up in new worktrees.
23
+ */
24
+ export async function createWorktreeWithSubmodules({
25
+ clientV2,
26
+ directory,
27
+ name,
28
+ }: {
29
+ clientV2: OpencodeClientV2
30
+ directory: string
31
+ name: string
32
+ }): Promise<WorktreeResult | Error> {
33
+ // 1. Create worktree via OpenCode SDK
34
+ const response = await clientV2.worktree.create({
35
+ directory,
36
+ worktreeCreateInput: { name },
37
+ })
38
+
39
+ if (response.error) {
40
+ return new Error(`SDK error: ${JSON.stringify(response.error)}`)
41
+ }
42
+
43
+ if (!response.data) {
44
+ return new Error('No worktree data returned from SDK')
45
+ }
46
+
47
+ const worktreeDir = response.data.directory
48
+
49
+ // 2. Init submodules in new worktree (don't block on failure)
50
+ try {
51
+ logger.log(`Initializing submodules in ${worktreeDir}`)
52
+ await execAsync('git submodule update --init --recursive', {
53
+ cwd: worktreeDir,
54
+ })
55
+ logger.log(`Submodules initialized in ${worktreeDir}`)
56
+ } catch (e) {
57
+ // Log but don't fail - submodules might not exist
58
+ logger.warn(
59
+ `Failed to init submodules in ${worktreeDir}: ${e instanceof Error ? e.message : String(e)}`,
60
+ )
61
+ }
62
+
63
+ // 3. Install dependencies using ni (detects package manager from lockfile)
64
+ try {
65
+ logger.log(`Installing dependencies in ${worktreeDir}`)
66
+ await execAsync('npx -y ni', {
67
+ cwd: worktreeDir,
68
+ })
69
+ logger.log(`Dependencies installed in ${worktreeDir}`)
70
+ } catch (e) {
71
+ // Log but don't fail - might not be a JS project or might fail for various reasons
72
+ logger.warn(
73
+ `Failed to install dependencies in ${worktreeDir}: ${e instanceof Error ? e.message : String(e)}`,
74
+ )
75
+ }
76
+
77
+ return response.data
78
+ }