@otto-assistant/otto 0.1.1 → 0.7.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (637) hide show
  1. package/bin.js +2 -0
  2. package/dist/agent-model.e2e.test.js +755 -0
  3. package/dist/ai-tool-to-genai.js +233 -0
  4. package/dist/ai-tool-to-genai.test.js +267 -0
  5. package/dist/ai-tool.js +6 -0
  6. package/dist/anthropic-account-identity.js +62 -0
  7. package/dist/anthropic-account-identity.test.js +38 -0
  8. package/dist/anthropic-auth-plugin.js +917 -0
  9. package/dist/anthropic-auth-state.js +303 -0
  10. package/dist/anthropic-auth-state.test.js +150 -0
  11. package/dist/bin.js +152 -0
  12. package/dist/btw-prefix-detection.js +17 -0
  13. package/dist/btw-prefix-detection.test.js +63 -0
  14. package/dist/channel-management.js +259 -0
  15. package/dist/cli-parsing.test.js +142 -0
  16. package/dist/cli-send-thread.e2e.test.js +353 -0
  17. package/dist/cli-telegram-options.test.js +99 -0
  18. package/dist/cli.js +4210 -568
  19. package/dist/commands/abort.js +65 -0
  20. package/dist/commands/action-buttons.js +245 -0
  21. package/dist/commands/add-dir.js +124 -0
  22. package/dist/commands/add-dir.test.js +126 -0
  23. package/dist/commands/add-project.js +113 -0
  24. package/dist/commands/agent.js +355 -0
  25. package/dist/commands/ask-question.js +320 -0
  26. package/dist/commands/ask-question.test.js +92 -0
  27. package/dist/commands/btw.js +121 -0
  28. package/dist/commands/cli-commands-group-a.test.js +728 -0
  29. package/dist/commands/cli-commands-group-b.test.js +695 -0
  30. package/dist/commands/compact.js +120 -0
  31. package/dist/commands/context-usage.js +140 -0
  32. package/dist/commands/create-new-project.js +130 -0
  33. package/dist/commands/diff.js +63 -0
  34. package/dist/commands/discord-commands-group-a.test.js +621 -0
  35. package/dist/commands/discord-commands-group-b.test.js +595 -0
  36. package/dist/commands/discord-commands-group-c.test.js +739 -0
  37. package/dist/commands/file-upload.js +275 -0
  38. package/dist/commands/fork-subagent.js +177 -0
  39. package/dist/commands/fork.js +262 -0
  40. package/dist/commands/gemini-apikey.js +70 -0
  41. package/dist/commands/login.js +887 -0
  42. package/dist/commands/mcp.js +239 -0
  43. package/dist/commands/memory-snapshot.js +24 -0
  44. package/dist/commands/mention-mode.js +44 -0
  45. package/dist/commands/merge-worktree.js +162 -0
  46. package/dist/commands/model-variant.js +366 -0
  47. package/dist/commands/model.js +794 -0
  48. package/dist/commands/new-worktree.js +465 -0
  49. package/dist/commands/paginated-select.js +57 -0
  50. package/dist/commands/permissions.js +274 -0
  51. package/dist/commands/queue.js +223 -0
  52. package/dist/commands/remove-project.js +115 -0
  53. package/dist/commands/restart-opencode-server.js +127 -0
  54. package/dist/commands/resume.js +149 -0
  55. package/dist/commands/run-command.js +79 -0
  56. package/dist/commands/screenshare.js +303 -0
  57. package/dist/commands/screenshare.test.js +20 -0
  58. package/dist/commands/session-id.js +78 -0
  59. package/dist/commands/session.js +176 -0
  60. package/dist/commands/share.js +80 -0
  61. package/dist/commands/tasks.js +205 -0
  62. package/dist/commands/thread-deletion-sync.js +50 -0
  63. package/dist/commands/types.js +2 -0
  64. package/dist/commands/undo-redo.js +305 -0
  65. package/dist/commands/unset-model.js +139 -0
  66. package/dist/commands/upgrade.js +48 -0
  67. package/dist/commands/user-command.js +155 -0
  68. package/dist/commands/verbosity.js +125 -0
  69. package/dist/commands/vscode.js +269 -0
  70. package/dist/commands/worktree-settings.js +43 -0
  71. package/dist/commands/worktrees.js +468 -0
  72. package/dist/condense-memory.js +33 -0
  73. package/dist/config.js +100 -255
  74. package/dist/context-awareness-plugin.js +340 -0
  75. package/dist/context-awareness-plugin.test.js +126 -0
  76. package/dist/critique-utils.js +95 -0
  77. package/dist/database.js +1355 -0
  78. package/dist/db.js +260 -0
  79. package/dist/db.test.js +138 -0
  80. package/dist/debounce-timeout.js +28 -0
  81. package/dist/debounced-process-flush.js +77 -0
  82. package/dist/discord-bot.js +1124 -0
  83. package/dist/discord-command-registration.js +567 -0
  84. package/dist/discord-urls.js +82 -0
  85. package/dist/discord-utils.js +616 -0
  86. package/dist/discord-utils.test.js +134 -0
  87. package/dist/errors.js +157 -0
  88. package/dist/escape-backticks.test.js +429 -0
  89. package/dist/event-stream-real-capture.e2e.test.js +533 -0
  90. package/dist/eventsource-parser.test.js +327 -0
  91. package/dist/exec-async.js +26 -0
  92. package/dist/external-opencode-sync.js +480 -0
  93. package/dist/format-tables.js +491 -0
  94. package/dist/format-tables.test.js +478 -0
  95. package/dist/forum-sync/config.js +79 -0
  96. package/dist/forum-sync/discord-operations.js +154 -0
  97. package/dist/forum-sync/index.js +5 -0
  98. package/dist/forum-sync/markdown.js +113 -0
  99. package/dist/forum-sync/sync-to-discord.js +417 -0
  100. package/dist/forum-sync/sync-to-files.js +190 -0
  101. package/dist/forum-sync/types.js +53 -0
  102. package/dist/forum-sync/watchers.js +307 -0
  103. package/dist/gateway-proxy-reconnect.e2e.test.js +394 -0
  104. package/dist/gateway-proxy.e2e.test.js +485 -0
  105. package/dist/genai-worker-wrapper.js +111 -0
  106. package/dist/genai-worker.js +311 -0
  107. package/dist/genai.js +232 -0
  108. package/dist/generated/browser.js +17 -0
  109. package/dist/generated/client.js +37 -0
  110. package/dist/generated/commonInputTypes.js +10 -0
  111. package/dist/generated/enums.js +58 -0
  112. package/dist/generated/internal/class.js +49 -0
  113. package/dist/generated/internal/prismaNamespace.js +254 -0
  114. package/dist/generated/internal/prismaNamespaceBrowser.js +224 -0
  115. package/dist/generated/models/bot_api_keys.js +1 -0
  116. package/dist/generated/models/bot_tokens.js +1 -0
  117. package/dist/generated/models/channel_agents.js +1 -0
  118. package/dist/generated/models/channel_directories.js +1 -0
  119. package/dist/generated/models/channel_mention_mode.js +1 -0
  120. package/dist/generated/models/channel_models.js +1 -0
  121. package/dist/generated/models/channel_verbosity.js +1 -0
  122. package/dist/generated/models/channel_worktrees.js +1 -0
  123. package/dist/generated/models/forum_sync_configs.js +1 -0
  124. package/dist/generated/models/global_models.js +1 -0
  125. package/dist/generated/models/ipc_requests.js +1 -0
  126. package/dist/generated/models/part_messages.js +1 -0
  127. package/dist/generated/models/scheduled_tasks.js +1 -0
  128. package/dist/generated/models/session_agents.js +1 -0
  129. package/dist/generated/models/session_events.js +1 -0
  130. package/dist/generated/models/session_models.js +1 -0
  131. package/dist/generated/models/session_start_sources.js +1 -0
  132. package/dist/generated/models/thread_sessions.js +1 -0
  133. package/dist/generated/models/thread_worktrees.js +1 -0
  134. package/dist/generated/models.js +1 -0
  135. package/dist/heap-monitor.js +122 -0
  136. package/dist/hrana-server.js +251 -0
  137. package/dist/hrana-server.test.js +370 -0
  138. package/dist/html-actions.js +123 -0
  139. package/dist/html-actions.test.js +70 -0
  140. package/dist/html-components.js +117 -0
  141. package/dist/html-components.test.js +34 -0
  142. package/dist/image-optimizer-plugin.js +153 -0
  143. package/dist/image-utils.js +112 -0
  144. package/dist/interaction-handler.js +420 -0
  145. package/dist/ipc-polling.js +327 -0
  146. package/dist/ipc-tools-plugin.js +193 -0
  147. package/dist/ipc-utils.js +18 -0
  148. package/dist/limit-heading-depth.js +25 -0
  149. package/dist/limit-heading-depth.test.js +105 -0
  150. package/dist/logger.js +171 -0
  151. package/dist/markdown.js +342 -0
  152. package/dist/markdown.test.js +264 -0
  153. package/dist/memory-overview-plugin.js +128 -0
  154. package/dist/message-finish-field.e2e.test.js +168 -0
  155. package/dist/message-formatting.js +415 -0
  156. package/dist/message-formatting.test.js +115 -0
  157. package/dist/message-preprocessing.js +359 -0
  158. package/dist/onboarding-tutorial.js +163 -0
  159. package/dist/onboarding-welcome.js +37 -0
  160. package/dist/openai-realtime.js +224 -0
  161. package/dist/opencode-command-detection.js +65 -0
  162. package/dist/opencode-command-detection.test.js +240 -0
  163. package/dist/opencode-command.js +131 -0
  164. package/dist/opencode-command.test.js +48 -0
  165. package/dist/opencode-interrupt-plugin.js +388 -0
  166. package/dist/opencode-interrupt-plugin.test.js +463 -0
  167. package/dist/opencode.js +1117 -0
  168. package/dist/otto/branding.js +22 -0
  169. package/dist/otto/index.js +21 -0
  170. package/dist/otto-digital-twin.e2e.test.js +161 -0
  171. package/dist/otto-opencode-plugin-loading.e2e.test.js +94 -0
  172. package/dist/otto-opencode-plugin.js +21 -0
  173. package/dist/otto-opencode-plugin.test.js +98 -0
  174. package/dist/parse-permission-rules.test.js +117 -0
  175. package/dist/patch-text-parser.js +97 -0
  176. package/dist/plugin-logger.js +68 -0
  177. package/dist/privacy-sanitizer.js +105 -0
  178. package/dist/queue-advanced-abort.e2e.test.js +293 -0
  179. package/dist/queue-advanced-action-buttons.e2e.test.js +206 -0
  180. package/dist/queue-advanced-e2e-setup.js +790 -0
  181. package/dist/queue-advanced-footer.e2e.test.js +481 -0
  182. package/dist/queue-advanced-model-switch.e2e.test.js +299 -0
  183. package/dist/queue-advanced-permissions-typing.e2e.test.js +179 -0
  184. package/dist/queue-advanced-question.e2e.test.js +261 -0
  185. package/dist/queue-advanced-typing-interrupt.e2e.test.js +114 -0
  186. package/dist/queue-advanced-typing.e2e.test.js +153 -0
  187. package/dist/queue-drain-after-interactive-ui.e2e.test.js +119 -0
  188. package/dist/queue-interrupt-drain.e2e.test.js +135 -0
  189. package/dist/queue-question-select-drain.e2e.test.js +256 -0
  190. package/dist/runtime-idle-sweeper.js +52 -0
  191. package/dist/runtime-lifecycle.e2e.test.js +514 -0
  192. package/dist/sentry.js +23 -0
  193. package/dist/session-handler/agent-utils.js +67 -0
  194. package/dist/session-handler/event-stream-state.js +475 -0
  195. package/dist/session-handler/event-stream-state.test.js +632 -0
  196. package/dist/session-handler/model-utils.js +147 -0
  197. package/dist/session-handler/opencode-session-event-log.js +94 -0
  198. package/dist/session-handler/thread-runtime-state.js +131 -0
  199. package/dist/session-handler/thread-session-runtime.js +3390 -0
  200. package/dist/session-handler.js +9 -0
  201. package/dist/session-search.js +100 -0
  202. package/dist/session-search.test.js +40 -0
  203. package/dist/session-title-rename.test.js +92 -0
  204. package/dist/skill-filter.js +31 -0
  205. package/dist/skill-filter.test.js +65 -0
  206. package/dist/startup-service.js +153 -0
  207. package/dist/startup-time.e2e.test.js +296 -0
  208. package/dist/store.js +19 -0
  209. package/dist/subagent-rate-limit-plugin.js +175 -0
  210. package/dist/system-message.js +702 -0
  211. package/dist/system-message.test.js +697 -0
  212. package/dist/task-runner.js +530 -0
  213. package/dist/task-schedule.js +213 -0
  214. package/dist/task-schedule.test.js +71 -0
  215. package/dist/test-utils.js +313 -0
  216. package/dist/thinking-utils.js +35 -0
  217. package/dist/thread-message-queue.e2e.test.js +1111 -0
  218. package/dist/tools.js +357 -0
  219. package/dist/undo-redo.e2e.test.js +161 -0
  220. package/dist/unnest-code-blocks.js +146 -0
  221. package/dist/unnest-code-blocks.test.js +673 -0
  222. package/dist/upgrade.js +156 -0
  223. package/dist/utils.js +172 -0
  224. package/dist/utils.test.js +130 -0
  225. package/dist/voice-attachment.js +34 -0
  226. package/dist/voice-handler.js +646 -0
  227. package/dist/voice-message.e2e.test.js +1021 -0
  228. package/dist/voice.js +456 -0
  229. package/dist/voice.test.js +235 -0
  230. package/dist/wait-session.js +171 -0
  231. package/dist/websockify.js +69 -0
  232. package/dist/worker-types.js +4 -0
  233. package/dist/worktree-lifecycle.e2e.test.js +311 -0
  234. package/dist/worktree-utils.js +3 -0
  235. package/dist/worktrees.js +991 -0
  236. package/dist/worktrees.test.js +415 -0
  237. package/dist/xml.js +92 -0
  238. package/dist/xml.test.js +32 -0
  239. package/package.json +90 -38
  240. package/schema.prisma +303 -0
  241. package/skills/batch/SKILL.md +87 -0
  242. package/skills/critique/SKILL.md +112 -0
  243. package/skills/egaki/SKILL.md +100 -0
  244. package/skills/errore/SKILL.md +647 -0
  245. package/skills/event-sourcing-state/SKILL.md +252 -0
  246. package/skills/goke/SKILL.md +38 -0
  247. package/skills/jitter/EDITOR.md +219 -0
  248. package/skills/jitter/EXPORT-INTERNALS.md +309 -0
  249. package/skills/jitter/SKILL.md +158 -0
  250. package/skills/jitter/jitter-clipboard.json +1042 -0
  251. package/skills/jitter/package.json +14 -0
  252. package/skills/jitter/tsconfig.json +15 -0
  253. package/skills/jitter/utils/actions.ts +212 -0
  254. package/skills/jitter/utils/export.ts +114 -0
  255. package/skills/jitter/utils/index.ts +141 -0
  256. package/skills/jitter/utils/snapshot.ts +154 -0
  257. package/skills/jitter/utils/traverse.ts +246 -0
  258. package/skills/jitter/utils/types.ts +279 -0
  259. package/skills/jitter/utils/wait.ts +133 -0
  260. package/skills/lintcn/SKILL.md +873 -0
  261. package/skills/manual-kimaki-upstream-adapt/SKILL.md +114 -0
  262. package/skills/new-skill/SKILL.md +237 -0
  263. package/skills/npm-package/SKILL.md +617 -0
  264. package/skills/opensrc/SKILL.md +78 -0
  265. package/skills/otto-publish/SKILL.md +61 -0
  266. package/skills/playwriter/SKILL.md +35 -0
  267. package/skills/profano/SKILL.md +16 -0
  268. package/skills/proxyman/SKILL.md +215 -0
  269. package/skills/security-review/SKILL.md +208 -0
  270. package/skills/sigillo/SKILL.md +101 -0
  271. package/skills/simplify/SKILL.md +58 -0
  272. package/skills/spiceflow/SKILL.md +28 -0
  273. package/skills/termcast/SKILL.md +945 -0
  274. package/skills/tuistory/SKILL.md +98 -0
  275. package/skills/usecomputer/SKILL.md +264 -0
  276. package/skills/x-articles/SKILL.md +554 -0
  277. package/skills/zele/SKILL.md +49 -0
  278. package/skills/zustand-centralized-state/SKILL.md +1004 -0
  279. package/src/agent-model.e2e.test.ts +979 -0
  280. package/src/ai-tool-to-genai.test.ts +296 -0
  281. package/src/ai-tool-to-genai.ts +283 -0
  282. package/src/ai-tool.ts +39 -0
  283. package/src/anthropic-account-identity.test.ts +52 -0
  284. package/src/anthropic-account-identity.ts +77 -0
  285. package/src/anthropic-auth-plugin.ts +1139 -0
  286. package/src/anthropic-auth-state.test.ts +187 -0
  287. package/src/anthropic-auth-state.ts +386 -0
  288. package/src/bin.ts +182 -0
  289. package/src/btw-prefix-detection.test.ts +73 -0
  290. package/src/btw-prefix-detection.ts +23 -0
  291. package/src/channel-management.ts +376 -0
  292. package/src/cli-parsing.test.ts +197 -0
  293. package/src/cli-send-thread.e2e.test.ts +463 -0
  294. package/src/cli-telegram-options.test.ts +114 -0
  295. package/src/cli.ts +5718 -580
  296. package/src/commands/abort.ts +89 -0
  297. package/src/commands/action-buttons.ts +364 -0
  298. package/src/commands/add-dir.test.ts +154 -0
  299. package/src/commands/add-dir.ts +175 -0
  300. package/src/commands/add-project.ts +149 -0
  301. package/src/commands/agent.ts +496 -0
  302. package/src/commands/ask-question.test.ts +111 -0
  303. package/src/commands/ask-question.ts +455 -0
  304. package/src/commands/btw.ts +184 -0
  305. package/src/commands/cli-commands-group-a.test.ts +837 -0
  306. package/src/commands/cli-commands-group-b.test.ts +800 -0
  307. package/src/commands/compact.ts +157 -0
  308. package/src/commands/context-usage.ts +199 -0
  309. package/src/commands/create-new-project.ts +190 -0
  310. package/src/commands/diff.ts +91 -0
  311. package/src/commands/discord-commands-group-a.test.ts +751 -0
  312. package/src/commands/discord-commands-group-b.test.ts +648 -0
  313. package/src/commands/discord-commands-group-c.test.ts +882 -0
  314. package/src/commands/file-upload.ts +389 -0
  315. package/src/commands/fork-subagent.ts +263 -0
  316. package/src/commands/fork.ts +386 -0
  317. package/src/commands/gemini-apikey.ts +104 -0
  318. package/src/commands/login.ts +1175 -0
  319. package/src/commands/mcp.ts +307 -0
  320. package/src/commands/memory-snapshot.ts +30 -0
  321. package/src/commands/mention-mode.ts +68 -0
  322. package/src/commands/merge-worktree.ts +226 -0
  323. package/src/commands/model-variant.ts +485 -0
  324. package/src/commands/model.ts +1078 -0
  325. package/src/commands/new-worktree.ts +645 -0
  326. package/src/commands/paginated-select.ts +81 -0
  327. package/src/commands/permissions.ts +397 -0
  328. package/src/commands/queue.ts +293 -0
  329. package/src/commands/remove-project.ts +155 -0
  330. package/src/commands/restart-opencode-server.ts +162 -0
  331. package/src/commands/resume.ts +230 -0
  332. package/src/commands/run-command.ts +123 -0
  333. package/src/commands/screenshare.test.ts +30 -0
  334. package/src/commands/screenshare.ts +366 -0
  335. package/src/commands/session-id.ts +109 -0
  336. package/src/commands/session.ts +227 -0
  337. package/src/commands/share.ts +106 -0
  338. package/src/commands/tasks.ts +293 -0
  339. package/src/commands/thread-deletion-sync.ts +80 -0
  340. package/src/commands/types.ts +25 -0
  341. package/src/commands/undo-redo.ts +386 -0
  342. package/src/commands/unset-model.ts +174 -0
  343. package/src/commands/upgrade.ts +59 -0
  344. package/src/commands/user-command.ts +198 -0
  345. package/src/commands/verbosity.ts +173 -0
  346. package/src/commands/vscode.ts +342 -0
  347. package/src/commands/worktree-settings.ts +70 -0
  348. package/src/commands/worktrees.ts +645 -0
  349. package/src/condense-memory.ts +36 -0
  350. package/src/config.ts +103 -339
  351. package/src/context-awareness-plugin.test.ts +144 -0
  352. package/src/context-awareness-plugin.ts +469 -0
  353. package/src/critique-utils.ts +139 -0
  354. package/src/database.ts +1949 -0
  355. package/src/db.test.ts +162 -0
  356. package/src/db.ts +295 -0
  357. package/src/debounce-timeout.ts +43 -0
  358. package/src/debounced-process-flush.ts +104 -0
  359. package/src/discord-bot.ts +1505 -0
  360. package/src/discord-command-registration.ts +752 -0
  361. package/src/discord-urls.ts +89 -0
  362. package/src/discord-utils.test.ts +153 -0
  363. package/src/discord-utils.ts +846 -0
  364. package/src/errors.ts +201 -0
  365. package/src/escape-backticks.test.ts +469 -0
  366. package/src/event-stream-real-capture.e2e.test.ts +692 -0
  367. package/src/eventsource-parser.test.ts +351 -0
  368. package/src/exec-async.ts +35 -0
  369. package/src/external-opencode-sync.ts +685 -0
  370. package/src/format-tables.test.ts +515 -0
  371. package/src/format-tables.ts +718 -0
  372. package/src/forum-sync/config.ts +92 -0
  373. package/src/forum-sync/discord-operations.ts +241 -0
  374. package/src/forum-sync/index.ts +9 -0
  375. package/src/forum-sync/markdown.ts +172 -0
  376. package/src/forum-sync/sync-to-discord.ts +595 -0
  377. package/src/forum-sync/sync-to-files.ts +294 -0
  378. package/src/forum-sync/types.ts +175 -0
  379. package/src/forum-sync/watchers.ts +454 -0
  380. package/src/gateway-proxy-reconnect.e2e.test.ts +523 -0
  381. package/src/gateway-proxy.e2e.test.ts +644 -0
  382. package/src/genai-worker-wrapper.ts +164 -0
  383. package/src/genai-worker.ts +386 -0
  384. package/src/genai.ts +321 -0
  385. package/src/generated/browser.ts +114 -0
  386. package/src/generated/client.ts +138 -0
  387. package/src/generated/commonInputTypes.ts +770 -0
  388. package/src/generated/enums.ts +98 -0
  389. package/src/generated/internal/class.ts +384 -0
  390. package/src/generated/internal/prismaNamespace.ts +2394 -0
  391. package/src/generated/internal/prismaNamespaceBrowser.ts +327 -0
  392. package/src/generated/models/bot_api_keys.ts +1288 -0
  393. package/src/generated/models/bot_tokens.ts +1700 -0
  394. package/src/generated/models/channel_agents.ts +1256 -0
  395. package/src/generated/models/channel_directories.ts +1859 -0
  396. package/src/generated/models/channel_mention_mode.ts +1300 -0
  397. package/src/generated/models/channel_models.ts +1288 -0
  398. package/src/generated/models/channel_verbosity.ts +1228 -0
  399. package/src/generated/models/channel_worktrees.ts +1300 -0
  400. package/src/generated/models/forum_sync_configs.ts +1452 -0
  401. package/src/generated/models/global_models.ts +1288 -0
  402. package/src/generated/models/ipc_requests.ts +1485 -0
  403. package/src/generated/models/part_messages.ts +1302 -0
  404. package/src/generated/models/scheduled_tasks.ts +2320 -0
  405. package/src/generated/models/session_agents.ts +1086 -0
  406. package/src/generated/models/session_events.ts +1439 -0
  407. package/src/generated/models/session_models.ts +1114 -0
  408. package/src/generated/models/session_start_sources.ts +1408 -0
  409. package/src/generated/models/thread_sessions.ts +1781 -0
  410. package/src/generated/models/thread_worktrees.ts +1356 -0
  411. package/src/generated/models.ts +30 -0
  412. package/src/heap-monitor.ts +152 -0
  413. package/src/hrana-server.test.ts +434 -0
  414. package/src/hrana-server.ts +299 -0
  415. package/src/html-actions.test.ts +87 -0
  416. package/src/html-actions.ts +174 -0
  417. package/src/html-components.test.ts +38 -0
  418. package/src/html-components.ts +181 -0
  419. package/src/image-optimizer-plugin.ts +194 -0
  420. package/src/image-utils.ts +149 -0
  421. package/src/interaction-handler.ts +610 -0
  422. package/src/ipc-polling.ts +427 -0
  423. package/src/ipc-tools-plugin.ts +236 -0
  424. package/src/ipc-utils.ts +29 -0
  425. package/src/limit-heading-depth.test.ts +116 -0
  426. package/src/limit-heading-depth.ts +26 -0
  427. package/src/logger.ts +215 -0
  428. package/src/markdown.test.ts +315 -0
  429. package/src/markdown.ts +410 -0
  430. package/src/memory-overview-plugin.ts +163 -0
  431. package/src/message-finish-field.e2e.test.ts +195 -0
  432. package/src/message-formatting.test.ts +126 -0
  433. package/src/message-formatting.ts +535 -0
  434. package/src/message-preprocessing.ts +488 -0
  435. package/src/onboarding-tutorial.ts +167 -0
  436. package/src/onboarding-welcome.ts +49 -0
  437. package/src/openai-realtime.ts +358 -0
  438. package/src/opencode-command-detection.test.ts +307 -0
  439. package/src/opencode-command-detection.ts +76 -0
  440. package/src/opencode-command.test.ts +70 -0
  441. package/src/opencode-command.ts +191 -0
  442. package/src/opencode-interrupt-plugin.test.ts +682 -0
  443. package/src/opencode-interrupt-plugin.ts +507 -0
  444. package/src/opencode.ts +1453 -0
  445. package/src/otto/branding.ts +23 -0
  446. package/src/otto/index.ts +22 -0
  447. package/src/otto-digital-twin.e2e.test.ts +199 -0
  448. package/src/otto-opencode-plugin-loading.e2e.test.ts +117 -0
  449. package/src/otto-opencode-plugin.test.ts +108 -0
  450. package/src/otto-opencode-plugin.ts +22 -0
  451. package/src/parse-permission-rules.test.ts +127 -0
  452. package/src/patch-text-parser.ts +107 -0
  453. package/src/plugin-logger.ts +84 -0
  454. package/src/privacy-sanitizer.ts +142 -0
  455. package/src/queue-advanced-abort.e2e.test.ts +382 -0
  456. package/src/queue-advanced-action-buttons.e2e.test.ts +268 -0
  457. package/src/queue-advanced-e2e-setup.ts +877 -0
  458. package/src/queue-advanced-footer.e2e.test.ts +591 -0
  459. package/src/queue-advanced-model-switch.e2e.test.ts +383 -0
  460. package/src/queue-advanced-permissions-typing.e2e.test.ts +246 -0
  461. package/src/queue-advanced-question.e2e.test.ts +316 -0
  462. package/src/queue-advanced-typing-interrupt.e2e.test.ts +146 -0
  463. package/src/queue-advanced-typing.e2e.test.ts +199 -0
  464. package/src/queue-drain-after-interactive-ui.e2e.test.ts +151 -0
  465. package/src/queue-interrupt-drain.e2e.test.ts +166 -0
  466. package/src/queue-question-select-drain.e2e.test.ts +327 -0
  467. package/src/runtime-idle-sweeper.ts +76 -0
  468. package/src/runtime-lifecycle.e2e.test.ts +651 -0
  469. package/src/schema.sql +174 -0
  470. package/src/sentry.ts +26 -0
  471. package/src/session-handler/agent-utils.ts +99 -0
  472. package/src/session-handler/event-stream-fixtures/real-session-action-buttons.jsonl +45 -0
  473. package/src/session-handler/event-stream-fixtures/real-session-footer-suppressed-on-pre-idle-interrupt.jsonl +40 -0
  474. package/src/session-handler/event-stream-fixtures/real-session-permission-external-file.jsonl +23 -0
  475. package/src/session-handler/event-stream-fixtures/real-session-task-normal.jsonl +22 -0
  476. package/src/session-handler/event-stream-fixtures/real-session-task-three-parallel-sleeps.jsonl +277 -0
  477. package/src/session-handler/event-stream-fixtures/real-session-task-user-interruption.jsonl +46 -0
  478. package/src/session-handler/event-stream-fixtures/session-abort-after-idle-race.jsonl +21 -0
  479. package/src/session-handler/event-stream-fixtures/session-concurrent-messages-serialized.jsonl +56 -0
  480. package/src/session-handler/event-stream-fixtures/session-explicit-abort.jsonl +44 -0
  481. package/src/session-handler/event-stream-fixtures/session-normal-completion.jsonl +29 -0
  482. package/src/session-handler/event-stream-fixtures/session-tool-call-noisy-stream.jsonl +29 -0
  483. package/src/session-handler/event-stream-fixtures/session-two-completions-same-session.jsonl +50 -0
  484. package/src/session-handler/event-stream-fixtures/session-user-interruption.jsonl +59 -0
  485. package/src/session-handler/event-stream-fixtures/session-voice-queued-followup.jsonl +52 -0
  486. package/src/session-handler/event-stream-state.test.ts +717 -0
  487. package/src/session-handler/event-stream-state.ts +706 -0
  488. package/src/session-handler/model-utils.ts +217 -0
  489. package/src/session-handler/opencode-session-event-log.ts +130 -0
  490. package/src/session-handler/thread-runtime-state.ts +247 -0
  491. package/src/session-handler/thread-session-runtime.ts +4440 -0
  492. package/src/session-handler.ts +15 -0
  493. package/src/session-search.test.ts +50 -0
  494. package/src/session-search.ts +148 -0
  495. package/src/session-title-rename.test.ts +130 -0
  496. package/src/skill-filter.test.ts +83 -0
  497. package/src/skill-filter.ts +42 -0
  498. package/src/startup-service.ts +200 -0
  499. package/src/startup-time.e2e.test.ts +373 -0
  500. package/src/store.ts +139 -0
  501. package/src/subagent-rate-limit-plugin.ts +218 -0
  502. package/src/system-message.test.ts +710 -0
  503. package/src/system-message.ts +814 -0
  504. package/src/task-runner.ts +725 -0
  505. package/src/task-schedule.test.ts +84 -0
  506. package/src/task-schedule.ts +317 -0
  507. package/src/test-utils.ts +451 -0
  508. package/src/thinking-utils.ts +61 -0
  509. package/src/thread-message-queue.e2e.test.ts +1350 -0
  510. package/src/tools.ts +430 -0
  511. package/src/undici.d.ts +12 -0
  512. package/src/undo-redo.e2e.test.ts +209 -0
  513. package/src/unnest-code-blocks.test.ts +713 -0
  514. package/src/unnest-code-blocks.ts +185 -0
  515. package/src/upgrade.ts +185 -0
  516. package/src/utils.test.ts +155 -0
  517. package/src/utils.ts +265 -0
  518. package/src/voice-attachment.ts +51 -0
  519. package/src/voice-handler.ts +908 -0
  520. package/src/voice-message.e2e.test.ts +1255 -0
  521. package/src/voice.test.ts +281 -0
  522. package/src/voice.ts +638 -0
  523. package/src/wait-session.ts +273 -0
  524. package/src/websockify.ts +101 -0
  525. package/src/worker-types.ts +64 -0
  526. package/src/worktree-lifecycle.e2e.test.ts +396 -0
  527. package/src/worktree-utils.ts +4 -0
  528. package/src/worktrees.test.ts +489 -0
  529. package/src/worktrees.ts +1370 -0
  530. package/src/xml.test.ts +38 -0
  531. package/src/xml.ts +121 -0
  532. package/dist/cli.d.ts +0 -3
  533. package/dist/cli.d.ts.map +0 -1
  534. package/dist/cli.js.map +0 -1
  535. package/dist/config.d.ts +0 -39
  536. package/dist/config.d.ts.map +0 -1
  537. package/dist/config.js.map +0 -1
  538. package/dist/config.test.d.ts +0 -2
  539. package/dist/config.test.d.ts.map +0 -1
  540. package/dist/config.test.js +0 -202
  541. package/dist/config.test.js.map +0 -1
  542. package/dist/detect.d.ts +0 -9
  543. package/dist/detect.d.ts.map +0 -1
  544. package/dist/detect.js +0 -40
  545. package/dist/detect.js.map +0 -1
  546. package/dist/detect.test.d.ts +0 -2
  547. package/dist/detect.test.d.ts.map +0 -1
  548. package/dist/detect.test.js +0 -26
  549. package/dist/detect.test.js.map +0 -1
  550. package/dist/docker.d.ts +0 -7
  551. package/dist/docker.d.ts.map +0 -1
  552. package/dist/docker.js +0 -17
  553. package/dist/docker.js.map +0 -1
  554. package/dist/docker.test.d.ts +0 -2
  555. package/dist/docker.test.d.ts.map +0 -1
  556. package/dist/docker.test.js +0 -12
  557. package/dist/docker.test.js.map +0 -1
  558. package/dist/health.d.ts +0 -31
  559. package/dist/health.d.ts.map +0 -1
  560. package/dist/health.js +0 -117
  561. package/dist/health.js.map +0 -1
  562. package/dist/health.test.d.ts +0 -2
  563. package/dist/health.test.d.ts.map +0 -1
  564. package/dist/health.test.js +0 -52
  565. package/dist/health.test.js.map +0 -1
  566. package/dist/index.d.ts +0 -20
  567. package/dist/index.d.ts.map +0 -1
  568. package/dist/index.js +0 -15
  569. package/dist/index.js.map +0 -1
  570. package/dist/index.test.d.ts +0 -2
  571. package/dist/index.test.d.ts.map +0 -1
  572. package/dist/index.test.js +0 -8
  573. package/dist/index.test.js.map +0 -1
  574. package/dist/installer.d.ts +0 -10
  575. package/dist/installer.d.ts.map +0 -1
  576. package/dist/installer.js +0 -50
  577. package/dist/installer.js.map +0 -1
  578. package/dist/installer.test.d.ts +0 -2
  579. package/dist/installer.test.d.ts.map +0 -1
  580. package/dist/installer.test.js +0 -43
  581. package/dist/installer.test.js.map +0 -1
  582. package/dist/lifecycle.d.ts +0 -10
  583. package/dist/lifecycle.d.ts.map +0 -1
  584. package/dist/lifecycle.js +0 -45
  585. package/dist/lifecycle.js.map +0 -1
  586. package/dist/lifecycle.test.d.ts +0 -2
  587. package/dist/lifecycle.test.d.ts.map +0 -1
  588. package/dist/lifecycle.test.js +0 -20
  589. package/dist/lifecycle.test.js.map +0 -1
  590. package/dist/manifest.d.ts +0 -18
  591. package/dist/manifest.d.ts.map +0 -1
  592. package/dist/manifest.js +0 -30
  593. package/dist/manifest.js.map +0 -1
  594. package/dist/skills-baseline.d.ts +0 -7
  595. package/dist/skills-baseline.d.ts.map +0 -1
  596. package/dist/skills-baseline.js +0 -9
  597. package/dist/skills-baseline.js.map +0 -1
  598. package/dist/skills.d.ts +0 -110
  599. package/dist/skills.d.ts.map +0 -1
  600. package/dist/skills.js +0 -429
  601. package/dist/skills.js.map +0 -1
  602. package/dist/skills.test.d.ts +0 -2
  603. package/dist/skills.test.d.ts.map +0 -1
  604. package/dist/skills.test.js +0 -416
  605. package/dist/skills.test.js.map +0 -1
  606. package/dist/sync.d.ts +0 -10
  607. package/dist/sync.d.ts.map +0 -1
  608. package/dist/sync.js +0 -39
  609. package/dist/sync.js.map +0 -1
  610. package/dist/tenant.d.ts +0 -13
  611. package/dist/tenant.d.ts.map +0 -1
  612. package/dist/tenant.js +0 -105
  613. package/dist/tenant.js.map +0 -1
  614. package/dist/tenant.test.d.ts +0 -2
  615. package/dist/tenant.test.d.ts.map +0 -1
  616. package/dist/tenant.test.js +0 -37
  617. package/dist/tenant.test.js.map +0 -1
  618. package/src/config.test.ts +0 -237
  619. package/src/detect.test.ts +0 -29
  620. package/src/detect.ts +0 -52
  621. package/src/docker.test.ts +0 -12
  622. package/src/docker.ts +0 -23
  623. package/src/health.test.ts +0 -61
  624. package/src/health.ts +0 -158
  625. package/src/index.test.ts +0 -8
  626. package/src/index.ts +0 -62
  627. package/src/installer.test.ts +0 -52
  628. package/src/installer.ts +0 -62
  629. package/src/lifecycle.test.ts +0 -23
  630. package/src/lifecycle.ts +0 -49
  631. package/src/manifest.ts +0 -42
  632. package/src/skills-baseline.ts +0 -14
  633. package/src/skills.test.ts +0 -503
  634. package/src/skills.ts +0 -512
  635. package/src/sync.ts +0 -53
  636. package/src/tenant.test.ts +0 -49
  637. package/src/tenant.ts +0 -120
