kimaki 0.4.58 → 0.4.60

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.
Files changed (74) hide show
  1. package/bin.js +1 -1
  2. package/dist/bin.js +68 -0
  3. package/dist/cli-parsing.test.js +50 -0
  4. package/dist/cli.js +611 -163
  5. package/dist/commands/abort.js +5 -6
  6. package/dist/commands/agent.js +35 -2
  7. package/dist/commands/compact.js +9 -9
  8. package/dist/commands/diff.js +7 -11
  9. package/dist/commands/file-upload.js +276 -0
  10. package/dist/commands/fork.js +8 -6
  11. package/dist/commands/merge-worktree.js +82 -212
  12. package/dist/commands/permissions.js +30 -2
  13. package/dist/commands/queue.js +5 -6
  14. package/dist/commands/restart-opencode-server.js +9 -21
  15. package/dist/commands/run-command.js +77 -0
  16. package/dist/commands/share.js +5 -6
  17. package/dist/commands/undo-redo.js +9 -10
  18. package/dist/commands/upgrade.js +38 -0
  19. package/dist/commands/worktree.js +32 -54
  20. package/dist/db.js +9 -2
  21. package/dist/discord-bot.js +114 -89
  22. package/dist/discord-utils.js +179 -23
  23. package/dist/errors.js +48 -0
  24. package/dist/format-tables.js +60 -41
  25. package/dist/format-tables.test.js +173 -392
  26. package/dist/heap-monitor.js +92 -0
  27. package/dist/interaction-handler.js +46 -0
  28. package/dist/logger.js +3 -1
  29. package/dist/message-formatting.js +9 -0
  30. package/dist/opencode-plugin.js +83 -118
  31. package/dist/opencode.js +2 -1
  32. package/dist/session-handler.js +39 -11
  33. package/dist/system-message.js +64 -2
  34. package/dist/upgrade.js +114 -0
  35. package/dist/utils.js +12 -0
  36. package/dist/voice-handler.js +20 -28
  37. package/dist/worktree-utils.js +484 -19
  38. package/package.json +6 -8
  39. package/src/bin.ts +82 -0
  40. package/src/cli-parsing.test.ts +65 -0
  41. package/src/cli.ts +764 -212
  42. package/src/commands/abort.ts +7 -7
  43. package/src/commands/agent.ts +49 -2
  44. package/src/commands/compact.ts +11 -10
  45. package/src/commands/diff.ts +9 -11
  46. package/src/commands/file-upload.ts +365 -0
  47. package/src/commands/fork.ts +9 -6
  48. package/src/commands/merge-worktree.ts +93 -288
  49. package/src/commands/permissions.ts +41 -2
  50. package/src/commands/queue.ts +5 -7
  51. package/src/commands/restart-opencode-server.ts +12 -20
  52. package/src/commands/run-command.ts +94 -0
  53. package/src/commands/share.ts +7 -7
  54. package/src/commands/undo-redo.ts +12 -11
  55. package/src/commands/upgrade.ts +46 -0
  56. package/src/commands/worktree.ts +41 -63
  57. package/src/db.ts +11 -2
  58. package/src/discord-bot.ts +128 -96
  59. package/src/discord-utils.ts +230 -23
  60. package/src/errors.ts +60 -0
  61. package/src/format-tables.test.ts +186 -412
  62. package/src/format-tables.ts +78 -45
  63. package/src/heap-monitor.ts +112 -0
  64. package/src/interaction-handler.ts +55 -1
  65. package/src/logger.ts +3 -1
  66. package/src/message-formatting.ts +11 -0
  67. package/src/opencode-plugin.ts +88 -131
  68. package/src/opencode.ts +2 -1
  69. package/src/session-handler.ts +46 -13
  70. package/src/system-message.ts +66 -2
  71. package/src/upgrade.ts +127 -0
  72. package/src/utils.ts +14 -0
  73. package/src/voice-handler.ts +22 -32
  74. package/src/worktree-utils.ts +588 -24
