@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,334 @@
|
|
|
1
|
+
// Discord channel and category management.
|
|
2
|
+
// Creates and manages Kimaki project channels (text + voice pairs),
|
|
3
|
+
// extracts channel metadata from topic tags, and ensures category structure.
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
ChannelType,
|
|
7
|
+
type CategoryChannel,
|
|
8
|
+
type Guild,
|
|
9
|
+
type TextChannel,
|
|
10
|
+
} from 'discord.js'
|
|
11
|
+
import fs from 'node:fs'
|
|
12
|
+
import path from 'node:path'
|
|
13
|
+
import {
|
|
14
|
+
getChannelDirectory,
|
|
15
|
+
setChannelDirectory,
|
|
16
|
+
findChannelsByDirectory,
|
|
17
|
+
} from './database.js'
|
|
18
|
+
import { getProjectsDir } from './config.js'
|
|
19
|
+
import { execAsync } from './worktrees.js'
|
|
20
|
+
import { createLogger, LogPrefix } from './logger.js'
|
|
21
|
+
|
|
22
|
+
const logger = createLogger(LogPrefix.CHANNEL)
|
|
23
|
+
|
|
24
|
+
export async function ensureKimakiCategory(
|
|
25
|
+
guild: Guild,
|
|
26
|
+
botName?: string,
|
|
27
|
+
): Promise<CategoryChannel> {
|
|
28
|
+
// Skip appending bot name if it's already "kimaki" to avoid "Kimaki kimaki"
|
|
29
|
+
const isKimakiBot = botName?.toLowerCase() === 'kimaki'
|
|
30
|
+
const categoryName = botName && !isKimakiBot ? `Kimaki ${botName}` : 'Kimaki'
|
|
31
|
+
|
|
32
|
+
const existingCategory = guild.channels.cache.find(
|
|
33
|
+
(channel): channel is CategoryChannel => {
|
|
34
|
+
if (channel.type !== ChannelType.GuildCategory) {
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return channel.name.toLowerCase() === categoryName.toLowerCase()
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if (existingCategory) {
|
|
43
|
+
return existingCategory
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return guild.channels.create({
|
|
47
|
+
name: categoryName,
|
|
48
|
+
type: ChannelType.GuildCategory,
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function ensureKimakiAudioCategory(
|
|
53
|
+
guild: Guild,
|
|
54
|
+
botName?: string,
|
|
55
|
+
): Promise<CategoryChannel> {
|
|
56
|
+
// Skip appending bot name if it's already "kimaki" to avoid "Kimaki Audio kimaki"
|
|
57
|
+
const isKimakiBot = botName?.toLowerCase() === 'kimaki'
|
|
58
|
+
const categoryName =
|
|
59
|
+
botName && !isKimakiBot ? `Kimaki Audio ${botName}` : 'Kimaki Audio'
|
|
60
|
+
|
|
61
|
+
const existingCategory = guild.channels.cache.find(
|
|
62
|
+
(channel): channel is CategoryChannel => {
|
|
63
|
+
if (channel.type !== ChannelType.GuildCategory) {
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return channel.name.toLowerCase() === categoryName.toLowerCase()
|
|
68
|
+
},
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if (existingCategory) {
|
|
72
|
+
return existingCategory
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return guild.channels.create({
|
|
76
|
+
name: categoryName,
|
|
77
|
+
type: ChannelType.GuildCategory,
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function createProjectChannels({
|
|
82
|
+
guild,
|
|
83
|
+
projectDirectory,
|
|
84
|
+
botName,
|
|
85
|
+
enableVoiceChannels = false,
|
|
86
|
+
}: {
|
|
87
|
+
guild: Guild
|
|
88
|
+
projectDirectory: string
|
|
89
|
+
botName?: string
|
|
90
|
+
enableVoiceChannels?: boolean
|
|
91
|
+
}): Promise<{
|
|
92
|
+
textChannelId: string
|
|
93
|
+
voiceChannelId: string | null
|
|
94
|
+
channelName: string
|
|
95
|
+
}> {
|
|
96
|
+
const baseName = path.basename(projectDirectory)
|
|
97
|
+
const channelName = `${baseName}`
|
|
98
|
+
.toLowerCase()
|
|
99
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
100
|
+
.slice(0, 100)
|
|
101
|
+
|
|
102
|
+
const kimakiCategory = await ensureKimakiCategory(guild, botName)
|
|
103
|
+
|
|
104
|
+
const textChannel = await guild.channels.create({
|
|
105
|
+
name: channelName,
|
|
106
|
+
type: ChannelType.GuildText,
|
|
107
|
+
parent: kimakiCategory,
|
|
108
|
+
// Channel configuration is stored in SQLite, not in the topic
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
await setChannelDirectory({
|
|
112
|
+
channelId: textChannel.id,
|
|
113
|
+
directory: projectDirectory,
|
|
114
|
+
channelType: 'text',
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
let voiceChannelId: string | null = null
|
|
118
|
+
|
|
119
|
+
if (enableVoiceChannels) {
|
|
120
|
+
const kimakiAudioCategory = await ensureKimakiAudioCategory(guild, botName)
|
|
121
|
+
|
|
122
|
+
const voiceChannel = await guild.channels.create({
|
|
123
|
+
name: channelName,
|
|
124
|
+
type: ChannelType.GuildVoice,
|
|
125
|
+
parent: kimakiAudioCategory,
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
await setChannelDirectory({
|
|
129
|
+
channelId: voiceChannel.id,
|
|
130
|
+
directory: projectDirectory,
|
|
131
|
+
channelType: 'voice',
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
voiceChannelId = voiceChannel.id
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
textChannelId: textChannel.id,
|
|
139
|
+
voiceChannelId,
|
|
140
|
+
channelName,
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export type ChannelWithTags = {
|
|
145
|
+
id: string
|
|
146
|
+
name: string
|
|
147
|
+
description: string | null
|
|
148
|
+
kimakiDirectory?: string
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function getChannelsWithDescriptions(
|
|
152
|
+
guild: Guild,
|
|
153
|
+
): Promise<ChannelWithTags[]> {
|
|
154
|
+
const channels: ChannelWithTags[] = []
|
|
155
|
+
|
|
156
|
+
const textChannels = guild.channels.cache.filter((channel) =>
|
|
157
|
+
channel.isTextBased(),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
for (const channel of textChannels.values()) {
|
|
161
|
+
const textChannel = channel as TextChannel
|
|
162
|
+
const description = textChannel.topic || null
|
|
163
|
+
|
|
164
|
+
// Get channel config from database instead of parsing XML from topic
|
|
165
|
+
const channelConfig = await getChannelDirectory(textChannel.id)
|
|
166
|
+
|
|
167
|
+
channels.push({
|
|
168
|
+
id: textChannel.id,
|
|
169
|
+
name: textChannel.name,
|
|
170
|
+
description,
|
|
171
|
+
kimakiDirectory: channelConfig?.directory,
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return channels
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const DEFAULT_GITIGNORE = `node_modules/
|
|
179
|
+
dist/
|
|
180
|
+
.env
|
|
181
|
+
.env.*
|
|
182
|
+
!.env.example
|
|
183
|
+
.DS_Store
|
|
184
|
+
tmp/
|
|
185
|
+
*.log
|
|
186
|
+
__pycache__/
|
|
187
|
+
*.pyc
|
|
188
|
+
.venv/
|
|
189
|
+
*.egg-info/
|
|
190
|
+
`
|
|
191
|
+
|
|
192
|
+
const DEFAULT_CHANNEL_TOPIC =
|
|
193
|
+
'General channel for misc tasks with Kimaki. Not connected to a specific OpenCode project or repository.'
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Create (or find) the default "kimaki" channel for general-purpose tasks.
|
|
197
|
+
* Channel name is "kimaki-{botName}" for self-hosted bots, "kimaki" for gateway.
|
|
198
|
+
* Directory is ~/.kimaki/projects/kimaki, git-initialized with a .gitignore.
|
|
199
|
+
*
|
|
200
|
+
* Idempotency: checks the database for an existing channel mapped to the
|
|
201
|
+
* kimaki projects directory. Also scans guild channels by name+category
|
|
202
|
+
* as a fallback for channels created before DB mapping existed.
|
|
203
|
+
*/
|
|
204
|
+
export async function createDefaultKimakiChannel({
|
|
205
|
+
guild,
|
|
206
|
+
botName,
|
|
207
|
+
appId,
|
|
208
|
+
isGatewayMode,
|
|
209
|
+
}: {
|
|
210
|
+
guild: Guild
|
|
211
|
+
botName?: string
|
|
212
|
+
appId: string
|
|
213
|
+
isGatewayMode: boolean
|
|
214
|
+
}): Promise<{
|
|
215
|
+
textChannel: TextChannel
|
|
216
|
+
textChannelId: string
|
|
217
|
+
channelName: string
|
|
218
|
+
projectDirectory: string
|
|
219
|
+
} | null> {
|
|
220
|
+
const projectDirectory = path.join(getProjectsDir(), 'kimaki')
|
|
221
|
+
|
|
222
|
+
// Ensure the default kimaki project directory exists before any DB mapping
|
|
223
|
+
// restoration or git setup. Custom data dirs may not have <dataDir>/projects
|
|
224
|
+
// created yet, and later writes assume the full path is present.
|
|
225
|
+
if (!fs.existsSync(projectDirectory)) {
|
|
226
|
+
fs.mkdirSync(projectDirectory, { recursive: true })
|
|
227
|
+
logger.log(`Created default kimaki directory: ${projectDirectory}`)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Hydrate guild channels from API so the cache scan is complete
|
|
231
|
+
try {
|
|
232
|
+
await guild.channels.fetch()
|
|
233
|
+
} catch (error) {
|
|
234
|
+
logger.warn(
|
|
235
|
+
`Could not fetch guild channels for ${guild.name}: ${error instanceof Error ? error.stack : String(error)}`,
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// 1. Check database for existing channel mapped to this directory.
|
|
240
|
+
// Check ALL mappings (not just the first) since the same directory could
|
|
241
|
+
// have stale rows from deleted channels or other guilds.
|
|
242
|
+
const existingMappings = await findChannelsByDirectory({
|
|
243
|
+
directory: projectDirectory,
|
|
244
|
+
channelType: 'text',
|
|
245
|
+
})
|
|
246
|
+
const mappedChannelInGuild = existingMappings
|
|
247
|
+
.map((row) => guild.channels.cache.get(row.channel_id))
|
|
248
|
+
.find((ch): ch is TextChannel => ch?.type === ChannelType.GuildText)
|
|
249
|
+
if (mappedChannelInGuild) {
|
|
250
|
+
logger.log(`Default kimaki channel already exists: ${mappedChannelInGuild.id}`)
|
|
251
|
+
return null
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// 2. Fallback: detect existing channel by name+category
|
|
255
|
+
const kimakiCategory = await ensureKimakiCategory(guild, botName)
|
|
256
|
+
const existingByName = guild.channels.cache.find((ch): ch is TextChannel => {
|
|
257
|
+
if (ch.type !== ChannelType.GuildText) {
|
|
258
|
+
return false
|
|
259
|
+
}
|
|
260
|
+
if (ch.parentId !== kimakiCategory.id) {
|
|
261
|
+
return false
|
|
262
|
+
}
|
|
263
|
+
return ch.name === 'kimaki' || ch.name.startsWith('kimaki-')
|
|
264
|
+
})
|
|
265
|
+
if (existingByName) {
|
|
266
|
+
logger.log(
|
|
267
|
+
`Found existing default kimaki channel by name: ${existingByName.id}, restoring DB mapping`,
|
|
268
|
+
)
|
|
269
|
+
await setChannelDirectory({
|
|
270
|
+
channelId: existingByName.id,
|
|
271
|
+
directory: projectDirectory,
|
|
272
|
+
channelType: 'text',
|
|
273
|
+
skipIfExists: true,
|
|
274
|
+
})
|
|
275
|
+
return null
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Git init — gracefully skip if git is not installed
|
|
279
|
+
const gitDir = path.join(projectDirectory, '.git')
|
|
280
|
+
if (!fs.existsSync(gitDir)) {
|
|
281
|
+
try {
|
|
282
|
+
await execAsync('git init', { cwd: projectDirectory, timeout: 10_000 })
|
|
283
|
+
logger.log(`Initialized git in: ${projectDirectory}`)
|
|
284
|
+
} catch (error) {
|
|
285
|
+
logger.warn(
|
|
286
|
+
`Could not initialize git in ${projectDirectory}: ${error instanceof Error ? error.stack : String(error)}`,
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Write .gitignore if it doesn't exist
|
|
292
|
+
const gitignorePath = path.join(projectDirectory, '.gitignore')
|
|
293
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
294
|
+
fs.writeFileSync(gitignorePath, DEFAULT_GITIGNORE)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Channel name: "kimaki-{botName}" for self-hosted, "kimaki" for gateway
|
|
298
|
+
const channelName = (() => {
|
|
299
|
+
if (isGatewayMode || !botName) {
|
|
300
|
+
return 'kimaki'
|
|
301
|
+
}
|
|
302
|
+
const sanitized = botName
|
|
303
|
+
.toLowerCase()
|
|
304
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
305
|
+
.replace(/-+/g, '-')
|
|
306
|
+
.replace(/^-|-$/g, '')
|
|
307
|
+
if (!sanitized || sanitized === 'kimaki') {
|
|
308
|
+
return 'kimaki'
|
|
309
|
+
}
|
|
310
|
+
return `kimaki-${sanitized}`.slice(0, 100)
|
|
311
|
+
})()
|
|
312
|
+
|
|
313
|
+
const textChannel = await guild.channels.create({
|
|
314
|
+
name: channelName,
|
|
315
|
+
type: ChannelType.GuildText,
|
|
316
|
+
parent: kimakiCategory,
|
|
317
|
+
topic: DEFAULT_CHANNEL_TOPIC,
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
await setChannelDirectory({
|
|
321
|
+
channelId: textChannel.id,
|
|
322
|
+
directory: projectDirectory,
|
|
323
|
+
channelType: 'text',
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
logger.log(`Created default kimaki channel: #${channelName} (${textChannel.id})`)
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
textChannel,
|
|
330
|
+
textChannelId: textChannel.id,
|
|
331
|
+
channelName,
|
|
332
|
+
projectDirectory,
|
|
333
|
+
}
|
|
334
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
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
|
+
|
|
5
|
+
function createCliForIdParsing() {
|
|
6
|
+
const cli = goke('kimaki')
|
|
7
|
+
|
|
8
|
+
cli
|
|
9
|
+
.command('send', 'Send a message')
|
|
10
|
+
.option('-c, --channel <channelId>', 'Discord channel ID')
|
|
11
|
+
.option('--thread <threadId>', 'Thread ID')
|
|
12
|
+
.option('--session <sessionId>', 'Session ID')
|
|
13
|
+
.option('--send-at <schedule>', 'Schedule')
|
|
14
|
+
|
|
15
|
+
cli.command('session archive <threadId>', 'Archive a thread')
|
|
16
|
+
cli
|
|
17
|
+
.command('session search <query>', 'Search sessions')
|
|
18
|
+
.option('--channel <channelId>', 'Discord channel ID')
|
|
19
|
+
.option('--project <path>', 'Project path')
|
|
20
|
+
cli
|
|
21
|
+
.command('session export-events-jsonl', 'Export in-memory events to JSONL')
|
|
22
|
+
.option('--session <sessionId>', 'Session ID')
|
|
23
|
+
.option('--out <file>', 'Output path')
|
|
24
|
+
|
|
25
|
+
cli
|
|
26
|
+
.command('add-project', 'Add a project')
|
|
27
|
+
.option('-g, --guild <guildId>', 'Discord guild/server ID')
|
|
28
|
+
|
|
29
|
+
cli.command('task delete <id>', 'Delete task')
|
|
30
|
+
cli.command('anthropic-accounts list', 'List stored Anthropic accounts').hidden()
|
|
31
|
+
cli.command('anthropic-accounts remove <index>', 'Remove stored Anthropic account').hidden()
|
|
32
|
+
|
|
33
|
+
return cli
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe('goke CLI ID parsing', () => {
|
|
37
|
+
test('keeps large Discord IDs as strings', () => {
|
|
38
|
+
const cli = createCliForIdParsing()
|
|
39
|
+
const channelId = '1234567890123456789'
|
|
40
|
+
const threadId = '9876543210987654321'
|
|
41
|
+
const sessionId = '1111222233334444555'
|
|
42
|
+
|
|
43
|
+
const channelResult = cli.parse(
|
|
44
|
+
['node', 'kimaki', 'send', '--channel', channelId],
|
|
45
|
+
{
|
|
46
|
+
run: false,
|
|
47
|
+
},
|
|
48
|
+
)
|
|
49
|
+
expect(channelResult.options.channel).toBe(channelId)
|
|
50
|
+
expect(typeof channelResult.options.channel).toBe('string')
|
|
51
|
+
|
|
52
|
+
const threadResult = cli.parse(
|
|
53
|
+
['node', 'kimaki', 'send', '--thread', threadId],
|
|
54
|
+
{ run: false },
|
|
55
|
+
)
|
|
56
|
+
expect(threadResult.options.thread).toBe(threadId)
|
|
57
|
+
expect(typeof threadResult.options.thread).toBe('string')
|
|
58
|
+
|
|
59
|
+
const sessionResult = cli.parse(
|
|
60
|
+
['node', 'kimaki', 'send', '--session', sessionId],
|
|
61
|
+
{
|
|
62
|
+
run: false,
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
expect(sessionResult.options.session).toBe(sessionId)
|
|
66
|
+
expect(typeof sessionResult.options.session).toBe('string')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('preserves leading zeros in Discord IDs', () => {
|
|
70
|
+
const cli = createCliForIdParsing()
|
|
71
|
+
const guildId = '001230045600789'
|
|
72
|
+
|
|
73
|
+
const result = cli.parse(
|
|
74
|
+
['node', 'kimaki', 'add-project', '--guild', guildId],
|
|
75
|
+
{ run: false },
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
expect(result.options.guild).toBe(guildId)
|
|
79
|
+
expect(typeof result.options.guild).toBe('string')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('keeps session archive thread ID as string', () => {
|
|
83
|
+
const cli = createCliForIdParsing()
|
|
84
|
+
const threadId = '0098765432109876543'
|
|
85
|
+
|
|
86
|
+
const result = cli.parse(
|
|
87
|
+
['node', 'kimaki', 'session', 'archive', threadId],
|
|
88
|
+
{
|
|
89
|
+
run: false,
|
|
90
|
+
},
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
expect(result.args[0]).toBe(threadId)
|
|
94
|
+
expect(typeof result.args[0]).toBe('string')
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test('keeps session search regex and channel ID as strings', () => {
|
|
98
|
+
const cli = createCliForIdParsing()
|
|
99
|
+
const channelId = '0012345678901234567'
|
|
100
|
+
const query = '/error\\s+42/i'
|
|
101
|
+
|
|
102
|
+
const result = cli.parse(
|
|
103
|
+
['node', 'kimaki', 'session', 'search', query, '--channel', channelId],
|
|
104
|
+
{
|
|
105
|
+
run: false,
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
expect(result.args[0]).toBe(query)
|
|
110
|
+
expect(typeof result.args[0]).toBe('string')
|
|
111
|
+
expect(result.options.channel).toBe(channelId)
|
|
112
|
+
expect(typeof result.options.channel).toBe('string')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test('keeps session export options as strings', () => {
|
|
116
|
+
const cli = createCliForIdParsing()
|
|
117
|
+
const sessionId = '001111222233334444'
|
|
118
|
+
const outPath = './tmp/session-events.jsonl'
|
|
119
|
+
|
|
120
|
+
const result = cli.parse(
|
|
121
|
+
[
|
|
122
|
+
'node',
|
|
123
|
+
'kimaki',
|
|
124
|
+
'session',
|
|
125
|
+
'export-events-jsonl',
|
|
126
|
+
'--session',
|
|
127
|
+
sessionId,
|
|
128
|
+
'--out',
|
|
129
|
+
outPath,
|
|
130
|
+
],
|
|
131
|
+
{
|
|
132
|
+
run: false,
|
|
133
|
+
},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
expect(result.options.session).toBe(sessionId)
|
|
137
|
+
expect(typeof result.options.session).toBe('string')
|
|
138
|
+
expect(result.options.out).toBe(outPath)
|
|
139
|
+
expect(typeof result.options.out).toBe('string')
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
test('keeps --send-at cron string intact', () => {
|
|
143
|
+
const cli = createCliForIdParsing()
|
|
144
|
+
const cron = '0 9 * * 1'
|
|
145
|
+
|
|
146
|
+
const result = cli.parse(['node', 'kimaki', 'send', '--send-at', cron], {
|
|
147
|
+
run: false,
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
expect(result.options.sendAt).toBe(cron)
|
|
151
|
+
expect(typeof result.options.sendAt).toBe('string')
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('keeps task delete ID as string before validation', () => {
|
|
155
|
+
const cli = createCliForIdParsing()
|
|
156
|
+
const taskId = '0012345'
|
|
157
|
+
|
|
158
|
+
const result = cli.parse(['node', 'kimaki', 'task', 'delete', taskId], {
|
|
159
|
+
run: false,
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
expect(result.args[0]).toBe(taskId)
|
|
163
|
+
expect(typeof result.args[0]).toBe('string')
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
test('hidden anthropic account commands still parse', () => {
|
|
167
|
+
const cli = createCliForIdParsing()
|
|
168
|
+
|
|
169
|
+
const result = cli.parse(
|
|
170
|
+
['node', 'kimaki', 'anthropic-accounts', 'remove', '2'],
|
|
171
|
+
{ run: false },
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
expect(result.args[0]).toBe('2')
|
|
175
|
+
expect(typeof result.args[0]).toBe('string')
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
test('hidden anthropic account commands are excluded from help output', () => {
|
|
179
|
+
const stdout = {
|
|
180
|
+
text: '',
|
|
181
|
+
write(data: string | Uint8Array) {
|
|
182
|
+
this.text += String(data)
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const cli = goke('kimaki', { stdout: stdout as never })
|
|
187
|
+
cli.command('send', 'Send a message')
|
|
188
|
+
cli.command('anthropic-accounts list', 'List stored Anthropic accounts').hidden()
|
|
189
|
+
cli.help()
|
|
190
|
+
cli.parse(['node', 'kimaki', '--help'], { run: false })
|
|
191
|
+
|
|
192
|
+
expect(stdout.text).toContain('send')
|
|
193
|
+
expect(stdout.text).not.toContain('anthropic-accounts')
|
|
194
|
+
})
|
|
195
|
+
})
|