@otto-assistant/otto 0.1.2 → 0.7.16
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-account-identity.js +62 -0
- package/dist/anthropic-account-identity.test.js +38 -0
- package/dist/anthropic-auth-plugin.js +917 -0
- package/dist/anthropic-auth-state.js +303 -0
- package/dist/anthropic-auth-state.test.js +150 -0
- package/dist/bin.js +152 -0
- package/dist/btw-prefix-detection.js +17 -0
- package/dist/btw-prefix-detection.test.js +63 -0
- package/dist/channel-management.js +259 -0
- package/dist/cli-parsing.test.js +142 -0
- package/dist/cli-send-thread.e2e.test.js +353 -0
- package/dist/cli-telegram-options.test.js +99 -0
- package/dist/cli.js +4210 -568
- package/dist/commands/abort.js +65 -0
- package/dist/commands/action-buttons.js +245 -0
- package/dist/commands/add-dir.js +124 -0
- package/dist/commands/add-dir.test.js +126 -0
- package/dist/commands/add-project.js +113 -0
- package/dist/commands/agent.js +355 -0
- package/dist/commands/ask-question.js +320 -0
- package/dist/commands/ask-question.test.js +92 -0
- package/dist/commands/btw.js +121 -0
- package/dist/commands/cli-commands-group-a.test.js +728 -0
- package/dist/commands/cli-commands-group-b.test.js +695 -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/discord-commands-group-a.test.js +655 -0
- package/dist/commands/discord-commands-group-b.test.js +595 -0
- package/dist/commands/discord-commands-group-c.test.js +739 -0
- package/dist/commands/file-upload.js +275 -0
- package/dist/commands/fork-subagent.js +177 -0
- package/dist/commands/fork.js +262 -0
- package/dist/commands/gemini-apikey.js +70 -0
- package/dist/commands/login.js +893 -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 +162 -0
- package/dist/commands/model-variant.js +369 -0
- package/dist/commands/model.js +798 -0
- package/dist/commands/new-worktree.js +465 -0
- package/dist/commands/paginated-select.js +57 -0
- package/dist/commands/permissions.js +274 -0
- package/dist/commands/queue.js +223 -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/thread-deletion-sync.js +50 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/undo-redo.js +305 -0
- package/dist/commands/unset-model.js +139 -0
- package/dist/commands/upgrade.js +48 -0
- package/dist/commands/user-command.js +155 -0
- package/dist/commands/verbosity.js +125 -0
- package/dist/commands/vscode.js +269 -0
- package/dist/commands/worktree-settings.js +43 -0
- package/dist/commands/worktrees.js +468 -0
- package/dist/condense-memory.js +33 -0
- package/dist/config.js +100 -255
- package/dist/context-awareness-plugin.js +340 -0
- package/dist/context-awareness-plugin.test.js +126 -0
- package/dist/critique-utils.js +95 -0
- package/dist/database.js +1355 -0
- package/dist/db.js +260 -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 +1124 -0
- package/dist/discord-command-registration.js +567 -0
- package/dist/discord-urls.js +82 -0
- package/dist/discord-utils.js +616 -0
- package/dist/discord-utils.test.js +134 -0
- package/dist/errors.js +179 -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 +491 -0
- package/dist/format-tables.test.js +478 -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 +485 -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 +58 -0
- package/dist/generated/internal/class.js +49 -0
- package/dist/generated/internal/prismaNamespace.js +254 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +224 -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 +251 -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 +420 -0
- package/dist/ipc-polling.js +327 -0
- package/dist/ipc-tools-plugin.js +193 -0
- package/dist/ipc-utils.js +18 -0
- package/dist/limit-heading-depth.js +25 -0
- package/dist/limit-heading-depth.test.js +105 -0
- package/dist/logger.js +171 -0
- package/dist/markdown.js +342 -0
- package/dist/markdown.test.js +264 -0
- package/dist/memory-overview-plugin.js +128 -0
- package/dist/message-finish-field.e2e.test.js +168 -0
- package/dist/message-formatting.js +415 -0
- package/dist/message-formatting.test.js +115 -0
- package/dist/message-preprocessing.js +359 -0
- package/dist/onboarding-tutorial.js +163 -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 +131 -0
- package/dist/opencode-command.test.js +48 -0
- package/dist/opencode-interrupt-plugin.js +388 -0
- package/dist/opencode-interrupt-plugin.test.js +463 -0
- package/dist/opencode.js +1124 -0
- package/dist/otto/branding.js +22 -0
- package/dist/otto/index.js +21 -0
- package/dist/otto-digital-twin.e2e.test.js +161 -0
- package/dist/otto-opencode-plugin-loading.e2e.test.js +94 -0
- package/dist/otto-opencode-plugin.js +21 -0
- package/dist/otto-opencode-plugin.test.js +98 -0
- package/dist/parse-permission-rules.test.js +117 -0
- package/dist/patch-text-parser.js +97 -0
- package/dist/plugin-logger.js +68 -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 +790 -0
- package/dist/queue-advanced-footer.e2e.test.js +481 -0
- package/dist/queue-advanced-model-switch.e2e.test.js +299 -0
- package/dist/queue-advanced-permissions-typing.e2e.test.js +179 -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 +256 -0
- package/dist/runtime-idle-sweeper.js +52 -0
- package/dist/runtime-lifecycle.e2e.test.js +514 -0
- package/dist/sentry.js +23 -0
- package/dist/session-handler/agent-utils.js +67 -0
- package/dist/session-handler/event-stream-state.js +475 -0
- package/dist/session-handler/event-stream-state.test.js +632 -0
- package/dist/session-handler/model-utils.js +147 -0
- package/dist/session-handler/opencode-session-event-log.js +94 -0
- package/dist/session-handler/thread-runtime-state.js +131 -0
- package/dist/session-handler/thread-session-runtime.js +3390 -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 +92 -0
- package/dist/skill-filter.js +31 -0
- package/dist/skill-filter.test.js +65 -0
- package/dist/startup-service.js +153 -0
- package/dist/startup-time.e2e.test.js +296 -0
- package/dist/store.js +19 -0
- package/dist/subagent-rate-limit-plugin.js +175 -0
- package/dist/system-message.js +702 -0
- package/dist/system-message.test.js +697 -0
- package/dist/task-runner.js +530 -0
- package/dist/task-schedule.js +213 -0
- package/dist/task-schedule.test.js +71 -0
- package/dist/test-utils.js +313 -0
- package/dist/thinking-utils.js +35 -0
- package/dist/thread-message-queue.e2e.test.js +1111 -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 +156 -0
- package/dist/utils.js +172 -0
- package/dist/utils.test.js +130 -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 +456 -0
- package/dist/voice.test.js +235 -0
- package/dist/wait-session.js +171 -0
- package/dist/websockify.js +69 -0
- package/dist/worker-types.js +4 -0
- package/dist/worktree-lifecycle.e2e.test.js +311 -0
- package/dist/worktree-utils.js +3 -0
- package/dist/worktrees.js +991 -0
- package/dist/worktrees.test.js +415 -0
- package/dist/xml.js +92 -0
- package/dist/xml.test.js +32 -0
- package/package.json +90 -38
- package/schema.prisma +303 -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/goke/SKILL.md +38 -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/manual-kimaki-upstream-adapt/SKILL.md +114 -0
- package/skills/new-skill/SKILL.md +237 -0
- package/skills/npm-package/SKILL.md +617 -0
- package/skills/opensrc/SKILL.md +78 -0
- package/skills/otto-publish/SKILL.md +61 -0
- package/skills/playwriter/SKILL.md +35 -0
- package/skills/profano/SKILL.md +16 -0
- package/skills/proxyman/SKILL.md +215 -0
- package/skills/security-review/SKILL.md +208 -0
- package/skills/sigillo/SKILL.md +101 -0
- package/skills/simplify/SKILL.md +58 -0
- package/skills/spiceflow/SKILL.md +28 -0
- package/skills/termcast/SKILL.md +945 -0
- package/skills/tuistory/SKILL.md +98 -0
- package/skills/usecomputer/SKILL.md +264 -0
- package/skills/x-articles/SKILL.md +554 -0
- package/skills/zele/SKILL.md +49 -0
- package/skills/zustand-centralized-state/SKILL.md +1004 -0
- package/src/agent-model.e2e.test.ts +979 -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-account-identity.test.ts +52 -0
- package/src/anthropic-account-identity.ts +77 -0
- package/src/anthropic-auth-plugin.ts +1139 -0
- package/src/anthropic-auth-state.test.ts +187 -0
- package/src/anthropic-auth-state.ts +386 -0
- package/src/bin.ts +182 -0
- package/src/btw-prefix-detection.test.ts +73 -0
- package/src/btw-prefix-detection.ts +23 -0
- package/src/channel-management.ts +376 -0
- package/src/cli-parsing.test.ts +197 -0
- package/src/cli-send-thread.e2e.test.ts +463 -0
- package/src/cli-telegram-options.test.ts +114 -0
- package/src/cli.ts +5718 -580
- package/src/commands/abort.ts +89 -0
- package/src/commands/action-buttons.ts +364 -0
- package/src/commands/add-dir.test.ts +154 -0
- package/src/commands/add-dir.ts +175 -0
- package/src/commands/add-project.ts +149 -0
- package/src/commands/agent.ts +496 -0
- package/src/commands/ask-question.test.ts +111 -0
- package/src/commands/ask-question.ts +455 -0
- package/src/commands/btw.ts +184 -0
- package/src/commands/cli-commands-group-a.test.ts +837 -0
- package/src/commands/cli-commands-group-b.test.ts +800 -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/discord-commands-group-a.test.ts +789 -0
- package/src/commands/discord-commands-group-b.test.ts +648 -0
- package/src/commands/discord-commands-group-c.test.ts +882 -0
- package/src/commands/file-upload.ts +389 -0
- package/src/commands/fork-subagent.ts +263 -0
- package/src/commands/fork.ts +386 -0
- package/src/commands/gemini-apikey.ts +104 -0
- package/src/commands/login.ts +1181 -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 +226 -0
- package/src/commands/model-variant.ts +488 -0
- package/src/commands/model.ts +1082 -0
- package/src/commands/new-worktree.ts +645 -0
- package/src/commands/paginated-select.ts +81 -0
- package/src/commands/permissions.ts +397 -0
- package/src/commands/queue.ts +293 -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/thread-deletion-sync.ts +80 -0
- package/src/commands/types.ts +25 -0
- package/src/commands/undo-redo.ts +386 -0
- package/src/commands/unset-model.ts +174 -0
- package/src/commands/upgrade.ts +59 -0
- package/src/commands/user-command.ts +198 -0
- package/src/commands/verbosity.ts +173 -0
- package/src/commands/vscode.ts +342 -0
- package/src/commands/worktree-settings.ts +70 -0
- package/src/commands/worktrees.ts +645 -0
- package/src/condense-memory.ts +36 -0
- package/src/config.ts +103 -339
- package/src/context-awareness-plugin.test.ts +144 -0
- package/src/context-awareness-plugin.ts +469 -0
- package/src/critique-utils.ts +139 -0
- package/src/database.ts +1949 -0
- package/src/db.test.ts +162 -0
- package/src/db.ts +295 -0
- package/src/debounce-timeout.ts +43 -0
- package/src/debounced-process-flush.ts +104 -0
- package/src/discord-bot.ts +1507 -0
- package/src/discord-command-registration.ts +752 -0
- package/src/discord-urls.ts +89 -0
- package/src/discord-utils.test.ts +153 -0
- package/src/discord-utils.ts +846 -0
- package/src/errors.ts +232 -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 +515 -0
- package/src/format-tables.ts +718 -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 +644 -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 +770 -0
- package/src/generated/enums.ts +98 -0
- package/src/generated/internal/class.ts +384 -0
- package/src/generated/internal/prismaNamespace.ts +2394 -0
- package/src/generated/internal/prismaNamespaceBrowser.ts +327 -0
- package/src/generated/models/bot_api_keys.ts +1288 -0
- package/src/generated/models/bot_tokens.ts +1700 -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 +299 -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 +610 -0
- package/src/ipc-polling.ts +427 -0
- package/src/ipc-tools-plugin.ts +236 -0
- package/src/ipc-utils.ts +29 -0
- package/src/limit-heading-depth.test.ts +116 -0
- package/src/limit-heading-depth.ts +26 -0
- package/src/logger.ts +215 -0
- package/src/markdown.test.ts +315 -0
- package/src/markdown.ts +410 -0
- package/src/memory-overview-plugin.ts +163 -0
- package/src/message-finish-field.e2e.test.ts +195 -0
- package/src/message-formatting.test.ts +126 -0
- package/src/message-formatting.ts +535 -0
- package/src/message-preprocessing.ts +488 -0
- package/src/onboarding-tutorial.ts +167 -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 +191 -0
- package/src/opencode-interrupt-plugin.test.ts +682 -0
- package/src/opencode-interrupt-plugin.ts +507 -0
- package/src/opencode.ts +1462 -0
- package/src/otto/branding.ts +23 -0
- package/src/otto/index.ts +22 -0
- package/src/otto-digital-twin.e2e.test.ts +199 -0
- package/src/otto-opencode-plugin-loading.e2e.test.ts +117 -0
- package/src/otto-opencode-plugin.test.ts +108 -0
- package/src/otto-opencode-plugin.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 +84 -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 +877 -0
- package/src/queue-advanced-footer.e2e.test.ts +591 -0
- package/src/queue-advanced-model-switch.e2e.test.ts +383 -0
- package/src/queue-advanced-permissions-typing.e2e.test.ts +246 -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 +327 -0
- package/src/runtime-idle-sweeper.ts +76 -0
- package/src/runtime-lifecycle.e2e.test.ts +651 -0
- package/src/schema.sql +174 -0
- package/src/sentry.ts +26 -0
- package/src/session-handler/agent-utils.ts +99 -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 +717 -0
- package/src/session-handler/event-stream-state.ts +706 -0
- package/src/session-handler/model-utils.ts +217 -0
- package/src/session-handler/opencode-session-event-log.ts +130 -0
- package/src/session-handler/thread-runtime-state.ts +247 -0
- package/src/session-handler/thread-session-runtime.ts +4440 -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 +130 -0
- package/src/skill-filter.test.ts +83 -0
- package/src/skill-filter.ts +42 -0
- package/src/startup-service.ts +200 -0
- package/src/startup-time.e2e.test.ts +373 -0
- package/src/store.ts +139 -0
- package/src/subagent-rate-limit-plugin.ts +218 -0
- package/src/system-message.test.ts +710 -0
- package/src/system-message.ts +814 -0
- package/src/task-runner.ts +725 -0
- package/src/task-schedule.test.ts +84 -0
- package/src/task-schedule.ts +317 -0
- package/src/test-utils.ts +451 -0
- package/src/thinking-utils.ts +61 -0
- package/src/thread-message-queue.e2e.test.ts +1350 -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 +185 -0
- package/src/utils.test.ts +155 -0
- package/src/utils.ts +265 -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 +638 -0
- package/src/wait-session.ts +273 -0
- package/src/websockify.ts +101 -0
- package/src/worker-types.ts +64 -0
- package/src/worktree-lifecycle.e2e.test.ts +396 -0
- package/src/worktree-utils.ts +4 -0
- package/src/worktrees.test.ts +489 -0
- package/src/worktrees.ts +1370 -0
- package/src/xml.test.ts +38 -0
- package/src/xml.ts +121 -0
- package/README.md +0 -142
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/config.d.ts +0 -39
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -202
- package/dist/config.test.js.map +0 -1
- package/dist/detect.d.ts +0 -9
- package/dist/detect.d.ts.map +0 -1
- package/dist/detect.js +0 -40
- package/dist/detect.js.map +0 -1
- package/dist/detect.test.d.ts +0 -2
- package/dist/detect.test.d.ts.map +0 -1
- package/dist/detect.test.js +0 -26
- package/dist/detect.test.js.map +0 -1
- package/dist/docker.d.ts +0 -7
- package/dist/docker.d.ts.map +0 -1
- package/dist/docker.js +0 -17
- package/dist/docker.js.map +0 -1
- package/dist/docker.test.d.ts +0 -2
- package/dist/docker.test.d.ts.map +0 -1
- package/dist/docker.test.js +0 -12
- package/dist/docker.test.js.map +0 -1
- package/dist/health.d.ts +0 -31
- package/dist/health.d.ts.map +0 -1
- package/dist/health.js +0 -117
- package/dist/health.js.map +0 -1
- package/dist/health.test.d.ts +0 -2
- package/dist/health.test.d.ts.map +0 -1
- package/dist/health.test.js +0 -52
- package/dist/health.test.js.map +0 -1
- package/dist/index.d.ts +0 -20
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -15
- package/dist/index.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -8
- package/dist/index.test.js.map +0 -1
- package/dist/installer.d.ts +0 -10
- package/dist/installer.d.ts.map +0 -1
- package/dist/installer.js +0 -50
- package/dist/installer.js.map +0 -1
- package/dist/installer.test.d.ts +0 -2
- package/dist/installer.test.d.ts.map +0 -1
- package/dist/installer.test.js +0 -43
- package/dist/installer.test.js.map +0 -1
- package/dist/lifecycle.d.ts +0 -10
- package/dist/lifecycle.d.ts.map +0 -1
- package/dist/lifecycle.js +0 -45
- package/dist/lifecycle.js.map +0 -1
- package/dist/lifecycle.test.d.ts +0 -2
- package/dist/lifecycle.test.d.ts.map +0 -1
- package/dist/lifecycle.test.js +0 -20
- package/dist/lifecycle.test.js.map +0 -1
- package/dist/manifest.d.ts +0 -18
- package/dist/manifest.d.ts.map +0 -1
- package/dist/manifest.js +0 -30
- package/dist/manifest.js.map +0 -1
- package/dist/skills-baseline.d.ts +0 -7
- package/dist/skills-baseline.d.ts.map +0 -1
- package/dist/skills-baseline.js +0 -9
- package/dist/skills-baseline.js.map +0 -1
- package/dist/skills.d.ts +0 -110
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js +0 -429
- package/dist/skills.js.map +0 -1
- package/dist/skills.test.d.ts +0 -2
- package/dist/skills.test.d.ts.map +0 -1
- package/dist/skills.test.js +0 -416
- package/dist/skills.test.js.map +0 -1
- package/dist/sync.d.ts +0 -10
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js +0 -39
- package/dist/sync.js.map +0 -1
- package/dist/tenant.d.ts +0 -13
- package/dist/tenant.d.ts.map +0 -1
- package/dist/tenant.js +0 -105
- package/dist/tenant.js.map +0 -1
- package/dist/tenant.test.d.ts +0 -2
- package/dist/tenant.test.d.ts.map +0 -1
- package/dist/tenant.test.js +0 -37
- package/dist/tenant.test.js.map +0 -1
- package/src/config.test.ts +0 -237
- package/src/detect.test.ts +0 -29
- package/src/detect.ts +0 -52
- package/src/docker.test.ts +0 -12
- package/src/docker.ts +0 -23
- package/src/health.test.ts +0 -61
- package/src/health.ts +0 -158
- package/src/index.test.ts +0 -8
- package/src/index.ts +0 -62
- package/src/installer.test.ts +0 -52
- package/src/installer.ts +0 -62
- package/src/lifecycle.test.ts +0 -23
- package/src/lifecycle.ts +0 -49
- package/src/manifest.ts +0 -42
- package/src/skills-baseline.ts +0 -14
- package/src/skills.test.ts +0 -503
- package/src/skills.ts +0 -512
- package/src/sync.ts +0 -53
- package/src/tenant.test.ts +0 -49
- package/src/tenant.ts +0 -120
package/src/skills.ts
DELETED
|
@@ -1,512 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs"
|
|
2
|
-
import path from "node:path"
|
|
3
|
-
import os from "node:os"
|
|
4
|
-
import { execFileSync } from "node:child_process"
|
|
5
|
-
import { OPENCODE_CONFIG_DIR } from "./manifest.js"
|
|
6
|
-
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
// Constants
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
|
|
11
|
-
/** Where the JSON index lives */
|
|
12
|
-
export const SKILLS_INDEX_PATH = (): string => {
|
|
13
|
-
const home = os.homedir()
|
|
14
|
-
const cacheDir = path.join(home, ".cache", "otto")
|
|
15
|
-
fs.mkdirSync(cacheDir, { recursive: true })
|
|
16
|
-
return path.join(cacheDir, "skills-index.json")
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Where installed skills go */
|
|
20
|
-
export const OPENCODE_SKILLS_DIR = (): string => {
|
|
21
|
-
return path.join(OPENCODE_CONFIG_DIR(), "skills")
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Default skill repos — curated, known to have quality skills */
|
|
25
|
-
export const DEFAULT_SKILL_REPOS: string[] = [
|
|
26
|
-
"otto-assistant/bridge",
|
|
27
|
-
"otto-assistant/skills",
|
|
28
|
-
"anthropics/skills",
|
|
29
|
-
"vercel-labs/agent-skills",
|
|
30
|
-
"microsoft/skills",
|
|
31
|
-
"obra/superpowers",
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
|
-
// Types
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
|
|
38
|
-
export interface SkillMeta {
|
|
39
|
-
name: string
|
|
40
|
-
description: string
|
|
41
|
-
metadata?: Record<string, string>
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** One entry in the skills index */
|
|
45
|
-
export interface SkillIndexEntry {
|
|
46
|
-
name: string
|
|
47
|
-
description: string
|
|
48
|
-
metadata?: Record<string, string>
|
|
49
|
-
source: string // e.g. "anthropics/skills"
|
|
50
|
-
path: string // e.g. "skills/frontend-design"
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** The full on-disk index */
|
|
54
|
-
export interface SkillsIndex {
|
|
55
|
-
version: number
|
|
56
|
-
updated: string // ISO timestamp
|
|
57
|
-
repos: Record<string, {
|
|
58
|
-
fetched: string // ISO timestamp
|
|
59
|
-
skills: SkillIndexEntry[]
|
|
60
|
-
}>
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export type RepoSyncResult = "updated" | "cached" | "offline"
|
|
64
|
-
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
|
-
// SKILL.md Parser (unchanged)
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Parses YAML frontmatter from skill markdown content.
|
|
71
|
-
* Returns null if no valid frontmatter or missing required fields (name, description).
|
|
72
|
-
*/
|
|
73
|
-
export function parseSkillMd(content: string): SkillMeta | null {
|
|
74
|
-
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/)
|
|
75
|
-
if (!match) return null
|
|
76
|
-
|
|
77
|
-
const frontmatter = match[1]
|
|
78
|
-
|
|
79
|
-
const nameMatch = frontmatter.match(/^name:\s*(.+)$/m)
|
|
80
|
-
if (!nameMatch) return null
|
|
81
|
-
const name = nameMatch[1].trim()
|
|
82
|
-
|
|
83
|
-
const descMatch = frontmatter.match(/^description:\s*(.+)$/m)
|
|
84
|
-
if (!descMatch) return null
|
|
85
|
-
const description = descMatch[1].trim()
|
|
86
|
-
|
|
87
|
-
const meta: Record<string, string> = {}
|
|
88
|
-
const metaMatch = frontmatter.match(/^metadata:\s*\r?\n((?:\s{2,}\S.*\r?\n?)*)/m)
|
|
89
|
-
if (metaMatch) {
|
|
90
|
-
const metaBlock = metaMatch[1]
|
|
91
|
-
const lines = metaBlock.split(/\r?\n/).filter((l) => l.trim().length > 0)
|
|
92
|
-
for (const line of lines) {
|
|
93
|
-
const kv = line.trim().match(/^(\S+):\s*(.+)$/)
|
|
94
|
-
if (kv) {
|
|
95
|
-
let val = kv[2].trim()
|
|
96
|
-
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
97
|
-
val = val.slice(1, -1)
|
|
98
|
-
}
|
|
99
|
-
meta[kv[1]] = val
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
name,
|
|
106
|
-
description,
|
|
107
|
-
...(Object.keys(meta).length > 0 ? { metadata: meta } : {}),
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// ---------------------------------------------------------------------------
|
|
112
|
-
// Local skill discovery
|
|
113
|
-
// ---------------------------------------------------------------------------
|
|
114
|
-
|
|
115
|
-
/** Lists installed skills from OPENCODE_SKILLS_DIR. */
|
|
116
|
-
export function listInstalledSkills(baseDir?: string): SkillMeta[] {
|
|
117
|
-
const dir = baseDir ?? OPENCODE_SKILLS_DIR()
|
|
118
|
-
return discoverSkills(dir)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** Internal: scan dir subdirectories and parse skill markdown files. */
|
|
122
|
-
function discoverSkills(dir: string): SkillMeta[] {
|
|
123
|
-
const results: SkillMeta[] = []
|
|
124
|
-
|
|
125
|
-
let entries: string[]
|
|
126
|
-
try {
|
|
127
|
-
entries = fs.readdirSync(dir)
|
|
128
|
-
} catch {
|
|
129
|
-
return results
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
for (const entry of entries) {
|
|
133
|
-
const skillMdPath = path.join(dir, entry, "SKILL.md")
|
|
134
|
-
try {
|
|
135
|
-
const content = fs.readFileSync(skillMdPath, "utf-8")
|
|
136
|
-
const meta = parseSkillMd(content)
|
|
137
|
-
if (meta) {
|
|
138
|
-
results.push(meta)
|
|
139
|
-
}
|
|
140
|
-
} catch {
|
|
141
|
-
// skip invalid or missing SKILL.md
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return results
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// ---------------------------------------------------------------------------
|
|
149
|
-
// Remove installed skill
|
|
150
|
-
// ---------------------------------------------------------------------------
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Removes an installed skill directory.
|
|
154
|
-
* Returns true if removed, false if not found.
|
|
155
|
-
*/
|
|
156
|
-
export function removeSkill(name: string, targetDir?: string): boolean {
|
|
157
|
-
const target = targetDir ?? OPENCODE_SKILLS_DIR()
|
|
158
|
-
const skillDir = path.join(target, name)
|
|
159
|
-
|
|
160
|
-
if (!fs.existsSync(skillDir)) return false
|
|
161
|
-
|
|
162
|
-
fs.rmSync(skillDir, { recursive: true, force: true })
|
|
163
|
-
return true
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// ---------------------------------------------------------------------------
|
|
167
|
-
// GitHub API (via gh CLI)
|
|
168
|
-
// ---------------------------------------------------------------------------
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Call GitHub API using `gh api`. Returns parsed JSON.
|
|
172
|
-
* Throws on network/auth errors or non-2xx responses.
|
|
173
|
-
*/
|
|
174
|
-
export function ghApi(endpoint: string): unknown {
|
|
175
|
-
const result = execFileSync("gh", ["api", endpoint], {
|
|
176
|
-
encoding: "utf-8",
|
|
177
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
178
|
-
timeout: 15_000,
|
|
179
|
-
})
|
|
180
|
-
return JSON.parse(result)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Fetch directory listing from a GitHub repo path.
|
|
185
|
-
* Returns array of {name, type, path} entries, or empty array on error.
|
|
186
|
-
*/
|
|
187
|
-
export function fetchRepoDir(ownerRepo: string, dirPath: string): Array<{ name: string; type: string; path: string }> {
|
|
188
|
-
try {
|
|
189
|
-
const result = ghApi(`repos/${ownerRepo}/contents/${dirPath}`)
|
|
190
|
-
if (!Array.isArray(result)) return []
|
|
191
|
-
return result
|
|
192
|
-
.filter((item: any) => typeof item.name === "string" && typeof item.type === "string")
|
|
193
|
-
.map((item: any) => ({ name: item.name, type: item.type, path: item.path }))
|
|
194
|
-
} catch {
|
|
195
|
-
return []
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Fetch a single file content from a GitHub repo.
|
|
201
|
-
* Returns decoded UTF-8 string, or null on error.
|
|
202
|
-
*/
|
|
203
|
-
export function fetchRepoFile(ownerRepo: string, filePath: string): string | null {
|
|
204
|
-
try {
|
|
205
|
-
const result = ghApi(`repos/${ownerRepo}/contents/${filePath}`) as { content?: string; encoding?: string }
|
|
206
|
-
if (!result.content || result.encoding !== "base64") return null
|
|
207
|
-
return Buffer.from(result.content, "base64").toString("utf-8")
|
|
208
|
-
} catch {
|
|
209
|
-
return null
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// ---------------------------------------------------------------------------
|
|
214
|
-
// Skills Index (JSON cache)
|
|
215
|
-
// ---------------------------------------------------------------------------
|
|
216
|
-
|
|
217
|
-
/** Load index from disk. Returns empty index if file doesn't exist. */
|
|
218
|
-
export function loadSkillsIndex(indexPath?: string): SkillsIndex {
|
|
219
|
-
const p = indexPath ?? SKILLS_INDEX_PATH()
|
|
220
|
-
try {
|
|
221
|
-
const raw = fs.readFileSync(p, "utf-8")
|
|
222
|
-
return JSON.parse(raw) as SkillsIndex
|
|
223
|
-
} catch {
|
|
224
|
-
return { version: 1, updated: "", repos: {} }
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/** Save index to disk. */
|
|
229
|
-
export function saveSkillsIndex(index: SkillsIndex, indexPath?: string): void {
|
|
230
|
-
const p = indexPath ?? SKILLS_INDEX_PATH()
|
|
231
|
-
fs.mkdirSync(path.dirname(p), { recursive: true })
|
|
232
|
-
fs.writeFileSync(p, JSON.stringify(index, null, 2), "utf-8")
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/** Check if a fetched timestamp is older than maxAgeHours. */
|
|
236
|
-
export function isIndexStale(fetchedAt: string, maxAgeHours = 24): boolean {
|
|
237
|
-
if (!fetchedAt) return true
|
|
238
|
-
const fetched = new Date(fetchedAt).getTime()
|
|
239
|
-
return Date.now() - fetched > maxAgeHours * 60 * 60 * 1000
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// ---------------------------------------------------------------------------
|
|
243
|
-
// Index a single repo via GitHub API
|
|
244
|
-
// ---------------------------------------------------------------------------
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Index all skills from a single GitHub repo by fetching directory listings + SKILL.md frontmatter.
|
|
248
|
-
* Works with repos that have skills at `skills/<name>/SKILL.md` (flat or nested one level).
|
|
249
|
-
* Returns array of SkillIndexEntry.
|
|
250
|
-
*/
|
|
251
|
-
export function fetchRepoSkillsIndex(ownerRepo: string): SkillIndexEntry[] {
|
|
252
|
-
const entries: SkillIndexEntry[] = []
|
|
253
|
-
|
|
254
|
-
const topDirs = fetchRepoDir(ownerRepo, "skills")
|
|
255
|
-
|
|
256
|
-
for (const item of topDirs) {
|
|
257
|
-
if (item.type !== "dir") continue
|
|
258
|
-
|
|
259
|
-
// Try flat: skills/<name>/SKILL.md
|
|
260
|
-
const skillMd = fetchRepoFile(ownerRepo, `${item.path}/SKILL.md`)
|
|
261
|
-
if (skillMd) {
|
|
262
|
-
const meta = parseSkillMd(skillMd)
|
|
263
|
-
if (meta) {
|
|
264
|
-
entries.push({
|
|
265
|
-
name: meta.name,
|
|
266
|
-
description: meta.description,
|
|
267
|
-
metadata: meta.metadata,
|
|
268
|
-
source: ownerRepo,
|
|
269
|
-
path: item.path,
|
|
270
|
-
})
|
|
271
|
-
continue
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Try nested: skills/<category>/<name>/SKILL.md (microsoft/skills pattern)
|
|
276
|
-
const subDirs = fetchRepoDir(ownerRepo, item.path)
|
|
277
|
-
for (const sub of subDirs) {
|
|
278
|
-
if (sub.type !== "dir") continue
|
|
279
|
-
const subMd = fetchRepoFile(ownerRepo, `${sub.path}/SKILL.md`)
|
|
280
|
-
if (subMd) {
|
|
281
|
-
const meta = parseSkillMd(subMd)
|
|
282
|
-
if (meta) {
|
|
283
|
-
entries.push({
|
|
284
|
-
name: meta.name,
|
|
285
|
-
description: meta.description,
|
|
286
|
-
metadata: meta.metadata,
|
|
287
|
-
source: ownerRepo,
|
|
288
|
-
path: sub.path,
|
|
289
|
-
})
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return entries
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// ---------------------------------------------------------------------------
|
|
299
|
-
// Search
|
|
300
|
-
// ---------------------------------------------------------------------------
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Search indexed skills by query. Matches against name and description (case-insensitive).
|
|
304
|
-
*/
|
|
305
|
-
export function searchSkills(query: string, indexPath?: string): SkillIndexEntry[] {
|
|
306
|
-
const index = loadSkillsIndex(indexPath)
|
|
307
|
-
const q = query.toLowerCase()
|
|
308
|
-
const results: SkillIndexEntry[] = []
|
|
309
|
-
|
|
310
|
-
for (const repoData of Object.values(index.repos)) {
|
|
311
|
-
for (const skill of repoData.skills) {
|
|
312
|
-
if (
|
|
313
|
-
skill.name.toLowerCase().includes(q) ||
|
|
314
|
-
skill.description.toLowerCase().includes(q)
|
|
315
|
-
) {
|
|
316
|
-
results.push(skill)
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return results
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Get all indexed skills across all repos.
|
|
326
|
-
*/
|
|
327
|
-
export function getAllIndexedSkills(indexPath?: string): SkillIndexEntry[] {
|
|
328
|
-
const index = loadSkillsIndex(indexPath)
|
|
329
|
-
const all: SkillIndexEntry[] = []
|
|
330
|
-
for (const repoData of Object.values(index.repos)) {
|
|
331
|
-
all.push(...repoData.skills)
|
|
332
|
-
}
|
|
333
|
-
return all
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// ---------------------------------------------------------------------------
|
|
337
|
-
// Index refresh
|
|
338
|
-
// ---------------------------------------------------------------------------
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Get configured repos: DEFAULT_SKILL_REPOS + any user-added repos.
|
|
342
|
-
*/
|
|
343
|
-
export function getConfiguredRepos(): string[] {
|
|
344
|
-
return [...DEFAULT_SKILL_REPOS]
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Refresh the skills index by fetching all configured repos.
|
|
349
|
-
* Skips repos that were fetched recently (within maxAgeHours).
|
|
350
|
-
* Returns the number of repos refreshed.
|
|
351
|
-
*/
|
|
352
|
-
export function ensureSkillsIndex(maxAgeHours = 24, indexPath?: string): { refreshed: number; total: number } {
|
|
353
|
-
const index = loadSkillsIndex(indexPath)
|
|
354
|
-
const repos = getConfiguredRepos()
|
|
355
|
-
let refreshed = 0
|
|
356
|
-
|
|
357
|
-
for (const repo of repos) {
|
|
358
|
-
const existing = index.repos[repo]
|
|
359
|
-
if (existing && !isIndexStale(existing.fetched, maxAgeHours)) {
|
|
360
|
-
continue // still fresh
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
try {
|
|
364
|
-
const skills = fetchRepoSkillsIndex(repo)
|
|
365
|
-
index.repos[repo] = {
|
|
366
|
-
fetched: new Date().toISOString(),
|
|
367
|
-
skills,
|
|
368
|
-
}
|
|
369
|
-
refreshed++
|
|
370
|
-
} catch {
|
|
371
|
-
if (!existing) {
|
|
372
|
-
index.repos[repo] = { fetched: "", skills: [] }
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
index.updated = new Date().toISOString()
|
|
378
|
-
saveSkillsIndex(index, indexPath)
|
|
379
|
-
return { refreshed, total: repos.length }
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// ---------------------------------------------------------------------------
|
|
383
|
-
// Install from index (API-based)
|
|
384
|
-
// ---------------------------------------------------------------------------
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Install a skill from the index by fetching its files from GitHub API.
|
|
388
|
-
* Looks up the skill in the index, fetches SKILL.md (and any supporting files),
|
|
389
|
-
* writes them to the target directory.
|
|
390
|
-
*
|
|
391
|
-
* Returns true if installed, false if not found in index or fetch failed.
|
|
392
|
-
*/
|
|
393
|
-
export function installSkillFromIndex(
|
|
394
|
-
skillName: string,
|
|
395
|
-
targetDir?: string,
|
|
396
|
-
indexPath?: string,
|
|
397
|
-
): boolean {
|
|
398
|
-
const target = targetDir ?? OPENCODE_SKILLS_DIR()
|
|
399
|
-
const index = loadSkillsIndex(indexPath)
|
|
400
|
-
|
|
401
|
-
// Find skill in index
|
|
402
|
-
let entry: SkillIndexEntry | undefined
|
|
403
|
-
for (const repoData of Object.values(index.repos)) {
|
|
404
|
-
entry = repoData.skills.find((s) => s.name === skillName)
|
|
405
|
-
if (entry) break
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
if (!entry) return false
|
|
409
|
-
|
|
410
|
-
// Fetch SKILL.md from GitHub
|
|
411
|
-
const skillMd = fetchRepoFile(entry.source, `${entry.path}/SKILL.md`)
|
|
412
|
-
if (!skillMd) return false
|
|
413
|
-
|
|
414
|
-
// Validate it parses
|
|
415
|
-
const meta = parseSkillMd(skillMd)
|
|
416
|
-
if (!meta) return false
|
|
417
|
-
|
|
418
|
-
// Write SKILL.md
|
|
419
|
-
const destDir = path.join(target, skillName)
|
|
420
|
-
fs.mkdirSync(destDir, { recursive: true })
|
|
421
|
-
fs.writeFileSync(path.join(destDir, "SKILL.md"), skillMd, "utf-8")
|
|
422
|
-
|
|
423
|
-
// Fetch any supporting files in the same directory
|
|
424
|
-
const dirContents = fetchRepoDir(entry.source, entry.path)
|
|
425
|
-
for (const item of dirContents) {
|
|
426
|
-
if (item.name === "SKILL.md") continue
|
|
427
|
-
if (item.type === "file") {
|
|
428
|
-
const fileContent = fetchRepoFile(entry.source, item.path)
|
|
429
|
-
if (fileContent) {
|
|
430
|
-
fs.writeFileSync(path.join(destDir, item.name), fileContent, "utf-8")
|
|
431
|
-
}
|
|
432
|
-
} else if (item.type === "dir") {
|
|
433
|
-
fetchDirRecursive(entry.source, item.path, path.join(destDir, item.name))
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
return true
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Install a baseline list of skills without removing user-installed ones.
|
|
442
|
-
*/
|
|
443
|
-
export function installSkillsBaseline(
|
|
444
|
-
skillNames: string[],
|
|
445
|
-
targetDir?: string,
|
|
446
|
-
indexPath?: string,
|
|
447
|
-
): { installed: string[]; alreadyPresent: string[]; failed: string[] } {
|
|
448
|
-
const installed: string[] = []
|
|
449
|
-
const alreadyPresent: string[] = []
|
|
450
|
-
const failed: string[] = []
|
|
451
|
-
|
|
452
|
-
const target = targetDir ?? OPENCODE_SKILLS_DIR()
|
|
453
|
-
fs.mkdirSync(target, { recursive: true })
|
|
454
|
-
|
|
455
|
-
const existing = new Set(listInstalledSkills(target).map((s) => s.name))
|
|
456
|
-
|
|
457
|
-
for (const name of skillNames) {
|
|
458
|
-
if (existing.has(name)) {
|
|
459
|
-
alreadyPresent.push(name)
|
|
460
|
-
continue
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
const ok = installSkillFromIndex(name, target, indexPath) || installSkillFromBuiltIn(name, target)
|
|
464
|
-
if (ok) {
|
|
465
|
-
installed.push(name)
|
|
466
|
-
existing.add(name)
|
|
467
|
-
} else {
|
|
468
|
-
failed.push(name)
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
return { installed, alreadyPresent, failed }
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
function installSkillFromBuiltIn(skillName: string, targetDir: string): boolean {
|
|
476
|
-
const builtInRoots = [
|
|
477
|
-
"/usr/local/lib/node_modules/@otto-assistant/bridge/skills",
|
|
478
|
-
"/usr/lib/node_modules/@otto-assistant/bridge/skills",
|
|
479
|
-
"/usr/lib/node_modules/kimaki/skills",
|
|
480
|
-
]
|
|
481
|
-
|
|
482
|
-
for (const root of builtInRoots) {
|
|
483
|
-
const sourceDir = path.join(root, skillName)
|
|
484
|
-
const sourceSkillMd = path.join(sourceDir, "SKILL.md")
|
|
485
|
-
if (!fs.existsSync(sourceSkillMd)) continue
|
|
486
|
-
|
|
487
|
-
const destDir = path.join(targetDir, skillName)
|
|
488
|
-
fs.mkdirSync(path.dirname(destDir), { recursive: true })
|
|
489
|
-
fs.cpSync(sourceDir, destDir, { recursive: true })
|
|
490
|
-
return true
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
return false
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/**
|
|
497
|
-
* Recursively fetch a directory from GitHub and write to local disk.
|
|
498
|
-
*/
|
|
499
|
-
function fetchDirRecursive(ownerRepo: string, ghPath: string, localPath: string): void {
|
|
500
|
-
fs.mkdirSync(localPath, { recursive: true })
|
|
501
|
-
const contents = fetchRepoDir(ownerRepo, ghPath)
|
|
502
|
-
for (const item of contents) {
|
|
503
|
-
if (item.type === "file") {
|
|
504
|
-
const content = fetchRepoFile(ownerRepo, item.path)
|
|
505
|
-
if (content) {
|
|
506
|
-
fs.writeFileSync(path.join(localPath, item.name), content, "utf-8")
|
|
507
|
-
}
|
|
508
|
-
} else if (item.type === "dir") {
|
|
509
|
-
fetchDirRecursive(ownerRepo, item.path, path.join(localPath, item.name))
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
}
|
package/src/sync.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { execSync } from "node:child_process"
|
|
2
|
-
import { UPSTREAM_REPOS } from "./manifest.js"
|
|
3
|
-
|
|
4
|
-
interface SyncTarget {
|
|
5
|
-
repo: string
|
|
6
|
-
upstream: string
|
|
7
|
-
branch: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function getSyncTargets(): SyncTarget[] {
|
|
11
|
-
return Object.entries(UPSTREAM_REPOS).map(([_pkgName, info]) => ({
|
|
12
|
-
repo: info.repo,
|
|
13
|
-
upstream: info.upstream,
|
|
14
|
-
branch: "main",
|
|
15
|
-
}))
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function syncUpstreams(): Promise<void> {
|
|
19
|
-
const targets = getSyncTargets()
|
|
20
|
-
|
|
21
|
-
if (targets.length === 0) {
|
|
22
|
-
console.log("No upstream repos configured for sync.")
|
|
23
|
-
return
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Check gh CLI is available
|
|
27
|
-
try {
|
|
28
|
-
execSync("gh --version", { stdio: "pipe" })
|
|
29
|
-
} catch {
|
|
30
|
-
console.error("Error: gh CLI is required for sync. Install: https://cli.github.com/")
|
|
31
|
-
process.exit(1)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
console.log("Triggering upstream sync for all forked repos:\n")
|
|
35
|
-
|
|
36
|
-
for (const target of targets) {
|
|
37
|
-
console.log(` ${target.repo} ← ${target.upstream}`)
|
|
38
|
-
try {
|
|
39
|
-
execSync(
|
|
40
|
-
`gh workflow run sync-upstream.yml --repo ${target.repo} --ref ${target.branch}`,
|
|
41
|
-
{ stdio: "pipe" },
|
|
42
|
-
)
|
|
43
|
-
console.log(` ✓ Sync triggered`)
|
|
44
|
-
} catch (err: unknown) {
|
|
45
|
-
const msg = err instanceof Error ? err.message : String(err)
|
|
46
|
-
console.error(` ✗ Failed: ${msg}`)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
console.log("\nSync workflows triggered. Check status with: gh run list --repo <repo>")
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export { getSyncTargets, UPSTREAM_REPOS }
|
package/src/tenant.test.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs"
|
|
2
|
-
import os from "node:os"
|
|
3
|
-
import path from "node:path"
|
|
4
|
-
import { describe, expect, it } from "vitest"
|
|
5
|
-
import {
|
|
6
|
-
deriveComposeProjectName,
|
|
7
|
-
ensureTenantMemoryLayout,
|
|
8
|
-
ensureTenantScaffold,
|
|
9
|
-
resolveTenantImage,
|
|
10
|
-
resolveTenantMode,
|
|
11
|
-
} from "./tenant.js"
|
|
12
|
-
|
|
13
|
-
describe("tenant", () => {
|
|
14
|
-
it("uses otto-<folder_name> default", () => {
|
|
15
|
-
expect(deriveComposeProjectName("/tmp/my-tenant")).toBe("otto-my-tenant")
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it("uses image from compose.yml when env override missing", () => {
|
|
19
|
-
const resolved = resolveTenantImage({
|
|
20
|
-
composeImage: "otto-assistant/otto:stable",
|
|
21
|
-
envImage: undefined,
|
|
22
|
-
})
|
|
23
|
-
expect(resolved).toBe("otto-assistant/otto:stable")
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it("defaults to safe mode when OTTO_MODE is unset", () => {
|
|
27
|
-
expect(resolveTenantMode(undefined)).toBe("safe")
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it("creates required memory files and mempalace dir", () => {
|
|
31
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), "otto-memory-"))
|
|
32
|
-
const report = ensureTenantMemoryLayout(root)
|
|
33
|
-
|
|
34
|
-
expect(report.created).toContain("AGENTS.md")
|
|
35
|
-
expect(report.created).toContain("soul.md")
|
|
36
|
-
expect(report.created).toContain("persona.md")
|
|
37
|
-
expect(report.created).toContain("mempalace/")
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it("creates compose scaffold for tenant", () => {
|
|
41
|
-
const tenantPath = fs.mkdtempSync(path.join(os.tmpdir(), "otto-tenant-"))
|
|
42
|
-
const report = ensureTenantScaffold(tenantPath)
|
|
43
|
-
|
|
44
|
-
expect(report.created).toContain("compose.yml")
|
|
45
|
-
expect(fs.existsSync(path.join(tenantPath, "compose.yml"))).toBe(true)
|
|
46
|
-
expect(fs.existsSync(path.join(tenantPath, "memory", "AGENTS.md"))).toBe(true)
|
|
47
|
-
expect(fs.existsSync(path.join(tenantPath, "projects"))).toBe(true)
|
|
48
|
-
})
|
|
49
|
-
})
|