kimaki 0.4.39 → 0.4.41
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/LICENSE +21 -0
- package/dist/cli.js +108 -51
- package/dist/commands/abort.js +1 -1
- package/dist/commands/add-project.js +2 -2
- package/dist/commands/agent.js +2 -2
- package/dist/commands/fork.js +2 -2
- package/dist/commands/model.js +2 -2
- package/dist/commands/remove-project.js +2 -2
- package/dist/commands/resume.js +2 -2
- package/dist/commands/session.js +4 -4
- package/dist/commands/share.js +1 -1
- package/dist/commands/undo-redo.js +2 -2
- package/dist/commands/worktree.js +180 -0
- package/dist/database.js +49 -1
- package/dist/discord-bot.js +29 -4
- package/dist/discord-utils.js +36 -0
- package/dist/errors.js +86 -87
- package/dist/genai-worker.js +1 -1
- package/dist/interaction-handler.js +6 -2
- package/dist/markdown.js +5 -1
- package/dist/message-formatting.js +2 -2
- package/dist/opencode.js +4 -4
- package/dist/session-handler.js +2 -2
- package/dist/tools.js +3 -3
- package/dist/voice-handler.js +3 -3
- package/dist/voice.js +4 -4
- package/package.json +16 -16
- package/src/cli.ts +166 -85
- package/src/commands/abort.ts +1 -1
- package/src/commands/add-project.ts +2 -2
- package/src/commands/agent.ts +2 -2
- package/src/commands/fork.ts +2 -2
- package/src/commands/model.ts +2 -2
- package/src/commands/remove-project.ts +2 -2
- package/src/commands/resume.ts +2 -2
- package/src/commands/session.ts +4 -4
- package/src/commands/share.ts +1 -1
- package/src/commands/undo-redo.ts +2 -2
- package/src/commands/worktree.ts +243 -0
- package/src/database.ts +96 -1
- package/src/discord-bot.ts +30 -4
- package/src/discord-utils.ts +50 -0
- package/src/errors.ts +90 -160
- package/src/genai-worker.ts +1 -1
- package/src/interaction-handler.ts +7 -2
- package/src/markdown.ts +5 -4
- package/src/message-formatting.ts +2 -2
- package/src/opencode.ts +4 -4
- package/src/session-handler.ts +2 -2
- package/src/tools.ts +3 -3
- package/src/voice-handler.ts +3 -3
- package/src/voice.ts +4 -4
package/package.json
CHANGED
|
@@ -2,18 +2,7 @@
|
|
|
2
2
|
"name": "kimaki",
|
|
3
3
|
"module": "index.ts",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.4.
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "tsx --env-file .env src/cli.ts",
|
|
8
|
-
"prepublishOnly": "pnpm tsc",
|
|
9
|
-
"dev:bun": "DEBUG=1 bun --env-file .env src/cli.ts",
|
|
10
|
-
"watch": "tsx scripts/watch-session.ts",
|
|
11
|
-
"test:events": "tsx test-events.ts",
|
|
12
|
-
"pcm-to-mp3": "bun scripts/pcm-to-mp3",
|
|
13
|
-
"test:send": "tsx send-test-message.ts",
|
|
14
|
-
"register-commands": "tsx scripts/register-commands.ts",
|
|
15
|
-
"format": "oxfmt src"
|
|
16
|
-
},
|
|
5
|
+
"version": "0.4.41",
|
|
17
6
|
"repository": "https://github.com/remorses/kimaki",
|
|
18
7
|
"bin": "bin.js",
|
|
19
8
|
"files": [
|
|
@@ -34,27 +23,38 @@
|
|
|
34
23
|
"@clack/prompts": "^0.11.0",
|
|
35
24
|
"@discordjs/voice": "^0.19.0",
|
|
36
25
|
"@google/genai": "^1.34.0",
|
|
37
|
-
"@opencode-ai/sdk": "^1.1.
|
|
26
|
+
"@opencode-ai/sdk": "^1.1.31",
|
|
38
27
|
"@purinton/resampler": "^1.0.4",
|
|
39
28
|
"ai": "^5.0.114",
|
|
40
29
|
"better-sqlite3": "^12.3.0",
|
|
41
30
|
"cac": "^6.7.14",
|
|
42
31
|
"discord.js": "^14.16.3",
|
|
43
32
|
"domhandler": "^5.0.3",
|
|
44
|
-
"errore": "^0.5.2",
|
|
45
33
|
"glob": "^13.0.0",
|
|
46
34
|
"htmlparser2": "^10.0.0",
|
|
47
35
|
"js-yaml": "^4.1.0",
|
|
48
36
|
"marked": "^16.3.0",
|
|
37
|
+
"mime": "^4.1.0",
|
|
49
38
|
"picocolors": "^1.1.1",
|
|
50
39
|
"pretty-ms": "^9.3.0",
|
|
51
40
|
"ripgrep-js": "^3.0.0",
|
|
52
41
|
"string-dedent": "^3.0.2",
|
|
53
42
|
"undici": "^7.16.0",
|
|
54
|
-
"zod": "^4.2.1"
|
|
43
|
+
"zod": "^4.2.1",
|
|
44
|
+
"errore": "^0.7.1"
|
|
55
45
|
},
|
|
56
46
|
"optionalDependencies": {
|
|
57
47
|
"@discordjs/opus": "^0.10.0",
|
|
58
48
|
"prism-media": "^1.3.5"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"dev": "tsx --env-file .env src/cli.ts",
|
|
52
|
+
"dev:bun": "DEBUG=1 bun --env-file .env src/cli.ts",
|
|
53
|
+
"watch": "tsx scripts/watch-session.ts",
|
|
54
|
+
"test:events": "tsx test-events.ts",
|
|
55
|
+
"pcm-to-mp3": "bun scripts/pcm-to-mp3",
|
|
56
|
+
"test:send": "tsx send-test-message.ts",
|
|
57
|
+
"register-commands": "tsx scripts/register-commands.ts",
|
|
58
|
+
"format": "oxfmt src"
|
|
59
59
|
}
|
|
60
|
-
}
|
|
60
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -43,6 +43,7 @@ import fs from 'node:fs'
|
|
|
43
43
|
import * as errore from 'errore'
|
|
44
44
|
|
|
45
45
|
import { createLogger } from './logger.js'
|
|
46
|
+
import { uploadFilesToDiscord } from './discord-utils.js'
|
|
46
47
|
import { spawn, spawnSync, execSync, type ExecSyncOptions } from 'node:child_process'
|
|
47
48
|
import http from 'node:http'
|
|
48
49
|
import { setDataDir, getDataDir, getLockPort } from './config.js'
|
|
@@ -211,7 +212,7 @@ async function registerCommands({
|
|
|
211
212
|
})
|
|
212
213
|
.toJSON(),
|
|
213
214
|
new SlashCommandBuilder()
|
|
214
|
-
.setName('session')
|
|
215
|
+
.setName('new-session')
|
|
215
216
|
.setDescription('Start a new OpenCode session')
|
|
216
217
|
.addStringOption((option) => {
|
|
217
218
|
option.setName('prompt').setDescription('Prompt content for the session').setRequired(true)
|
|
@@ -236,6 +237,18 @@ async function registerCommands({
|
|
|
236
237
|
return option
|
|
237
238
|
})
|
|
238
239
|
.toJSON(),
|
|
240
|
+
new SlashCommandBuilder()
|
|
241
|
+
.setName('new-worktree')
|
|
242
|
+
.setDescription('Create a new git worktree and start a session thread')
|
|
243
|
+
.addStringOption((option) => {
|
|
244
|
+
option
|
|
245
|
+
.setName('name')
|
|
246
|
+
.setDescription('Name for the worktree (will be formatted: lowercase, spaces to dashes)')
|
|
247
|
+
.setRequired(true)
|
|
248
|
+
|
|
249
|
+
return option
|
|
250
|
+
})
|
|
251
|
+
.toJSON(),
|
|
239
252
|
new SlashCommandBuilder()
|
|
240
253
|
.setName('add-project')
|
|
241
254
|
.setDescription('Create Discord channels for a new OpenCode project')
|
|
@@ -376,6 +389,133 @@ async function registerCommands({
|
|
|
376
389
|
}
|
|
377
390
|
}
|
|
378
391
|
|
|
392
|
+
/**
|
|
393
|
+
* Store channel-directory mappings in the database.
|
|
394
|
+
* Called after Discord login to persist channel configurations.
|
|
395
|
+
*/
|
|
396
|
+
function storeChannelDirectories({
|
|
397
|
+
kimakiChannels,
|
|
398
|
+
db,
|
|
399
|
+
}: {
|
|
400
|
+
kimakiChannels: { guild: Guild; channels: ChannelWithTags[] }[]
|
|
401
|
+
db: ReturnType<typeof getDatabase>
|
|
402
|
+
}): void {
|
|
403
|
+
for (const { guild, channels } of kimakiChannels) {
|
|
404
|
+
for (const channel of channels) {
|
|
405
|
+
if (channel.kimakiDirectory) {
|
|
406
|
+
db.prepare(
|
|
407
|
+
'INSERT OR IGNORE INTO channel_directories (channel_id, directory, channel_type, app_id) VALUES (?, ?, ?, ?)',
|
|
408
|
+
).run(channel.id, channel.kimakiDirectory, 'text', channel.kimakiApp || null)
|
|
409
|
+
|
|
410
|
+
const voiceChannel = guild.channels.cache.find(
|
|
411
|
+
(ch) => ch.type === ChannelType.GuildVoice && ch.name === channel.name,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
if (voiceChannel) {
|
|
415
|
+
db.prepare(
|
|
416
|
+
'INSERT OR IGNORE INTO channel_directories (channel_id, directory, channel_type, app_id) VALUES (?, ?, ?, ?)',
|
|
417
|
+
).run(voiceChannel.id, channel.kimakiDirectory, 'voice', channel.kimakiApp || null)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Show the ready message with channel links.
|
|
426
|
+
* Called at the end of startup to display available channels.
|
|
427
|
+
*/
|
|
428
|
+
function showReadyMessage({
|
|
429
|
+
kimakiChannels,
|
|
430
|
+
createdChannels,
|
|
431
|
+
appId,
|
|
432
|
+
}: {
|
|
433
|
+
kimakiChannels: { guild: Guild; channels: ChannelWithTags[] }[]
|
|
434
|
+
createdChannels: { name: string; id: string; guildId: string }[]
|
|
435
|
+
appId: string
|
|
436
|
+
}): void {
|
|
437
|
+
const allChannels: {
|
|
438
|
+
name: string
|
|
439
|
+
id: string
|
|
440
|
+
guildId: string
|
|
441
|
+
directory?: string
|
|
442
|
+
}[] = []
|
|
443
|
+
|
|
444
|
+
allChannels.push(...createdChannels)
|
|
445
|
+
|
|
446
|
+
kimakiChannels.forEach(({ guild, channels }) => {
|
|
447
|
+
channels.forEach((ch) => {
|
|
448
|
+
allChannels.push({
|
|
449
|
+
name: ch.name,
|
|
450
|
+
id: ch.id,
|
|
451
|
+
guildId: guild.id,
|
|
452
|
+
directory: ch.kimakiDirectory,
|
|
453
|
+
})
|
|
454
|
+
})
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
if (allChannels.length > 0) {
|
|
458
|
+
const channelLinks = allChannels
|
|
459
|
+
.map((ch) => `• #${ch.name}: https://discord.com/channels/${ch.guildId}/${ch.id}`)
|
|
460
|
+
.join('\n')
|
|
461
|
+
|
|
462
|
+
note(
|
|
463
|
+
`Your kimaki channels are ready! Click any link below to open in Discord:\n\n${channelLinks}\n\nSend a message in any channel to start using OpenCode!`,
|
|
464
|
+
'🚀 Ready to Use',
|
|
465
|
+
)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
note(
|
|
469
|
+
'Leave this process running to keep the bot active.\n\nIf you close this process or restart your machine, run `npx kimaki` again to start the bot.',
|
|
470
|
+
'⚠️ Keep Running',
|
|
471
|
+
)
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Background initialization for quick start mode.
|
|
476
|
+
* Starts OpenCode server and registers slash commands without blocking bot startup.
|
|
477
|
+
*/
|
|
478
|
+
async function backgroundInit({
|
|
479
|
+
currentDir,
|
|
480
|
+
token,
|
|
481
|
+
appId,
|
|
482
|
+
}: {
|
|
483
|
+
currentDir: string
|
|
484
|
+
token: string
|
|
485
|
+
appId: string
|
|
486
|
+
}): Promise<void> {
|
|
487
|
+
try {
|
|
488
|
+
const opencodeResult = await initializeOpencodeForDirectory(currentDir)
|
|
489
|
+
if (opencodeResult instanceof Error) {
|
|
490
|
+
cliLogger.warn('Background OpenCode init failed:', opencodeResult.message)
|
|
491
|
+
// Still try to register basic commands without user commands/agents
|
|
492
|
+
await registerCommands({ token, appId, userCommands: [], agents: [] })
|
|
493
|
+
return
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const getClient = opencodeResult
|
|
497
|
+
|
|
498
|
+
const [userCommands, agents] = await Promise.all([
|
|
499
|
+
getClient()
|
|
500
|
+
.command.list({ query: { directory: currentDir } })
|
|
501
|
+
.then((r) => r.data || [])
|
|
502
|
+
.catch(() => []),
|
|
503
|
+
getClient()
|
|
504
|
+
.app.agents({ query: { directory: currentDir } })
|
|
505
|
+
.then((r) => r.data || [])
|
|
506
|
+
.catch(() => []),
|
|
507
|
+
])
|
|
508
|
+
|
|
509
|
+
await registerCommands({ token, appId, userCommands, agents })
|
|
510
|
+
cliLogger.log('Slash commands registered!')
|
|
511
|
+
} catch (error) {
|
|
512
|
+
cliLogger.error(
|
|
513
|
+
'Background init failed:',
|
|
514
|
+
error instanceof Error ? error.message : String(error),
|
|
515
|
+
)
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
379
519
|
async function run({ restart, addChannels }: CliOptions) {
|
|
380
520
|
const forceSetup = Boolean(restart)
|
|
381
521
|
|
|
@@ -582,7 +722,7 @@ async function run({ restart, addChannels }: CliOptions) {
|
|
|
582
722
|
const currentDir = process.cwd()
|
|
583
723
|
s.start('Starting OpenCode server...')
|
|
584
724
|
const opencodePromise = initializeOpencodeForDirectory(currentDir).then((result) => {
|
|
585
|
-
if (
|
|
725
|
+
if (result instanceof Error) {
|
|
586
726
|
throw new Error(result.message)
|
|
587
727
|
}
|
|
588
728
|
return result
|
|
@@ -666,25 +806,8 @@ async function run({ restart, addChannels }: CliOptions) {
|
|
|
666
806
|
}
|
|
667
807
|
db.prepare('INSERT OR REPLACE INTO bot_tokens (app_id, token) VALUES (?, ?)').run(appId, token)
|
|
668
808
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
if (channel.kimakiDirectory) {
|
|
672
|
-
db.prepare(
|
|
673
|
-
'INSERT OR IGNORE INTO channel_directories (channel_id, directory, channel_type, app_id) VALUES (?, ?, ?, ?)',
|
|
674
|
-
).run(channel.id, channel.kimakiDirectory, 'text', channel.kimakiApp || null)
|
|
675
|
-
|
|
676
|
-
const voiceChannel = guild.channels.cache.find(
|
|
677
|
-
(ch) => ch.type === ChannelType.GuildVoice && ch.name === channel.name,
|
|
678
|
-
)
|
|
679
|
-
|
|
680
|
-
if (voiceChannel) {
|
|
681
|
-
db.prepare(
|
|
682
|
-
'INSERT OR IGNORE INTO channel_directories (channel_id, directory, channel_type, app_id) VALUES (?, ?, ?, ?)',
|
|
683
|
-
).run(voiceChannel.id, channel.kimakiDirectory, 'voice', channel.kimakiApp || null)
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
}
|
|
809
|
+
// Store channel-directory mappings
|
|
810
|
+
storeChannelDirectories({ kimakiChannels, db })
|
|
688
811
|
|
|
689
812
|
if (kimakiChannels.length > 0) {
|
|
690
813
|
const channelList = kimakiChannels
|
|
@@ -700,6 +823,22 @@ async function run({ restart, addChannels }: CliOptions) {
|
|
|
700
823
|
note(channelList, 'Existing Kimaki Channels')
|
|
701
824
|
}
|
|
702
825
|
|
|
826
|
+
// Quick start: if setup is already done, start bot immediately and background the rest
|
|
827
|
+
const isQuickStart = existingBot && !forceSetup && !addChannels
|
|
828
|
+
if (isQuickStart) {
|
|
829
|
+
s.start('Starting Discord bot...')
|
|
830
|
+
await startDiscordBot({ token, appId, discordClient })
|
|
831
|
+
s.stop('Discord bot is running!')
|
|
832
|
+
|
|
833
|
+
// Background: OpenCode init + slash command registration (non-blocking)
|
|
834
|
+
void backgroundInit({ currentDir, token, appId })
|
|
835
|
+
|
|
836
|
+
showReadyMessage({ kimakiChannels, createdChannels, appId })
|
|
837
|
+
outro('✨ Bot ready! Listening for messages...')
|
|
838
|
+
return
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// Full setup path: wait for OpenCode, show prompts, create channels if needed
|
|
703
842
|
// Await the OpenCode server that was started in parallel with Discord login
|
|
704
843
|
s.start('Waiting for OpenCode server...')
|
|
705
844
|
const getClient = await opencodePromise
|
|
@@ -862,42 +1001,7 @@ async function run({ restart, addChannels }: CliOptions) {
|
|
|
862
1001
|
await startDiscordBot({ token, appId, discordClient })
|
|
863
1002
|
s.stop('Discord bot is running!')
|
|
864
1003
|
|
|
865
|
-
|
|
866
|
-
name: string
|
|
867
|
-
id: string
|
|
868
|
-
guildId: string
|
|
869
|
-
directory?: string
|
|
870
|
-
}[] = []
|
|
871
|
-
|
|
872
|
-
allChannels.push(...createdChannels)
|
|
873
|
-
|
|
874
|
-
kimakiChannels.forEach(({ guild, channels }) => {
|
|
875
|
-
channels.forEach((ch) => {
|
|
876
|
-
allChannels.push({
|
|
877
|
-
name: ch.name,
|
|
878
|
-
id: ch.id,
|
|
879
|
-
guildId: guild.id,
|
|
880
|
-
directory: ch.kimakiDirectory,
|
|
881
|
-
})
|
|
882
|
-
})
|
|
883
|
-
})
|
|
884
|
-
|
|
885
|
-
if (allChannels.length > 0) {
|
|
886
|
-
const channelLinks = allChannels
|
|
887
|
-
.map((ch) => `• #${ch.name}: https://discord.com/channels/${ch.guildId}/${ch.id}`)
|
|
888
|
-
.join('\n')
|
|
889
|
-
|
|
890
|
-
note(
|
|
891
|
-
`Your kimaki channels are ready! Click any link below to open in Discord:\n\n${channelLinks}\n\nSend a message in any channel to start using OpenCode!`,
|
|
892
|
-
'🚀 Ready to Use',
|
|
893
|
-
)
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
note(
|
|
897
|
-
'Leave this process running to keep the bot active.\n\nIf you close this process or restart your machine, run `npx kimaki` again to start the bot.',
|
|
898
|
-
'⚠️ Keep Running',
|
|
899
|
-
)
|
|
900
|
-
|
|
1004
|
+
showReadyMessage({ kimakiChannels, createdChannels, appId })
|
|
901
1005
|
outro('✨ Setup complete! Listening for new messages... do not close this process.')
|
|
902
1006
|
}
|
|
903
1007
|
|
|
@@ -998,34 +1102,11 @@ cli
|
|
|
998
1102
|
const s = spinner()
|
|
999
1103
|
s.start(`Uploading ${resolvedFiles.length} file(s)...`)
|
|
1000
1104
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
'payload_json',
|
|
1007
|
-
JSON.stringify({
|
|
1008
|
-
attachments: [{ id: 0, filename: path.basename(file) }],
|
|
1009
|
-
}),
|
|
1010
|
-
)
|
|
1011
|
-
formData.append('files[0]', new Blob([buffer]), path.basename(file))
|
|
1012
|
-
|
|
1013
|
-
const response = await fetch(
|
|
1014
|
-
`https://discord.com/api/v10/channels/${threadRow.thread_id}/messages`,
|
|
1015
|
-
{
|
|
1016
|
-
method: 'POST',
|
|
1017
|
-
headers: {
|
|
1018
|
-
Authorization: `Bot ${botRow.token}`,
|
|
1019
|
-
},
|
|
1020
|
-
body: formData,
|
|
1021
|
-
},
|
|
1022
|
-
)
|
|
1023
|
-
|
|
1024
|
-
if (!response.ok) {
|
|
1025
|
-
const error = await response.text()
|
|
1026
|
-
throw new Error(`Discord API error: ${response.status} - ${error}`)
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1105
|
+
await uploadFilesToDiscord({
|
|
1106
|
+
threadId: threadRow.thread_id,
|
|
1107
|
+
botToken: botRow.token,
|
|
1108
|
+
files: resolvedFiles,
|
|
1109
|
+
})
|
|
1029
1110
|
|
|
1030
1111
|
s.stop(`Uploaded ${resolvedFiles.length} file(s)!`)
|
|
1031
1112
|
|
package/src/commands/abort.ts
CHANGED
|
@@ -72,7 +72,7 @@ export async function handleAbortCommand({ command }: CommandContext): Promise<v
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
const getClient = await initializeOpencodeForDirectory(directory)
|
|
75
|
-
if (
|
|
75
|
+
if (getClient instanceof Error) {
|
|
76
76
|
await command.reply({
|
|
77
77
|
content: `Failed to abort: ${getClient.message}`,
|
|
78
78
|
ephemeral: true,
|
|
@@ -26,7 +26,7 @@ export async function handleAddProjectCommand({ command, appId }: CommandContext
|
|
|
26
26
|
try {
|
|
27
27
|
const currentDir = process.cwd()
|
|
28
28
|
const getClient = await initializeOpencodeForDirectory(currentDir)
|
|
29
|
-
if (
|
|
29
|
+
if (getClient instanceof Error) {
|
|
30
30
|
await command.editReply(getClient.message)
|
|
31
31
|
return
|
|
32
32
|
}
|
|
@@ -94,7 +94,7 @@ export async function handleAddProjectAutocomplete({
|
|
|
94
94
|
try {
|
|
95
95
|
const currentDir = process.cwd()
|
|
96
96
|
const getClient = await initializeOpencodeForDirectory(currentDir)
|
|
97
|
-
if (
|
|
97
|
+
if (getClient instanceof Error) {
|
|
98
98
|
await interaction.respond([])
|
|
99
99
|
return
|
|
100
100
|
}
|
package/src/commands/agent.ts
CHANGED
|
@@ -162,7 +162,7 @@ export async function handleAgentCommand({
|
|
|
162
162
|
|
|
163
163
|
try {
|
|
164
164
|
const getClient = await initializeOpencodeForDirectory(context.dir)
|
|
165
|
-
if (
|
|
165
|
+
if (getClient instanceof Error) {
|
|
166
166
|
await interaction.editReply({ content: getClient.message })
|
|
167
167
|
return
|
|
168
168
|
}
|
|
@@ -297,7 +297,7 @@ export async function handleQuickAgentCommand({
|
|
|
297
297
|
|
|
298
298
|
try {
|
|
299
299
|
const getClient = await initializeOpencodeForDirectory(context.dir)
|
|
300
|
-
if (
|
|
300
|
+
if (getClient instanceof Error) {
|
|
301
301
|
await command.editReply({ content: getClient.message })
|
|
302
302
|
return
|
|
303
303
|
}
|
package/src/commands/fork.ts
CHANGED
|
@@ -73,7 +73,7 @@ export async function handleForkCommand(interaction: ChatInputCommandInteraction
|
|
|
73
73
|
const sessionId = row.session_id
|
|
74
74
|
|
|
75
75
|
const getClient = await initializeOpencodeForDirectory(directory)
|
|
76
|
-
if (
|
|
76
|
+
if (getClient instanceof Error) {
|
|
77
77
|
await interaction.editReply({
|
|
78
78
|
content: `Failed to load messages: ${getClient.message}`,
|
|
79
79
|
})
|
|
@@ -171,7 +171,7 @@ export async function handleForkSelectMenu(
|
|
|
171
171
|
await interaction.deferReply({ ephemeral: false })
|
|
172
172
|
|
|
173
173
|
const getClient = await initializeOpencodeForDirectory(directory)
|
|
174
|
-
if (
|
|
174
|
+
if (getClient instanceof Error) {
|
|
175
175
|
await interaction.editReply(`Failed to fork session: ${getClient.message}`)
|
|
176
176
|
return
|
|
177
177
|
}
|
package/src/commands/model.ts
CHANGED
|
@@ -129,7 +129,7 @@ export async function handleModelCommand({
|
|
|
129
129
|
|
|
130
130
|
try {
|
|
131
131
|
const getClient = await initializeOpencodeForDirectory(projectDirectory)
|
|
132
|
-
if (
|
|
132
|
+
if (getClient instanceof Error) {
|
|
133
133
|
await interaction.editReply({ content: getClient.message })
|
|
134
134
|
return
|
|
135
135
|
}
|
|
@@ -237,7 +237,7 @@ export async function handleProviderSelectMenu(
|
|
|
237
237
|
|
|
238
238
|
try {
|
|
239
239
|
const getClient = await initializeOpencodeForDirectory(context.dir)
|
|
240
|
-
if (
|
|
240
|
+
if (getClient instanceof Error) {
|
|
241
241
|
await interaction.editReply({
|
|
242
242
|
content: getClient.message,
|
|
243
243
|
components: [],
|
|
@@ -42,7 +42,7 @@ export async function handleRemoveProjectCommand({ command, appId }: CommandCont
|
|
|
42
42
|
catch: (e) => e as Error,
|
|
43
43
|
})
|
|
44
44
|
|
|
45
|
-
if (
|
|
45
|
+
if (channel instanceof Error) {
|
|
46
46
|
logger.error(`Failed to fetch channel ${channel_id}:`, channel)
|
|
47
47
|
failedChannels.push(`${channel_type}: ${channel_id}`)
|
|
48
48
|
continue
|
|
@@ -116,7 +116,7 @@ export async function handleRemoveProjectAutocomplete({
|
|
|
116
116
|
try: () => guild.channels.fetch(channel_id),
|
|
117
117
|
catch: (e) => e as Error,
|
|
118
118
|
})
|
|
119
|
-
if (
|
|
119
|
+
if (channel instanceof Error) {
|
|
120
120
|
// Channel not in this guild, skip
|
|
121
121
|
continue
|
|
122
122
|
}
|
package/src/commands/resume.ts
CHANGED
|
@@ -61,7 +61,7 @@ export async function handleResumeCommand({ command, appId }: CommandContext): P
|
|
|
61
61
|
|
|
62
62
|
try {
|
|
63
63
|
const getClient = await initializeOpencodeForDirectory(projectDirectory)
|
|
64
|
-
if (
|
|
64
|
+
if (getClient instanceof Error) {
|
|
65
65
|
await command.editReply(getClient.message)
|
|
66
66
|
return
|
|
67
67
|
}
|
|
@@ -173,7 +173,7 @@ export async function handleResumeAutocomplete({
|
|
|
173
173
|
|
|
174
174
|
try {
|
|
175
175
|
const getClient = await initializeOpencodeForDirectory(projectDirectory)
|
|
176
|
-
if (
|
|
176
|
+
if (getClient instanceof Error) {
|
|
177
177
|
await interaction.respond([])
|
|
178
178
|
return
|
|
179
179
|
}
|
package/src/commands/session.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// /session command - Start a new OpenCode session.
|
|
1
|
+
// /new-session command - Start a new OpenCode session.
|
|
2
2
|
|
|
3
3
|
import { ChannelType, type TextChannel } from 'discord.js'
|
|
4
4
|
import fs from 'node:fs'
|
|
@@ -59,7 +59,7 @@ export async function handleSessionCommand({ command, appId }: CommandContext):
|
|
|
59
59
|
|
|
60
60
|
try {
|
|
61
61
|
const getClient = await initializeOpencodeForDirectory(projectDirectory)
|
|
62
|
-
if (
|
|
62
|
+
if (getClient instanceof Error) {
|
|
63
63
|
await command.editReply(getClient.message)
|
|
64
64
|
return
|
|
65
65
|
}
|
|
@@ -133,7 +133,7 @@ async function handleAgentAutocomplete({ interaction, appId }: AutocompleteConte
|
|
|
133
133
|
|
|
134
134
|
try {
|
|
135
135
|
const getClient = await initializeOpencodeForDirectory(projectDirectory)
|
|
136
|
-
if (
|
|
136
|
+
if (getClient instanceof Error) {
|
|
137
137
|
await interaction.respond([])
|
|
138
138
|
return
|
|
139
139
|
}
|
|
@@ -216,7 +216,7 @@ export async function handleSessionAutocomplete({
|
|
|
216
216
|
|
|
217
217
|
try {
|
|
218
218
|
const getClient = await initializeOpencodeForDirectory(projectDirectory)
|
|
219
|
-
if (
|
|
219
|
+
if (getClient instanceof Error) {
|
|
220
220
|
await interaction.respond([])
|
|
221
221
|
return
|
|
222
222
|
}
|
package/src/commands/share.ts
CHANGED
|
@@ -65,7 +65,7 @@ export async function handleShareCommand({ command }: CommandContext): Promise<v
|
|
|
65
65
|
const sessionId = row.session_id
|
|
66
66
|
|
|
67
67
|
const getClient = await initializeOpencodeForDirectory(directory)
|
|
68
|
-
if (
|
|
68
|
+
if (getClient instanceof Error) {
|
|
69
69
|
await command.reply({
|
|
70
70
|
content: `Failed to share session: ${getClient.message}`,
|
|
71
71
|
ephemeral: true,
|
|
@@ -67,7 +67,7 @@ export async function handleUndoCommand({ command }: CommandContext): Promise<vo
|
|
|
67
67
|
await command.deferReply({ flags: SILENT_MESSAGE_FLAGS })
|
|
68
68
|
|
|
69
69
|
const getClient = await initializeOpencodeForDirectory(directory)
|
|
70
|
-
if (
|
|
70
|
+
if (getClient instanceof Error) {
|
|
71
71
|
await command.editReply(`Failed to undo: ${getClient.message}`)
|
|
72
72
|
return
|
|
73
73
|
}
|
|
@@ -174,7 +174,7 @@ export async function handleRedoCommand({ command }: CommandContext): Promise<vo
|
|
|
174
174
|
await command.deferReply({ flags: SILENT_MESSAGE_FLAGS })
|
|
175
175
|
|
|
176
176
|
const getClient = await initializeOpencodeForDirectory(directory)
|
|
177
|
-
if (
|
|
177
|
+
if (getClient instanceof Error) {
|
|
178
178
|
await command.editReply(`Failed to redo: ${getClient.message}`)
|
|
179
179
|
return
|
|
180
180
|
}
|