kimaki 0.4.12 → 0.4.14

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/src/cli.ts CHANGED
@@ -33,6 +33,7 @@ import {
33
33
  REST,
34
34
  Routes,
35
35
  SlashCommandBuilder,
36
+ AttachmentBuilder,
36
37
  } from 'discord.js'
37
38
  import path from 'node:path'
38
39
  import fs from 'node:fs'
@@ -134,12 +135,16 @@ async function registerCommands(token: string, appId: string) {
134
135
  .toJSON(),
135
136
  new SlashCommandBuilder()
136
137
  .setName('accept-always')
137
- .setDescription('Accept and auto-approve future requests matching this pattern (e.g. "git *" approves all git commands)')
138
+ .setDescription('Accept and auto-approve future requests matching this pattern')
138
139
  .toJSON(),
139
140
  new SlashCommandBuilder()
140
141
  .setName('reject')
141
142
  .setDescription('Reject a pending permission request')
142
143
  .toJSON(),
144
+ new SlashCommandBuilder()
145
+ .setName('abort')
146
+ .setDescription('Abort the current OpenCode request in this thread')
147
+ .toJSON(),
143
148
  ]
144
149
 
145
150
  const rest = new REST().setToken(token)
@@ -832,28 +837,120 @@ cli
832
837
  })
833
838
 
834
839
  cli
835
- .command('install-plugin', 'Install the OpenCode plugin for /send-to-kimaki-discord command')
840
+ .command('upload-to-discord [...files]', 'Upload files to a Discord thread for a session')
841
+ .option('-s, --session <sessionId>', 'OpenCode session ID')
842
+ .action(async (files: string[], options: { session?: string }) => {
843
+ try {
844
+ const { session: sessionId } = options
845
+
846
+ if (!sessionId) {
847
+ cliLogger.error('Session ID is required. Use --session <sessionId>')
848
+ process.exit(EXIT_NO_RESTART)
849
+ }
850
+
851
+ if (!files || files.length === 0) {
852
+ cliLogger.error('At least one file path is required')
853
+ process.exit(EXIT_NO_RESTART)
854
+ }
855
+
856
+ const resolvedFiles = files.map((f) => path.resolve(f))
857
+ for (const file of resolvedFiles) {
858
+ if (!fs.existsSync(file)) {
859
+ cliLogger.error(`File not found: ${file}`)
860
+ process.exit(EXIT_NO_RESTART)
861
+ }
862
+ }
863
+
864
+ const db = getDatabase()
865
+
866
+ const threadRow = db
867
+ .prepare('SELECT thread_id FROM thread_sessions WHERE session_id = ?')
868
+ .get(sessionId) as { thread_id: string } | undefined
869
+
870
+ if (!threadRow) {
871
+ cliLogger.error(`No Discord thread found for session: ${sessionId}`)
872
+ cliLogger.error('Make sure the session has been sent to Discord first using /send-to-kimaki-discord')
873
+ process.exit(EXIT_NO_RESTART)
874
+ }
875
+
876
+ const botRow = db
877
+ .prepare(
878
+ 'SELECT app_id, token FROM bot_tokens ORDER BY created_at DESC LIMIT 1',
879
+ )
880
+ .get() as { app_id: string; token: string } | undefined
881
+
882
+ if (!botRow) {
883
+ cliLogger.error('No bot credentials found. Run `kimaki` first to set up the bot.')
884
+ process.exit(EXIT_NO_RESTART)
885
+ }
886
+
887
+ const s = spinner()
888
+ s.start(`Uploading ${resolvedFiles.length} file(s)...`)
889
+
890
+ for (const file of resolvedFiles) {
891
+ const buffer = fs.readFileSync(file)
892
+
893
+ const formData = new FormData()
894
+ formData.append('payload_json', JSON.stringify({
895
+ attachments: [{ id: 0, filename: path.basename(file) }]
896
+ }))
897
+ formData.append('files[0]', new Blob([buffer]), path.basename(file))
898
+
899
+ const response = await fetch(
900
+ `https://discord.com/api/v10/channels/${threadRow.thread_id}/messages`,
901
+ {
902
+ method: 'POST',
903
+ headers: {
904
+ 'Authorization': `Bot ${botRow.token}`,
905
+ },
906
+ body: formData,
907
+ }
908
+ )
909
+
910
+ if (!response.ok) {
911
+ const error = await response.text()
912
+ throw new Error(`Discord API error: ${response.status} - ${error}`)
913
+ }
914
+ }
915
+
916
+ s.stop(`Uploaded ${resolvedFiles.length} file(s)!`)
917
+
918
+ note(
919
+ `Files uploaded to Discord thread!\n\nFiles: ${resolvedFiles.map((f) => path.basename(f)).join(', ')}`,
920
+ '✅ Success',
921
+ )
922
+
923
+ process.exit(0)
924
+ } catch (error) {
925
+ cliLogger.error(
926
+ 'Error:',
927
+ error instanceof Error ? error.message : String(error),
928
+ )
929
+ process.exit(EXIT_NO_RESTART)
930
+ }
931
+ })
932
+
933
+ cli
934
+ .command('install-plugin', 'Install the OpenCode commands for kimaki Discord integration')
836
935
  .action(async () => {
837
936
  try {
838
937
  const require = createRequire(import.meta.url)
839
- const pluginSrc = require.resolve('./opencode-plugin.ts')
840
- const commandSrc = require.resolve('./opencode-command.md')
938
+ const sendCommandSrc = require.resolve('./opencode-command-send-to-discord.md')
939
+ const uploadCommandSrc = require.resolve('./opencode-command-upload-to-discord.md')
841
940
 
842
941
  const opencodeConfig = path.join(os.homedir(), '.config', 'opencode')
843
- const pluginDir = path.join(opencodeConfig, 'plugin')
844
942
  const commandDir = path.join(opencodeConfig, 'command')
845
943
 
846
- fs.mkdirSync(pluginDir, { recursive: true })
847
944
  fs.mkdirSync(commandDir, { recursive: true })
848
945
 
849
- const pluginDest = path.join(pluginDir, 'send-to-kimaki-discord.ts')
850
- const commandDest = path.join(commandDir, 'send-to-kimaki-discord.md')
946
+ const sendCommandDest = path.join(commandDir, 'send-to-kimaki-discord.md')
947
+ const uploadCommandDest = path.join(commandDir, 'upload-to-discord.md')
851
948
 
852
- fs.copyFileSync(pluginSrc, pluginDest)
853
- fs.copyFileSync(commandSrc, commandDest)
949
+ fs.copyFileSync(sendCommandSrc, sendCommandDest)
950
+ fs.copyFileSync(uploadCommandSrc, uploadCommandDest)
854
951
 
855
952
  note(
856
- `Plugin: ${pluginDest}\nCommand: ${commandDest}\n\nUse /send-to-kimaki-discord in OpenCode to send the current session to Discord.`,
953
+ `Commands installed:\n- ${sendCommandDest}\n- ${uploadCommandDest}\n\nUse /send-to-kimaki-discord to send session to Discord.\nUse /upload-to-discord to upload files to the thread.`,
857
954
  '✅ Installed',
858
955
  )
859
956