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/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import { cac } from 'cac';
3
3
  import { intro, outro, text, password, note, cancel, isCancel, confirm, log, multiselect, spinner, } from '@clack/prompts';
4
4
  import { deduplicateByKey, generateBotInstallUrl } from './utils.js';
5
5
  import { getChannelsWithDescriptions, createDiscordClient, getDatabase, startDiscordBot, initializeOpencodeForDirectory, ensureKimakiCategory, createProjectChannels, } from './discordBot.js';
6
- import { Events, ChannelType, REST, Routes, SlashCommandBuilder, } from 'discord.js';
6
+ import { Events, ChannelType, REST, Routes, SlashCommandBuilder, AttachmentBuilder, } from 'discord.js';
7
7
  import path from 'node:path';
8
8
  import fs from 'node:fs';
9
9
  import { createRequire } from 'node:module';
@@ -78,12 +78,16 @@ async function registerCommands(token, appId) {
78
78
  .toJSON(),
79
79
  new SlashCommandBuilder()
80
80
  .setName('accept-always')
81
- .setDescription('Accept and auto-approve future requests matching this pattern (e.g. "git *" approves all git commands)')
81
+ .setDescription('Accept and auto-approve future requests matching this pattern')
82
82
  .toJSON(),
83
83
  new SlashCommandBuilder()
84
84
  .setName('reject')
85
85
  .setDescription('Reject a pending permission request')
86
86
  .toJSON(),
87
+ new SlashCommandBuilder()
88
+ .setName('abort')
89
+ .setDescription('Abort the current OpenCode request in this thread')
90
+ .toJSON(),
87
91
  ];
88
92
  const rest = new REST().setToken(token);
89
93
  try {
@@ -558,22 +562,87 @@ cli
558
562
  }
559
563
  });
560
564
  cli
561
- .command('install-plugin', 'Install the OpenCode plugin for /send-to-kimaki-discord command')
565
+ .command('upload-to-discord [...files]', 'Upload files to a Discord thread for a session')
566
+ .option('-s, --session <sessionId>', 'OpenCode session ID')
567
+ .action(async (files, options) => {
568
+ try {
569
+ const { session: sessionId } = options;
570
+ if (!sessionId) {
571
+ cliLogger.error('Session ID is required. Use --session <sessionId>');
572
+ process.exit(EXIT_NO_RESTART);
573
+ }
574
+ if (!files || files.length === 0) {
575
+ cliLogger.error('At least one file path is required');
576
+ process.exit(EXIT_NO_RESTART);
577
+ }
578
+ const resolvedFiles = files.map((f) => path.resolve(f));
579
+ for (const file of resolvedFiles) {
580
+ if (!fs.existsSync(file)) {
581
+ cliLogger.error(`File not found: ${file}`);
582
+ process.exit(EXIT_NO_RESTART);
583
+ }
584
+ }
585
+ const db = getDatabase();
586
+ const threadRow = db
587
+ .prepare('SELECT thread_id FROM thread_sessions WHERE session_id = ?')
588
+ .get(sessionId);
589
+ if (!threadRow) {
590
+ cliLogger.error(`No Discord thread found for session: ${sessionId}`);
591
+ cliLogger.error('Make sure the session has been sent to Discord first using /send-to-kimaki-discord');
592
+ process.exit(EXIT_NO_RESTART);
593
+ }
594
+ const botRow = db
595
+ .prepare('SELECT app_id, token FROM bot_tokens ORDER BY created_at DESC LIMIT 1')
596
+ .get();
597
+ if (!botRow) {
598
+ cliLogger.error('No bot credentials found. Run `kimaki` first to set up the bot.');
599
+ process.exit(EXIT_NO_RESTART);
600
+ }
601
+ const s = spinner();
602
+ s.start(`Uploading ${resolvedFiles.length} file(s)...`);
603
+ for (const file of resolvedFiles) {
604
+ const buffer = fs.readFileSync(file);
605
+ const formData = new FormData();
606
+ formData.append('payload_json', JSON.stringify({
607
+ attachments: [{ id: 0, filename: path.basename(file) }]
608
+ }));
609
+ formData.append('files[0]', new Blob([buffer]), path.basename(file));
610
+ const response = await fetch(`https://discord.com/api/v10/channels/${threadRow.thread_id}/messages`, {
611
+ method: 'POST',
612
+ headers: {
613
+ 'Authorization': `Bot ${botRow.token}`,
614
+ },
615
+ body: formData,
616
+ });
617
+ if (!response.ok) {
618
+ const error = await response.text();
619
+ throw new Error(`Discord API error: ${response.status} - ${error}`);
620
+ }
621
+ }
622
+ s.stop(`Uploaded ${resolvedFiles.length} file(s)!`);
623
+ note(`Files uploaded to Discord thread!\n\nFiles: ${resolvedFiles.map((f) => path.basename(f)).join(', ')}`, '✅ Success');
624
+ process.exit(0);
625
+ }
626
+ catch (error) {
627
+ cliLogger.error('Error:', error instanceof Error ? error.message : String(error));
628
+ process.exit(EXIT_NO_RESTART);
629
+ }
630
+ });
631
+ cli
632
+ .command('install-plugin', 'Install the OpenCode commands for kimaki Discord integration')
562
633
  .action(async () => {
563
634
  try {
564
635
  const require = createRequire(import.meta.url);
565
- const pluginSrc = require.resolve('./opencode-plugin.ts');
566
- const commandSrc = require.resolve('./opencode-command.md');
636
+ const sendCommandSrc = require.resolve('./opencode-command-send-to-discord.md');
637
+ const uploadCommandSrc = require.resolve('./opencode-command-upload-to-discord.md');
567
638
  const opencodeConfig = path.join(os.homedir(), '.config', 'opencode');
568
- const pluginDir = path.join(opencodeConfig, 'plugin');
569
639
  const commandDir = path.join(opencodeConfig, 'command');
570
- fs.mkdirSync(pluginDir, { recursive: true });
571
640
  fs.mkdirSync(commandDir, { recursive: true });
572
- const pluginDest = path.join(pluginDir, 'send-to-kimaki-discord.ts');
573
- const commandDest = path.join(commandDir, 'send-to-kimaki-discord.md');
574
- fs.copyFileSync(pluginSrc, pluginDest);
575
- fs.copyFileSync(commandSrc, commandDest);
576
- note(`Plugin: ${pluginDest}\nCommand: ${commandDest}\n\nUse /send-to-kimaki-discord in OpenCode to send the current session to Discord.`, '✅ Installed');
641
+ const sendCommandDest = path.join(commandDir, 'send-to-kimaki-discord.md');
642
+ const uploadCommandDest = path.join(commandDir, 'upload-to-discord.md');
643
+ fs.copyFileSync(sendCommandSrc, sendCommandDest);
644
+ fs.copyFileSync(uploadCommandSrc, uploadCommandDest);
645
+ note(`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.`, '✅ Installed');
577
646
  process.exit(0);
578
647
  }
579
648
  catch (error) {