kimaki 0.15.0 → 0.16.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.
@@ -457,6 +457,7 @@ cli
457
457
  botToken,
458
458
  embeds: autoStartEmbed,
459
459
  rest,
460
+ splitInsteadOfAttach: notifyOnly,
460
461
  });
461
462
  // For notify-only on non-project channels, just post the message without
462
463
  // creating a thread. There's no session to start, so a thread is unnecessary.
@@ -107,7 +107,7 @@ export function isThreadChannelType(type) {
107
107
  ChannelType.AnnouncementThread,
108
108
  ].includes(type);
109
109
  }
110
- export async function sendDiscordMessageWithOptionalAttachment({ channelId, prompt, botToken, embeds, rest, }) {
110
+ export async function sendDiscordMessageWithOptionalAttachment({ channelId, prompt, botToken, embeds, rest, splitInsteadOfAttach, }) {
111
111
  const discordMaxLength = 2000;
112
112
  if (prompt.length <= discordMaxLength) {
113
113
  return (await rest.post(Routes.channelMessages(channelId), {
@@ -118,6 +118,33 @@ export async function sendDiscordMessageWithOptionalAttachment({ channelId, prom
118
118
  },
119
119
  }));
120
120
  }
121
+ if (splitInsteadOfAttach) {
122
+ const { splitMarkdownForDiscord } = await import('./discord-utils.js');
123
+ const chunks = splitMarkdownForDiscord({
124
+ content: prompt,
125
+ maxLength: discordMaxLength,
126
+ });
127
+ let firstMessage;
128
+ for (let chunk of chunks) {
129
+ if (!chunk?.trim())
130
+ continue;
131
+ // Safety net: hard-truncate if splitting still produced an oversized chunk
132
+ if (chunk.length > discordMaxLength) {
133
+ chunk = chunk.slice(0, discordMaxLength - 4) + '...';
134
+ }
135
+ const message = (await rest.post(Routes.channelMessages(channelId), {
136
+ body: {
137
+ content: chunk,
138
+ // Only attach embeds to the first message
139
+ ...(firstMessage ? {} : { embeds }),
140
+ allowed_mentions: { parse: store.getState().allowedMentions },
141
+ },
142
+ }));
143
+ if (!firstMessage)
144
+ firstMessage = message;
145
+ }
146
+ return firstMessage;
147
+ }
121
148
  const preview = prompt.slice(0, 100).replace(/\n/g, ' ');
122
149
  const summaryContent = `Prompt attached as file (${prompt.length} chars)\n\n> ${preview}...`;
123
150
  const tmpDir = path.join(process.cwd(), 'tmp');
@@ -759,8 +786,7 @@ export async function resolveCredentials({ forceRestartOnboarding, forceGateway,
759
786
  options: [
760
787
  {
761
788
  value: 'gateway',
762
- disabled: true,
763
- label: 'Gateway (pre-built Kimaki bot, currently disabled because of Discord verification process. will be re-enabled soon)',
789
+ label: 'Gateway (pre-built Kimaki bot, no setup needed)',
764
790
  },
765
791
  {
766
792
  value: 'self_hosted',
@@ -535,7 +535,7 @@ When you are approaching the **context window limit** or the user explicitly ask
535
535
  kimaki send --channel ${channelId} --prompt 'Continuing from previous session: <summary of current task and state>' --agent <current_agent>${userArg}
536
536
  \`\`\`
537
537
 
538
- The command automatically handles long prompts (over 2000 chars) by sending them as file attachments.
538
+ The command automatically handles long prompts (over 2000 chars) by sending them as file attachments. With \`--notify-only\`, long prompts are split into multiple messages instead so the content is directly visible.
539
539
 
540
540
  Use this for handoff when:
541
541
  - User asks to "handoff", "continue in new thread", or "start fresh session"
@@ -296,7 +296,7 @@ describe('system-message', () => {
296
296
  kimaki send --channel chan_123 --prompt 'Continuing from previous session: <summary of current task and state>' --agent <current_agent> --user '<discord-user-id>'
297
297
  \`\`\`
298
298
 
299
- The command automatically handles long prompts (over 2000 chars) by sending them as file attachments.
299
+ The command automatically handles long prompts (over 2000 chars) by sending them as file attachments. With \`--notify-only\`, long prompts are split into multiple messages instead so the content is directly visible.
300
300
 
301
301
  Use this for handoff when:
302
302
  - User asks to "handoff", "continue in new thread", or "start fresh session"
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "kimaki",
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
- "version": "0.15.0",
5
+ "version": "0.16.0",
6
6
  "repository": "https://github.com/remorses/kimaki",
7
7
  "bin": "bin.js",
8
8
  "files": [
@@ -25,8 +25,8 @@
25
25
  "tsx": "^4.20.5",
26
26
  "undici": "^8.0.2",
27
27
  "discord-digital-twin": "^0.1.0",
28
- "opencode-deterministic-provider": "^0.0.1",
29
28
  "opencode-cached-provider": "^0.0.1",
29
+ "opencode-deterministic-provider": "^0.0.1",
30
30
  "db": "^0.0.0"
31
31
  },
32
32
  "dependencies": {
@@ -62,10 +62,10 @@
62
62
  "yaml": "^2.8.3",
63
63
  "zod": "^4.3.6",
64
64
  "zustand": "^5.0.11",
65
- "traforo": "^0.7.0",
66
65
  "errore": "^0.14.1",
67
- "libsqlproxy": "^0.1.0",
68
- "opencode-injection-guard": "^0.2.1"
66
+ "opencode-injection-guard": "^0.2.1",
67
+ "traforo": "^0.7.0",
68
+ "libsqlproxy": "^0.1.0"
69
69
  },
70
70
  "optionalDependencies": {
71
71
  "@snazzah/davey": "^0.1.10",
@@ -621,6 +621,7 @@ cli
621
621
  botToken,
622
622
  embeds: autoStartEmbed,
623
623
  rest,
624
+ splitInsteadOfAttach: notifyOnly,
624
625
  })
625
626
 
626
627
  // For notify-only on non-project channels, just post the message without
package/src/cli-runner.ts CHANGED
@@ -172,12 +172,17 @@ export async function sendDiscordMessageWithOptionalAttachment({
172
172
  botToken,
173
173
  embeds,
174
174
  rest,
175
+ splitInsteadOfAttach,
175
176
  }: {
176
177
  channelId: string
177
178
  prompt: string
178
179
  botToken: string
179
180
  embeds?: Array<{ color: number; footer: { text: string } }>
180
181
  rest: REST
182
+ /** When true, long messages are split into multiple Discord messages instead of
183
+ * being attached as a file. Useful for notify-only messages where the content
184
+ * should be directly visible in the channel. */
185
+ splitInsteadOfAttach?: boolean
181
186
  }): Promise<{ id: string }> {
182
187
  const discordMaxLength = 2000
183
188
  if (prompt.length <= discordMaxLength) {
@@ -190,6 +195,32 @@ export async function sendDiscordMessageWithOptionalAttachment({
190
195
  })) as { id: string }
191
196
  }
192
197
 
198
+ if (splitInsteadOfAttach) {
199
+ const { splitMarkdownForDiscord } = await import('./discord-utils.js')
200
+ const chunks = splitMarkdownForDiscord({
201
+ content: prompt,
202
+ maxLength: discordMaxLength,
203
+ })
204
+ let firstMessage: { id: string } | undefined
205
+ for (let chunk of chunks) {
206
+ if (!chunk?.trim()) continue
207
+ // Safety net: hard-truncate if splitting still produced an oversized chunk
208
+ if (chunk.length > discordMaxLength) {
209
+ chunk = chunk.slice(0, discordMaxLength - 4) + '...'
210
+ }
211
+ const message = (await rest.post(Routes.channelMessages(channelId), {
212
+ body: {
213
+ content: chunk,
214
+ // Only attach embeds to the first message
215
+ ...(firstMessage ? {} : { embeds }),
216
+ allowed_mentions: { parse: store.getState().allowedMentions },
217
+ },
218
+ })) as { id: string }
219
+ if (!firstMessage) firstMessage = message
220
+ }
221
+ return firstMessage!
222
+ }
223
+
193
224
  const preview = prompt.slice(0, 100).replace(/\n/g, ' ')
194
225
  const summaryContent = `Prompt attached as file (${prompt.length} chars)\n\n> ${preview}...`
195
226
 
@@ -1109,8 +1140,7 @@ export async function resolveCredentials({
1109
1140
  options: [
1110
1141
  {
1111
1142
  value: 'gateway' as const,
1112
- disabled: true,
1113
- label: 'Gateway (pre-built Kimaki bot, currently disabled because of Discord verification process. will be re-enabled soon)',
1143
+ label: 'Gateway (pre-built Kimaki bot, no setup needed)',
1114
1144
  },
1115
1145
  {
1116
1146
  value: 'self_hosted' as const,
@@ -306,7 +306,7 @@ describe('system-message', () => {
306
306
  kimaki send --channel chan_123 --prompt 'Continuing from previous session: <summary of current task and state>' --agent <current_agent> --user '<discord-user-id>'
307
307
  \`\`\`
308
308
 
309
- The command automatically handles long prompts (over 2000 chars) by sending them as file attachments.
309
+ The command automatically handles long prompts (over 2000 chars) by sending them as file attachments. With \`--notify-only\`, long prompts are split into multiple messages instead so the content is directly visible.
310
310
 
311
311
  Use this for handoff when:
312
312
  - User asks to "handoff", "continue in new thread", or "start fresh session"
@@ -649,7 +649,7 @@ When you are approaching the **context window limit** or the user explicitly ask
649
649
  kimaki send --channel ${channelId} --prompt 'Continuing from previous session: <summary of current task and state>' --agent <current_agent>${userArg}
650
650
  \`\`\`
651
651
 
652
- The command automatically handles long prompts (over 2000 chars) by sending them as file attachments.
652
+ The command automatically handles long prompts (over 2000 chars) by sending them as file attachments. With \`--notify-only\`, long prompts are split into multiple messages instead so the content is directly visible.
653
653
 
654
654
  Use this for handoff when:
655
655
  - User asks to "handoff", "continue in new thread", or "start fresh session"