kimaki 0.4.52 → 0.4.54
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 +93 -52
- package/dist/commands/agent.js +3 -3
- package/dist/commands/login.js +488 -0
- package/dist/commands/model.js +68 -4
- package/dist/commands/restart-opencode-server.js +77 -0
- package/dist/commands/resume.js +6 -0
- package/dist/commands/session.js +1 -1
- package/dist/commands/sqlitedb.js +16 -0
- package/dist/commands/worktree-settings.js +12 -50
- package/dist/interaction-handler.js +31 -6
- package/dist/logger.js +16 -10
- package/dist/message-formatting.js +1 -1
- package/dist/opencode.js +39 -0
- package/dist/session-handler.js +46 -21
- package/dist/system-message.js +89 -8
- package/package.json +3 -2
- package/src/cli.ts +71 -14
- package/src/commands/agent.ts +3 -3
- package/src/commands/login.ts +616 -0
- package/src/commands/model.ts +102 -4
- package/src/commands/restart-opencode-server.ts +90 -0
- package/src/commands/resume.ts +11 -0
- package/src/commands/session.ts +1 -1
- package/src/commands/sqlitedb.ts +20 -0
- package/src/commands/worktree-settings.ts +14 -65
- package/src/interaction-handler.ts +43 -10
- package/src/logger.ts +18 -10
- package/src/message-formatting.ts +1 -1
- package/src/opencode.ts +57 -1
- package/src/session-handler.ts +51 -25
- package/src/system-message.ts +90 -8
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Main CLI entrypoint for the Kimaki Discord bot.
|
|
3
3
|
// Handles interactive setup, Discord OAuth, slash command registration,
|
|
4
4
|
// project channel creation, and launching the bot with opencode integration.
|
|
5
|
-
import { cac } from 'cac';
|
|
5
|
+
import { cac } from '@xmorse/cac';
|
|
6
6
|
import { intro, outro, text, password, note, cancel, isCancel, confirm, log, multiselect, spinner, } from '@clack/prompts';
|
|
7
7
|
import { deduplicateByKey, generateBotInstallUrl, abbreviatePath } from './utils.js';
|
|
8
8
|
import { getChannelsWithDescriptions, createDiscordClient, getDatabase, getChannelDirectory, startDiscordBot, initializeOpencodeForDirectory, ensureKimakiCategory, createProjectChannels, } from './discord-bot.js';
|
|
@@ -204,12 +204,8 @@ async function registerCommands({ token, appId, userCommands = [], agents = [],
|
|
|
204
204
|
.setDescription('Merge the worktree branch into the default branch')
|
|
205
205
|
.toJSON(),
|
|
206
206
|
new SlashCommandBuilder()
|
|
207
|
-
.setName('
|
|
208
|
-
.setDescription('
|
|
209
|
-
.toJSON(),
|
|
210
|
-
new SlashCommandBuilder()
|
|
211
|
-
.setName('disable-worktrees')
|
|
212
|
-
.setDescription('Disable automatic git worktree creation for new sessions in this channel')
|
|
207
|
+
.setName('toggle-worktrees')
|
|
208
|
+
.setDescription('Toggle automatic git worktree creation for new sessions in this channel')
|
|
213
209
|
.toJSON(),
|
|
214
210
|
new SlashCommandBuilder()
|
|
215
211
|
.setName('add-project')
|
|
@@ -267,6 +263,10 @@ async function registerCommands({ token, appId, userCommands = [], agents = [],
|
|
|
267
263
|
.setName('model')
|
|
268
264
|
.setDescription('Set the preferred model for this channel or session')
|
|
269
265
|
.toJSON(),
|
|
266
|
+
new SlashCommandBuilder()
|
|
267
|
+
.setName('login')
|
|
268
|
+
.setDescription('Authenticate with an AI provider (OAuth or API key). Use this instead of /connect')
|
|
269
|
+
.toJSON(),
|
|
270
270
|
new SlashCommandBuilder()
|
|
271
271
|
.setName('agent')
|
|
272
272
|
.setDescription('Set the preferred agent for this channel or session')
|
|
@@ -303,6 +303,14 @@ async function registerCommands({ token, appId, userCommands = [], agents = [],
|
|
|
303
303
|
return option;
|
|
304
304
|
})
|
|
305
305
|
.toJSON(),
|
|
306
|
+
new SlashCommandBuilder()
|
|
307
|
+
.setName('restart-opencode-server')
|
|
308
|
+
.setDescription('Restart the opencode server for this channel only (fixes state/auth/plugins)')
|
|
309
|
+
.toJSON(),
|
|
310
|
+
new SlashCommandBuilder()
|
|
311
|
+
.setName('sqlitedb')
|
|
312
|
+
.setDescription('Show the location of the SQLite database file')
|
|
313
|
+
.toJSON(),
|
|
306
314
|
];
|
|
307
315
|
// Add user-defined commands with -cmd suffix
|
|
308
316
|
for (const cmd of userCommands) {
|
|
@@ -433,54 +441,57 @@ async function run({ restart, addChannels, useWorktrees, enableVoiceChannels })
|
|
|
433
441
|
const forceSetup = Boolean(restart);
|
|
434
442
|
intro('🤖 Discord Bot Setup');
|
|
435
443
|
// Step 0: Check if OpenCode CLI is available
|
|
436
|
-
|
|
437
|
-
if (
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
cancel('OpenCode CLI is required to run this bot');
|
|
444
|
-
process.exit(0);
|
|
445
|
-
}
|
|
446
|
-
const s = spinner();
|
|
447
|
-
s.start('Installing OpenCode CLI...');
|
|
448
|
-
try {
|
|
449
|
-
execSync('curl -fsSL https://opencode.ai/install | bash', {
|
|
450
|
-
stdio: 'inherit',
|
|
451
|
-
shell: '/bin/bash',
|
|
452
|
-
});
|
|
453
|
-
s.stop('OpenCode CLI installed successfully!');
|
|
454
|
-
// The install script adds opencode to PATH via shell configuration
|
|
455
|
-
// For the current process, we need to check common installation paths
|
|
456
|
-
const possiblePaths = [
|
|
457
|
-
`${process.env.HOME}/.local/bin/opencode`,
|
|
458
|
-
`${process.env.HOME}/.opencode/bin/opencode`,
|
|
459
|
-
'/usr/local/bin/opencode',
|
|
460
|
-
'/opt/opencode/bin/opencode',
|
|
461
|
-
];
|
|
462
|
-
const installedPath = possiblePaths.find((p) => {
|
|
463
|
-
try {
|
|
464
|
-
fs.accessSync(p, fs.constants.F_OK);
|
|
465
|
-
return true;
|
|
466
|
-
}
|
|
467
|
-
catch (error) {
|
|
468
|
-
cliLogger.debug(`OpenCode path not found at ${p}:`, error instanceof Error ? error.message : String(error));
|
|
469
|
-
return false;
|
|
470
|
-
}
|
|
444
|
+
// Skip check if user set OPENCODE_PATH (for custom forks like shuvcode)
|
|
445
|
+
if (!process.env.OPENCODE_PATH) {
|
|
446
|
+
const opencodeCheck = spawnSync(process.platform === 'win32' ? 'where' : 'which', ['opencode'], { shell: true });
|
|
447
|
+
if (opencodeCheck.status !== 0) {
|
|
448
|
+
note('OpenCode CLI is required but not found in your PATH.', '⚠️ OpenCode Not Found');
|
|
449
|
+
const shouldInstall = await confirm({
|
|
450
|
+
message: 'Would you like to install OpenCode right now?',
|
|
471
451
|
});
|
|
472
|
-
if (!
|
|
473
|
-
|
|
474
|
-
'Please restart your terminal and run this command again.', '⚠️ Restart Required');
|
|
452
|
+
if (isCancel(shouldInstall) || !shouldInstall) {
|
|
453
|
+
cancel('OpenCode CLI is required to run this bot');
|
|
475
454
|
process.exit(0);
|
|
476
455
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
456
|
+
const s = spinner();
|
|
457
|
+
s.start('Installing OpenCode CLI...');
|
|
458
|
+
try {
|
|
459
|
+
execSync('curl -fsSL https://opencode.ai/install | bash', {
|
|
460
|
+
stdio: 'inherit',
|
|
461
|
+
shell: '/bin/bash',
|
|
462
|
+
});
|
|
463
|
+
s.stop('OpenCode CLI installed successfully!');
|
|
464
|
+
// The install script adds opencode to PATH via shell configuration
|
|
465
|
+
// For the current process, we need to check common installation paths
|
|
466
|
+
const possiblePaths = [
|
|
467
|
+
`${process.env.HOME}/.local/bin/opencode`,
|
|
468
|
+
`${process.env.HOME}/.opencode/bin/opencode`,
|
|
469
|
+
'/usr/local/bin/opencode',
|
|
470
|
+
'/opt/opencode/bin/opencode',
|
|
471
|
+
];
|
|
472
|
+
const installedPath = possiblePaths.find((p) => {
|
|
473
|
+
try {
|
|
474
|
+
fs.accessSync(p, fs.constants.F_OK);
|
|
475
|
+
return true;
|
|
476
|
+
}
|
|
477
|
+
catch (error) {
|
|
478
|
+
cliLogger.debug(`OpenCode path not found at ${p}:`, error instanceof Error ? error.message : String(error));
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
if (!installedPath) {
|
|
483
|
+
note('OpenCode was installed but may not be available in this session.\n' +
|
|
484
|
+
'Please restart your terminal and run this command again.', '⚠️ Restart Required');
|
|
485
|
+
process.exit(0);
|
|
486
|
+
}
|
|
487
|
+
// For subsequent spawn calls in this session, we can use the full path
|
|
488
|
+
process.env.OPENCODE_PATH = installedPath;
|
|
489
|
+
}
|
|
490
|
+
catch (error) {
|
|
491
|
+
s.stop('Failed to install OpenCode CLI');
|
|
492
|
+
cliLogger.error('Installation error:', error instanceof Error ? error.message : String(error));
|
|
493
|
+
process.exit(EXIT_NO_RESTART);
|
|
494
|
+
}
|
|
484
495
|
}
|
|
485
496
|
}
|
|
486
497
|
const db = getDatabase();
|
|
@@ -1382,5 +1393,35 @@ cli
|
|
|
1382
1393
|
process.exit(EXIT_NO_RESTART);
|
|
1383
1394
|
}
|
|
1384
1395
|
});
|
|
1396
|
+
cli
|
|
1397
|
+
.command('tunnel', 'Expose a local port via tunnel')
|
|
1398
|
+
.option('-p, --port <port>', 'Local port to expose (required)')
|
|
1399
|
+
.option('-t, --tunnel-id [id]', 'Tunnel ID (random if omitted)')
|
|
1400
|
+
.option('-h, --host [host]', 'Local host (default: localhost)')
|
|
1401
|
+
.option('-d, --domain [domain]', 'Base domain (default: kimaki.xyz)')
|
|
1402
|
+
.option('-s, --server [url]', 'Tunnel server URL (overrides domain)')
|
|
1403
|
+
.action(async (options) => {
|
|
1404
|
+
const { runTunnel, parseCommandFromArgv, CLI_NAME } = await import('traforo/run-tunnel');
|
|
1405
|
+
if (!options.port) {
|
|
1406
|
+
cliLogger.error('Error: --port is required');
|
|
1407
|
+
cliLogger.error(`\nUsage: kimaki tunnel -p <port> [-- command]`);
|
|
1408
|
+
process.exit(EXIT_NO_RESTART);
|
|
1409
|
+
}
|
|
1410
|
+
const port = parseInt(options.port, 10);
|
|
1411
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
1412
|
+
cliLogger.error(`Error: Invalid port number: ${options.port}`);
|
|
1413
|
+
process.exit(EXIT_NO_RESTART);
|
|
1414
|
+
}
|
|
1415
|
+
// Parse command after -- from argv
|
|
1416
|
+
const { command } = parseCommandFromArgv(process.argv);
|
|
1417
|
+
await runTunnel({
|
|
1418
|
+
port,
|
|
1419
|
+
tunnelId: options.tunnelId,
|
|
1420
|
+
localHost: options.host,
|
|
1421
|
+
baseDomain: options.domain || 'kimaki.xyz',
|
|
1422
|
+
serverUrl: options.server,
|
|
1423
|
+
command: command.length > 0 ? command : undefined,
|
|
1424
|
+
});
|
|
1425
|
+
});
|
|
1385
1426
|
cli.help();
|
|
1386
1427
|
cli.parse();
|
package/dist/commands/agent.js
CHANGED
|
@@ -79,15 +79,15 @@ export async function resolveAgentCommandContext({ interaction, appId, }) {
|
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* Set the agent preference for a context (session or channel).
|
|
82
|
-
* When switching agents for a session,
|
|
83
|
-
* so the new agent's model takes effect.
|
|
82
|
+
* When switching agents for a session, clears session model preference
|
|
83
|
+
* so the new agent's model takes effect (agent model > channel model).
|
|
84
84
|
*/
|
|
85
85
|
export function setAgentForContext({ context, agentName, }) {
|
|
86
86
|
if (context.isThread && context.sessionId) {
|
|
87
87
|
setSessionAgent(context.sessionId, agentName);
|
|
88
88
|
// Clear session model so the new agent's model takes effect
|
|
89
89
|
clearSessionModel(context.sessionId);
|
|
90
|
-
agentLogger.log(`Set agent ${agentName} for session ${context.sessionId} (cleared model
|
|
90
|
+
agentLogger.log(`Set agent ${agentName} for session ${context.sessionId} (cleared session model)`);
|
|
91
91
|
}
|
|
92
92
|
else {
|
|
93
93
|
setChannelAgent(context.channelId, agentName);
|