@otto-assistant/bridge 0.4.92
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/bin.js +2 -0
- package/dist/agent-model.e2e.test.js +755 -0
- package/dist/ai-tool-to-genai.js +233 -0
- package/dist/ai-tool-to-genai.test.js +267 -0
- package/dist/ai-tool.js +6 -0
- package/dist/anthropic-auth-plugin.js +728 -0
- package/dist/anthropic-auth-plugin.test.js +125 -0
- package/dist/anthropic-auth-state.js +231 -0
- package/dist/bin.js +90 -0
- package/dist/channel-management.js +227 -0
- package/dist/cli-parsing.test.js +137 -0
- package/dist/cli-send-thread.e2e.test.js +356 -0
- package/dist/cli.js +3276 -0
- package/dist/commands/abort.js +65 -0
- package/dist/commands/action-buttons.js +245 -0
- package/dist/commands/add-project.js +113 -0
- package/dist/commands/agent.js +335 -0
- package/dist/commands/ask-question.js +274 -0
- package/dist/commands/btw.js +116 -0
- package/dist/commands/compact.js +120 -0
- package/dist/commands/context-usage.js +140 -0
- package/dist/commands/create-new-project.js +130 -0
- package/dist/commands/diff.js +63 -0
- package/dist/commands/file-upload.js +275 -0
- package/dist/commands/fork.js +220 -0
- package/dist/commands/gemini-apikey.js +70 -0
- package/dist/commands/login.js +885 -0
- package/dist/commands/mcp.js +239 -0
- package/dist/commands/memory-snapshot.js +24 -0
- package/dist/commands/mention-mode.js +44 -0
- package/dist/commands/merge-worktree.js +159 -0
- package/dist/commands/model-variant.js +364 -0
- package/dist/commands/model.js +776 -0
- package/dist/commands/new-worktree.js +366 -0
- package/dist/commands/paginated-select.js +57 -0
- package/dist/commands/permissions.js +274 -0
- package/dist/commands/queue.js +206 -0
- package/dist/commands/remove-project.js +115 -0
- package/dist/commands/restart-opencode-server.js +127 -0
- package/dist/commands/resume.js +149 -0
- package/dist/commands/run-command.js +79 -0
- package/dist/commands/screenshare.js +303 -0
- package/dist/commands/screenshare.test.js +20 -0
- package/dist/commands/session-id.js +78 -0
- package/dist/commands/session.js +176 -0
- package/dist/commands/share.js +80 -0
- package/dist/commands/tasks.js +205 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/undo-redo.js +305 -0
- package/dist/commands/unset-model.js +138 -0
- package/dist/commands/upgrade.js +42 -0
- package/dist/commands/user-command.js +155 -0
- package/dist/commands/verbosity.js +125 -0
- package/dist/commands/worktree-settings.js +43 -0
- package/dist/commands/worktrees.js +410 -0
- package/dist/condense-memory.js +33 -0
- package/dist/config.js +94 -0
- package/dist/context-awareness-plugin.js +363 -0
- package/dist/context-awareness-plugin.test.js +124 -0
- package/dist/critique-utils.js +95 -0
- package/dist/database.js +1310 -0
- package/dist/db.js +251 -0
- package/dist/db.test.js +138 -0
- package/dist/debounce-timeout.js +28 -0
- package/dist/debounced-process-flush.js +77 -0
- package/dist/discord-bot.js +1008 -0
- package/dist/discord-command-registration.js +524 -0
- package/dist/discord-urls.js +81 -0
- package/dist/discord-utils.js +591 -0
- package/dist/discord-utils.test.js +134 -0
- package/dist/errors.js +157 -0
- package/dist/escape-backticks.test.js +429 -0
- package/dist/event-stream-real-capture.e2e.test.js +533 -0
- package/dist/eventsource-parser.test.js +327 -0
- package/dist/exec-async.js +26 -0
- package/dist/external-opencode-sync.js +480 -0
- package/dist/format-tables.js +302 -0
- package/dist/format-tables.test.js +308 -0
- package/dist/forum-sync/config.js +79 -0
- package/dist/forum-sync/discord-operations.js +154 -0
- package/dist/forum-sync/index.js +5 -0
- package/dist/forum-sync/markdown.js +113 -0
- package/dist/forum-sync/sync-to-discord.js +417 -0
- package/dist/forum-sync/sync-to-files.js +190 -0
- package/dist/forum-sync/types.js +53 -0
- package/dist/forum-sync/watchers.js +307 -0
- package/dist/gateway-proxy-reconnect.e2e.test.js +394 -0
- package/dist/gateway-proxy.e2e.test.js +483 -0
- package/dist/genai-worker-wrapper.js +111 -0
- package/dist/genai-worker.js +311 -0
- package/dist/genai.js +232 -0
- package/dist/generated/browser.js +17 -0
- package/dist/generated/client.js +37 -0
- package/dist/generated/commonInputTypes.js +10 -0
- package/dist/generated/enums.js +52 -0
- package/dist/generated/internal/class.js +49 -0
- package/dist/generated/internal/prismaNamespace.js +253 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +223 -0
- package/dist/generated/models/bot_api_keys.js +1 -0
- package/dist/generated/models/bot_tokens.js +1 -0
- package/dist/generated/models/channel_agents.js +1 -0
- package/dist/generated/models/channel_directories.js +1 -0
- package/dist/generated/models/channel_mention_mode.js +1 -0
- package/dist/generated/models/channel_models.js +1 -0
- package/dist/generated/models/channel_verbosity.js +1 -0
- package/dist/generated/models/channel_worktrees.js +1 -0
- package/dist/generated/models/forum_sync_configs.js +1 -0
- package/dist/generated/models/global_models.js +1 -0
- package/dist/generated/models/ipc_requests.js +1 -0
- package/dist/generated/models/part_messages.js +1 -0
- package/dist/generated/models/scheduled_tasks.js +1 -0
- package/dist/generated/models/session_agents.js +1 -0
- package/dist/generated/models/session_events.js +1 -0
- package/dist/generated/models/session_models.js +1 -0
- package/dist/generated/models/session_start_sources.js +1 -0
- package/dist/generated/models/thread_sessions.js +1 -0
- package/dist/generated/models/thread_worktrees.js +1 -0
- package/dist/generated/models.js +1 -0
- package/dist/heap-monitor.js +122 -0
- package/dist/hrana-server.js +263 -0
- package/dist/hrana-server.test.js +370 -0
- package/dist/html-actions.js +123 -0
- package/dist/html-actions.test.js +70 -0
- package/dist/html-components.js +117 -0
- package/dist/html-components.test.js +34 -0
- package/dist/image-optimizer-plugin.js +153 -0
- package/dist/image-utils.js +112 -0
- package/dist/interaction-handler.js +397 -0
- package/dist/ipc-polling.js +252 -0
- package/dist/ipc-tools-plugin.js +193 -0
- package/dist/kimaki-digital-twin.e2e.test.js +161 -0
- package/dist/kimaki-opencode-plugin-loading.e2e.test.js +87 -0
- package/dist/kimaki-opencode-plugin.js +17 -0
- package/dist/kimaki-opencode-plugin.test.js +98 -0
- package/dist/limit-heading-depth.js +25 -0
- package/dist/limit-heading-depth.test.js +105 -0
- package/dist/logger.js +165 -0
- package/dist/markdown.js +342 -0
- package/dist/markdown.test.js +257 -0
- package/dist/message-finish-field.e2e.test.js +165 -0
- package/dist/message-formatting.js +413 -0
- package/dist/message-formatting.test.js +73 -0
- package/dist/message-preprocessing.js +330 -0
- package/dist/onboarding-tutorial.js +172 -0
- package/dist/onboarding-welcome.js +37 -0
- package/dist/openai-realtime.js +224 -0
- package/dist/opencode-command-detection.js +65 -0
- package/dist/opencode-command-detection.test.js +240 -0
- package/dist/opencode-command.js +129 -0
- package/dist/opencode-command.test.js +48 -0
- package/dist/opencode-interrupt-plugin.js +361 -0
- package/dist/opencode-interrupt-plugin.test.js +458 -0
- package/dist/opencode.js +861 -0
- package/dist/otto/branding.js +22 -0
- package/dist/otto/index.js +21 -0
- package/dist/parse-permission-rules.test.js +117 -0
- package/dist/patch-text-parser.js +97 -0
- package/dist/plugin-logger.js +59 -0
- package/dist/privacy-sanitizer.js +105 -0
- package/dist/queue-advanced-abort.e2e.test.js +293 -0
- package/dist/queue-advanced-action-buttons.e2e.test.js +206 -0
- package/dist/queue-advanced-e2e-setup.js +786 -0
- package/dist/queue-advanced-footer.e2e.test.js +472 -0
- package/dist/queue-advanced-model-switch.e2e.test.js +299 -0
- package/dist/queue-advanced-permissions-typing.e2e.test.js +180 -0
- package/dist/queue-advanced-question.e2e.test.js +261 -0
- package/dist/queue-advanced-typing-interrupt.e2e.test.js +114 -0
- package/dist/queue-advanced-typing.e2e.test.js +153 -0
- package/dist/queue-drain-after-interactive-ui.e2e.test.js +119 -0
- package/dist/queue-interrupt-drain.e2e.test.js +135 -0
- package/dist/queue-question-select-drain.e2e.test.js +120 -0
- package/dist/runtime-idle-sweeper.js +52 -0
- package/dist/runtime-lifecycle.e2e.test.js +508 -0
- package/dist/sentry.js +23 -0
- package/dist/session-handler/agent-utils.js +67 -0
- package/dist/session-handler/event-stream-state.js +420 -0
- package/dist/session-handler/event-stream-state.test.js +563 -0
- package/dist/session-handler/model-utils.js +124 -0
- package/dist/session-handler/opencode-session-event-log.js +94 -0
- package/dist/session-handler/thread-runtime-state.js +104 -0
- package/dist/session-handler/thread-session-runtime.js +3258 -0
- package/dist/session-handler.js +9 -0
- package/dist/session-search.js +100 -0
- package/dist/session-search.test.js +40 -0
- package/dist/session-title-rename.test.js +80 -0
- package/dist/startup-service.js +153 -0
- package/dist/startup-time.e2e.test.js +296 -0
- package/dist/store.js +17 -0
- package/dist/system-message.js +613 -0
- package/dist/system-message.test.js +602 -0
- package/dist/task-runner.js +295 -0
- package/dist/task-schedule.js +209 -0
- package/dist/task-schedule.test.js +71 -0
- package/dist/test-utils.js +299 -0
- package/dist/thinking-utils.js +35 -0
- package/dist/thread-message-queue.e2e.test.js +999 -0
- package/dist/tools.js +357 -0
- package/dist/undo-redo.e2e.test.js +161 -0
- package/dist/unnest-code-blocks.js +146 -0
- package/dist/unnest-code-blocks.test.js +673 -0
- package/dist/upgrade.js +114 -0
- package/dist/utils.js +144 -0
- package/dist/voice-attachment.js +34 -0
- package/dist/voice-handler.js +646 -0
- package/dist/voice-message.e2e.test.js +1021 -0
- package/dist/voice.js +447 -0
- package/dist/voice.test.js +235 -0
- package/dist/wait-session.js +94 -0
- package/dist/websockify.js +69 -0
- package/dist/worker-types.js +4 -0
- package/dist/worktree-lifecycle.e2e.test.js +308 -0
- package/dist/worktree-utils.js +3 -0
- package/dist/worktrees.js +929 -0
- package/dist/worktrees.test.js +189 -0
- package/dist/xml.js +92 -0
- package/dist/xml.test.js +32 -0
- package/package.json +98 -0
- package/schema.prisma +295 -0
- package/skills/batch/SKILL.md +87 -0
- package/skills/critique/SKILL.md +112 -0
- package/skills/egaki/SKILL.md +100 -0
- package/skills/errore/SKILL.md +647 -0
- package/skills/event-sourcing-state/SKILL.md +252 -0
- package/skills/gitchamber/SKILL.md +93 -0
- package/skills/goke/SKILL.md +644 -0
- package/skills/jitter/EDITOR.md +219 -0
- package/skills/jitter/EXPORT-INTERNALS.md +309 -0
- package/skills/jitter/SKILL.md +158 -0
- package/skills/jitter/jitter-clipboard.json +1042 -0
- package/skills/jitter/package.json +14 -0
- package/skills/jitter/tsconfig.json +15 -0
- package/skills/jitter/utils/actions.ts +212 -0
- package/skills/jitter/utils/export.ts +114 -0
- package/skills/jitter/utils/index.ts +141 -0
- package/skills/jitter/utils/snapshot.ts +154 -0
- package/skills/jitter/utils/traverse.ts +246 -0
- package/skills/jitter/utils/types.ts +279 -0
- package/skills/jitter/utils/wait.ts +133 -0
- package/skills/lintcn/SKILL.md +873 -0
- package/skills/new-skill/SKILL.md +211 -0
- package/skills/npm-package/SKILL.md +239 -0
- package/skills/playwriter/SKILL.md +35 -0
- package/skills/proxyman/SKILL.md +215 -0
- package/skills/security-review/SKILL.md +208 -0
- package/skills/simplify/SKILL.md +58 -0
- package/skills/spiceflow/SKILL.md +14 -0
- package/skills/termcast/SKILL.md +945 -0
- package/skills/tuistory/SKILL.md +250 -0
- package/skills/usecomputer/SKILL.md +264 -0
- package/skills/x-articles/SKILL.md +554 -0
- package/skills/zele/SKILL.md +112 -0
- package/skills/zustand-centralized-state/SKILL.md +1004 -0
- package/src/agent-model.e2e.test.ts +976 -0
- package/src/ai-tool-to-genai.test.ts +296 -0
- package/src/ai-tool-to-genai.ts +283 -0
- package/src/ai-tool.ts +39 -0
- package/src/anthropic-auth-plugin.test.ts +159 -0
- package/src/anthropic-auth-plugin.ts +861 -0
- package/src/anthropic-auth-state.ts +282 -0
- package/src/bin.ts +111 -0
- package/src/channel-management.ts +334 -0
- package/src/cli-parsing.test.ts +195 -0
- package/src/cli-send-thread.e2e.test.ts +464 -0
- package/src/cli.ts +4581 -0
- package/src/commands/abort.ts +89 -0
- package/src/commands/action-buttons.ts +364 -0
- package/src/commands/add-project.ts +149 -0
- package/src/commands/agent.ts +473 -0
- package/src/commands/ask-question.ts +390 -0
- package/src/commands/btw.ts +164 -0
- package/src/commands/compact.ts +157 -0
- package/src/commands/context-usage.ts +199 -0
- package/src/commands/create-new-project.ts +190 -0
- package/src/commands/diff.ts +91 -0
- package/src/commands/file-upload.ts +389 -0
- package/src/commands/fork.ts +321 -0
- package/src/commands/gemini-apikey.ts +104 -0
- package/src/commands/login.ts +1173 -0
- package/src/commands/mcp.ts +307 -0
- package/src/commands/memory-snapshot.ts +30 -0
- package/src/commands/mention-mode.ts +68 -0
- package/src/commands/merge-worktree.ts +223 -0
- package/src/commands/model-variant.ts +483 -0
- package/src/commands/model.ts +1053 -0
- package/src/commands/new-worktree.ts +510 -0
- package/src/commands/paginated-select.ts +81 -0
- package/src/commands/permissions.ts +397 -0
- package/src/commands/queue.ts +271 -0
- package/src/commands/remove-project.ts +155 -0
- package/src/commands/restart-opencode-server.ts +162 -0
- package/src/commands/resume.ts +230 -0
- package/src/commands/run-command.ts +123 -0
- package/src/commands/screenshare.test.ts +30 -0
- package/src/commands/screenshare.ts +366 -0
- package/src/commands/session-id.ts +109 -0
- package/src/commands/session.ts +227 -0
- package/src/commands/share.ts +106 -0
- package/src/commands/tasks.ts +293 -0
- package/src/commands/types.ts +25 -0
- package/src/commands/undo-redo.ts +386 -0
- package/src/commands/unset-model.ts +173 -0
- package/src/commands/upgrade.ts +52 -0
- package/src/commands/user-command.ts +198 -0
- package/src/commands/verbosity.ts +173 -0
- package/src/commands/worktree-settings.ts +70 -0
- package/src/commands/worktrees.ts +552 -0
- package/src/condense-memory.ts +36 -0
- package/src/config.ts +111 -0
- package/src/context-awareness-plugin.test.ts +142 -0
- package/src/context-awareness-plugin.ts +510 -0
- package/src/critique-utils.ts +139 -0
- package/src/database.ts +1876 -0
- package/src/db.test.ts +162 -0
- package/src/db.ts +286 -0
- package/src/debounce-timeout.ts +43 -0
- package/src/debounced-process-flush.ts +104 -0
- package/src/discord-bot.ts +1330 -0
- package/src/discord-command-registration.ts +693 -0
- package/src/discord-urls.ts +88 -0
- package/src/discord-utils.test.ts +153 -0
- package/src/discord-utils.ts +800 -0
- package/src/errors.ts +201 -0
- package/src/escape-backticks.test.ts +469 -0
- package/src/event-stream-real-capture.e2e.test.ts +692 -0
- package/src/eventsource-parser.test.ts +351 -0
- package/src/exec-async.ts +35 -0
- package/src/external-opencode-sync.ts +685 -0
- package/src/format-tables.test.ts +335 -0
- package/src/format-tables.ts +445 -0
- package/src/forum-sync/config.ts +92 -0
- package/src/forum-sync/discord-operations.ts +241 -0
- package/src/forum-sync/index.ts +9 -0
- package/src/forum-sync/markdown.ts +172 -0
- package/src/forum-sync/sync-to-discord.ts +595 -0
- package/src/forum-sync/sync-to-files.ts +294 -0
- package/src/forum-sync/types.ts +175 -0
- package/src/forum-sync/watchers.ts +454 -0
- package/src/gateway-proxy-reconnect.e2e.test.ts +523 -0
- package/src/gateway-proxy.e2e.test.ts +640 -0
- package/src/genai-worker-wrapper.ts +164 -0
- package/src/genai-worker.ts +386 -0
- package/src/genai.ts +321 -0
- package/src/generated/browser.ts +114 -0
- package/src/generated/client.ts +138 -0
- package/src/generated/commonInputTypes.ts +736 -0
- package/src/generated/enums.ts +88 -0
- package/src/generated/internal/class.ts +384 -0
- package/src/generated/internal/prismaNamespace.ts +2386 -0
- package/src/generated/internal/prismaNamespaceBrowser.ts +326 -0
- package/src/generated/models/bot_api_keys.ts +1288 -0
- package/src/generated/models/bot_tokens.ts +1656 -0
- package/src/generated/models/channel_agents.ts +1256 -0
- package/src/generated/models/channel_directories.ts +1859 -0
- package/src/generated/models/channel_mention_mode.ts +1300 -0
- package/src/generated/models/channel_models.ts +1288 -0
- package/src/generated/models/channel_verbosity.ts +1228 -0
- package/src/generated/models/channel_worktrees.ts +1300 -0
- package/src/generated/models/forum_sync_configs.ts +1452 -0
- package/src/generated/models/global_models.ts +1288 -0
- package/src/generated/models/ipc_requests.ts +1485 -0
- package/src/generated/models/part_messages.ts +1302 -0
- package/src/generated/models/scheduled_tasks.ts +2320 -0
- package/src/generated/models/session_agents.ts +1086 -0
- package/src/generated/models/session_events.ts +1439 -0
- package/src/generated/models/session_models.ts +1114 -0
- package/src/generated/models/session_start_sources.ts +1408 -0
- package/src/generated/models/thread_sessions.ts +1781 -0
- package/src/generated/models/thread_worktrees.ts +1356 -0
- package/src/generated/models.ts +30 -0
- package/src/heap-monitor.ts +152 -0
- package/src/hrana-server.test.ts +434 -0
- package/src/hrana-server.ts +314 -0
- package/src/html-actions.test.ts +87 -0
- package/src/html-actions.ts +174 -0
- package/src/html-components.test.ts +38 -0
- package/src/html-components.ts +181 -0
- package/src/image-optimizer-plugin.ts +194 -0
- package/src/image-utils.ts +149 -0
- package/src/interaction-handler.ts +576 -0
- package/src/ipc-polling.ts +326 -0
- package/src/ipc-tools-plugin.ts +236 -0
- package/src/kimaki-digital-twin.e2e.test.ts +199 -0
- package/src/kimaki-opencode-plugin-loading.e2e.test.ts +109 -0
- package/src/kimaki-opencode-plugin.test.ts +108 -0
- package/src/kimaki-opencode-plugin.ts +18 -0
- package/src/limit-heading-depth.test.ts +116 -0
- package/src/limit-heading-depth.ts +26 -0
- package/src/logger.ts +208 -0
- package/src/markdown.test.ts +308 -0
- package/src/markdown.ts +410 -0
- package/src/message-finish-field.e2e.test.ts +192 -0
- package/src/message-formatting.test.ts +81 -0
- package/src/message-formatting.ts +533 -0
- package/src/message-preprocessing.ts +455 -0
- package/src/onboarding-tutorial.ts +176 -0
- package/src/onboarding-welcome.ts +49 -0
- package/src/openai-realtime.ts +358 -0
- package/src/opencode-command-detection.test.ts +307 -0
- package/src/opencode-command-detection.ts +76 -0
- package/src/opencode-command.test.ts +70 -0
- package/src/opencode-command.ts +188 -0
- package/src/opencode-interrupt-plugin.test.ts +677 -0
- package/src/opencode-interrupt-plugin.ts +477 -0
- package/src/opencode.ts +1110 -0
- package/src/otto/branding.ts +23 -0
- package/src/otto/index.ts +22 -0
- package/src/parse-permission-rules.test.ts +127 -0
- package/src/patch-text-parser.ts +107 -0
- package/src/plugin-logger.ts +68 -0
- package/src/privacy-sanitizer.ts +142 -0
- package/src/queue-advanced-abort.e2e.test.ts +382 -0
- package/src/queue-advanced-action-buttons.e2e.test.ts +268 -0
- package/src/queue-advanced-e2e-setup.ts +873 -0
- package/src/queue-advanced-footer.e2e.test.ts +576 -0
- package/src/queue-advanced-model-switch.e2e.test.ts +383 -0
- package/src/queue-advanced-permissions-typing.e2e.test.ts +245 -0
- package/src/queue-advanced-question.e2e.test.ts +316 -0
- package/src/queue-advanced-typing-interrupt.e2e.test.ts +146 -0
- package/src/queue-advanced-typing.e2e.test.ts +199 -0
- package/src/queue-drain-after-interactive-ui.e2e.test.ts +151 -0
- package/src/queue-interrupt-drain.e2e.test.ts +166 -0
- package/src/queue-question-select-drain.e2e.test.ts +152 -0
- package/src/runtime-idle-sweeper.ts +76 -0
- package/src/runtime-lifecycle.e2e.test.ts +641 -0
- package/src/schema.sql +173 -0
- package/src/sentry.ts +26 -0
- package/src/session-handler/agent-utils.ts +97 -0
- package/src/session-handler/event-stream-fixtures/real-session-action-buttons.jsonl +45 -0
- package/src/session-handler/event-stream-fixtures/real-session-footer-suppressed-on-pre-idle-interrupt.jsonl +40 -0
- package/src/session-handler/event-stream-fixtures/real-session-permission-external-file.jsonl +23 -0
- package/src/session-handler/event-stream-fixtures/real-session-task-normal.jsonl +22 -0
- package/src/session-handler/event-stream-fixtures/real-session-task-three-parallel-sleeps.jsonl +277 -0
- package/src/session-handler/event-stream-fixtures/real-session-task-user-interruption.jsonl +46 -0
- package/src/session-handler/event-stream-fixtures/session-abort-after-idle-race.jsonl +21 -0
- package/src/session-handler/event-stream-fixtures/session-concurrent-messages-serialized.jsonl +56 -0
- package/src/session-handler/event-stream-fixtures/session-explicit-abort.jsonl +44 -0
- package/src/session-handler/event-stream-fixtures/session-normal-completion.jsonl +29 -0
- package/src/session-handler/event-stream-fixtures/session-tool-call-noisy-stream.jsonl +29 -0
- package/src/session-handler/event-stream-fixtures/session-two-completions-same-session.jsonl +50 -0
- package/src/session-handler/event-stream-fixtures/session-user-interruption.jsonl +59 -0
- package/src/session-handler/event-stream-fixtures/session-voice-queued-followup.jsonl +52 -0
- package/src/session-handler/event-stream-state.test.ts +645 -0
- package/src/session-handler/event-stream-state.ts +608 -0
- package/src/session-handler/model-utils.ts +183 -0
- package/src/session-handler/opencode-session-event-log.ts +130 -0
- package/src/session-handler/thread-runtime-state.ts +212 -0
- package/src/session-handler/thread-session-runtime.ts +4281 -0
- package/src/session-handler.ts +15 -0
- package/src/session-search.test.ts +50 -0
- package/src/session-search.ts +148 -0
- package/src/session-title-rename.test.ts +112 -0
- package/src/startup-service.ts +200 -0
- package/src/startup-time.e2e.test.ts +373 -0
- package/src/store.ts +122 -0
- package/src/system-message.test.ts +612 -0
- package/src/system-message.ts +723 -0
- package/src/task-runner.ts +421 -0
- package/src/task-schedule.test.ts +84 -0
- package/src/task-schedule.ts +311 -0
- package/src/test-utils.ts +435 -0
- package/src/thinking-utils.ts +61 -0
- package/src/thread-message-queue.e2e.test.ts +1219 -0
- package/src/tools.ts +430 -0
- package/src/undici.d.ts +12 -0
- package/src/undo-redo.e2e.test.ts +209 -0
- package/src/unnest-code-blocks.test.ts +713 -0
- package/src/unnest-code-blocks.ts +185 -0
- package/src/upgrade.ts +127 -0
- package/src/utils.ts +212 -0
- package/src/voice-attachment.ts +51 -0
- package/src/voice-handler.ts +908 -0
- package/src/voice-message.e2e.test.ts +1255 -0
- package/src/voice.test.ts +281 -0
- package/src/voice.ts +627 -0
- package/src/wait-session.ts +147 -0
- package/src/websockify.ts +101 -0
- package/src/worker-types.ts +64 -0
- package/src/worktree-lifecycle.e2e.test.ts +391 -0
- package/src/worktree-utils.ts +4 -0
- package/src/worktrees.test.ts +223 -0
- package/src/worktrees.ts +1294 -0
- package/src/xml.test.ts +38 -0
- package/src/xml.ts +121 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// /abort command - Abort the current OpenCode request in this thread.
|
|
2
|
+
import { ChannelType, MessageFlags, } from 'discord.js';
|
|
3
|
+
import { getThreadSession } from '../database.js';
|
|
4
|
+
import { initializeOpencodeForDirectory } from '../opencode.js';
|
|
5
|
+
import { resolveWorkingDirectory, SILENT_MESSAGE_FLAGS, } from '../discord-utils.js';
|
|
6
|
+
import { getRuntime } from '../session-handler/thread-session-runtime.js';
|
|
7
|
+
import { createLogger, LogPrefix } from '../logger.js';
|
|
8
|
+
const logger = createLogger(LogPrefix.ABORT);
|
|
9
|
+
export async function handleAbortCommand({ command, }) {
|
|
10
|
+
const channel = command.channel;
|
|
11
|
+
if (!channel) {
|
|
12
|
+
await command.reply({
|
|
13
|
+
content: 'This command can only be used in a channel',
|
|
14
|
+
flags: MessageFlags.Ephemeral | SILENT_MESSAGE_FLAGS,
|
|
15
|
+
});
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const isThread = [
|
|
19
|
+
ChannelType.PublicThread,
|
|
20
|
+
ChannelType.PrivateThread,
|
|
21
|
+
ChannelType.AnnouncementThread,
|
|
22
|
+
].includes(channel.type);
|
|
23
|
+
if (!isThread) {
|
|
24
|
+
await command.reply({
|
|
25
|
+
content: 'This command can only be used in a thread with an active session',
|
|
26
|
+
flags: MessageFlags.Ephemeral | SILENT_MESSAGE_FLAGS,
|
|
27
|
+
});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
await command.deferReply({ flags: SILENT_MESSAGE_FLAGS });
|
|
31
|
+
const resolved = await resolveWorkingDirectory({
|
|
32
|
+
channel: channel,
|
|
33
|
+
});
|
|
34
|
+
if (!resolved) {
|
|
35
|
+
await command.editReply('Could not determine project directory for this channel');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const { projectDirectory } = resolved;
|
|
39
|
+
const sessionId = await getThreadSession(channel.id);
|
|
40
|
+
if (!sessionId) {
|
|
41
|
+
await command.editReply('No active session in this thread');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// abortActiveRun delegates to session.abort(), run settlement stays event-driven.
|
|
45
|
+
const runtime = getRuntime(channel.id);
|
|
46
|
+
if (runtime) {
|
|
47
|
+
runtime.abortActiveRun('user-requested');
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// No runtime but session exists ā fall back to direct API abort
|
|
51
|
+
const getClient = await initializeOpencodeForDirectory(projectDirectory);
|
|
52
|
+
if (getClient instanceof Error) {
|
|
53
|
+
await command.editReply(`Failed to abort: ${getClient.message}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
await getClient().session.abort({ sessionID: sessionId });
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
logger.error('[ABORT] API abort failed:', error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
await command.editReply('Request **aborted**');
|
|
64
|
+
logger.log(`Session ${sessionId} aborted by user`);
|
|
65
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// Action button tool handler - Shows Discord buttons for quick model actions.
|
|
2
|
+
// Used by the kimaki_action_buttons tool to render up to 3 buttons and route
|
|
3
|
+
// button clicks back into the session as a new user message.
|
|
4
|
+
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, MessageFlags, } from 'discord.js';
|
|
5
|
+
import crypto from 'node:crypto';
|
|
6
|
+
import { getThreadSession } from '../database.js';
|
|
7
|
+
import { NOTIFY_MESSAGE_FLAGS, SILENT_MESSAGE_FLAGS, resolveWorkingDirectory, sendThreadMessage, } from '../discord-utils.js';
|
|
8
|
+
import { createLogger } from '../logger.js';
|
|
9
|
+
import { notifyError } from '../sentry.js';
|
|
10
|
+
import { getOrCreateRuntime, } from '../session-handler/thread-session-runtime.js';
|
|
11
|
+
const logger = createLogger('ACT_BTN');
|
|
12
|
+
const PENDING_TTL_MS = 24 * 60 * 60 * 1000;
|
|
13
|
+
export const pendingActionButtonContexts = new Map();
|
|
14
|
+
const pendingActionButtonRequests = new Map();
|
|
15
|
+
const pendingActionButtonRequestWaiters = new Map();
|
|
16
|
+
export function queueActionButtonsRequest(request) {
|
|
17
|
+
pendingActionButtonRequests.set(request.sessionId, request);
|
|
18
|
+
const waiter = pendingActionButtonRequestWaiters.get(request.sessionId);
|
|
19
|
+
if (!waiter) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
pendingActionButtonRequestWaiters.delete(request.sessionId);
|
|
23
|
+
waiter(request);
|
|
24
|
+
}
|
|
25
|
+
export async function waitForQueuedActionButtonsRequest({ sessionId, timeoutMs, }) {
|
|
26
|
+
const queued = pendingActionButtonRequests.get(sessionId);
|
|
27
|
+
if (queued) {
|
|
28
|
+
pendingActionButtonRequests.delete(sessionId);
|
|
29
|
+
return queued;
|
|
30
|
+
}
|
|
31
|
+
return await new Promise((resolve) => {
|
|
32
|
+
const timeout = setTimeout(() => {
|
|
33
|
+
const currentWaiter = pendingActionButtonRequestWaiters.get(sessionId);
|
|
34
|
+
if (!currentWaiter || currentWaiter !== onRequest) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
pendingActionButtonRequestWaiters.delete(sessionId);
|
|
38
|
+
resolve(undefined);
|
|
39
|
+
}, timeoutMs);
|
|
40
|
+
const onRequest = (request) => {
|
|
41
|
+
clearTimeout(timeout);
|
|
42
|
+
pendingActionButtonRequests.delete(sessionId);
|
|
43
|
+
resolve(request);
|
|
44
|
+
};
|
|
45
|
+
pendingActionButtonRequestWaiters.set(sessionId, onRequest);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function toButtonStyle(color) {
|
|
49
|
+
if (color === 'blue') {
|
|
50
|
+
return ButtonStyle.Primary;
|
|
51
|
+
}
|
|
52
|
+
if (color === 'green') {
|
|
53
|
+
return ButtonStyle.Success;
|
|
54
|
+
}
|
|
55
|
+
if (color === 'red') {
|
|
56
|
+
return ButtonStyle.Danger;
|
|
57
|
+
}
|
|
58
|
+
return ButtonStyle.Secondary;
|
|
59
|
+
}
|
|
60
|
+
function resolveContext(context) {
|
|
61
|
+
if (context.resolved) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
context.resolved = true;
|
|
65
|
+
clearTimeout(context.timer);
|
|
66
|
+
pendingActionButtonContexts.delete(context.contextHash);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
function updateButtonMessage({ context, status, }) {
|
|
70
|
+
if (!context.messageId) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
context.thread.messages
|
|
74
|
+
.fetch(context.messageId)
|
|
75
|
+
.then((message) => {
|
|
76
|
+
return message.edit({
|
|
77
|
+
content: `**Action Required**\n${status}`,
|
|
78
|
+
components: [],
|
|
79
|
+
});
|
|
80
|
+
})
|
|
81
|
+
.catch(() => { });
|
|
82
|
+
}
|
|
83
|
+
async function sendClickedActionToModel({ interaction, thread, prompt, }) {
|
|
84
|
+
const resolved = await resolveWorkingDirectory({ channel: thread });
|
|
85
|
+
if (!resolved) {
|
|
86
|
+
throw new Error('Could not resolve project directory for thread');
|
|
87
|
+
}
|
|
88
|
+
const username = interaction.user.globalName || interaction.user.username;
|
|
89
|
+
// Action button clicks use opencode queue mode.
|
|
90
|
+
const runtime = getOrCreateRuntime({
|
|
91
|
+
threadId: thread.id,
|
|
92
|
+
thread,
|
|
93
|
+
projectDirectory: resolved.projectDirectory,
|
|
94
|
+
sdkDirectory: resolved.workingDirectory,
|
|
95
|
+
channelId: thread.parentId || thread.id,
|
|
96
|
+
});
|
|
97
|
+
await runtime.enqueueIncoming({
|
|
98
|
+
prompt,
|
|
99
|
+
userId: interaction.user.id,
|
|
100
|
+
username,
|
|
101
|
+
mode: 'opencode',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
export async function showActionButtons({ thread, sessionId, directory, buttons, silent, }) {
|
|
105
|
+
const safeButtons = buttons
|
|
106
|
+
.slice(0, 3)
|
|
107
|
+
.map((button) => {
|
|
108
|
+
return {
|
|
109
|
+
label: button.label.trim().slice(0, 80),
|
|
110
|
+
color: button.color,
|
|
111
|
+
};
|
|
112
|
+
})
|
|
113
|
+
.filter((button) => {
|
|
114
|
+
return button.label.length > 0;
|
|
115
|
+
});
|
|
116
|
+
if (safeButtons.length === 0) {
|
|
117
|
+
throw new Error('No valid buttons to display');
|
|
118
|
+
}
|
|
119
|
+
const contextHash = crypto.randomBytes(8).toString('hex');
|
|
120
|
+
const timer = setTimeout(() => {
|
|
121
|
+
const current = pendingActionButtonContexts.get(contextHash);
|
|
122
|
+
if (!current || current.resolved) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
resolveContext(current);
|
|
126
|
+
updateButtonMessage({ context: current, status: '_Expired_' });
|
|
127
|
+
}, PENDING_TTL_MS);
|
|
128
|
+
const context = {
|
|
129
|
+
sessionId,
|
|
130
|
+
directory,
|
|
131
|
+
thread,
|
|
132
|
+
buttons: safeButtons,
|
|
133
|
+
contextHash,
|
|
134
|
+
resolved: false,
|
|
135
|
+
timer,
|
|
136
|
+
};
|
|
137
|
+
pendingActionButtonContexts.set(contextHash, context);
|
|
138
|
+
const row = new ActionRowBuilder().addComponents(...safeButtons.map((button, index) => {
|
|
139
|
+
return new ButtonBuilder()
|
|
140
|
+
.setCustomId(`action_button:${contextHash}:${index}`)
|
|
141
|
+
.setLabel(button.label)
|
|
142
|
+
.setStyle(toButtonStyle(button.color));
|
|
143
|
+
}));
|
|
144
|
+
try {
|
|
145
|
+
const message = await thread.send({
|
|
146
|
+
content: '**Action Required**',
|
|
147
|
+
components: [row],
|
|
148
|
+
flags: silent ? SILENT_MESSAGE_FLAGS : NOTIFY_MESSAGE_FLAGS,
|
|
149
|
+
});
|
|
150
|
+
context.messageId = message.id;
|
|
151
|
+
logger.log(`Showed ${safeButtons.length} action button(s) for session ${sessionId}`);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
clearTimeout(timer);
|
|
155
|
+
pendingActionButtonContexts.delete(contextHash);
|
|
156
|
+
throw new Error('Failed to send action buttons', { cause: error });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export async function handleActionButton(interaction) {
|
|
160
|
+
const customId = interaction.customId;
|
|
161
|
+
if (!customId.startsWith('action_button:')) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const [, contextHash, indexPart] = customId.split(':');
|
|
165
|
+
if (!contextHash || !indexPart) {
|
|
166
|
+
await interaction.reply({
|
|
167
|
+
content: 'Invalid action button.',
|
|
168
|
+
flags: MessageFlags.Ephemeral,
|
|
169
|
+
});
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const context = pendingActionButtonContexts.get(contextHash);
|
|
173
|
+
if (!context || context.resolved) {
|
|
174
|
+
await interaction.reply({
|
|
175
|
+
content: 'This action is no longer available.',
|
|
176
|
+
flags: MessageFlags.Ephemeral,
|
|
177
|
+
});
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const buttonIndex = Number.parseInt(indexPart, 10);
|
|
181
|
+
const button = context.buttons[buttonIndex];
|
|
182
|
+
if (!button) {
|
|
183
|
+
await interaction.reply({
|
|
184
|
+
content: 'This action is no longer available.',
|
|
185
|
+
flags: MessageFlags.Ephemeral,
|
|
186
|
+
});
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
await interaction.deferUpdate();
|
|
190
|
+
const claimed = resolveContext(context);
|
|
191
|
+
if (!claimed) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const thread = interaction.channel;
|
|
195
|
+
if (!thread?.isThread()) {
|
|
196
|
+
logger.warn('[ACTION] Button clicked outside thread channel');
|
|
197
|
+
await interaction.editReply({
|
|
198
|
+
content: '**Action Required**\n_This action is no longer available._',
|
|
199
|
+
components: [],
|
|
200
|
+
});
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const currentSessionId = await getThreadSession(thread.id);
|
|
204
|
+
if (!currentSessionId || currentSessionId !== context.sessionId) {
|
|
205
|
+
await interaction.editReply({
|
|
206
|
+
content: '**Action Required**\n_Expired due to session change._',
|
|
207
|
+
components: [],
|
|
208
|
+
});
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
await interaction.editReply({
|
|
212
|
+
content: `**Action Required**\n_Selected: ${button.label}_`,
|
|
213
|
+
components: [],
|
|
214
|
+
});
|
|
215
|
+
const prompt = `User clicked: ${button.label}`;
|
|
216
|
+
try {
|
|
217
|
+
await sendClickedActionToModel({
|
|
218
|
+
interaction,
|
|
219
|
+
thread,
|
|
220
|
+
prompt,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
logger.error('[ACTION] Failed to send click to model:', error);
|
|
225
|
+
void notifyError(error, 'Action button click send to model failed');
|
|
226
|
+
await sendThreadMessage(thread, `Failed to send action click: ${error instanceof Error ? error.message : String(error)}`, { flags: NOTIFY_MESSAGE_FLAGS });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Dismiss pending action buttons for a thread (e.g. user sent a new message).
|
|
231
|
+
* Removes buttons from the message and cleans up context.
|
|
232
|
+
*/
|
|
233
|
+
export function cancelPendingActionButtons(threadId) {
|
|
234
|
+
for (const [, ctx] of pendingActionButtonContexts) {
|
|
235
|
+
if (ctx.thread.id !== threadId) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (!resolveContext(ctx)) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
updateButtonMessage({ context: ctx, status: '_Buttons dismissed._' });
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// /add-project command - Create Discord channels for an existing OpenCode project.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { findChannelsByDirectory, getAllTextChannelDirectories, } from '../database.js';
|
|
5
|
+
import { initializeOpencodeForDirectory } from '../opencode.js';
|
|
6
|
+
import { createProjectChannels } from '../channel-management.js';
|
|
7
|
+
import { createLogger, LogPrefix } from '../logger.js';
|
|
8
|
+
import { abbreviatePath } from '../utils.js';
|
|
9
|
+
import * as errore from 'errore';
|
|
10
|
+
const logger = createLogger(LogPrefix.ADD_PROJECT);
|
|
11
|
+
export async function handleAddProjectCommand({ command, }) {
|
|
12
|
+
await command.deferReply();
|
|
13
|
+
const projectId = command.options.getString('project', true);
|
|
14
|
+
const guild = command.guild;
|
|
15
|
+
if (!guild) {
|
|
16
|
+
await command.editReply('This command can only be used in a guild');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const currentDir = process.cwd();
|
|
21
|
+
const getClient = await initializeOpencodeForDirectory(currentDir);
|
|
22
|
+
if (getClient instanceof Error) {
|
|
23
|
+
await command.editReply(getClient.message);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const projectsResponse = await getClient().project.list({});
|
|
27
|
+
if (!projectsResponse.data) {
|
|
28
|
+
await command.editReply('Failed to fetch projects');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const project = projectsResponse.data.find((p) => p.id === projectId);
|
|
32
|
+
if (!project) {
|
|
33
|
+
await command.editReply('Project not found');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const directory = project.worktree;
|
|
37
|
+
if (!fs.existsSync(directory)) {
|
|
38
|
+
await command.editReply(`Directory does not exist: ${directory}`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const existingChannels = await findChannelsByDirectory({
|
|
42
|
+
directory,
|
|
43
|
+
channelType: 'text',
|
|
44
|
+
});
|
|
45
|
+
if (existingChannels.length > 0) {
|
|
46
|
+
await command.editReply(`A channel already exists for this directory: <#${existingChannels[0].channel_id}>`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const { textChannelId, voiceChannelId, channelName } = await createProjectChannels({
|
|
50
|
+
guild,
|
|
51
|
+
projectDirectory: directory,
|
|
52
|
+
botName: command.client.user?.username,
|
|
53
|
+
});
|
|
54
|
+
const voiceInfo = voiceChannelId ? `\nš Voice: <#${voiceChannelId}>` : '';
|
|
55
|
+
await command.editReply(`ā
Created channels for project:\nš Text: <#${textChannelId}>${voiceInfo}\nš Directory: \`${directory}\``);
|
|
56
|
+
logger.log(`Created channels for project ${channelName} at ${directory}`);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
logger.error('[ADD-PROJECT] Error:', error);
|
|
60
|
+
await command.editReply(`Failed to create channels: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export async function handleAddProjectAutocomplete({ interaction, }) {
|
|
64
|
+
const focusedValue = interaction.options.getFocused();
|
|
65
|
+
try {
|
|
66
|
+
const currentDir = process.cwd();
|
|
67
|
+
const getClient = await initializeOpencodeForDirectory(currentDir);
|
|
68
|
+
if (getClient instanceof Error) {
|
|
69
|
+
await interaction.respond([]);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const projectsResponse = await getClient().project.list({});
|
|
73
|
+
if (!projectsResponse.data) {
|
|
74
|
+
await interaction.respond([]);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const existingDirs = await getAllTextChannelDirectories();
|
|
78
|
+
const existingDirSet = new Set(existingDirs);
|
|
79
|
+
const availableProjects = projectsResponse.data.filter((project) => {
|
|
80
|
+
if (existingDirSet.has(project.worktree)) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
if (path.basename(project.worktree).startsWith('opencode-test-')) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
});
|
|
88
|
+
const projects = availableProjects
|
|
89
|
+
.filter((project) => {
|
|
90
|
+
const baseName = path.basename(project.worktree);
|
|
91
|
+
const searchText = `${baseName} ${project.worktree}`.toLowerCase();
|
|
92
|
+
return searchText.includes(focusedValue.toLowerCase());
|
|
93
|
+
})
|
|
94
|
+
.sort((a, b) => {
|
|
95
|
+
const aTime = a.time.initialized || a.time.created;
|
|
96
|
+
const bTime = b.time.initialized || b.time.created;
|
|
97
|
+
return bTime - aTime;
|
|
98
|
+
})
|
|
99
|
+
.slice(0, 25)
|
|
100
|
+
.map((project) => {
|
|
101
|
+
const name = `${path.basename(project.worktree)} (${abbreviatePath(project.worktree)})`;
|
|
102
|
+
return {
|
|
103
|
+
name: name.length > 100 ? name.slice(0, 99) + 'ā¦' : name,
|
|
104
|
+
value: project.id,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
await interaction.respond(projects);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
logger.error('[AUTOCOMPLETE] Error fetching projects:', error);
|
|
111
|
+
await interaction.respond([]);
|
|
112
|
+
}
|
|
113
|
+
}
|