package/bin.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import './dist/cli.js'
2
+ import './dist/bin.js'
package/dist/bin.js ADDED
@@ -0,0 +1,68 @@
1
+ // Respawn wrapper for the kimaki bot process.
2
+ // When running the default command (no subcommand) with --auto-restart,
3
+ // spawns cli.js as a child process and restarts it on non-zero exit codes
4
+ // (crash, OOM kill, etc). Intentional exits (code 0 or EXIT_NO_RESTART=64)
5
+ // are not restarted.
6
+ //
7
+ // Subcommands (send, tunnel, project, etc.) run directly without the wrapper
8
+ // since they are short-lived and don't need crash recovery.
9
+ //
10
+ // When __KIMAKI_CHILD is set, we're the child process -- just run cli.js directly.
11
+ import { spawn } from 'node:child_process';
12
+ // First arg after node + script is either a subcommand or a flag.
13
+ // If it doesn't start with '-', it's a subcommand (e.g. "send", "tunnel", "project").
14
+ const firstArg = process.argv[2];
15
+ const isSubcommand = firstArg && !firstArg.startsWith('-');
16
+ const hasAutoRestart = process.argv.includes('--auto-restart');
17
+ if (process.env.__KIMAKI_CHILD || isSubcommand || !hasAutoRestart) {
18
+ await import('./cli.js');
19
+ }
20
+ else {
21
+ const EXIT_NO_RESTART = 64;
22
+ const MAX_RAPID_RESTARTS = 5;
23
+ const RAPID_RESTART_WINDOW_MS = 60_000;
24
+ const RESTART_DELAY_MS = 2_000;
25
+ const restartTimestamps = [];
26
+ let child = null;
27
+ // Track when we forwarded a termination signal so we don't restart after graceful shutdown
28
+ let shutdownRequested = false;
29
+ function start() {
30
+ child = spawn(process.argv[0], [...process.execArgv, ...process.argv.slice(1)], {
31
+ stdio: 'inherit',
32
+ env: { ...process.env, __KIMAKI_CHILD: '1' },
33
+ });
34
+ child.on('exit', (code, signal) => {
35
+ if (code === 0 || code === EXIT_NO_RESTART || shutdownRequested) {
36
+ process.exit(code ?? 0);
37
+ return;
38
+ }
39
+ const now = Date.now();
40
+ restartTimestamps.push(now);
41
+ while (restartTimestamps.length > 0 && restartTimestamps[0] < now - RAPID_RESTART_WINDOW_MS) {
42
+ restartTimestamps.shift();
43
+ }
44
+ if (restartTimestamps.length > MAX_RAPID_RESTARTS) {
45
+ console.error(`[kimaki] Crash loop detected (${MAX_RAPID_RESTARTS} crashes in ${RAPID_RESTART_WINDOW_MS / 1000}s), exiting`);
46
+ process.exit(1);
47
+ return;
48
+ }
49
+ const reason = signal ? `signal ${signal}` : `code ${code}`;
50
+ console.error(`[kimaki] Process exited with ${reason}, restarting in ${RESTART_DELAY_MS / 1000}s...`);
51
+ setTimeout(start, RESTART_DELAY_MS);
52
+ });
53
+ }
54
+ // Forward signals to child so graceful shutdown and heap snapshots work.
55
+ // SIGTERM/SIGINT mark shutdownRequested so we don't restart after graceful exit.
56
+ for (const sig of ['SIGTERM', 'SIGINT']) {
57
+ process.on(sig, () => {
58
+ shutdownRequested = true;
59
+ child?.kill(sig);
60
+ });
61
+ }
62
+ for (const sig of ['SIGUSR1', 'SIGUSR2']) {
63
+ process.on(sig, () => {
64
+ child?.kill(sig);
65
+ });
66
+ }
67
+ start();
68
+ }
@@ -0,0 +1,50 @@
1
+ // Regression tests for CLI argument parsing around Discord ID string preservation.
2
+ import { describe, expect, test } from 'vitest';
3
+ import { goke } from 'goke';
4
+ function createCliForIdParsing() {
5
+ const cli = goke('kimaki');
6
+ cli
7
+ .command('send', 'Send a message')
8
+ .option('-c, --channel <channelId>', 'Discord channel ID')
9
+ .option('--thread <threadId>', 'Thread ID')
10
+ .option('--session <sessionId>', 'Session ID');
11
+ cli
12
+ .command('session archive <threadId>', 'Archive a thread');
13
+ cli
14
+ .command('add-project', 'Add a project')
15
+ .option('-g, --guild <guildId>', 'Discord guild/server ID');
16
+ return cli;
17
+ }
18
+ describe('goke CLI ID parsing', () => {
19
+ test('keeps large Discord IDs as strings', () => {
20
+ const cli = createCliForIdParsing();
21
+ const channelId = '1234567890123456789';
22
+ const threadId = '9876543210987654321';
23
+ const sessionId = '1111222233334444555';
24
+ const channelResult = cli.parse(['node', 'kimaki', 'send', '--channel', channelId], { run: false });
25
+ expect(channelResult.options.channel).toBe(channelId);
26
+ expect(typeof channelResult.options.channel).toBe('string');
27
+ const threadResult = cli.parse(['node', 'kimaki', 'send', '--thread', threadId], { run: false });
28
+ expect(threadResult.options.thread).toBe(threadId);
29
+ expect(typeof threadResult.options.thread).toBe('string');
30
+ const sessionResult = cli.parse(['node', 'kimaki', 'send', '--session', sessionId], { run: false });
31
+ expect(sessionResult.options.session).toBe(sessionId);
32
+ expect(typeof sessionResult.options.session).toBe('string');
33
+ });
34
+ test('preserves leading zeros in Discord IDs', () => {
35
+ const cli = createCliForIdParsing();
36
+ const guildId = '001230045600789';
37
+ const result = cli.parse(['node', 'kimaki', 'add-project', '--guild', guildId], { run: false });
38
+ expect(result.options.guild).toBe(guildId);
39
+ expect(typeof result.options.guild).toBe('string');
40
+ });
41
+ test('keeps session archive thread ID as string', () => {
42
+ const cli = createCliForIdParsing();
43
+ const threadId = '0098765432109876543';
44
+ const result = cli.parse(['node', 'kimaki', 'session', 'archive', threadId], {
45
+ run: false,
46
+ });
47
+ expect(result.args[0]).toBe(threadId);
48
+ expect(typeof result.args[0]).toBe('string');
49
+ });
50
+ });