kimaki 0.1.4 → 0.2.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.
package/dist/cli.js CHANGED
@@ -7,6 +7,7 @@ import { Events, ChannelType, REST, Routes, SlashCommandBuilder, } from 'discord
7
7
  import path from 'node:path';
8
8
  import fs from 'node:fs';
9
9
  import { createLogger } from './logger.js';
10
+ import { spawnSync, execSync } from 'node:child_process';
10
11
  const cliLogger = createLogger('CLI');
11
12
  const cli = cac('kimaki');
12
13
  process.title = 'kimaki';
@@ -75,6 +76,56 @@ async function ensureKimakiCategory(guild) {
75
76
  async function run({ restart, addChannels }) {
76
77
  const forceSetup = Boolean(restart);
77
78
  intro('🤖 Discord Bot Setup');
79
+ // Step 0: Check if OpenCode CLI is available
80
+ const opencodeCheck = spawnSync('which', ['opencode'], { shell: true });
81
+ if (opencodeCheck.status !== 0) {
82
+ note('OpenCode CLI is required but not found in your PATH.', '⚠️ OpenCode Not Found');
83
+ const shouldInstall = await confirm({
84
+ message: 'Would you like to install OpenCode right now?',
85
+ });
86
+ if (isCancel(shouldInstall) || !shouldInstall) {
87
+ cancel('OpenCode CLI is required to run this bot');
88
+ process.exit(0);
89
+ }
90
+ const s = spinner();
91
+ s.start('Installing OpenCode CLI...');
92
+ try {
93
+ execSync('curl -fsSL https://opencode.ai/install | bash', {
94
+ stdio: 'inherit',
95
+ shell: '/bin/bash',
96
+ });
97
+ s.stop('OpenCode CLI installed successfully!');
98
+ // The install script adds opencode to PATH via shell configuration
99
+ // For the current process, we need to check common installation paths
100
+ const possiblePaths = [
101
+ `${process.env.HOME}/.local/bin/opencode`,
102
+ `${process.env.HOME}/.opencode/bin/opencode`,
103
+ '/usr/local/bin/opencode',
104
+ '/opt/opencode/bin/opencode',
105
+ ];
106
+ const installedPath = possiblePaths.find((p) => {
107
+ try {
108
+ fs.accessSync(p, fs.constants.F_OK);
109
+ return true;
110
+ }
111
+ catch {
112
+ return false;
113
+ }
114
+ });
115
+ if (!installedPath) {
116
+ note('OpenCode was installed but may not be available in this session.\n' +
117
+ 'Please restart your terminal and run this command again.', '⚠️ Restart Required');
118
+ process.exit(0);
119
+ }
120
+ // For subsequent spawn calls in this session, we can use the full path
121
+ process.env.OPENCODE_PATH = installedPath;
122
+ }
123
+ catch (error) {
124
+ s.stop('Failed to install OpenCode CLI');
125
+ cliLogger.error('Installation error:', error instanceof Error ? error.message : String(error));
126
+ process.exit(EXIT_NO_RESTART);
127
+ }
128
+ }
78
129
  const db = getDatabase();
79
130
  let appId;
80
131
  let token;
@@ -231,7 +282,7 @@ async function run({ restart, addChannels }) {
231
282
  s.start('Fetching OpenCode projects...');
232
283
  let projects = [];
233
284
  try {
234
- const projectsResponse = await getClient().project.list();
285
+ const projectsResponse = await getClient().project.list({});
235
286
  if (!projectsResponse.data) {
236
287
  throw new Error('Failed to fetch projects');
237
288
  }
@@ -270,23 +321,23 @@ async function run({ restart, addChannels }) {
270
321
  }
271
322
  if (guilds.length === 1) {
272
323
  targetGuild = guilds[0];
324
+ note(`Using server: ${targetGuild.name}`, 'Server Selected');
273
325
  }
274
326
  else {
275
- const guildId = await text({
276
- message: 'Enter the Discord server ID to create channels in:',
277
- placeholder: guilds[0]?.id,
278
- validate(value) {
279
- if (!value)
280
- return 'Server ID is required';
281
- if (!guilds.find((g) => g.id === value))
282
- return 'Invalid server ID';
283
- },
327
+ const guildSelection = await multiselect({
328
+ message: 'Select a Discord server to create channels in:',
329
+ options: guilds.map((guild) => ({
330
+ value: guild.id,
331
+ label: `${guild.name} (${guild.memberCount} members)`,
332
+ })),
333
+ required: true,
334
+ maxItems: 1,
284
335
  });
285
- if (isCancel(guildId)) {
336
+ if (isCancel(guildSelection)) {
286
337
  cancel('Setup cancelled');
287
338
  process.exit(0);
288
339
  }
289
- targetGuild = guilds.find((g) => g.id === guildId);
340
+ targetGuild = guilds.find((g) => g.id === guildSelection[0]);
290
341
  }
291
342
  s.start('Creating Discord channels...');
292
343
  for (const projectId of selectedProjects) {