@@ -1,37 +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 { deriveComposeProjectName, ensureTenantMemoryLayout, ensureTenantScaffold, resolveTenantImage, resolveTenantMode, } from "./tenant.js";
6
- describe("tenant", () => {
7
- it("uses otto-<folder_name> default", () => {
8
- expect(deriveComposeProjectName("/tmp/my-tenant")).toBe("otto-my-tenant");
9
- });
10
- it("uses image from compose.yml when env override missing", () => {
11
- const resolved = resolveTenantImage({
12
- composeImage: "otto-assistant/otto:stable",
13
- envImage: undefined,
14
- });
15
- expect(resolved).toBe("otto-assistant/otto:stable");
16
- });
17
- it("defaults to safe mode when OTTO_MODE is unset", () => {
18
- expect(resolveTenantMode(undefined)).toBe("safe");
19
- });
20
- it("creates required memory files and mempalace dir", () => {
21
- const root = fs.mkdtempSync(path.join(os.tmpdir(), "otto-memory-"));
22
- const report = ensureTenantMemoryLayout(root);
23
- expect(report.created).toContain("AGENTS.md");
24
- expect(report.created).toContain("soul.md");
25
- expect(report.created).toContain("persona.md");
26
- expect(report.created).toContain("mempalace/");
27
- });
28
- it("creates compose scaffold for tenant", () => {
29
- const tenantPath = fs.mkdtempSync(path.join(os.tmpdir(), "otto-tenant-"));
30
- const report = ensureTenantScaffold(tenantPath);
31
- expect(report.created).toContain("compose.yml");
32
- expect(fs.existsSync(path.join(tenantPath, "compose.yml"))).toBe(true);
33
- expect(fs.existsSync(path.join(tenantPath, "memory", "AGENTS.md"))).toBe(true);
34
- expect(fs.existsSync(path.join(tenantPath, "projects"))).toBe(true);
35
- });
36
- });
37
- //# sourceMappingURL=tenant.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tenant.test.js","sourceRoot":"","sources":["../src/tenant.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,aAAa,CAAA;AAEpB,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;IACtB,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,wBAAwB,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;IAC3E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,QAAQ,GAAG,kBAAkB,CAAC;YAClC,YAAY,EAAE,4BAA4B;YAC1C,QAAQ,EAAE,SAAS;SACpB,CAAC,CAAA;QACF,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACnD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC,CAAA;QACnE,MAAM,MAAM,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAA;QAE7C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAA;QAC7C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;QAC3C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAA;QAC9C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAA;IAChD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,UAAU,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC,CAAA;QACzE,MAAM,MAAM,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAA;QAE/C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAA;QAC/C,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACtE,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC9E,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACrE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -1,237 +0,0 @@
1
- import { describe, expect, it } from "vitest"
2
- import {
3
- mergePlugins,
4
- readOttoConfig,
5
- writeOttoConfig,
6
- OTTO_DEFAULTS,
7
- buildSubagentThreadPolicy,
8
- mergeAgentPrompts,
9
- ensureSubagentThreadSkill,
10
- readOpenCodeConfigState,
11
- type OpenCodeConfig,
12
- type OttoConfig,
13
- } from "./config.js"
14
- import fs from "node:fs"
15
- import path from "node:path"
16
- import os from "node:os"
17
-
18
- describe("config", () => {
19
- it("adds plugin to empty config", () => {
20
- const config: OpenCodeConfig = {}
21
- const result = mergePlugins(config, "mempalace")
22
- expect(result.plugin).toEqual(["mempalace"])
23
- })
24
-
25
- it("appends plugin to existing array", () => {
26
- const config: OpenCodeConfig = { plugin: ["existing-plugin"] }
27
- const result = mergePlugins(config, "mempalace")
28
- expect(result.plugin).toEqual(["existing-plugin", "mempalace"])
29
- })
30
-
31
- it("does not duplicate existing plugin", () => {
32
- const config: OpenCodeConfig = { plugin: ["mempalace"] }
33
- const result = mergePlugins(config, "mempalace")
34
- expect(result.plugin).toEqual(["mempalace"])
35
- })
36
-
37
- it("preserves other config fields", () => {
38
- const config: OpenCodeConfig = {
39
- model: "gpt-4",
40
- plugin: ["existing"],
41
- provider: { cursor: { name: "Cursor" } },
42
- }
43
- const result = mergePlugins(config, "mempalace")
44
- expect(result.model).toBe("gpt-4")
45
- expect(result.provider).toEqual({ cursor: { name: "Cursor" } })
46
- expect(result.plugin).toEqual(["existing", "mempalace"])
47
- })
48
-
49
- it("readOttoConfig returns defaults when otto.json missing", () => {
50
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "otto-test-"))
51
- try {
52
- const result = readOttoConfig(tmpDir)
53
- expect(result.subagentThreads).toEqual({
54
- enabled: true,
55
- askBeforeDelete: true,
56
- autoDeleteOnComplete: false,
57
- })
58
- } finally {
59
- fs.rmSync(tmpDir, { recursive: true, force: true })
60
- }
61
- })
62
-
63
- it("writeOttoConfig + readOttoConfig round-trip", () => {
64
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "otto-test-"))
65
- try {
66
- const config: OttoConfig = {
67
- subagentThreads: {
68
- enabled: false,
69
- askBeforeDelete: false,
70
- autoDeleteOnComplete: true,
71
- },
72
- }
73
- const written = writeOttoConfig(config, tmpDir)
74
- expect(written).toBe(true)
75
-
76
- const read = readOttoConfig(tmpDir)
77
- expect(read.subagentThreads).toEqual({
78
- enabled: false,
79
- askBeforeDelete: false,
80
- autoDeleteOnComplete: true,
81
- })
82
-
83
- // Idempotent
84
- const writtenAgain = writeOttoConfig(config, tmpDir)
85
- expect(writtenAgain).toBe(false)
86
- } finally {
87
- fs.rmSync(tmpDir, { recursive: true, force: true })
88
- }
89
- })
90
-
91
- it("readOttoConfig fills defaults for partial config", () => {
92
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "otto-test-"))
93
- try {
94
- fs.mkdirSync(tmpDir, { recursive: true })
95
- fs.writeFileSync(
96
- path.join(tmpDir, "otto.json"),
97
- JSON.stringify({ subagentThreads: { enabled: false } }, null, 2) + "\n",
98
- "utf-8",
99
- )
100
- const result = readOttoConfig(tmpDir)
101
- expect(result.subagentThreads.enabled).toBe(false)
102
- expect(result.subagentThreads.askBeforeDelete).toBe(true) // default
103
- expect(result.subagentThreads.autoDeleteOnComplete).toBe(false) // default
104
- } finally {
105
- fs.rmSync(tmpDir, { recursive: true, force: true })
106
- }
107
- })
108
-
109
- it("policy includes kimaki send commands when enabled", () => {
110
- const ottoConfig: OttoConfig = {
111
- subagentThreads: { enabled: true, askBeforeDelete: true, autoDeleteOnComplete: false },
112
- }
113
- const policy = buildSubagentThreadPolicy(ottoConfig)
114
-
115
- expect(policy).toContain("Otto subagent policy (must follow):")
116
- expect(policy).toContain("subagent_threads_enabled: true")
117
- expect(policy).toContain("kimaki send")
118
- expect(policy).toContain("--channel")
119
- expect(policy).toContain("--wait")
120
- expect(policy).toContain("ask the user")
121
- })
122
-
123
- it("policy is short when threads disabled", () => {
124
- const ottoConfig: OttoConfig = {
125
- subagentThreads: { enabled: false, askBeforeDelete: true, autoDeleteOnComplete: false },
126
- }
127
- const policy = buildSubagentThreadPolicy(ottoConfig)
128
-
129
- expect(policy).toContain("subagent_threads_enabled: false")
130
- expect(policy).toContain("without creating Discord threads")
131
- expect(policy).not.toContain("kimaki send")
132
- })
133
-
134
- it("policy includes auto-archive when autoDeleteOnComplete is true", () => {
135
- const ottoConfig: OttoConfig = {
136
- subagentThreads: { enabled: true, askBeforeDelete: false, autoDeleteOnComplete: true },
137
- }
138
- const policy = buildSubagentThreadPolicy(ottoConfig)
139
-
140
- expect(policy).toContain("auto_delete_on_complete: true")
141
- expect(policy).toContain("kimaki session archive")
142
- expect(policy).not.toContain("ask the user")
143
- })
144
-
145
- it("injects policy into existing agent prompt", () => {
146
- const config: OpenCodeConfig = {
147
- agent: { build: { prompt: "existing rule" } },
148
- }
149
-
150
- const ottoConfig: OttoConfig = OTTO_DEFAULTS
151
- const policy = buildSubagentThreadPolicy(ottoConfig)
152
- const result = mergeAgentPrompts(config, policy)
153
- const prompt = (result.agent as Record<string, { prompt?: string }>).build.prompt
154
-
155
- expect(prompt).toContain("existing rule")
156
- expect(prompt).toContain("Otto subagent policy (must follow):")
157
- expect(prompt).toContain("subagent_threads_enabled: true")
158
- })
159
-
160
- it("does not duplicate policy when already present", () => {
161
- const ottoConfig: OttoConfig = OTTO_DEFAULTS
162
- const policy = buildSubagentThreadPolicy(ottoConfig)
163
- const config: OpenCodeConfig = {
164
- agent: { build: { prompt: policy } },
165
- }
166
-
167
- const result = mergeAgentPrompts(config, policy)
168
- const prompt = (result.agent as Record<string, { prompt?: string }>).build.prompt ?? ""
169
- expect(prompt.match(/Otto subagent policy \(must follow\):/g)?.length).toBe(1)
170
- })
171
-
172
- it("creates build agent with policy when agent section is empty", () => {
173
- const ottoConfig: OttoConfig = OTTO_DEFAULTS
174
- const policy = buildSubagentThreadPolicy(ottoConfig)
175
- const result = mergeAgentPrompts({}, policy)
176
- const agents = result.agent as Record<string, { prompt?: string }>
177
-
178
- expect(agents.build?.prompt).toContain("Otto subagent policy (must follow):")
179
- })
180
-
181
- it("readOpenCodeConfigState returns missing when file absent", () => {
182
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "otto-test-"))
183
- try {
184
- const result = readOpenCodeConfigState(tmpDir)
185
- expect(result.status).toBe("missing")
186
- expect(result.config).toEqual({})
187
- } finally {
188
- fs.rmSync(tmpDir, { recursive: true, force: true })
189
- }
190
- })
191
-
192
- it("readOpenCodeConfigState returns ok for valid JSON", () => {
193
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "otto-test-"))
194
- try {
195
- fs.mkdirSync(tmpDir, { recursive: true })
196
- fs.writeFileSync(path.join(tmpDir, "opencode.json"), "{\"model\":\"x\"}\n", "utf-8")
197
- const result = readOpenCodeConfigState(tmpDir)
198
- expect(result.status).toBe("ok")
199
- expect(result.config).toEqual({ model: "x" })
200
- } finally {
201
- fs.rmSync(tmpDir, { recursive: true, force: true })
202
- }
203
- })
204
-
205
- it("readOpenCodeConfigState returns invalid for broken JSON", () => {
206
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "otto-test-"))
207
- try {
208
- fs.mkdirSync(tmpDir, { recursive: true })
209
- fs.writeFileSync(path.join(tmpDir, "opencode.json"), "{not json", "utf-8")
210
- const result = readOpenCodeConfigState(tmpDir)
211
- expect(result.status).toBe("invalid")
212
- expect(result.config).toEqual({})
213
- } finally {
214
- fs.rmSync(tmpDir, { recursive: true, force: true })
215
- }
216
- })
217
-
218
- it("ensureSubagentThreadSkill creates skill file", () => {
219
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "otto-test-"))
220
- try {
221
- const created = ensureSubagentThreadSkill(tmpDir)
222
- expect(created).toBe(true)
223
-
224
- const skillPath = path.join(tmpDir, "skills", "otto-subagent-threads", "SKILL.md")
225
- const content = fs.readFileSync(skillPath, "utf-8")
226
- expect(content).toContain("name: otto-subagent-threads")
227
- expect(content).toContain("kimaki send")
228
- expect(content).toContain("kimaki session archive")
229
-
230
- // Idempotent — second call returns false
231
- const second = ensureSubagentThreadSkill(tmpDir)
232
- expect(second).toBe(false)
233
- } finally {
234
- fs.rmSync(tmpDir, { recursive: true, force: true })
235
- }
236
- })
237
- })
@@ -1,29 +0,0 @@
1
- import { describe, expect, it } from "vitest"
2
- import { getInstalledVersion, detectPackage, type InstalledPackage } from "./detect.js"
3
-
4
- describe("detect", () => {
5
- it("returns null for non-existent package", { timeout: 30_000 }, () => {
6
- const result = getInstalledVersion("nonexistent-package-xyz-123")
7
- expect(result).toBeNull()
8
- })
9
-
10
- // These tests require kimaki to be globally installed
11
- const hasKimakiInstalled = getInstalledVersion("kimaki") !== null
12
- const describeIfInstalled = hasKimakiInstalled ? describe : describe.skip
13
-
14
- describeIfInstalled("with kimaki installed", () => {
15
- it("detects an existing global package", () => {
16
- const result = getInstalledVersion("kimaki")
17
- expect(result).not.toBeNull()
18
- expect(result).toMatch(/^\d+\.\d+\.\d+/)
19
- })
20
-
21
- it("detectPackage returns full info", async () => {
22
- const result = await detectPackage("kimaki")
23
- expect(result).toEqual({
24
- name: "kimaki",
25
- installed: expect.any(String),
26
- })
27
- })
28
- })
29
- })
package/src/detect.ts DELETED
@@ -1,52 +0,0 @@
1
- import { execFileSync } from "node:child_process"
2
-
3
- export interface InstalledPackage {
4
- name: string
5
- installed: string | null
6
- }
7
-
8
- let globalVersionCache: Map<string, string> | null = null
9
-
10
- function loadGlobalPackageVersions(): Map<string, string> {
11
- if (globalVersionCache) {
12
- return globalVersionCache
13
- }
14
- const map = new Map<string, string>()
15
- try {
16
- const output = execFileSync(
17
- process.platform === "win32" ? "npm.cmd" : "npm",
18
- ["list", "-g", "--json", "--depth=0"],
19
- {
20
- encoding: "utf-8",
21
- stdio: ["pipe", "pipe", "pipe"],
22
- },
23
- )
24
- const parsed = JSON.parse(output) as { dependencies?: Record<string, { version?: string }> }
25
- const deps = parsed.dependencies ?? {}
26
- for (const [name, info] of Object.entries(deps)) {
27
- if (info?.version) {
28
- map.set(name, info.version)
29
- }
30
- }
31
- } catch {
32
- // leave map empty — callers treat missing entries as not installed
33
- }
34
- globalVersionCache = map
35
- return map
36
- }
37
-
38
- /** For tests: reset cached `npm list -g` result between cases. */
39
- export function clearGlobalNpmListCache(): void {
40
- globalVersionCache = null
41
- }
42
-
43
- export function getInstalledVersion(packageName: string): string | null {
44
- return loadGlobalPackageVersions().get(packageName) ?? null
45
- }
46
-
47
- export async function detectPackage(name: string): Promise<InstalledPackage> {
48
- return {
49
- name,
50
- installed: getInstalledVersion(name),
51
- }
52
- }
@@ -1,12 +0,0 @@
1
- import { describe, expect, it } from "vitest"
2
- import { buildComposeCommand } from "./docker.js"
3
-
4
- describe("docker", () => {
5
- it("builds docker compose command with tenant path", () => {
6
- const cmd = buildComposeCommand("/tmp/tenant-a", ["up", "-d"])
7
- expect(cmd).toEqual({
8
- command: "docker",
9
- args: ["compose", "-f", "/tmp/tenant-a/compose.yml", "up", "-d"],
10
- })
11
- })
12
- })
package/src/docker.ts DELETED
@@ -1,23 +0,0 @@
1
- import { execSync } from "node:child_process"
2
- import path from "node:path"
3
-
4
- export interface DockerComposeCommand {
5
- command: string
6
- args: string[]
7
- }
8
-
9
- export function buildComposeCommand(tenantPath: string, subArgs: string[]): DockerComposeCommand {
10
- const composeFile = path.join(path.resolve(tenantPath), "compose.yml")
11
- return {
12
- command: "docker",
13
- args: ["compose", "-f", composeFile, ...subArgs],
14
- }
15
- }
16
-
17
- export function runCompose(tenantPath: string, subArgs: string[]): void {
18
- const built = buildComposeCommand(tenantPath, subArgs)
19
- execSync(`${built.command} ${built.args.join(" ")}`, {
20
- stdio: "inherit",
21
- timeout: 120_000,
22
- })
23
- }
@@ -1,61 +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
- checkPackagePresence,
7
- checkConfigHealth,
8
- checkDirectoryHealth,
9
- checkTenantHealth,
10
- } from "./health.js"
11
-
12
- describe("health", () => {
13
- it("checkPackagePresence returns results for all manifest packages", { timeout: 30_000 }, () => {
14
- const results = checkPackagePresence()
15
- expect(results).toHaveLength(2) // opencode-ai + kimaki (not mempalace — it's a plugin)
16
- for (const r of results) {
17
- expect(r).toHaveProperty("name")
18
- expect(r).toHaveProperty("installed")
19
- expect(r).toHaveProperty("status")
20
- }
21
- })
22
-
23
- it("checkConfigHealth returns structured result", () => {
24
- const result = checkConfigHealth()
25
- expect(result).toHaveProperty("opencodeJson")
26
- expect(result).toHaveProperty("ottoJson")
27
- expect(result).toHaveProperty("agentMemoryJson")
28
- expect(result).toHaveProperty("memoryPluginEnabled")
29
- expect(result).toHaveProperty("subagentPolicyInjected")
30
- expect(result).toHaveProperty("subagentThreadsEnabled")
31
- expect(result).toHaveProperty("subagentThreadsAskBeforeDelete")
32
- expect(result).toHaveProperty("subagentThreadsAutoDelete")
33
- expect(result).toHaveProperty("plugins")
34
- expect(result).toHaveProperty("kimakiRunning")
35
- expect(Array.isArray(result.plugins)).toBe(true)
36
- })
37
-
38
- it("checkDirectoryHealth returns results", () => {
39
- const results = checkDirectoryHealth()
40
- expect(Array.isArray(results)).toBe(true)
41
- expect(results.length).toBeGreaterThan(0)
42
- })
43
-
44
- it("checkTenantHealth reports missing memory bind root as error", () => {
45
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "otto-health-"))
46
- const health = checkTenantHealth({ tenantPath: tmpDir })
47
- const memoryItem = health.find((h) => h.name === "memory root")
48
- expect(memoryItem).toBeDefined()
49
- expect(memoryItem!.status).toBe("error")
50
- })
51
-
52
- it("checkTenantHealth reports ok when all paths present", () => {
53
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "otto-health-"))
54
- fs.mkdirSync(path.join(tmpDir, "memory"))
55
- fs.mkdirSync(path.join(tmpDir, "projects"))
56
- fs.writeFileSync(path.join(tmpDir, "compose.yml"), "services:", "utf-8")
57
- const health = checkTenantHealth({ tenantPath: tmpDir })
58
- const errors = health.filter((h) => h.status === "error")
59
- expect(errors).toHaveLength(0)
60
- })
61
- })
package/src/health.ts DELETED
@@ -1,158 +0,0 @@
1
- import fs from "node:fs"
2
- import path from "node:path"
3
- import { getInstalledVersion } from "./detect.js"
4
- import { readOpenCodeConfigState, readOttoConfig, type OpenCodeConfig } from "./config.js"
5
- import { MANIFEST, OPENCODE_CONFIG_DIR, KIMAKI_DATA_DIR } from "./manifest.js"
6
- import { isKimakiRunning } from "./lifecycle.js"
7
-
8
- export interface HealthResult {
9
- name: string
10
- status: "ok" | "warn" | "error"
11
- message: string
12
- }
13
-
14
- export interface PackageCheck {
15
- name: string
16
- installed: string | null
17
- required: string
18
- status: "ok" | "missing"
19
- }
20
-
21
- const SUBAGENT_POLICY_MARKER = "Otto subagent policy (must follow):"
22
-
23
- function subagentPolicyInjectedInPrompts(config: OpenCodeConfig): boolean {
24
- const agent = config.agent
25
- if (!agent || typeof agent !== "object" || Array.isArray(agent)) {
26
- return false
27
- }
28
- for (const value of Object.values(agent as Record<string, unknown>)) {
29
- if (value && typeof value === "object" && !Array.isArray(value)) {
30
- const prompt = (value as { prompt?: unknown }).prompt
31
- if (typeof prompt === "string" && prompt.includes(SUBAGENT_POLICY_MARKER)) {
32
- return true
33
- }
34
- }
35
- }
36
- return false
37
- }
38
-
39
- export function checkPackagePresence(): PackageCheck[] {
40
- return Object.entries(MANIFEST.packages).map(([name, required]) => {
41
- const installed = getInstalledVersion(name)
42
- return { name, installed, required, status: installed ? ("ok" as const) : ("missing" as const) }
43
- })
44
- }
45
-
46
- export interface ConfigHealth {
47
- opencodeJson: "ok" | "missing" | "error"
48
- ottoJson: "ok" | "missing"
49
- agentMemoryJson: "ok" | "missing"
50
- memoryPluginEnabled: boolean
51
- subagentPolicyInjected: boolean
52
- subagentThreadsEnabled: boolean
53
- subagentThreadsAskBeforeDelete: boolean
54
- subagentThreadsAutoDelete: boolean
55
- plugins: string[]
56
- kimakiRunning: boolean
57
- }
58
-
59
- export function checkConfigHealth(): ConfigHealth {
60
- const configDir = OPENCODE_CONFIG_DIR()
61
- const agentMemoryPath = path.join(configDir, "agent-memory.json")
62
- const ottoJsonPath = path.join(configDir, "otto.json")
63
-
64
- const state = readOpenCodeConfigState()
65
- const opencodeJson: ConfigHealth["opencodeJson"] =
66
- state.status === "missing" ? "missing" : state.status === "invalid" ? "error" : "ok"
67
- const agentMemoryJson: ConfigHealth["agentMemoryJson"] = fs.existsSync(agentMemoryPath) ? "ok" : "missing"
68
- const ottoJson: ConfigHealth["ottoJson"] = fs.existsSync(ottoJsonPath) ? "ok" : "missing"
69
-
70
- const config = state.status === "ok" ? state.config : {}
71
- const configuredPlugins = config.plugin ?? []
72
- const memoryPluginEnabled = configuredPlugins.includes("mempalace")
73
-
74
- const subagentPolicyInjected = subagentPolicyInjectedInPrompts(config)
75
-
76
- // Read Otto config from otto.json (separate from opencode.json)
77
- const ottoConfig = readOttoConfig()
78
- const subagentThreadsEnabled = ottoConfig.subagentThreads.enabled
79
- const subagentThreadsAskBeforeDelete = ottoConfig.subagentThreads.askBeforeDelete
80
- const subagentThreadsAutoDelete = ottoConfig.subagentThreads.autoDeleteOnComplete
81
- const kimakiRunning = isKimakiRunning()
82
-
83
- return {
84
- opencodeJson,
85
- ottoJson,
86
- agentMemoryJson,
87
- memoryPluginEnabled,
88
- subagentPolicyInjected,
89
- subagentThreadsEnabled,
90
- subagentThreadsAskBeforeDelete,
91
- subagentThreadsAutoDelete,
92
- plugins: configuredPlugins,
93
- kimakiRunning,
94
- }
95
- }
96
-
97
- export function checkDirectoryHealth(): HealthResult[] {
98
- const results: HealthResult[] = []
99
- const dirs = [
100
- { p: OPENCODE_CONFIG_DIR(), label: "opencode config dir" },
101
- { p: path.join(OPENCODE_CONFIG_DIR(), "memory"), label: "opencode memory dir" },
102
- { p: path.join(OPENCODE_CONFIG_DIR(), "journal"), label: "opencode journal dir" },
103
- { p: KIMAKI_DATA_DIR(), label: "kimaki data dir" },
104
- ]
105
-
106
- for (const { p, label } of dirs) {
107
- if (fs.existsSync(p)) {
108
- results.push({ name: label, status: "ok", message: `exists: ${p}` })
109
- } else {
110
- results.push({ name: label, status: "warn", message: `missing: ${p}` })
111
- }
112
- }
113
- return results
114
- }
115
-
116
- export interface TenantHealthInput {
117
- tenantPath: string
118
- }
119
-
120
- export function checkTenantHealth(input: TenantHealthInput): HealthResult[] {
121
- const results: HealthResult[] = []
122
- const { tenantPath } = input
123
-
124
- if (fs.existsSync(tenantPath)) {
125
- results.push({ name: "tenant path", status: "ok", message: `exists: ${tenantPath}` })
126
- } else {
127
- results.push({ name: "tenant path", status: "error", message: `missing: ${tenantPath}` })
128
- return results
129
- }
130
-
131
- const composePath = path.join(tenantPath, "compose.yml")
132
- if (fs.existsSync(composePath)) {
133
- results.push({ name: "compose.yml", status: "ok", message: "present" })
134
- } else {
135
- results.push({ name: "compose.yml", status: "error", message: "missing — run `otto tenant init`" })
136
- }
137
-
138
- const memoryPath = path.join(tenantPath, "memory")
139
- if (fs.existsSync(memoryPath)) {
140
- try {
141
- fs.accessSync(memoryPath, fs.constants.W_OK)
142
- results.push({ name: "memory root", status: "ok", message: "writable" })
143
- } catch {
144
- results.push({ name: "memory root", status: "error", message: "not writable" })
145
- }
146
- } else {
147
- results.push({ name: "memory root", status: "error", message: "missing — run `otto tenant init`" })
148
- }
149
-
150
- const projectsPath = path.join(tenantPath, "projects")
151
- if (fs.existsSync(projectsPath)) {
152
- results.push({ name: "projects mount", status: "ok", message: "present" })
153
- } else {
154
- results.push({ name: "projects mount", status: "warn", message: "missing — will be created on first up" })
155
- }
156
-
157
- return results
158
- }
package/src/index.test.ts DELETED
@@ -1,8 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { ottoVersion } from "./index.js";
3
-
4
- describe("otto", () => {
5
- it("returns version string", () => {
6
- expect(ottoVersion()).toMatch(/^\d+\.\d+\.\d+$/);
7
- });
8
- });