kimaki 0.4.81 → 0.4.83
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/LICENSE +21 -0
- package/dist/anthropic-auth-plugin.js +7 -0
- package/dist/cli.js +44 -509
- package/dist/commands/abort.js +5 -16
- package/dist/commands/action-buttons.js +3 -3
- package/dist/commands/add-project.js +1 -1
- package/dist/commands/ask-question.js +3 -3
- package/dist/commands/context-usage.js +1 -1
- package/dist/commands/create-new-project.js +1 -1
- package/dist/commands/fork.js +11 -8
- package/dist/commands/merge-worktree.js +1 -1
- package/dist/commands/new-worktree.js +63 -44
- package/dist/commands/remove-project.js +1 -1
- package/dist/commands/restart-opencode-server.js +61 -8
- package/dist/commands/resume.js +11 -8
- package/dist/commands/session.js +1 -1
- package/dist/commands/undo-redo.js +91 -7
- package/dist/commands/user-command.js +1 -1
- package/dist/condense-memory.js +1 -1
- package/dist/context-awareness-plugin.js +1 -1
- package/dist/database.js +53 -2
- package/dist/db.js +6 -0
- package/dist/discord-bot.js +104 -90
- package/dist/discord-command-registration.js +512 -0
- package/dist/external-opencode-sync.js +538 -0
- package/dist/external-opencode-sync.test.js +151 -0
- package/dist/gateway-proxy.e2e.test.js +51 -0
- package/dist/genai.js +1 -1
- package/dist/generated/enums.js +4 -0
- package/dist/generated/internal/class.js +4 -4
- package/dist/generated/internal/prismaNamespace.js +1 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +1 -0
- package/dist/generated/models/external_session_pending_prompts.js +1 -0
- package/dist/hrana-server.js +14 -285
- package/dist/hrana-server.test.js +4 -2
- package/dist/ipc-polling.js +4 -3
- package/dist/ipc-tools-plugin.js +1 -1
- package/dist/kimaki-opencode-plugin-loading.e2e.test.js +87 -0
- package/dist/kimaki-opencode-plugin.js +15 -0
- package/dist/kimaki-opencode-plugin.test.js +98 -0
- package/dist/kitty-graphics-parser.js +3 -0
- package/dist/kitty-graphics-parser.test.js +276 -0
- package/dist/kitty-graphics-plugin.js +3 -0
- package/dist/markdown.js +4 -4
- package/dist/markdown.test.js +1 -1
- package/dist/message-formatting.js +54 -15
- package/dist/openai-realtime.js +9 -13
- package/dist/opencode.js +29 -6
- package/dist/queue-advanced-e2e-setup.js +53 -0
- package/dist/queue-advanced-permissions-typing.e2e.test.js +5 -5
- package/dist/queue-advanced-typing.e2e.test.js +9 -22
- package/dist/runtime-idle-sweeper.js +3 -1
- package/dist/sentry.js +1 -1
- package/dist/session-handler/event-stream-state.js +101 -7
- package/dist/session-handler/event-stream-state.test.js +7 -3
- package/dist/session-handler/thread-session-runtime.js +77 -9
- package/dist/system-message.js +6 -0
- package/dist/system-message.test.js +19 -0
- package/dist/task-runner.js +1 -1
- package/dist/thread-message-queue.e2e.test.js +8 -14
- package/dist/tools.js +1 -1
- package/dist/undo-redo.e2e.test.js +20 -25
- package/package.json +9 -6
- package/schema.prisma +6 -0
- package/skills/npm-package/SKILL.md +1 -0
- package/skills/proxyman/SKILL.md +215 -0
- package/skills/usecomputer/SKILL.md +339 -0
- package/src/ai-tool-to-genai.ts +1 -0
- package/src/anthropic-auth-plugin.ts +7 -0
- package/src/cli.ts +46 -670
- package/src/commands/abort.ts +6 -16
- package/src/commands/action-buttons.ts +5 -1
- package/src/commands/add-project.ts +1 -1
- package/src/commands/ask-question.ts +5 -2
- package/src/commands/context-usage.ts +1 -1
- package/src/commands/create-new-project.ts +1 -1
- package/src/commands/fork.ts +12 -11
- package/src/commands/merge-worktree.ts +1 -1
- package/src/commands/new-worktree.ts +74 -55
- package/src/commands/remove-project.ts +1 -1
- package/src/commands/restart-opencode-server.ts +67 -7
- package/src/commands/resume.ts +12 -10
- package/src/commands/session.ts +1 -1
- package/src/commands/undo-redo.ts +108 -10
- package/src/commands/user-command.ts +1 -1
- package/src/condense-memory.ts +1 -1
- package/src/context-awareness-plugin.ts +1 -1
- package/src/database.ts +72 -3
- package/src/db.ts +8 -0
- package/src/discord-bot.ts +125 -97
- package/src/discord-command-registration.ts +678 -0
- package/src/external-opencode-sync.ts +760 -0
- package/src/gateway-proxy.e2e.test.ts +63 -0
- package/src/genai.ts +1 -1
- package/src/generated/commonInputTypes.ts +34 -0
- package/src/generated/enums.ts +8 -0
- package/src/generated/internal/class.ts +4 -4
- package/src/generated/internal/prismaNamespace.ts +8 -0
- package/src/generated/internal/prismaNamespaceBrowser.ts +1 -0
- package/src/generated/models/thread_sessions.ts +53 -1
- package/src/hrana-server.test.ts +8 -2
- package/src/hrana-server.ts +18 -390
- package/src/ipc-polling.ts +4 -3
- package/src/ipc-tools-plugin.ts +1 -1
- package/src/{opencode-plugin-loading.e2e.test.ts → kimaki-opencode-plugin-loading.e2e.test.ts} +8 -1
- package/src/{opencode-plugin.ts → kimaki-opencode-plugin.ts} +2 -0
- package/src/markdown.test.ts +1 -1
- package/src/markdown.ts +4 -4
- package/src/message-formatting.ts +66 -17
- package/src/openai-realtime.ts +6 -10
- package/src/opencode.ts +32 -8
- package/src/queue-advanced-e2e-setup.ts +55 -0
- package/src/queue-advanced-permissions-typing.e2e.test.ts +5 -5
- package/src/queue-advanced-typing.e2e.test.ts +9 -22
- package/src/runtime-idle-sweeper.ts +3 -1
- package/src/schema.sql +1 -0
- package/src/sentry.ts +1 -1
- package/src/session-handler/event-stream-state.test.ts +7 -2
- package/src/session-handler/event-stream-state.ts +128 -7
- package/src/session-handler/thread-runtime-state.ts +5 -0
- package/src/session-handler/thread-session-runtime.ts +93 -11
- package/src/system-message.ts +11 -0
- package/src/task-runner.ts +1 -1
- package/src/thread-message-queue.e2e.test.ts +8 -14
- package/src/tools.ts +1 -1
- package/src/undo-redo.e2e.test.ts +28 -26
- package/skills/jitter/node_modules/.bin/esbuild +0 -21
- package/skills/jitter/node_modules/.bin/tsc +0 -21
- package/skills/jitter/node_modules/.bin/tsserver +0 -21
- package/skills/jitter/node_modules/typescript/LICENSE.txt +0 -55
- package/skills/jitter/node_modules/typescript/README.md +0 -50
- package/skills/jitter/node_modules/typescript/SECURITY.md +0 -41
- package/skills/jitter/node_modules/typescript/ThirdPartyNoticeText.txt +0 -193
- package/skills/jitter/node_modules/typescript/bin/tsc +0 -2
- package/skills/jitter/node_modules/typescript/bin/tsserver +0 -2
- package/skills/jitter/node_modules/typescript/lib/_tsc.js +0 -133792
- package/skills/jitter/node_modules/typescript/lib/_tsserver.js +0 -659
- package/skills/jitter/node_modules/typescript/lib/_typingsInstaller.js +0 -222
- package/skills/jitter/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/de/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/es/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/it/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/lib.d.ts +0 -22
- package/skills/jitter/node_modules/typescript/lib/lib.decorators.d.ts +0 -384
- package/skills/jitter/node_modules/typescript/lib/lib.decorators.legacy.d.ts +0 -22
- package/skills/jitter/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +0 -41
- package/skills/jitter/node_modules/typescript/lib/lib.dom.d.ts +0 -39429
- package/skills/jitter/node_modules/typescript/lib/lib.dom.iterable.d.ts +0 -571
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.collection.d.ts +0 -147
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.core.d.ts +0 -597
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.d.ts +0 -28
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.generator.d.ts +0 -77
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.iterable.d.ts +0 -605
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.promise.d.ts +0 -81
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.proxy.d.ts +0 -128
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.reflect.d.ts +0 -144
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.symbol.d.ts +0 -46
- package/skills/jitter/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +0 -326
- package/skills/jitter/node_modules/typescript/lib/lib.es2016.array.include.d.ts +0 -116
- package/skills/jitter/node_modules/typescript/lib/lib.es2016.d.ts +0 -21
- package/skills/jitter/node_modules/typescript/lib/lib.es2016.full.d.ts +0 -23
- package/skills/jitter/node_modules/typescript/lib/lib.es2016.intl.d.ts +0 -31
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +0 -21
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.d.ts +0 -26
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.date.d.ts +0 -31
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.full.d.ts +0 -23
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.intl.d.ts +0 -44
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.object.d.ts +0 -49
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +0 -135
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.string.d.ts +0 -45
- package/skills/jitter/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +0 -53
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +0 -77
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +0 -53
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.intl.d.ts +0 -83
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.promise.d.ts +0 -30
- package/skills/jitter/node_modules/typescript/lib/lib.es2018.regexp.d.ts +0 -37
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.array.d.ts +0 -79
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.intl.d.ts +0 -23
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.object.d.ts +0 -33
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.string.d.ts +0 -37
- package/skills/jitter/node_modules/typescript/lib/lib.es2019.symbol.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.bigint.d.ts +0 -765
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.d.ts +0 -27
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.date.d.ts +0 -42
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.intl.d.ts +0 -474
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.number.d.ts +0 -28
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.promise.d.ts +0 -47
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +0 -99
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.string.d.ts +0 -44
- package/skills/jitter/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +0 -41
- package/skills/jitter/node_modules/typescript/lib/lib.es2021.d.ts +0 -23
- package/skills/jitter/node_modules/typescript/lib/lib.es2021.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2021.intl.d.ts +0 -166
- package/skills/jitter/node_modules/typescript/lib/lib.es2021.promise.d.ts +0 -48
- package/skills/jitter/node_modules/typescript/lib/lib.es2021.string.d.ts +0 -33
- package/skills/jitter/node_modules/typescript/lib/lib.es2021.weakref.d.ts +0 -78
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.array.d.ts +0 -121
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.d.ts +0 -25
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.error.d.ts +0 -75
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.intl.d.ts +0 -145
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.object.d.ts +0 -26
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.regexp.d.ts +0 -39
- package/skills/jitter/node_modules/typescript/lib/lib.es2022.string.d.ts +0 -25
- package/skills/jitter/node_modules/typescript/lib/lib.es2023.array.d.ts +0 -924
- package/skills/jitter/node_modules/typescript/lib/lib.es2023.collection.d.ts +0 -21
- package/skills/jitter/node_modules/typescript/lib/lib.es2023.d.ts +0 -22
- package/skills/jitter/node_modules/typescript/lib/lib.es2023.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2023.intl.d.ts +0 -56
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +0 -65
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.collection.d.ts +0 -29
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.d.ts +0 -26
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.object.d.ts +0 -29
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.promise.d.ts +0 -35
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.regexp.d.ts +0 -25
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +0 -68
- package/skills/jitter/node_modules/typescript/lib/lib.es2024.string.d.ts +0 -29
- package/skills/jitter/node_modules/typescript/lib/lib.es5.d.ts +0 -4601
- package/skills/jitter/node_modules/typescript/lib/lib.es6.d.ts +0 -23
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.array.d.ts +0 -35
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.collection.d.ts +0 -96
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.d.ts +0 -29
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.decorators.d.ts +0 -28
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.disposable.d.ts +0 -193
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.error.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.float16.d.ts +0 -443
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.full.d.ts +0 -24
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.intl.d.ts +0 -21
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.iterator.d.ts +0 -148
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.promise.d.ts +0 -34
- package/skills/jitter/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +0 -25
- package/skills/jitter/node_modules/typescript/lib/lib.scripthost.d.ts +0 -322
- package/skills/jitter/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +0 -41
- package/skills/jitter/node_modules/typescript/lib/lib.webworker.d.ts +0 -13150
- package/skills/jitter/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +0 -23
- package/skills/jitter/node_modules/typescript/lib/lib.webworker.iterable.d.ts +0 -340
- package/skills/jitter/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/tsc.js +0 -8
- package/skills/jitter/node_modules/typescript/lib/tsserver.js +0 -8
- package/skills/jitter/node_modules/typescript/lib/tsserverlibrary.d.ts +0 -17
- package/skills/jitter/node_modules/typescript/lib/tsserverlibrary.js +0 -21
- package/skills/jitter/node_modules/typescript/lib/typesMap.json +0 -497
- package/skills/jitter/node_modules/typescript/lib/typescript.d.ts +0 -11438
- package/skills/jitter/node_modules/typescript/lib/typescript.js +0 -200253
- package/skills/jitter/node_modules/typescript/lib/typingsInstaller.js +0 -8
- package/skills/jitter/node_modules/typescript/lib/watchGuard.js +0 -53
- package/skills/jitter/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +0 -2122
- package/skills/jitter/node_modules/typescript/node_modules/.bin/tsc +0 -21
- package/skills/jitter/node_modules/typescript/node_modules/.bin/tsserver +0 -21
- package/skills/jitter/node_modules/typescript/package.json +0 -120
- /package/src/{opencode-plugin.test.ts → kimaki-opencode-plugin.test.ts} +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Kimaki
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Anthropic OAuth authentication plugin for OpenCode.
|
|
3
3
|
*
|
|
4
|
+
* If you're copy-pasting this plugin into your OpenCode config folder,
|
|
5
|
+
* you need to install the runtime dependencies first:
|
|
6
|
+
*
|
|
7
|
+
* cd ~/.config/opencode
|
|
8
|
+
* bun init -y
|
|
9
|
+
* bun add @openauthjs/openauth proper-lockfile
|
|
10
|
+
*
|
|
4
11
|
* Handles two concerns:
|
|
5
12
|
* 1. OAuth login + token refresh (PKCE flow against claude.ai)
|
|
6
13
|
* 2. Request/response rewriting (tool names, system prompt, beta headers)
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { z } from 'zod';
|
|
|
7
7
|
import { intro, outro, text, password, note, cancel, isCancel, confirm, log, multiselect, select, spinner, } from '@clack/prompts';
|
|
8
8
|
import { deduplicateByKey, generateBotInstallUrl, generateDiscordInstallUrlForBot, KIMAKI_GATEWAY_APP_ID, KIMAKI_WEBSITE_URL, abbreviatePath, } from './utils.js';
|
|
9
9
|
import { getChannelsWithDescriptions, createDiscordClient, initDatabase, getChannelDirectory, startDiscordBot, initializeOpencodeForDirectory, ensureKimakiCategory, createProjectChannels, createDefaultKimakiChannel, } from './discord-bot.js';
|
|
10
|
-
import { getBotTokenWithMode, ensureServiceAuthToken, setBotToken, setBotMode, setChannelDirectory, findChannelsByDirectory, getThreadSession, getThreadIdBySessionId, getSessionEventSnapshot, getPrisma, createScheduledTask, listScheduledTasks, cancelScheduledTask, getScheduledTask, updateScheduledTask, getSessionStartSourcesBySessionIds, } from './database.js';
|
|
10
|
+
import { getBotTokenWithMode, ensureServiceAuthToken, setBotToken, setBotMode, setChannelDirectory, findChannelsByDirectory, getThreadSession, getThreadIdBySessionId, getSessionEventSnapshot, getPrisma, createScheduledTask, listScheduledTasks, cancelScheduledTask, getScheduledTask, updateScheduledTask, getSessionStartSourcesBySessionIds, deleteChannelDirectoryById, } from './database.js';
|
|
11
11
|
import { ShareMarkdown } from './markdown.js';
|
|
12
12
|
import { parseSessionSearchPattern, findFirstSessionSearchHit, buildSessionSearchSnippet, getPartSearchTexts, } from './session-search.js';
|
|
13
13
|
import { formatWorktreeName } from './commands/new-worktree.js';
|
|
@@ -16,7 +16,7 @@ import { sendWelcomeMessage } from './onboarding-welcome.js';
|
|
|
16
16
|
import { buildOpencodeEventLogLine } from './session-handler/opencode-session-event-log.js';
|
|
17
17
|
import { selectResolvedCommand } from './opencode-command.js';
|
|
18
18
|
import yaml from 'js-yaml';
|
|
19
|
-
import { Events, ChannelType, ActivityType, Routes,
|
|
19
|
+
import { Events, ChannelType, ActivityType, Routes, AttachmentBuilder, } from 'discord.js';
|
|
20
20
|
import { createDiscordRest, discordApiUrl, getDiscordRestApiUrl, getGatewayProxyRestBaseUrl, getInternetReachableBaseUrl } from './discord-urls.js';
|
|
21
21
|
import crypto from 'node:crypto';
|
|
22
22
|
import path from 'node:path';
|
|
@@ -27,7 +27,6 @@ import { initSentry, notifyError } from './sentry.js';
|
|
|
27
27
|
import { archiveThread, uploadFilesToDiscord, stripMentions, } from './discord-utils.js';
|
|
28
28
|
import { spawn, execSync } from 'node:child_process';
|
|
29
29
|
import { setDataDir, getDataDir, getProjectsDir, } from './config.js';
|
|
30
|
-
import { sanitizeAgentName, buildQuickAgentCommandDescription, } from './commands/agent.js';
|
|
31
30
|
import { execAsync } from './worktrees.js';
|
|
32
31
|
import { backgroundUpgradeKimaki, upgrade, getCurrentVersion, } from './upgrade.js';
|
|
33
32
|
import { startHranaServer } from './hrana-server.js';
|
|
@@ -437,509 +436,8 @@ function startCaffeinate() {
|
|
|
437
436
|
}
|
|
438
437
|
const cli = goke('kimaki');
|
|
439
438
|
process.title = 'kimaki';
|
|
440
|
-
// Commands to skip when registering user commands (reserved names)
|
|
441
|
-
const SKIP_USER_COMMANDS = ['init'];
|
|
442
|
-
function getDiscordCommandSuffix(command) {
|
|
443
|
-
if (command.source === 'skill') {
|
|
444
|
-
return '-skill';
|
|
445
|
-
}
|
|
446
|
-
if (command.source === 'mcp') {
|
|
447
|
-
return '-mcp-prompt';
|
|
448
|
-
}
|
|
449
|
-
return '-cmd';
|
|
450
|
-
}
|
|
451
439
|
import { store } from './store.js';
|
|
452
|
-
|
|
453
|
-
if (typeof value !== 'object' || value === null) {
|
|
454
|
-
return false;
|
|
455
|
-
}
|
|
456
|
-
const id = Reflect.get(value, 'id');
|
|
457
|
-
const name = Reflect.get(value, 'name');
|
|
458
|
-
return typeof id === 'string' && typeof name === 'string';
|
|
459
|
-
}
|
|
460
|
-
async function deleteLegacyGlobalCommands({ rest, appId, commandNames, }) {
|
|
461
|
-
try {
|
|
462
|
-
const response = await rest.get(Routes.applicationCommands(appId));
|
|
463
|
-
if (!Array.isArray(response)) {
|
|
464
|
-
cliLogger.warn('COMMANDS: Unexpected global command payload while cleaning legacy global commands');
|
|
465
|
-
return;
|
|
466
|
-
}
|
|
467
|
-
const legacyGlobalCommands = response
|
|
468
|
-
.filter(isDiscordCommandSummary)
|
|
469
|
-
.filter((command) => {
|
|
470
|
-
return commandNames.has(command.name);
|
|
471
|
-
});
|
|
472
|
-
if (legacyGlobalCommands.length === 0) {
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
const deletionResults = await Promise.allSettled(legacyGlobalCommands.map(async (command) => {
|
|
476
|
-
await rest.delete(Routes.applicationCommand(appId, command.id));
|
|
477
|
-
return command;
|
|
478
|
-
}));
|
|
479
|
-
const failedDeletions = deletionResults.filter((result) => {
|
|
480
|
-
return result.status === 'rejected';
|
|
481
|
-
});
|
|
482
|
-
if (failedDeletions.length > 0) {
|
|
483
|
-
cliLogger.warn(`COMMANDS: Failed to delete ${failedDeletions.length} legacy global command(s)`);
|
|
484
|
-
}
|
|
485
|
-
const deletedCount = deletionResults.length - failedDeletions.length;
|
|
486
|
-
if (deletedCount > 0) {
|
|
487
|
-
cliLogger.info(`COMMANDS: Deleted ${deletedCount} legacy global command(s) to avoid guild/global duplicates`);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
catch (error) {
|
|
491
|
-
cliLogger.warn(`COMMANDS: Could not clean legacy global commands: ${error instanceof Error ? error.stack : String(error)}`);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
// Discord slash command descriptions must be 1-100 chars.
|
|
495
|
-
// Truncate to 100 so @sapphire/shapeshift validation never throws.
|
|
496
|
-
function truncateCommandDescription(description) {
|
|
497
|
-
return description.slice(0, 100);
|
|
498
|
-
}
|
|
499
|
-
async function registerCommands({ token, appId, guildIds, userCommands = [], agents = [], }) {
|
|
500
|
-
const commands = [
|
|
501
|
-
new SlashCommandBuilder()
|
|
502
|
-
.setName('resume')
|
|
503
|
-
.setDescription(truncateCommandDescription('Resume an existing OpenCode session'))
|
|
504
|
-
.addStringOption((option) => {
|
|
505
|
-
option
|
|
506
|
-
.setName('session')
|
|
507
|
-
.setDescription(truncateCommandDescription('The session to resume'))
|
|
508
|
-
.setRequired(true)
|
|
509
|
-
.setAutocomplete(true);
|
|
510
|
-
return option;
|
|
511
|
-
})
|
|
512
|
-
.setDMPermission(false)
|
|
513
|
-
.toJSON(),
|
|
514
|
-
new SlashCommandBuilder()
|
|
515
|
-
.setName('new-session')
|
|
516
|
-
.setDescription(truncateCommandDescription('Start a new OpenCode session'))
|
|
517
|
-
.addStringOption((option) => {
|
|
518
|
-
option
|
|
519
|
-
.setName('prompt')
|
|
520
|
-
.setDescription(truncateCommandDescription('Prompt content for the session'))
|
|
521
|
-
.setRequired(true);
|
|
522
|
-
return option;
|
|
523
|
-
})
|
|
524
|
-
.addStringOption((option) => {
|
|
525
|
-
option
|
|
526
|
-
.setName('files')
|
|
527
|
-
.setDescription(truncateCommandDescription('Files to mention (comma or space separated; autocomplete)'))
|
|
528
|
-
.setAutocomplete(true)
|
|
529
|
-
.setMaxLength(6000);
|
|
530
|
-
return option;
|
|
531
|
-
})
|
|
532
|
-
.addStringOption((option) => {
|
|
533
|
-
option
|
|
534
|
-
.setName('agent')
|
|
535
|
-
.setDescription(truncateCommandDescription('Agent to use for this session'))
|
|
536
|
-
.setAutocomplete(true);
|
|
537
|
-
return option;
|
|
538
|
-
})
|
|
539
|
-
.setDMPermission(false)
|
|
540
|
-
.toJSON(),
|
|
541
|
-
new SlashCommandBuilder()
|
|
542
|
-
.setName('new-worktree')
|
|
543
|
-
.setDescription(truncateCommandDescription('Create a git worktree branch from origin/HEAD (or main). Optionally pick a base branch.'))
|
|
544
|
-
.addStringOption((option) => {
|
|
545
|
-
option
|
|
546
|
-
.setName('name')
|
|
547
|
-
.setDescription(truncateCommandDescription('Name for worktree (optional in threads - uses thread name)'))
|
|
548
|
-
.setRequired(false);
|
|
549
|
-
return option;
|
|
550
|
-
})
|
|
551
|
-
.addStringOption((option) => {
|
|
552
|
-
option
|
|
553
|
-
.setName('base-branch')
|
|
554
|
-
.setDescription(truncateCommandDescription('Branch to create the worktree from (default: origin/HEAD or main)'))
|
|
555
|
-
.setRequired(false)
|
|
556
|
-
.setAutocomplete(true);
|
|
557
|
-
return option;
|
|
558
|
-
})
|
|
559
|
-
.setDMPermission(false)
|
|
560
|
-
.toJSON(),
|
|
561
|
-
new SlashCommandBuilder()
|
|
562
|
-
.setName('merge-worktree')
|
|
563
|
-
.setDescription(truncateCommandDescription('Squash-merge worktree into default branch. Aborts if main has uncommitted changes.'))
|
|
564
|
-
.addStringOption((option) => {
|
|
565
|
-
option
|
|
566
|
-
.setName('target-branch')
|
|
567
|
-
.setDescription(truncateCommandDescription('Branch to merge into (default: origin/HEAD or main)'))
|
|
568
|
-
.setRequired(false)
|
|
569
|
-
.setAutocomplete(true);
|
|
570
|
-
return option;
|
|
571
|
-
})
|
|
572
|
-
.setDMPermission(false)
|
|
573
|
-
.toJSON(),
|
|
574
|
-
new SlashCommandBuilder()
|
|
575
|
-
.setName('toggle-worktrees')
|
|
576
|
-
.setDescription(truncateCommandDescription('Toggle automatic git worktree creation for new sessions in this channel'))
|
|
577
|
-
.setDMPermission(false)
|
|
578
|
-
.toJSON(),
|
|
579
|
-
new SlashCommandBuilder()
|
|
580
|
-
.setName('worktrees')
|
|
581
|
-
.setDescription(truncateCommandDescription('List all active worktree sessions'))
|
|
582
|
-
.setDMPermission(false)
|
|
583
|
-
.toJSON(),
|
|
584
|
-
new SlashCommandBuilder()
|
|
585
|
-
.setName('tasks')
|
|
586
|
-
.setDescription(truncateCommandDescription('List scheduled tasks created via send --send-at'))
|
|
587
|
-
.addBooleanOption((option) => {
|
|
588
|
-
return option
|
|
589
|
-
.setName('all')
|
|
590
|
-
.setDescription(truncateCommandDescription('Include completed, cancelled, and failed tasks'));
|
|
591
|
-
})
|
|
592
|
-
.setDMPermission(false)
|
|
593
|
-
.toJSON(),
|
|
594
|
-
new SlashCommandBuilder()
|
|
595
|
-
.setName('toggle-mention-mode')
|
|
596
|
-
.setDescription(truncateCommandDescription('Toggle mention-only mode (bot only responds when @mentioned)'))
|
|
597
|
-
.setDMPermission(false)
|
|
598
|
-
.toJSON(),
|
|
599
|
-
new SlashCommandBuilder()
|
|
600
|
-
.setName('add-project')
|
|
601
|
-
.setDescription(truncateCommandDescription('Create Discord channels for a project. Use `npx kimaki project add` for unlisted projects'))
|
|
602
|
-
.addStringOption((option) => {
|
|
603
|
-
option
|
|
604
|
-
.setName('project')
|
|
605
|
-
.setDescription(truncateCommandDescription('Recent OpenCode projects. Use `npx kimaki project add` if not listed'))
|
|
606
|
-
.setRequired(true)
|
|
607
|
-
.setAutocomplete(true);
|
|
608
|
-
return option;
|
|
609
|
-
})
|
|
610
|
-
.setDMPermission(false)
|
|
611
|
-
.toJSON(),
|
|
612
|
-
new SlashCommandBuilder()
|
|
613
|
-
.setName('remove-project')
|
|
614
|
-
.setDescription(truncateCommandDescription('Remove Discord channels for a project'))
|
|
615
|
-
.addStringOption((option) => {
|
|
616
|
-
option
|
|
617
|
-
.setName('project')
|
|
618
|
-
.setDescription(truncateCommandDescription('Select a project to remove'))
|
|
619
|
-
.setRequired(true)
|
|
620
|
-
.setAutocomplete(true);
|
|
621
|
-
return option;
|
|
622
|
-
})
|
|
623
|
-
.setDMPermission(false)
|
|
624
|
-
.toJSON(),
|
|
625
|
-
new SlashCommandBuilder()
|
|
626
|
-
.setName('create-new-project')
|
|
627
|
-
.setDescription(truncateCommandDescription('Create a new project folder, initialize git, and start a session'))
|
|
628
|
-
.addStringOption((option) => {
|
|
629
|
-
option
|
|
630
|
-
.setName('name')
|
|
631
|
-
.setDescription(truncateCommandDescription('Name for the new project folder'))
|
|
632
|
-
.setRequired(true);
|
|
633
|
-
return option;
|
|
634
|
-
})
|
|
635
|
-
.setDMPermission(false)
|
|
636
|
-
.toJSON(),
|
|
637
|
-
new SlashCommandBuilder()
|
|
638
|
-
.setName('abort')
|
|
639
|
-
.setDescription(truncateCommandDescription('Abort the current OpenCode request in this thread'))
|
|
640
|
-
.setDMPermission(false)
|
|
641
|
-
.toJSON(),
|
|
642
|
-
new SlashCommandBuilder()
|
|
643
|
-
.setName('compact')
|
|
644
|
-
.setDescription(truncateCommandDescription('Compact the session context by summarizing conversation history'))
|
|
645
|
-
.setDMPermission(false)
|
|
646
|
-
.toJSON(),
|
|
647
|
-
new SlashCommandBuilder()
|
|
648
|
-
.setName('stop')
|
|
649
|
-
.setDescription(truncateCommandDescription('Abort the current OpenCode request in this thread'))
|
|
650
|
-
.setDMPermission(false)
|
|
651
|
-
.toJSON(),
|
|
652
|
-
new SlashCommandBuilder()
|
|
653
|
-
.setName('share')
|
|
654
|
-
.setDescription(truncateCommandDescription('Share the current session as a public URL'))
|
|
655
|
-
.setDMPermission(false)
|
|
656
|
-
.toJSON(),
|
|
657
|
-
new SlashCommandBuilder()
|
|
658
|
-
.setName('diff')
|
|
659
|
-
.setDescription(truncateCommandDescription('Show git diff as a shareable URL'))
|
|
660
|
-
.setDMPermission(false)
|
|
661
|
-
.toJSON(),
|
|
662
|
-
new SlashCommandBuilder()
|
|
663
|
-
.setName('fork')
|
|
664
|
-
.setDescription(truncateCommandDescription('Fork the session from a past user message'))
|
|
665
|
-
.setDMPermission(false)
|
|
666
|
-
.toJSON(),
|
|
667
|
-
new SlashCommandBuilder()
|
|
668
|
-
.setName('model')
|
|
669
|
-
.setDescription(truncateCommandDescription('Set the preferred model for this channel or session'))
|
|
670
|
-
.setDMPermission(false)
|
|
671
|
-
.toJSON(),
|
|
672
|
-
new SlashCommandBuilder()
|
|
673
|
-
.setName('model-variant')
|
|
674
|
-
.setDescription(truncateCommandDescription('Quickly change the thinking level variant for the current model'))
|
|
675
|
-
.setDMPermission(false)
|
|
676
|
-
.toJSON(),
|
|
677
|
-
new SlashCommandBuilder()
|
|
678
|
-
.setName('unset-model-override')
|
|
679
|
-
.setDescription(truncateCommandDescription('Remove model override and use default instead'))
|
|
680
|
-
.setDMPermission(false)
|
|
681
|
-
.toJSON(),
|
|
682
|
-
new SlashCommandBuilder()
|
|
683
|
-
.setName('login')
|
|
684
|
-
.setDescription(truncateCommandDescription('Authenticate with an AI provider (OAuth or API key). Use this instead of /connect'))
|
|
685
|
-
.setDMPermission(false)
|
|
686
|
-
.toJSON(),
|
|
687
|
-
new SlashCommandBuilder()
|
|
688
|
-
.setName('agent')
|
|
689
|
-
.setDescription(truncateCommandDescription('Set the preferred agent for this channel or session'))
|
|
690
|
-
.setDMPermission(false)
|
|
691
|
-
.toJSON(),
|
|
692
|
-
new SlashCommandBuilder()
|
|
693
|
-
.setName('queue')
|
|
694
|
-
.setDescription(truncateCommandDescription('Queue a message to be sent after the current response finishes'))
|
|
695
|
-
.addStringOption((option) => {
|
|
696
|
-
option
|
|
697
|
-
.setName('message')
|
|
698
|
-
.setDescription(truncateCommandDescription('The message to queue'))
|
|
699
|
-
.setRequired(true);
|
|
700
|
-
return option;
|
|
701
|
-
})
|
|
702
|
-
.setDMPermission(false)
|
|
703
|
-
.toJSON(),
|
|
704
|
-
new SlashCommandBuilder()
|
|
705
|
-
.setName('clear-queue')
|
|
706
|
-
.setDescription(truncateCommandDescription('Clear all queued messages in this thread'))
|
|
707
|
-
.setDMPermission(false)
|
|
708
|
-
.toJSON(),
|
|
709
|
-
new SlashCommandBuilder()
|
|
710
|
-
.setName('queue-command')
|
|
711
|
-
.setDescription(truncateCommandDescription('Queue a user command to run after the current response finishes'))
|
|
712
|
-
.addStringOption((option) => {
|
|
713
|
-
option
|
|
714
|
-
.setName('command')
|
|
715
|
-
.setDescription(truncateCommandDescription('The command to run'))
|
|
716
|
-
.setRequired(true)
|
|
717
|
-
.setAutocomplete(true);
|
|
718
|
-
return option;
|
|
719
|
-
})
|
|
720
|
-
.addStringOption((option) => {
|
|
721
|
-
option
|
|
722
|
-
.setName('arguments')
|
|
723
|
-
.setDescription(truncateCommandDescription('Arguments to pass to the command'))
|
|
724
|
-
.setRequired(false);
|
|
725
|
-
return option;
|
|
726
|
-
})
|
|
727
|
-
.setDMPermission(false)
|
|
728
|
-
.toJSON(),
|
|
729
|
-
new SlashCommandBuilder()
|
|
730
|
-
.setName('undo')
|
|
731
|
-
.setDescription(truncateCommandDescription('Undo the last assistant message (revert file changes)'))
|
|
732
|
-
.setDMPermission(false)
|
|
733
|
-
.toJSON(),
|
|
734
|
-
new SlashCommandBuilder()
|
|
735
|
-
.setName('redo')
|
|
736
|
-
.setDescription(truncateCommandDescription('Redo previously undone changes'))
|
|
737
|
-
.setDMPermission(false)
|
|
738
|
-
.toJSON(),
|
|
739
|
-
new SlashCommandBuilder()
|
|
740
|
-
.setName('verbosity')
|
|
741
|
-
.setDescription(truncateCommandDescription('Set output verbosity for this channel'))
|
|
742
|
-
.setDMPermission(false)
|
|
743
|
-
.toJSON(),
|
|
744
|
-
new SlashCommandBuilder()
|
|
745
|
-
.setName('restart-opencode-server')
|
|
746
|
-
.setDescription(truncateCommandDescription('Restart the shared opencode server (fixes state/auth/plugins)'))
|
|
747
|
-
.setDMPermission(false)
|
|
748
|
-
.toJSON(),
|
|
749
|
-
new SlashCommandBuilder()
|
|
750
|
-
.setName('run-shell-command')
|
|
751
|
-
.setDescription(truncateCommandDescription('Run a shell command in the project directory. Tip: prefix messages with ! as shortcut'))
|
|
752
|
-
.addStringOption((option) => {
|
|
753
|
-
option
|
|
754
|
-
.setName('command')
|
|
755
|
-
.setDescription(truncateCommandDescription('Command to run'))
|
|
756
|
-
.setRequired(true);
|
|
757
|
-
return option;
|
|
758
|
-
})
|
|
759
|
-
.setDMPermission(false)
|
|
760
|
-
.toJSON(),
|
|
761
|
-
new SlashCommandBuilder()
|
|
762
|
-
.setName('context-usage')
|
|
763
|
-
.setDescription(truncateCommandDescription('Show token usage and context window percentage for this session'))
|
|
764
|
-
.setDMPermission(false)
|
|
765
|
-
.toJSON(),
|
|
766
|
-
new SlashCommandBuilder()
|
|
767
|
-
.setName('session-id')
|
|
768
|
-
.setDescription(truncateCommandDescription('Show current session ID and opencode attach command for this thread'))
|
|
769
|
-
.setDMPermission(false)
|
|
770
|
-
.toJSON(),
|
|
771
|
-
new SlashCommandBuilder()
|
|
772
|
-
.setName('memory-snapshot')
|
|
773
|
-
.setDescription(truncateCommandDescription('Write a V8 heap snapshot to disk for memory debugging'))
|
|
774
|
-
.setDMPermission(false)
|
|
775
|
-
.toJSON(),
|
|
776
|
-
new SlashCommandBuilder()
|
|
777
|
-
.setName('upgrade-and-restart')
|
|
778
|
-
.setDescription(truncateCommandDescription('Upgrade kimaki to the latest version and restart the bot'))
|
|
779
|
-
.setDMPermission(false)
|
|
780
|
-
.toJSON(),
|
|
781
|
-
new SlashCommandBuilder()
|
|
782
|
-
.setName('transcription-key')
|
|
783
|
-
.setDescription(truncateCommandDescription('Set API key for voice message transcription (OpenAI or Gemini)'))
|
|
784
|
-
.setDMPermission(false)
|
|
785
|
-
.toJSON(),
|
|
786
|
-
new SlashCommandBuilder()
|
|
787
|
-
.setName('mcp')
|
|
788
|
-
.setDescription(truncateCommandDescription('List and manage MCP servers for this project'))
|
|
789
|
-
.setDMPermission(false)
|
|
790
|
-
.toJSON(),
|
|
791
|
-
new SlashCommandBuilder()
|
|
792
|
-
.setName('screenshare')
|
|
793
|
-
.setDescription(truncateCommandDescription('Start screen sharing via VNC tunnel (auto-stops after 1 hour)'))
|
|
794
|
-
.setDMPermission(false)
|
|
795
|
-
.toJSON(),
|
|
796
|
-
new SlashCommandBuilder()
|
|
797
|
-
.setName('screenshare-stop')
|
|
798
|
-
.setDescription(truncateCommandDescription('Stop screen sharing'))
|
|
799
|
-
.setDMPermission(false)
|
|
800
|
-
.toJSON(),
|
|
801
|
-
];
|
|
802
|
-
// Add user-defined commands with source-based suffixes (-cmd / -skill)
|
|
803
|
-
// Also populate registeredUserCommands in the store for /queue-command autocomplete
|
|
804
|
-
const newRegisteredCommands = [];
|
|
805
|
-
for (const cmd of userCommands) {
|
|
806
|
-
if (SKIP_USER_COMMANDS.includes(cmd.name)) {
|
|
807
|
-
continue;
|
|
808
|
-
}
|
|
809
|
-
// Sanitize command name: oh-my-opencode uses MCP commands with colons and slashes,
|
|
810
|
-
// which Discord doesn't allow in command names.
|
|
811
|
-
// Discord command names: lowercase, alphanumeric and hyphens only, must start with letter/number.
|
|
812
|
-
const sanitizedName = cmd.name
|
|
813
|
-
.toLowerCase()
|
|
814
|
-
.replace(/[:/]/g, '-') // Replace : and / with hyphens first
|
|
815
|
-
.replace(/[^a-z0-9-]/g, '-') // Replace any other non-alphanumeric chars
|
|
816
|
-
.replace(/-+/g, '-') // Collapse multiple hyphens
|
|
817
|
-
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
|
818
|
-
// Skip if sanitized name is empty - would create invalid command name like "-cmd"
|
|
819
|
-
if (!sanitizedName) {
|
|
820
|
-
continue;
|
|
821
|
-
}
|
|
822
|
-
const commandSuffix = getDiscordCommandSuffix(cmd);
|
|
823
|
-
// Truncate base name before appending suffix so the suffix is never
|
|
824
|
-
// lost to Discord's 32-char command name limit.
|
|
825
|
-
const baseName = sanitizedName.slice(0, 32 - commandSuffix.length);
|
|
826
|
-
const commandName = `${baseName}${commandSuffix}`;
|
|
827
|
-
const description = cmd.description || `Run /${cmd.name} command`;
|
|
828
|
-
newRegisteredCommands.push({
|
|
829
|
-
name: cmd.name,
|
|
830
|
-
discordCommandName: commandName,
|
|
831
|
-
description,
|
|
832
|
-
source: cmd.source,
|
|
833
|
-
});
|
|
834
|
-
commands.push(new SlashCommandBuilder()
|
|
835
|
-
.setName(commandName)
|
|
836
|
-
.setDescription(truncateCommandDescription(description))
|
|
837
|
-
.addStringOption((option) => {
|
|
838
|
-
option
|
|
839
|
-
.setName('arguments')
|
|
840
|
-
.setDescription(truncateCommandDescription('Arguments to pass to the command'))
|
|
841
|
-
.setRequired(false);
|
|
842
|
-
return option;
|
|
843
|
-
})
|
|
844
|
-
.setDMPermission(false)
|
|
845
|
-
.toJSON());
|
|
846
|
-
}
|
|
847
|
-
store.setState({ registeredUserCommands: newRegisteredCommands });
|
|
848
|
-
// Add agent-specific quick commands like /plan-agent, /build-agent
|
|
849
|
-
// Filter to primary/all mode agents (same as /agent command shows), excluding hidden agents
|
|
850
|
-
const primaryAgents = agents.filter((a) => (a.mode === 'primary' || a.mode === 'all') && !a.hidden);
|
|
851
|
-
for (const agent of primaryAgents) {
|
|
852
|
-
const sanitizedName = sanitizeAgentName(agent.name);
|
|
853
|
-
// Skip if sanitized name is empty or would create invalid command name
|
|
854
|
-
// Discord command names must start with a lowercase letter or number
|
|
855
|
-
if (!sanitizedName || !/^[a-z0-9]/.test(sanitizedName)) {
|
|
856
|
-
continue;
|
|
857
|
-
}
|
|
858
|
-
// Truncate base name before appending suffix so the -agent suffix is never
|
|
859
|
-
// lost to Discord's 32-char command name limit.
|
|
860
|
-
const agentSuffix = '-agent';
|
|
861
|
-
const agentBaseName = sanitizedName.slice(0, 32 - agentSuffix.length);
|
|
862
|
-
const commandName = `${agentBaseName}${agentSuffix}`;
|
|
863
|
-
const description = buildQuickAgentCommandDescription({
|
|
864
|
-
agentName: agent.name,
|
|
865
|
-
description: agent.description,
|
|
866
|
-
});
|
|
867
|
-
commands.push(new SlashCommandBuilder()
|
|
868
|
-
.setName(commandName)
|
|
869
|
-
.setDescription(truncateCommandDescription(description))
|
|
870
|
-
.setDMPermission(false)
|
|
871
|
-
.toJSON());
|
|
872
|
-
}
|
|
873
|
-
const rest = createDiscordRest(token);
|
|
874
|
-
const uniqueGuildIds = Array.from(new Set(guildIds.filter((guildId) => guildId)));
|
|
875
|
-
const guildCommandNames = new Set(commands
|
|
876
|
-
.map((command) => {
|
|
877
|
-
return command.name;
|
|
878
|
-
})
|
|
879
|
-
.filter((name) => {
|
|
880
|
-
return typeof name === 'string';
|
|
881
|
-
}));
|
|
882
|
-
if (uniqueGuildIds.length === 0) {
|
|
883
|
-
cliLogger.warn('COMMANDS: No guilds available, skipping slash command registration');
|
|
884
|
-
return;
|
|
885
|
-
}
|
|
886
|
-
try {
|
|
887
|
-
// PUT is a bulk overwrite: Discord matches by name, updates changed fields
|
|
888
|
-
// (description, options, etc.) in place, creates new commands, and deletes
|
|
889
|
-
// any not present in the body. No local diffing needed.
|
|
890
|
-
const results = await Promise.allSettled(uniqueGuildIds.map(async (guildId) => {
|
|
891
|
-
const response = await rest.put(Routes.applicationGuildCommands(appId, guildId), {
|
|
892
|
-
body: commands,
|
|
893
|
-
});
|
|
894
|
-
const registeredCount = Array.isArray(response)
|
|
895
|
-
? response.length
|
|
896
|
-
: commands.length;
|
|
897
|
-
return { guildId, registeredCount };
|
|
898
|
-
}));
|
|
899
|
-
const failedGuilds = results
|
|
900
|
-
.map((result, index) => {
|
|
901
|
-
if (result.status === 'fulfilled') {
|
|
902
|
-
return null;
|
|
903
|
-
}
|
|
904
|
-
return {
|
|
905
|
-
guildId: uniqueGuildIds[index],
|
|
906
|
-
error: result.reason instanceof Error
|
|
907
|
-
? result.reason.message
|
|
908
|
-
: String(result.reason),
|
|
909
|
-
};
|
|
910
|
-
})
|
|
911
|
-
.filter((value) => {
|
|
912
|
-
return value !== null;
|
|
913
|
-
});
|
|
914
|
-
if (failedGuilds.length > 0) {
|
|
915
|
-
failedGuilds.forEach((failure) => {
|
|
916
|
-
cliLogger.warn(`COMMANDS: Failed to register slash commands for guild ${failure.guildId}: ${failure.error}`);
|
|
917
|
-
});
|
|
918
|
-
throw new Error(`Failed to register slash commands for ${failedGuilds.length} guild(s)`);
|
|
919
|
-
}
|
|
920
|
-
const successfulGuilds = results.length;
|
|
921
|
-
const firstRegisteredCount = results[0];
|
|
922
|
-
const registeredCommandCount = firstRegisteredCount && firstRegisteredCount.status === 'fulfilled'
|
|
923
|
-
? firstRegisteredCount.value.registeredCount
|
|
924
|
-
: commands.length;
|
|
925
|
-
// In gateway mode, global application routes (/applications/{app_id}/commands)
|
|
926
|
-
// are denied by the proxy (DeniedWithoutGuild). Legacy global commands only
|
|
927
|
-
// exist for self-hosted bots that previously registered commands globally.
|
|
928
|
-
const isGateway = store.getState().discordBaseUrl !== 'https://discord.com';
|
|
929
|
-
if (!isGateway) {
|
|
930
|
-
await deleteLegacyGlobalCommands({
|
|
931
|
-
rest,
|
|
932
|
-
appId,
|
|
933
|
-
commandNames: guildCommandNames,
|
|
934
|
-
});
|
|
935
|
-
}
|
|
936
|
-
cliLogger.info(`COMMANDS: Successfully registered ${registeredCommandCount} slash commands for ${successfulGuilds} guild(s)`);
|
|
937
|
-
}
|
|
938
|
-
catch (error) {
|
|
939
|
-
cliLogger.error('COMMANDS: Failed to register slash commands: ' + String(error));
|
|
940
|
-
throw error;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
440
|
+
import { registerCommands, SKIP_USER_COMMANDS } from './discord-command-registration.js';
|
|
943
441
|
async function reconcileKimakiRole({ guild }) {
|
|
944
442
|
try {
|
|
945
443
|
const roles = await guild.roles.fetch();
|
|
@@ -2855,6 +2353,7 @@ cli
|
|
|
2855
2353
|
cli
|
|
2856
2354
|
.command('project list', 'List all registered projects with their Discord channels')
|
|
2857
2355
|
.option('--json', 'Output as JSON')
|
|
2356
|
+
.option('--prune', 'Remove stale entries whose Discord channel no longer exists')
|
|
2858
2357
|
.action(async (options) => {
|
|
2859
2358
|
await initDatabase();
|
|
2860
2359
|
const prisma = await getPrisma();
|
|
@@ -2871,31 +2370,67 @@ cli
|
|
|
2871
2370
|
const rest = botRow ? createDiscordRest(botRow.token) : null;
|
|
2872
2371
|
const enriched = await Promise.all(channels.map(async (ch) => {
|
|
2873
2372
|
let channelName = '';
|
|
2373
|
+
let deleted = false;
|
|
2874
2374
|
if (rest) {
|
|
2875
2375
|
try {
|
|
2876
2376
|
const data = (await rest.get(Routes.channel(ch.channel_id)));
|
|
2877
2377
|
channelName = data.name || '';
|
|
2878
2378
|
}
|
|
2879
|
-
catch {
|
|
2880
|
-
//
|
|
2379
|
+
catch (error) {
|
|
2380
|
+
// Only mark as deleted for Unknown Channel (10003) or 404,
|
|
2381
|
+
// not transient errors like rate limits or 5xx
|
|
2382
|
+
const isUnknownChannel = error instanceof Error &&
|
|
2383
|
+
'code' in error &&
|
|
2384
|
+
'status' in error &&
|
|
2385
|
+
(error.code === 10003 ||
|
|
2386
|
+
error.status === 404);
|
|
2387
|
+
deleted = isUnknownChannel;
|
|
2881
2388
|
}
|
|
2882
2389
|
}
|
|
2883
|
-
return { ...ch, channelName };
|
|
2390
|
+
return { ...ch, channelName, deleted };
|
|
2884
2391
|
}));
|
|
2392
|
+
// Prune stale entries if requested
|
|
2393
|
+
if (options.prune) {
|
|
2394
|
+
const stale = enriched.filter((ch) => {
|
|
2395
|
+
return ch.deleted;
|
|
2396
|
+
});
|
|
2397
|
+
if (stale.length === 0) {
|
|
2398
|
+
cliLogger.log('No stale channels to prune');
|
|
2399
|
+
}
|
|
2400
|
+
else {
|
|
2401
|
+
for (const ch of stale) {
|
|
2402
|
+
await deleteChannelDirectoryById(ch.channel_id);
|
|
2403
|
+
cliLogger.log(`Pruned stale channel ${ch.channel_id} (${path.basename(ch.directory)})`);
|
|
2404
|
+
}
|
|
2405
|
+
cliLogger.log(`Pruned ${stale.length} stale channel(s)`);
|
|
2406
|
+
}
|
|
2407
|
+
// Re-filter to only show live entries after pruning
|
|
2408
|
+
const live = enriched.filter((ch) => {
|
|
2409
|
+
return !ch.deleted;
|
|
2410
|
+
});
|
|
2411
|
+
if (live.length === 0) {
|
|
2412
|
+
cliLogger.log('No projects registered');
|
|
2413
|
+
process.exit(0);
|
|
2414
|
+
}
|
|
2415
|
+
enriched.length = 0;
|
|
2416
|
+
enriched.push(...live);
|
|
2417
|
+
}
|
|
2885
2418
|
if (options.json) {
|
|
2886
2419
|
const output = enriched.map((ch) => ({
|
|
2887
2420
|
channel_id: ch.channel_id,
|
|
2888
2421
|
channel_name: ch.channelName,
|
|
2889
2422
|
directory: ch.directory,
|
|
2890
2423
|
folder_name: path.basename(ch.directory),
|
|
2424
|
+
deleted: ch.deleted,
|
|
2891
2425
|
}));
|
|
2892
2426
|
console.log(JSON.stringify(output, null, 2));
|
|
2893
2427
|
process.exit(0);
|
|
2894
2428
|
}
|
|
2895
2429
|
for (const ch of enriched) {
|
|
2896
2430
|
const folderName = path.basename(ch.directory);
|
|
2431
|
+
const deletedTag = ch.deleted ? ' (deleted from Discord)' : '';
|
|
2897
2432
|
const channelLabel = ch.channelName ? `#${ch.channelName}` : ch.channel_id;
|
|
2898
|
-
console.log(`\n${channelLabel}`);
|
|
2433
|
+
console.log(`\n${channelLabel}${deletedTag}`);
|
|
2899
2434
|
console.log(` Folder: ${folderName}`);
|
|
2900
2435
|
console.log(` Directory: ${ch.directory}`);
|
|
2901
2436
|
console.log(` Channel ID: ${ch.channel_id}`);
|