kimaki 0.4.53 → 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 CHANGED
@@ -307,6 +307,10 @@ async function registerCommands({ token, appId, userCommands = [], agents = [],
307
307
  .setName('restart-opencode-server')
308
308
  .setDescription('Restart the opencode server for this channel only (fixes state/auth/plugins)')
309
309
  .toJSON(),
310
+ new SlashCommandBuilder()
311
+ .setName('sqlitedb')
312
+ .setDescription('Show the location of the SQLite database file')
313
+ .toJSON(),
310
314
  ];
311
315
  // Add user-defined commands with -cmd suffix
312
316
  for (const cmd of userCommands) {
@@ -437,54 +441,57 @@ async function run({ restart, addChannels, useWorktrees, enableVoiceChannels })
437
441
  const forceSetup = Boolean(restart);
438
442
  intro('🤖 Discord Bot Setup');
439
443
  // Step 0: Check if OpenCode CLI is available
440
- const opencodeCheck = spawnSync('which', ['opencode'], { shell: true });
441
- if (opencodeCheck.status !== 0) {
442
- note('OpenCode CLI is required but not found in your PATH.', '⚠️ OpenCode Not Found');
443
- const shouldInstall = await confirm({
444
- message: 'Would you like to install OpenCode right now?',
445
- });
446
- if (isCancel(shouldInstall) || !shouldInstall) {
447
- cancel('OpenCode CLI is required to run this bot');
448
- process.exit(0);
449
- }
450
- const s = spinner();
451
- s.start('Installing OpenCode CLI...');
452
- try {
453
- execSync('curl -fsSL https://opencode.ai/install | bash', {
454
- stdio: 'inherit',
455
- shell: '/bin/bash',
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?',
456
451
  });
457
- s.stop('OpenCode CLI installed successfully!');
458
- // The install script adds opencode to PATH via shell configuration
459
- // For the current process, we need to check common installation paths
460
- const possiblePaths = [
461
- `${process.env.HOME}/.local/bin/opencode`,
462
- `${process.env.HOME}/.opencode/bin/opencode`,
463
- '/usr/local/bin/opencode',
464
- '/opt/opencode/bin/opencode',
465
- ];
466
- const installedPath = possiblePaths.find((p) => {
467
- try {
468
- fs.accessSync(p, fs.constants.F_OK);
469
- return true;
470
- }
471
- catch (error) {
472
- cliLogger.debug(`OpenCode path not found at ${p}:`, error instanceof Error ? error.message : String(error));
473
- return false;
474
- }
475
- });
476
- if (!installedPath) {
477
- note('OpenCode was installed but may not be available in this session.\n' +
478
- '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');
479
454
  process.exit(0);
480
455
  }
481
- // For subsequent spawn calls in this session, we can use the full path
482
- process.env.OPENCODE_PATH = installedPath;
483
- }
484
- catch (error) {
485
- s.stop('Failed to install OpenCode CLI');
486
- cliLogger.error('Installation error:', error instanceof Error ? error.message : String(error));
487
- process.exit(EXIT_NO_RESTART);
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
+ }
488
495
  }
489
496
  }
490
497
  const db = getDatabase();
@@ -1391,7 +1398,8 @@ cli
1391
1398
  .option('-p, --port <port>', 'Local port to expose (required)')
1392
1399
  .option('-t, --tunnel-id [id]', 'Tunnel ID (random if omitted)')
1393
1400
  .option('-h, --host [host]', 'Local host (default: localhost)')
1394
- .option('-s, --server [url]', 'Tunnel server URL')
1401
+ .option('-d, --domain [domain]', 'Base domain (default: kimaki.xyz)')
1402
+ .option('-s, --server [url]', 'Tunnel server URL (overrides domain)')
1395
1403
  .action(async (options) => {
1396
1404
  const { runTunnel, parseCommandFromArgv, CLI_NAME } = await import('traforo/run-tunnel');
1397
1405
  if (!options.port) {
@@ -1410,6 +1418,7 @@ cli
1410
1418
  port,
1411
1419
  tunnelId: options.tunnelId,
1412
1420
  localHost: options.host,
1421
+ baseDomain: options.domain || 'kimaki.xyz',
1413
1422
  serverUrl: options.server,
1414
1423
  command: command.length > 0 ? command : undefined,
1415
1424
  });
@@ -0,0 +1,16 @@
1
+ // /sqlitedb command.
2
+ // Prints the current location of the SQLite database to the console.
3
+ import path from 'node:path';
4
+ import { getDataDir } from '../config.js';
5
+ /**
6
+ * Handle the /sqlitedb slash command.
7
+ * Displays the path to the SQLite database file.
8
+ */
9
+ export async function handleSqliteDbCommand({ command }) {
10
+ const dataDir = getDataDir();
11
+ const dbPath = path.join(dataDir, 'discord-sessions.db');
12
+ await command.reply({
13
+ content: `SQLite database location:\n\`${dbPath}\``,
14
+ ephemeral: true,
15
+ });
16
+ }
@@ -24,6 +24,7 @@ import { handleUndoCommand, handleRedoCommand } from './commands/undo-redo.js';
24
24
  import { handleUserCommand } from './commands/user-command.js';
25
25
  import { handleVerbosityCommand } from './commands/verbosity.js';
26
26
  import { handleRestartOpencodeServerCommand } from './commands/restart-opencode-server.js';
27
+ import { handleSqliteDbCommand } from './commands/sqlitedb.js';
27
28
  import { createLogger, LogPrefix } from './logger.js';
28
29
  const interactionLogger = createLogger(LogPrefix.INTERACTION);
29
30
  export function registerInteractionHandler({ discordClient, appId, }) {
@@ -121,6 +122,9 @@ export function registerInteractionHandler({ discordClient, appId, }) {
121
122
  case 'restart-opencode-server':
122
123
  await handleRestartOpencodeServerCommand({ command: interaction, appId });
123
124
  return;
125
+ case 'sqlitedb':
126
+ await handleSqliteDbCommand({ command: interaction, appId });
127
+ return;
124
128
  }
125
129
  // Handle quick agent commands (ending with -agent suffix, but not the base /agent command)
126
130
  if (interaction.commandName.endsWith('-agent') && interaction.commandName !== 'agent') {
package/dist/logger.js CHANGED
@@ -1,6 +1,6 @@
1
- // Prefixed logging utility.
2
- // Uses picocolors for compact frequent logs (log, info, debug).
3
- // Uses @clack/prompts only for important events (warn, error) with visual distinction.
1
+ // Prefixed logging utility using @clack/prompts for consistent visual style.
2
+ // All log methods use clack's log.message() with appropriate symbols to prevent
3
+ // output interleaving from concurrent async operations.
4
4
  import { log as clackLog } from '@clack/prompts';
5
5
  import fs from 'node:fs';
6
6
  import path, { dirname } from 'node:path';
@@ -78,30 +78,35 @@ function getTimestamp() {
78
78
  function padPrefix(prefix) {
79
79
  return prefix.padEnd(MAX_PREFIX_LENGTH);
80
80
  }
81
+ function formatMessage(timestamp, prefix, args) {
82
+ return [pc.dim(timestamp), prefix, ...args.map(formatArg)].join(' ');
83
+ }
84
+ const noSpacing = { spacing: 0 };
81
85
  export function createLogger(prefix) {
82
86
  const paddedPrefix = padPrefix(prefix);
83
87
  return {
84
88
  log: (...args) => {
85
89
  writeToFile('INFO', prefix, args);
86
- console.log(pc.dim(getTimestamp()), pc.cyan(paddedPrefix), ...args.map(formatArg));
90
+ clackLog.step(formatMessage(getTimestamp(), pc.cyan(paddedPrefix), args), noSpacing);
87
91
  },
88
92
  error: (...args) => {
89
93
  writeToFile('ERROR', prefix, args);
90
- // use clack for errors - visually distinct
91
- clackLog.error([paddedPrefix, ...args.map(formatArg)].join(' '));
94
+ clackLog.error(formatMessage(getTimestamp(), pc.red(paddedPrefix), args), noSpacing);
92
95
  },
93
96
  warn: (...args) => {
94
97
  writeToFile('WARN', prefix, args);
95
- // use clack for warnings - visually distinct
96
- clackLog.warn([paddedPrefix, ...args.map(formatArg)].join(' '));
98
+ clackLog.warn(formatMessage(getTimestamp(), pc.yellow(paddedPrefix), args), noSpacing);
97
99
  },
98
100
  info: (...args) => {
99
101
  writeToFile('INFO', prefix, args);
100
- console.log(pc.dim(getTimestamp()), pc.blue(paddedPrefix), ...args.map(formatArg));
102
+ clackLog.info(formatMessage(getTimestamp(), pc.blue(paddedPrefix), args), noSpacing);
101
103
  },
102
104
  debug: (...args) => {
103
105
  writeToFile('DEBUG', prefix, args);
104
- console.log(pc.dim(getTimestamp()), pc.dim(paddedPrefix), ...args.map(formatArg));
106
+ clackLog.message(formatMessage(getTimestamp(), pc.cyan(paddedPrefix), args), {
107
+ ...noSpacing,
108
+ symbol: pc.cyan('│'),
109
+ });
105
110
  },
106
111
  };
107
112
  }
package/dist/opencode.js CHANGED
@@ -86,6 +86,7 @@ export async function initializeOpencodeForDirectory(directory) {
86
86
  stdio: 'pipe',
87
87
  detached: false,
88
88
  cwd: directory,
89
+ shell: true, // Required for .cmd files on Windows
89
90
  env: {
90
91
  ...process.env,
91
92
  OPENCODE_CONFIG_CONTENT: JSON.stringify({
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "kimaki",
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
- "version": "0.4.53",
5
+ "version": "0.4.54",
6
6
  "repository": "https://github.com/remorses/kimaki",
7
7
  "bin": "bin.js",
8
8
  "files": [
@@ -43,8 +43,8 @@
43
43
  "undici": "^7.16.0",
44
44
  "xdg-basedir": "^5.1.0",
45
45
  "zod": "^4.2.1",
46
- "errore": "^0.10.0",
47
- "traforo": "0.0.4"
46
+ "traforo": "0.0.5",
47
+ "errore": "^0.10.0"
48
48
  },
49
49
  "optionalDependencies": {
50
50
  "@discordjs/opus": "^0.10.0",
package/src/cli.ts CHANGED
@@ -401,6 +401,10 @@ async function registerCommands({
401
401
  .setName('restart-opencode-server')
402
402
  .setDescription('Restart the opencode server for this channel only (fixes state/auth/plugins)')
403
403
  .toJSON(),
404
+ new SlashCommandBuilder()
405
+ .setName('sqlitedb')
406
+ .setDescription('Show the location of the SQLite database file')
407
+ .toJSON(),
404
408
  ]
405
409
 
406
410
  // Add user-defined commands with -cmd suffix
@@ -608,9 +612,14 @@ async function run({ restart, addChannels, useWorktrees, enableVoiceChannels }:
608
612
  intro('🤖 Discord Bot Setup')
609
613
 
610
614
  // Step 0: Check if OpenCode CLI is available
611
- const opencodeCheck = spawnSync('which', ['opencode'], { shell: true })
612
-
613
- if (opencodeCheck.status !== 0) {
615
+ // Skip check if user set OPENCODE_PATH (for custom forks like shuvcode)
616
+ if (!process.env.OPENCODE_PATH) {
617
+ const opencodeCheck = spawnSync(
618
+ process.platform === 'win32' ? 'where' : 'which',
619
+ ['opencode'],
620
+ { shell: true }
621
+ )
622
+ if (opencodeCheck.status !== 0) {
614
623
  note('OpenCode CLI is required but not found in your PATH.', '⚠️ OpenCode Not Found')
615
624
 
616
625
  const shouldInstall = await confirm({
@@ -670,6 +679,7 @@ async function run({ restart, addChannels, useWorktrees, enableVoiceChannels }:
670
679
  cliLogger.error('Installation error:', error instanceof Error ? error.message : String(error))
671
680
  process.exit(EXIT_NO_RESTART)
672
681
  }
682
+ }
673
683
  }
674
684
 
675
685
  const db = getDatabase()
@@ -1860,12 +1870,14 @@ cli
1860
1870
  .option('-p, --port <port>', 'Local port to expose (required)')
1861
1871
  .option('-t, --tunnel-id [id]', 'Tunnel ID (random if omitted)')
1862
1872
  .option('-h, --host [host]', 'Local host (default: localhost)')
1863
- .option('-s, --server [url]', 'Tunnel server URL')
1873
+ .option('-d, --domain [domain]', 'Base domain (default: kimaki.xyz)')
1874
+ .option('-s, --server [url]', 'Tunnel server URL (overrides domain)')
1864
1875
  .action(
1865
1876
  async (options: {
1866
1877
  port?: string
1867
1878
  tunnelId?: string
1868
1879
  host?: string
1880
+ domain?: string
1869
1881
  server?: string
1870
1882
  }) => {
1871
1883
  const { runTunnel, parseCommandFromArgv, CLI_NAME } = await import('traforo/run-tunnel')
@@ -1889,6 +1901,7 @@ cli
1889
1901
  port,
1890
1902
  tunnelId: options.tunnelId,
1891
1903
  localHost: options.host,
1904
+ baseDomain: options.domain || 'kimaki.xyz',
1892
1905
  serverUrl: options.server,
1893
1906
  command: command.length > 0 ? command : undefined,
1894
1907
  })
@@ -0,0 +1,20 @@
1
+ // /sqlitedb command.
2
+ // Prints the current location of the SQLite database to the console.
3
+
4
+ import path from 'node:path'
5
+ import type { CommandContext } from './types.js'
6
+ import { getDataDir } from '../config.js'
7
+
8
+ /**
9
+ * Handle the /sqlitedb slash command.
10
+ * Displays the path to the SQLite database file.
11
+ */
12
+ export async function handleSqliteDbCommand({ command }: CommandContext): Promise<void> {
13
+ const dataDir = getDataDir()
14
+ const dbPath = path.join(dataDir, 'discord-sessions.db')
15
+
16
+ await command.reply({
17
+ content: `SQLite database location:\n\`${dbPath}\``,
18
+ ephemeral: true,
19
+ })
20
+ }
@@ -37,6 +37,7 @@ import { handleUndoCommand, handleRedoCommand } from './commands/undo-redo.js'
37
37
  import { handleUserCommand } from './commands/user-command.js'
38
38
  import { handleVerbosityCommand } from './commands/verbosity.js'
39
39
  import { handleRestartOpencodeServerCommand } from './commands/restart-opencode-server.js'
40
+ import { handleSqliteDbCommand } from './commands/sqlitedb.js'
40
41
  import { createLogger, LogPrefix } from './logger.js'
41
42
 
42
43
  const interactionLogger = createLogger(LogPrefix.INTERACTION)
@@ -174,6 +175,10 @@ export function registerInteractionHandler({
174
175
  case 'restart-opencode-server':
175
176
  await handleRestartOpencodeServerCommand({ command: interaction, appId })
176
177
  return
178
+
179
+ case 'sqlitedb':
180
+ await handleSqliteDbCommand({ command: interaction, appId })
181
+ return
177
182
  }
178
183
 
179
184
  // Handle quick agent commands (ending with -agent suffix, but not the base /agent command)
package/src/logger.ts CHANGED
@@ -1,6 +1,6 @@
1
- // Prefixed logging utility.
2
- // Uses picocolors for compact frequent logs (log, info, debug).
3
- // Uses @clack/prompts only for important events (warn, error) with visual distinction.
1
+ // Prefixed logging utility using @clack/prompts for consistent visual style.
2
+ // All log methods use clack's log.message() with appropriate symbols to prevent
3
+ // output interleaving from concurrent async operations.
4
4
 
5
5
  import { log as clackLog } from '@clack/prompts'
6
6
  import fs from 'node:fs'
@@ -91,30 +91,37 @@ function padPrefix(prefix: string): string {
91
91
  return prefix.padEnd(MAX_PREFIX_LENGTH)
92
92
  }
93
93
 
94
+ function formatMessage(timestamp: string, prefix: string, args: unknown[]): string {
95
+ return [pc.dim(timestamp), prefix, ...args.map(formatArg)].join(' ')
96
+ }
97
+
98
+ const noSpacing = { spacing: 0 }
99
+
94
100
  export function createLogger(prefix: LogPrefixType | string) {
95
101
  const paddedPrefix = padPrefix(prefix)
96
102
  return {
97
103
  log: (...args: unknown[]) => {
98
104
  writeToFile('INFO', prefix, args)
99
- console.log(pc.dim(getTimestamp()), pc.cyan(paddedPrefix), ...args.map(formatArg))
105
+ clackLog.step(formatMessage(getTimestamp(), pc.cyan(paddedPrefix), args), noSpacing)
100
106
  },
101
107
  error: (...args: unknown[]) => {
102
108
  writeToFile('ERROR', prefix, args)
103
- // use clack for errors - visually distinct
104
- clackLog.error([paddedPrefix, ...args.map(formatArg)].join(' '))
109
+ clackLog.error(formatMessage(getTimestamp(), pc.red(paddedPrefix), args), noSpacing)
105
110
  },
106
111
  warn: (...args: unknown[]) => {
107
112
  writeToFile('WARN', prefix, args)
108
- // use clack for warnings - visually distinct
109
- clackLog.warn([paddedPrefix, ...args.map(formatArg)].join(' '))
113
+ clackLog.warn(formatMessage(getTimestamp(), pc.yellow(paddedPrefix), args), noSpacing)
110
114
  },
111
115
  info: (...args: unknown[]) => {
112
116
  writeToFile('INFO', prefix, args)
113
- console.log(pc.dim(getTimestamp()), pc.blue(paddedPrefix), ...args.map(formatArg))
117
+ clackLog.info(formatMessage(getTimestamp(), pc.blue(paddedPrefix), args), noSpacing)
114
118
  },
115
119
  debug: (...args: unknown[]) => {
116
120
  writeToFile('DEBUG', prefix, args)
117
- console.log(pc.dim(getTimestamp()), pc.dim(paddedPrefix), ...args.map(formatArg))
121
+ clackLog.message(formatMessage(getTimestamp(), pc.cyan(paddedPrefix), args), {
122
+ ...noSpacing,
123
+ symbol: pc.cyan('│'),
124
+ })
118
125
  },
119
126
  }
120
127
  }
package/src/opencode.ts CHANGED
@@ -128,6 +128,7 @@ export async function initializeOpencodeForDirectory(directory: string): Promise
128
128
  stdio: 'pipe',
129
129
  detached: false,
130
130
  cwd: directory,
131
+ shell: true, // Required for .cmd files on Windows
131
132
  env: {
132
133
  ...process.env,
133
134
  OPENCODE_CONFIG_CONTENT: JSON.stringify({