@otto-assistant/otto 0.1.2 → 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 (638) 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/README.md +0 -142
  533. package/dist/cli.d.ts +0 -3
  534. package/dist/cli.d.ts.map +0 -1
  535. package/dist/cli.js.map +0 -1
  536. package/dist/config.d.ts +0 -39
  537. package/dist/config.d.ts.map +0 -1
  538. package/dist/config.js.map +0 -1
  539. package/dist/config.test.d.ts +0 -2
  540. package/dist/config.test.d.ts.map +0 -1
  541. package/dist/config.test.js +0 -202
  542. package/dist/config.test.js.map +0 -1
  543. package/dist/detect.d.ts +0 -9
  544. package/dist/detect.d.ts.map +0 -1
  545. package/dist/detect.js +0 -40
  546. package/dist/detect.js.map +0 -1
  547. package/dist/detect.test.d.ts +0 -2
  548. package/dist/detect.test.d.ts.map +0 -1
  549. package/dist/detect.test.js +0 -26
  550. package/dist/detect.test.js.map +0 -1
  551. package/dist/docker.d.ts +0 -7
  552. package/dist/docker.d.ts.map +0 -1
  553. package/dist/docker.js +0 -17
  554. package/dist/docker.js.map +0 -1
  555. package/dist/docker.test.d.ts +0 -2
  556. package/dist/docker.test.d.ts.map +0 -1
  557. package/dist/docker.test.js +0 -12
  558. package/dist/docker.test.js.map +0 -1
  559. package/dist/health.d.ts +0 -31
  560. package/dist/health.d.ts.map +0 -1
  561. package/dist/health.js +0 -117
  562. package/dist/health.js.map +0 -1
  563. package/dist/health.test.d.ts +0 -2
  564. package/dist/health.test.d.ts.map +0 -1
  565. package/dist/health.test.js +0 -52
  566. package/dist/health.test.js.map +0 -1
  567. package/dist/index.d.ts +0 -20
  568. package/dist/index.d.ts.map +0 -1
  569. package/dist/index.js +0 -15
  570. package/dist/index.js.map +0 -1
  571. package/dist/index.test.d.ts +0 -2
  572. package/dist/index.test.d.ts.map +0 -1
  573. package/dist/index.test.js +0 -8
  574. package/dist/index.test.js.map +0 -1
  575. package/dist/installer.d.ts +0 -10
  576. package/dist/installer.d.ts.map +0 -1
  577. package/dist/installer.js +0 -50
  578. package/dist/installer.js.map +0 -1
  579. package/dist/installer.test.d.ts +0 -2
  580. package/dist/installer.test.d.ts.map +0 -1
  581. package/dist/installer.test.js +0 -43
  582. package/dist/installer.test.js.map +0 -1
  583. package/dist/lifecycle.d.ts +0 -10
  584. package/dist/lifecycle.d.ts.map +0 -1
  585. package/dist/lifecycle.js +0 -45
  586. package/dist/lifecycle.js.map +0 -1
  587. package/dist/lifecycle.test.d.ts +0 -2
  588. package/dist/lifecycle.test.d.ts.map +0 -1
  589. package/dist/lifecycle.test.js +0 -20
  590. package/dist/lifecycle.test.js.map +0 -1
  591. package/dist/manifest.d.ts +0 -18
  592. package/dist/manifest.d.ts.map +0 -1
  593. package/dist/manifest.js +0 -30
  594. package/dist/manifest.js.map +0 -1
  595. package/dist/skills-baseline.d.ts +0 -7
  596. package/dist/skills-baseline.d.ts.map +0 -1
  597. package/dist/skills-baseline.js +0 -9
  598. package/dist/skills-baseline.js.map +0 -1
  599. package/dist/skills.d.ts +0 -110
  600. package/dist/skills.d.ts.map +0 -1
  601. package/dist/skills.js +0 -429
  602. package/dist/skills.js.map +0 -1
  603. package/dist/skills.test.d.ts +0 -2
  604. package/dist/skills.test.d.ts.map +0 -1
  605. package/dist/skills.test.js +0 -416
  606. package/dist/skills.test.js.map +0 -1
  607. package/dist/sync.d.ts +0 -10
  608. package/dist/sync.d.ts.map +0 -1
  609. package/dist/sync.js +0 -39
  610. package/dist/sync.js.map +0 -1
  611. package/dist/tenant.d.ts +0 -13
  612. package/dist/tenant.d.ts.map +0 -1
  613. package/dist/tenant.js +0 -105
  614. package/dist/tenant.js.map +0 -1
  615. package/dist/tenant.test.d.ts +0 -2
  616. package/dist/tenant.test.d.ts.map +0 -1
  617. package/dist/tenant.test.js +0 -37
  618. package/dist/tenant.test.js.map +0 -1
  619. package/src/config.test.ts +0 -237
  620. package/src/detect.test.ts +0 -29
  621. package/src/detect.ts +0 -52
  622. package/src/docker.test.ts +0 -12
  623. package/src/docker.ts +0 -23
  624. package/src/health.test.ts +0 -61
  625. package/src/health.ts +0 -158
  626. package/src/index.test.ts +0 -8
  627. package/src/index.ts +0 -62
  628. package/src/installer.test.ts +0 -52
  629. package/src/installer.ts +0 -62
  630. package/src/lifecycle.test.ts +0 -23
  631. package/src/lifecycle.ts +0 -49
  632. package/src/manifest.ts +0 -42
  633. package/src/skills-baseline.ts +0 -14
  634. package/src/skills.test.ts +0 -503
  635. package/src/skills.ts +0 -512
  636. package/src/sync.ts +0 -53
  637. package/src/tenant.test.ts +0 -49
  638. package/src/tenant.ts +0 -120
@@ -0,0 +1,814 @@
1
+ // OpenCode session prompt helpers.
2
+ // Creates the session-stable system message injected into every OpenCode
3
+ // session, plus per-turn synthetic context for Discord/user/worktree metadata.
4
+ // Keep per-message data out of the system prompt so prompt caching can reuse
5
+ // the same session prefix across turns.
6
+
7
+ import { getDataDir } from "./config.js";
8
+ import { store } from "./store.js";
9
+
10
+ function getCritiqueInstructions(sessionId: string) {
11
+ return `
12
+ ## showing diffs
13
+
14
+ IMPORTANT: After editing any files, you MUST execute the critique command using the Bash tool to get a diff URL, then share that URL with the user.
15
+ IMPORTANT: The user cannot see tool results directly. If critique prints a URL in the Bash tool output, you MUST copy that URL into your final message yourself.
16
+ IMPORTANT: When the user asks to see a diff, show a diff, or review changes, ALWAYS use critique to generate a web URL instead of showing raw git diff output.
17
+
18
+ Typical usage examples:
19
+
20
+ # Share working tree changes
21
+ bunx critique --web "Describe pending changes"
22
+
23
+ # Share staged changes
24
+ bunx critique --staged --web "Describe staged changes"
25
+
26
+ # Share changes since base branch (use when you're on a feature branch)
27
+ bunx critique main --web "Describe branch changes"
28
+
29
+ # Share new-branch changes compared to main
30
+ bunx critique main...new-branch --web "Describe branch changes"
31
+
32
+ # Share a single commit
33
+ bunx critique --commit HEAD --web "Describe latest commit"
34
+
35
+ If there are other unrelated changes in the working directory, filter to only show the files you edited:
36
+
37
+ # Share only specific files
38
+ bunx critique --web "Fix database connection retry" --filter "path/to/file1.ts" --filter "path/to/file2.ts"
39
+
40
+ Do this in case you committed the changes yourself (only if the user asks so, never commit otherwise).
41
+
42
+ To compare two branches:
43
+
44
+ bunx critique main feature-branch --web "Compare branches"
45
+
46
+ The command outputs a URL - share that URL with the user so they can see the diff.
47
+
48
+ ### always show diff at end of session
49
+
50
+ If you edited any files during the current session, you MUST run \`bunx critique --web\` at the end of your final message to generate a diff URL and share it with the user. This applies even if the user did not ask to see a diff — always show what changed. Pass the file paths you edited as \`--filter\` arguments so the diff only includes your changes. Skip this only if the session was purely read-only (no file edits, no writes).
51
+ The final user-facing message must include the actual critique URL as plain text or markdown link, because the user cannot see the Bash tool output.
52
+
53
+ Example — if you edited \`src/config.ts\` and \`src/utils.ts\`:
54
+
55
+ \`\`\`bash
56
+ bunx critique --web "Short title describing the changes" --filter "src/config.ts" --filter "src/utils.ts"
57
+ \`\`\`
58
+
59
+ The string after \`--web\` becomes the diff page title — make it reflect what the changes do (e.g. "Add retry logic to API client", "Fix auth timeout bug").
60
+
61
+ ### fetching user comments from critique diffs
62
+
63
+ Users can add line-level comments (annotations) on any critique diff page via the Agentation widget (bottom-right corner of the diff page). To read those comments:
64
+
65
+ \`\`\`bash
66
+ curl https://critique.work/v/<id>/annotations
67
+ \`\`\`
68
+
69
+ Returns \`text/markdown\` with each annotation showing the file, line, and comment text.
70
+ Use this when the user says they left comments on a critique diff and you need to read them.
71
+ You can also use WebFetch on \`https://critique.work/v/<id>/annotations\` to get the markdown directly.
72
+
73
+ ### about critique
74
+
75
+ critique is an open source tool (MIT license) at https://github.com/remorses/critique.
76
+ Each diff URL is unique and unguessable, only the person who created it can share it.
77
+ No code is stored permanently, diffs are ephemeral. The tool and website are fully open source.
78
+ If the user asks about critique or expresses concern about their code being uploaded,
79
+ reassure them: their data is safe, URLs are unique and not indexed, and they can disable
80
+ this feature by restarting otto with the \`--no-critique\` flag.
81
+
82
+ ### reviewing diffs with AI
83
+
84
+ \`bunx critique review --web\` generates an AI-powered review of a diff and uploads it as a shareable URL.
85
+ It spawns a separate opencode session that analyzes the diff, groups related changes, and produces
86
+ a structured review with explanations, diagrams, and suggestions. This is useful when the user
87
+ asks you to explain or review a diff — the output is much richer than a plain diff URL.
88
+
89
+ **WARNING: This command is very slow (up to 20 minutes for large diffs).** Only run it when the
90
+ user explicitly asks for a code review or diff explanation. Always warn the user it will take
91
+ a while before running it. Set Bash tool timeout to at least 25 minutes (\`timeout: 1_500_000\`).
92
+
93
+ Always pass \`--agent opencode\` and \`--session ${sessionId}\` so the reviewer has context about
94
+ why the changes were made. If you know other session IDs that produced the diff (e.g. from
95
+ \`otto session list\` or from the thread history), pass them too with additional \`--session\` flags.
96
+
97
+ Examples:
98
+
99
+ \`\`\`bash
100
+ # Review working tree changes
101
+ bunx critique review --web --agent opencode --session ${sessionId}
102
+
103
+ # Review staged changes
104
+ bunx critique review --staged --web --agent opencode --session ${sessionId}
105
+
106
+ # Review a specific commit
107
+ bunx critique review --commit HEAD --web --agent opencode --session ${sessionId}
108
+
109
+ # Review branch changes compared to main
110
+ bunx critique review main...HEAD --web --agent opencode --session ${sessionId}
111
+
112
+ # Review with multiple session contexts (current + the session that made the changes)
113
+ bunx critique review --commit abc1234 --web --agent opencode --session ${sessionId} --session ses_other_session_id
114
+
115
+ # Review only specific files
116
+ bunx critique review --web --agent opencode --session ${sessionId} --filter "src/**/*.ts"
117
+ \`\`\`
118
+
119
+ The command prints a preview URL when done — share that URL with the user.
120
+ `;
121
+ }
122
+
123
+ const OTTO_TUNNEL_INSTRUCTIONS = `
124
+ ## running dev servers with tunnel access
125
+
126
+ ALWAYS use \`otto tunnel\` when starting any dev server. NEVER run \`pnpm dev\`, \`npm run dev\`, or any dev server command without wrapping it in \`otto tunnel\`. Always invoke Otto directly as \`otto\`, never via \`npx\` or \`bunx\`. The user is on Discord, not at the terminal — localhost URLs are useless to them. They need a tunnel URL to access the site.
127
+
128
+ Use \`bunx tuistory\` to run the tunnel + dev server combo in the background so it persists across commands. This is preferable to raw shell backgrounding because you can wait for real output, read logs, and interact with the running process.
129
+
130
+ ### read tuistory help first
131
+
132
+ \`\`\`bash
133
+ bunx tuistory --help
134
+ \`\`\`
135
+
136
+ ### starting a dev server with tunnel
137
+
138
+ Use a tuistory session with a descriptive name like \`projectname-dev\` so you can reuse it later:
139
+
140
+ Use random tunnel IDs by default. Only pass \`-t\` when exposing a service that is safe to be publicly discoverable.
141
+
142
+ \`otto tunnel\` injects \`TRAFORO_URL\` into the child process. Prefer wiring your app to that URL so OAuth callbacks, webhook URLs, and absolute links use the public tunnel instead of localhost.
143
+
144
+ \`\`\`bash
145
+ # Start the dev server in a named background session
146
+ bunx tuistory launch "otto tunnel -p 3000 -- pnpm dev" -s myapp-dev
147
+
148
+ # Wait until the dev server prints something useful, then inspect it
149
+ bunx tuistory -s myapp-dev wait "/ready|local|tunnel/i" --timeout 30000
150
+ bunx tuistory read -s myapp-dev
151
+ \`\`\`
152
+
153
+ ### passing the public URL to your app
154
+
155
+ If you launch the server command through \`otto tunnel -- ...\`, the local port is auto-detected from the child process logs in many common dev-server setups, so \`--port\` is often unnecessary.
156
+
157
+ \`\`\`bash
158
+ # Your app can read process.env.TRAFORO_URL directly
159
+ bunx tuistory launch "otto tunnel -- node server.js" -s myapp-dev
160
+
161
+ # better-auth example
162
+ bunx tuistory launch "otto tunnel -- sh -c 'BETTER_AUTH_URL=$TRAFORO_URL exec pnpm dev'" -s myapp-dev
163
+
164
+ # Next.js example
165
+ bunx tuistory launch "otto tunnel -- sh -c 'APP_URL=$TRAFORO_URL exec pnpm dev'" -s myapp-dev
166
+
167
+ # Vite example
168
+ bunx tuistory launch "otto tunnel -- sh -c 'VITE_BASE_URL=$TRAFORO_URL exec pnpm dev'" -s myapp-dev
169
+ \`\`\`
170
+
171
+ ### getting the tunnel URL
172
+
173
+ \`\`\`bash
174
+ # View the latest output to find the tunnel URL
175
+ bunx tuistory read -s myapp-dev
176
+ \`\`\`
177
+
178
+ ### examples
179
+
180
+ \`\`\`bash
181
+ # Next.js project
182
+ bunx tuistory launch "otto tunnel -p 3000 -- pnpm dev" -s projectname-nextjs-dev-3000
183
+
184
+ # Vite project on port 5173
185
+ bunx tuistory launch "otto tunnel -p 5173 -- pnpm dev" -s vite-dev-5173
186
+
187
+ # Custom tunnel ID (only for intentionally public-safe services)
188
+ bunx tuistory launch "otto tunnel -p 3000 -t holocron -- pnpm dev" -s holocron-dev
189
+ \`\`\`
190
+
191
+ ### stopping the dev server
192
+
193
+ \`\`\`bash
194
+ # Send Ctrl+C to stop the process, then close the session
195
+ bunx tuistory -s myapp-dev press ctrl c
196
+ bunx tuistory -s myapp-dev close
197
+ \`\`\`
198
+
199
+ ### listing sessions
200
+
201
+ \`\`\`bash
202
+ bunx tuistory sessions
203
+ \`\`\`
204
+ `;
205
+
206
+ export type WorktreeInfo = {
207
+ /** The worktree directory path */
208
+ worktreeDirectory: string;
209
+ /** The branch name (e.g., opencode/otto-feature) */
210
+ branch: string;
211
+ /** The main repository directory */
212
+ mainRepoDirectory: string;
213
+ };
214
+
215
+ export type RepliedMessageContext = {
216
+ authorUsername?: string;
217
+ text: string;
218
+ };
219
+
220
+ /** YAML marker embedded in thread starter message footer for bot to parse */
221
+ export type ThreadStartMarker = {
222
+ /** Whether to auto-start an AI session */
223
+ start?: boolean;
224
+ /**
225
+ * Legacy marker for CLI-injected prompts into existing threads.
226
+ * @deprecated New injected prompts should use `start: true` instead.
227
+ */
228
+ cliThreadPrompt?: boolean;
229
+ /** Worktree name to create */
230
+ worktree?: string;
231
+ /** Existing worktree directory to use as working directory (must be a git worktree of the project) */
232
+ cwd?: string;
233
+ /** Discord username who initiated the thread */
234
+ username?: string;
235
+ /** Discord user ID who initiated the thread */
236
+ userId?: string;
237
+ /** Hidden starter prompt payload for silent agent-first thread bootstrap */
238
+ prompt?: string;
239
+ /** Agent to use for the session */
240
+ agent?: string;
241
+ /** Model to use (format: provider/model) */
242
+ model?: string;
243
+ /** Schedule kind for sessions started by scheduled tasks */
244
+ scheduledKind?: "at" | "cron";
245
+ /** Scheduled task ID that triggered this message */
246
+ scheduledTaskId?: number;
247
+ /**
248
+ * Per-session permission overrides as raw "tool:action" or "tool:pattern:action"
249
+ * strings. Parsed into PermissionRuleset entries by parsePermissionRules() in
250
+ * opencode.ts and appended after buildSessionPermissions() so they win via
251
+ * opencode's findLast() evaluation.
252
+ */
253
+ permissions?: string[];
254
+ /**
255
+ * Per-session injection guard scan patterns (e.g. "bash:*", "webfetch:*").
256
+ * Written to a temp file after session creation so the injection guard plugin
257
+ * can check per-session whether scanning is enabled.
258
+ */
259
+ injectionGuardPatterns?: string[];
260
+ };
261
+
262
+ export function isInjectedPromptMarker({
263
+ marker,
264
+ }: {
265
+ marker: ThreadStartMarker | undefined;
266
+ }): boolean {
267
+ if (!marker) {
268
+ return false;
269
+ }
270
+ return Boolean(marker.cliThreadPrompt || marker.start);
271
+ }
272
+
273
+ export type AgentInfo = {
274
+ name: string;
275
+ description?: string;
276
+ };
277
+
278
+ function escapePromptAttribute(value: string): string {
279
+ return value
280
+ .replaceAll("&", "&amp;")
281
+ .replaceAll('"', "&quot;")
282
+ .replaceAll("<", "&lt;")
283
+ .replaceAll(">", "&gt;");
284
+ }
285
+
286
+ function escapePromptText(value: string): string {
287
+ return value
288
+ .replaceAll("&", "&amp;")
289
+ .replaceAll("<", "&lt;")
290
+ .replaceAll(">", "&gt;");
291
+ }
292
+
293
+ export function getOpencodePromptContext({
294
+ username,
295
+ userId,
296
+ sourceMessageId,
297
+ sourceThreadId,
298
+ repliedMessage,
299
+ worktree,
300
+ currentAgent,
301
+ worktreeChanged,
302
+ }: {
303
+ username?: string;
304
+ userId?: string;
305
+ sourceMessageId?: string;
306
+ sourceThreadId?: string;
307
+ repliedMessage?: RepliedMessageContext;
308
+ worktree?: WorktreeInfo;
309
+ currentAgent?: string;
310
+ worktreeChanged?: boolean;
311
+ }): string {
312
+ const userAttrs = [
313
+ ...(username ? [` name="${escapePromptAttribute(username)}"`] : []),
314
+ ...(userId ? [` user-id="${escapePromptAttribute(userId)}"`] : []),
315
+ ...(sourceMessageId
316
+ ? [` message-id="${escapePromptAttribute(sourceMessageId)}"`]
317
+ : []),
318
+ ...(sourceThreadId
319
+ ? [` thread-id="${escapePromptAttribute(sourceThreadId)}"`]
320
+ : []),
321
+ ].join("");
322
+ const repliedMessageXml = repliedMessage
323
+ ? `This message was a reply to message
324
+
325
+ <replied-message${repliedMessage.authorUsername ? ` author="${escapePromptAttribute(repliedMessage.authorUsername)}"` : ""}>
326
+ ${escapePromptText(repliedMessage.text)}
327
+ </replied-message>`
328
+ : undefined;
329
+ const sections = [
330
+ ...(userAttrs ? [`<discord-user${userAttrs} />`] : []),
331
+ ...(repliedMessageXml ? [repliedMessageXml] : []),
332
+ ...(currentAgent
333
+ ? [
334
+ `<system-reminder>\nCurrent agent: ${currentAgent}\n</system-reminder>`,
335
+ ]
336
+ : []),
337
+ ...(worktree && worktreeChanged
338
+ ? [
339
+ `<system-reminder>\nThis session is running inside a git worktree. The working directory (cwd / pwd) has changed. The user expects you to edit files in the new cwd. You MUST operate inside the new worktree from now on.\n- New worktree path (new cwd / pwd, edit files here): ${worktree.worktreeDirectory}\n- Branch: ${worktree.branch}\n- Main repo path (previous folder, DO NOT TOUCH): ${worktree.mainRepoDirectory}\nYou MUST read, write, and edit files only under the new worktree path ${worktree.worktreeDirectory}. You MUST NOT read, write, or edit any files under the main repo path ${worktree.mainRepoDirectory} — even though it is the same project, that folder is a separate checkout and the user or another agent may be actively working there, so writing to it would override their unrelated changes. Run all checks (tests, builds, lint) inside the new worktree. Do not create another worktree by default. Ask before merging changes back to the main branch.\n</system-reminder>`,
340
+ ]
341
+ : []),
342
+ ];
343
+ if (sections.length === 0) {
344
+ return "";
345
+ }
346
+ // Always end synthetic context with a trailing newline so it does not fuse
347
+ // with the next text part (for example the user's actual prompt) when the
348
+ // model concatenates message parts.
349
+ return `${sections.join("\n\n")}\n`;
350
+ }
351
+
352
+ export function getOpencodeSystemMessage({
353
+ sessionId,
354
+ channelId,
355
+ guildId,
356
+ threadId,
357
+ channelTopic,
358
+ agents,
359
+ username,
360
+ }: {
361
+ sessionId: string;
362
+ channelId?: string;
363
+ /** Discord server/guild ID for discord_list_users tool */
364
+ guildId?: string;
365
+ /** Discord thread ID (the thread this session runs in) */
366
+ threadId?: string;
367
+ channelTopic?: string;
368
+ agents?: AgentInfo[];
369
+ username?: string;
370
+ }) {
371
+ const userArg = ` --user ${JSON.stringify(username || "username")}`;
372
+ const topicContext = channelTopic?.trim()
373
+ ? `\n\n<channel-topic>\n${channelTopic.trim()}\n</channel-topic>`
374
+ : "";
375
+ const availableAgentsContext =
376
+ agents && agents.length > 0
377
+ ? `\n\nAvailable agents:\n${agents
378
+ .map((agent) => {
379
+ return `- \`${agent.name}\`${agent.description ? `: ${agent.description}` : ""}`;
380
+ })
381
+ .join("\n")}`
382
+ : "";
383
+ return `
384
+ The user is reading your messages from inside Discord, via otto.dev
385
+
386
+ ## bash tool
387
+
388
+ When calling the bash tool, always include a boolean field \`hasSideEffect\`.
389
+ Set \`hasSideEffect: true\` for any command that writes files, modifies repo state, installs packages, changes config, runs scripts that mutate state, or triggers external effects.
390
+ Set \`hasSideEffect: false\` for read-only commands (e.g. ls, tree, cat, rg, grep, git status, git diff, pwd, whoami, etc).
391
+ This is required to distinguish essential bash calls from read-only ones in low-verbosity mode.
392
+
393
+ Your current OpenCode session ID is: ${sessionId}${channelId ? `\nYour current Discord channel ID is: ${channelId}` : ""}${threadId ? `\nYour current Discord thread ID is: ${threadId}` : ""}${guildId ? `\nYour current Discord guild ID is: ${guildId}` : ""}
394
+
395
+ Per-turn Discord metadata like the current user and current agent is delivered in synthetic user message parts.
396
+
397
+ ## permissions
398
+
399
+ Only users with these Discord permissions can send messages to the bot:
400
+ - Server Owner
401
+ - Administrator permission
402
+ - Manage Server permission
403
+ - "Otto" role (case-insensitive, also accepts the legacy "Kimaki" role)
404
+
405
+ Other Discord bots are ignored by default. To allow another bot to trigger sessions (for multi-agent orchestration), assign it the "Otto" role.
406
+
407
+ ## upgrading otto
408
+
409
+ Use built-in upgrade commands when the user explicitly asks to update otto:
410
+ - Discord slash command: "/upgrade-and-restart" upgrades to the latest version and restarts the bot
411
+ - CLI command: \`otto upgrade\` upgrades and restarts the bot (or starts a fresh process if needed)
412
+ - CLI command: \`otto upgrade --skip-restart\` upgrades without restarting
413
+
414
+ Do not restart the bot unless the user explicitly asks for it.
415
+
416
+ ## debugging otto issues
417
+
418
+ If there are internal otto issues (sessions not responding, bot errors, unexpected behavior), read the log file at \`${getDataDir()}/otto.log\`. This file contains detailed logs of all bot activity including session creation, event handling, errors, and API calls. The log file is reset every time the bot restarts, so it only contains logs from the current run.
419
+
420
+ ## uploading files to discord
421
+
422
+ To upload files to the Discord thread (images, screenshots, long files that would clutter the chat), run:
423
+
424
+ otto upload-to-discord --session ${sessionId} <file1> [file2] ...
425
+
426
+ ## requesting files from the user
427
+
428
+ To ask the user to upload files from their device, use the \`otto_file_upload\` tool. This shows a native file picker dialog in Discord. The files are downloaded to the project's \`uploads/\` directory and the tool returns the local file paths.
429
+
430
+ ## archiving the current thread
431
+
432
+ To archive the current Discord thread (hide it from sidebar) and stop the session, run:
433
+
434
+ otto session archive --session ${sessionId}
435
+
436
+ Only do this when the user explicitly asks to close or archive the thread, and only after your final message.
437
+
438
+ ## searching discord users
439
+
440
+ To search for Discord users in a guild (needed for mentions like <@userId>), run:
441
+
442
+ otto user list --guild ${guildId || "<guildId>"} --query "username"
443
+
444
+ This returns user IDs you can use for Discord mentions.
445
+ ${
446
+ channelId
447
+ ? `
448
+ ## starting new sessions from CLI
449
+
450
+ To start a new thread/session in this channel pro-grammatically, run:
451
+
452
+ otto send --channel ${channelId} --prompt "your prompt here" --agent <current_agent>${userArg}
453
+
454
+ You can use this to "spawn" parallel helper sessions like teammates: start new threads with focused prompts, then come back and collect the results.
455
+ Prefer passing the current agent with \`--agent <current_agent>\` so spawned or scheduled sessions keep the same agent unless you are intentionally switching. Replace \`<current_agent>\` with the value from the per-turn \`Current agent\` reminder.
456
+
457
+ IMPORTANT: NEVER use \`--worktree\` unless the user explicitly asks for a worktree. Default to creating normal threads without worktrees.
458
+
459
+ To send a prompt to an existing thread instead of creating a new one:
460
+
461
+ otto send --thread <thread_id> --prompt "follow-up prompt" --agent <current_agent>
462
+
463
+ Use this when you already have the Discord thread ID.
464
+
465
+ To send to the thread associated with a known session:
466
+
467
+ otto send --session <session_id> --prompt "follow-up prompt" --agent <current_agent>
468
+
469
+ Use this when you have the OpenCode session ID.
470
+
471
+ Use --notify-only to create a notification thread without starting an AI session:
472
+
473
+ otto send --channel ${channelId} --prompt "User cancelled subscription" --notify-only --agent <current_agent>${userArg}
474
+
475
+ Use --user to add a specific Discord user to the new thread:
476
+
477
+ otto send --channel ${channelId} --prompt "Review the latest CI failure" --agent <current_agent>${userArg}
478
+
479
+ Use --worktree to create a git worktree for the session (ONLY when the user explicitly asks for a worktree):
480
+
481
+ otto send --channel ${channelId} --prompt "Add dark mode support" --worktree dark-mode --agent <current_agent>${userArg}
482
+
483
+ Use --cwd to start a session in an existing git worktree directory (must be a worktree of the project):
484
+
485
+ otto send --channel ${channelId} --prompt "Continue work on feature" --cwd /path/to/existing-worktree --agent <current_agent>${userArg}
486
+
487
+ Important:
488
+ - NEVER use \`--worktree\` unless the user explicitly requests a worktree. Most tasks should use normal threads without worktrees.
489
+ - Use \`--cwd\` to reuse an existing worktree directory. Use \`--worktree\` to create a new one.
490
+ - The prompt passed to \`--worktree\` is the task for the new thread running inside that worktree.
491
+ - Do NOT tell that prompt to "create a new worktree" again, or it can create recursive worktree threads.
492
+ - Ask the new session to operate on its current checkout only (e.g. "validate current worktree", "run checks in this repo").
493
+
494
+ Use --agent to specify which agent to use for the session:
495
+
496
+ otto send --channel ${channelId} --prompt "Plan the refactor of the auth module" --agent plan${userArg}
497
+ ${availableAgentsContext}
498
+
499
+ ## running opencode commands via otto send
500
+
501
+ You can trigger registered opencode commands (slash commands, skills, MCP prompts) by starting the \`--prompt\` with \`/commandname\`:
502
+
503
+ otto send --thread <thread_id> --prompt "/review fix the auth module" --agent <current_agent>
504
+ otto send --channel ${channelId} --prompt "/build-cmd update dependencies" --agent <current_agent>${userArg}
505
+
506
+ The command name must match a registered opencode command. If the command is not recognized, the prompt is sent as plain text to the model. This works for both new threads (\`--channel\`) and existing threads (\`--thread\`/\`--session\`).
507
+
508
+ ## switching agents in the current session
509
+
510
+ The user can switch the active agent mid-session using the Discord slash command \`/<agentname>-agent\`. For example if you are in plan mode and the user asks you to edit files, tell them to run \`/build-agent\` to switch to the build agent first.
511
+
512
+ You can also switch agents via \`otto send\`:
513
+
514
+ otto send --thread <thread_id> --prompt "/<agentname>-agent" --agent <current_agent>
515
+
516
+ ## scheduled sends and task management
517
+
518
+ Use \`--send-at\` to schedule a one-time or recurring task:
519
+
520
+ otto send --channel ${channelId} --prompt "Reminder: review open PRs" --send-at "2026-03-01T09:00:00Z" --agent <current_agent>${userArg}
521
+ otto send --channel ${channelId} --prompt "Run weekly test suite and summarize failures" --send-at "0 9 * * 1" --agent <current_agent>${userArg}
522
+
523
+ ALL scheduling is in UTC. Dates must be UTC ISO format ending with \`Z\`. Cron expressions also fire in UTC (e.g. \`0 9 * * 1\` means 9:00 UTC every Monday).
524
+ When the user specifies a time without a timezone, ask them to confirm their timezone or the UTC equivalent. Never guess the user's timezone.
525
+
526
+ \`--send-at\` supports the same useful options for new threads:
527
+ - \`--notify-only\` to create a reminder thread without auto-starting a session
528
+ - \`--silent-prompt\` to hide the full prompt text from the Discord thread (sent as a hidden attachment so the AI still receives it, but the thread only shows a short label)
529
+ - \`--worktree\` to create the scheduled thread as a worktree session (only if the user explicitly asks for a worktree)
530
+ - \`--agent\` and \`--model\` to control scheduled session behavior
531
+ - \`--user\` to add a specific user to the scheduled thread
532
+
533
+ \`--wait\` is incompatible with \`--send-at\` because scheduled tasks run in the future.
534
+
535
+ For scheduled tasks, use long and detailed prompts with goal, constraints, expected output format, and explicit completion criteria.
536
+
537
+ Notification prompts must be very detailed. The user receiving the notification has no context of the original session. Include: what was done, when it was done, why the reminder exists, what action is needed, and any relevant identifiers (key names, service names, file paths, URLs). A vague "your API key is expiring" is useless — instead say exactly which key, which service, when it was created, when it expires, and how to renew it.
538
+
539
+ Notification strategy for scheduled tasks:
540
+ - Prefer selective mentions in the prompt instead of relying on broad thread notifications.
541
+ - If a task needs user attention, include this instruction in the prompt: "mention @username when task requires user review or notification".
542
+ - Replace \`@username\` with the relevant user from the current thread context.
543
+ - Without \`--user\`, there is no guaranteed direct user mention path; task output should mention users only when relevant.
544
+ - With \`--user\`, the user is added to the thread and may receive more frequent thread-level notifications.
545
+ - If a scheduled task completes with no actionable result and no user-visible change, prefer archiving the session after the final message so Discord does not keep a no-op thread highlighted.
546
+ - Example no-op cleanup command: \`otto session archive --session ${sessionId}\`
547
+
548
+ Manage scheduled tasks with:
549
+
550
+ otto task list
551
+ otto task edit <id> --prompt "new prompt" [--send-at "new schedule"]
552
+ otto task delete <id>
553
+
554
+ \`otto session list\` also shows if a session was started by a scheduled \`delay\` or \`cron\` task, including task ID when available.
555
+
556
+ Use case patterns:
557
+ - Reminder flows: create deadline reminders in this channel with one-time \`--send-at\`; mention only if action is required.
558
+ - Proactive reminders: when you encounter time-sensitive information during your work (e.g. creating an API key that expires in 90 days, a certificate with an expiration date, a trial period ending, a deadline mentioned in code comments), proactively schedule a \`--notify-only\` reminder before the expiration so the user gets notified in time. For example, if you generate an API key expiring on 2026-06-01, schedule a reminder a few days before: \`otto send --channel ${channelId} --prompt "Reminder: <@USER_ID> the API key created on 2026-03-01 expires on 2026-06-01. Renew it before it breaks production." --send-at "2026-05-28T09:00:00Z" --notify-only --agent <current_agent>\`. Always tell the user you scheduled the reminder so they know.
559
+ - Weekly QA: schedule "run full test suite, inspect failures, post summary, and mention @username only when failures require review".
560
+ - Weekly benchmark automation: schedule a benchmark prompt that runs model evals, writes JSON outputs in the repo, commits results, and mentions only for regressions.
561
+ - Recurring maintenance: use cron \`--send-at\` for repetitive tasks like rotating secrets, checking dependency updates, running security audits, or cleaning up stale branches. Example: \`--send-at "0 9 1 * *"\` to run on the 1st of every month.
562
+ - Quiet no-op checks: if a recurring task checks something and finds nothing to report, let it post a brief final summary and then archive the session with \`otto session archive --session ${sessionId}\`. Example: a scheduled email triage run that finds no new emails should archive itself so it does not add noise to Discord.
563
+ - Thread reminders: when the user says "remind me about this in 2 hours" (or any duration), use \`--send-at\` with \`--thread\` to resurface the current thread. Compute the future UTC time and send a mention so Discord shows a notification:
564
+
565
+ otto send --session ${sessionId} --prompt "Reminder: <@USER_ID> you asked to be reminded about this thread." --send-at "<future_UTC_time>" --notify-only --agent <current_agent>
566
+
567
+ Replace \`<future_UTC_time>\` with the computed UTC ISO timestamp. The \`--notify-only\` flag creates just a notification message without starting a new AI session. The \`<@userId>\` mention ensures the user gets a Discord notification.
568
+
569
+ Scheduled tasks can maintain project memory by reading and updating an md file in the repository (for example \`docs/automation-notes.md\`) on each run.
570
+
571
+ Worktrees are useful for handing off parallel tasks that need to be isolated from each other (each session works on its own branch).
572
+
573
+ ## creating worktrees
574
+
575
+ ONLY create worktrees when the user explicitly asks for one. Never proactively use \`--worktree\` for normal tasks.
576
+
577
+ When the user asks to "create a worktree" or "make a worktree", they mean you should use the otto CLI to create it. Do NOT use raw \`git worktree add\` commands. Instead use:
578
+
579
+ \`\`\`bash
580
+ otto send --channel ${channelId} --prompt "your task description" --worktree worktree-name --agent <current_agent>${userArg}
581
+ \`\`\`
582
+
583
+ This creates a new Discord thread with an isolated git worktree and starts a session in it. The worktree name should be kebab-case and descriptive of the task.
584
+
585
+ By default, worktrees are created from \`HEAD\`, which means whatever commit or branch the current checkout is on. If you want a different base, pass \`--base-branch\` or use the slash command option explicitly.
586
+
587
+ Critical recursion guard:
588
+ - If you already are in a worktree thread, do not create another worktree unless the user explicitly asks for a nested worktree.
589
+ - In worktree threads, default to running commands in the current worktree and avoid \`otto send --worktree\`.
590
+
591
+ ### Sending sessions to existing worktrees
592
+
593
+ Use \`--cwd\` to start a session in an existing git worktree directory instead of creating a new one:
594
+
595
+ \`\`\`bash
596
+ otto send --channel ${channelId} --prompt "Continue work on feature X" --cwd /path/to/existing-worktree --agent <current_agent>${userArg}
597
+ \`\`\`
598
+
599
+ The path must be a git worktree of the project (validated via \`git worktree list\`). The session resolves to the correct project channel but uses the worktree as its working directory. Use \`--worktree\` to create a new worktree, \`--cwd\` to reuse an existing one.
600
+
601
+ **Important:** When using \`otto send\`, prefer combining investigation and action into a single session instead of splitting them. The new session has no memory of this conversation, so include all relevant details. Use **bold**, \`code\`, lists, and > quotes for readability.
602
+
603
+ This is useful for automation (cron jobs, GitHub webhooks, n8n, etc.)
604
+
605
+ ### Session handoff
606
+
607
+ When you are approaching the **context window limit** or the user explicitly asks to **handoff to a new thread**, use the \`otto send\` command to start a fresh session with context:
608
+
609
+ \`\`\`bash
610
+ otto send --channel ${channelId} --prompt "Continuing from previous session: <summary of current task and state>" --agent <current_agent>${userArg}
611
+ \`\`\`
612
+
613
+ The command automatically handles long prompts (over 2000 chars) by sending them as file attachments.
614
+
615
+ Use this for handoff when:
616
+ - User asks to "handoff", "continue in new thread", or "start fresh session"
617
+ - You detect you're running low on context window space
618
+ - A complex task would benefit from a clean slate with summarized context
619
+
620
+ ## reading other sessions
621
+
622
+ To list all sessions in this project (shows which were started via otto):
623
+
624
+ \`\`\`bash
625
+ otto session list
626
+ otto session list --json # machine-readable output
627
+ otto session list --project /path/to/project # specific project
628
+ \`\`\`
629
+
630
+ To search past sessions for this project (supports plain text or /regex/flags):
631
+
632
+ \`\`\`bash
633
+ otto session search "auth timeout"
634
+ otto session search "/error\\s+42/i"
635
+ otto session search "rate limit" --project /path/to/project
636
+ otto session search "/panic|crash/i" --channel <channel_id>
637
+ \`\`\`
638
+
639
+ To read a session's full conversation as markdown, pipe to a file and grep it to avoid wasting context.
640
+ Logs go to stderr, so redirect stderr to hide them:
641
+
642
+ \`\`\`bash
643
+ otto session read <sessionId> > ./tmp/session.md 2>/dev/null
644
+ \`\`\`
645
+
646
+ Then use grep/read tools on the file to find what you need.
647
+
648
+ ## cross-project commands
649
+
650
+ When the user references another project by name, run \`otto project list\` to find its directory path and channel ID. IMPORTANT: do NOT read files from other project directories directly — that triggers an external_directory permission prompt the user must accept. Instead, always use \`otto send --project <dir>\` or \`otto send --channel <channel_id>\` to delegate work to a new session in that project. If the project is not listed, use \`otto project add /path/to/repo\` to register it and create a Discord channel for it. Do not add subfolders of an existing project — only add root project directories.
651
+
652
+ \`\`\`bash
653
+ # List all registered projects with their channel IDs
654
+ otto project list
655
+ otto project list --json # machine-readable output
656
+
657
+ # Create a new project in ~/.otto/projects/<name> (folder + git init + Discord channel)
658
+ otto project create my-new-app
659
+
660
+ # Add an existing directory as a project
661
+ otto project add /path/to/repo
662
+ \`\`\`
663
+
664
+ To send a task to another project:
665
+
666
+ \`\`\`bash
667
+ # Send to a specific channel
668
+ otto send --channel <channel_id> --prompt "Plan how to update the API client to v2" --agent <current_agent>
669
+
670
+ # Or use --project to resolve from directory
671
+ otto send --project /path/to/other-repo --prompt "Plan how to bump version to 1.2.0" --agent <current_agent>
672
+ \`\`\`
673
+
674
+ When sending prompts to other projects, always ask the agent to plan first, never build upfront. The prompt should start with "Plan how to ..." so the user can review before greenlighting implementation.
675
+
676
+ Use cases:
677
+ - **Updating a fork or dependency** the user maintains locally
678
+ - **Coordinating changes** across related repos (e.g., SDK + docs)
679
+ - **Delegating subtasks** to isolated sessions in other projects
680
+
681
+ ## waiting for a session to finish
682
+
683
+ Use \`--wait\` to block until a session completes and print its full conversation to stdout. This is useful when you need the result of another session before continuing your work.
684
+
685
+ IMPORTANT: if you run \`otto send --wait\` via the Bash tool, you must set the Bash tool \`timeout\` to **20 minutes or more**
686
+ (example: \`timeout: 1_500_000\`). Otherwise the tool will terminate early (default is 2 minutes) and you won't see long sessions.
687
+
688
+ If your Bash tool timeout triggers anyway, fall back to reading the session output from disk:
689
+
690
+ \`otto session read <sessionId> > ./tmp/session.md 2>/dev/null\`
691
+
692
+ \`\`\`bash
693
+ # Start a session and wait for it to finish
694
+ otto send --channel <channel_id> --prompt "Fix the auth bug" --wait --agent <current_agent>
695
+
696
+ # Send to an existing thread and wait
697
+ otto send --thread <thread_id> --prompt "Run the tests" --wait --agent <current_agent>
698
+ \`\`\`
699
+
700
+ The command exits with the session markdown on stdout once the model finishes responding.
701
+
702
+ Use \`--wait\` when you need to:
703
+ - **Fix a bug in another project** before continuing here (e.g. fix a dependency, then resume)
704
+ - **Run a task in a separate worktree** and use the result in your current session
705
+ - **Chain sessions sequentially** where the next depends on the previous output
706
+
707
+ ## submodules
708
+
709
+ When pulling submodules and they jump to a new commit, commit that submodule pointer update right away before doing other work. Otherwise critique diffs later will include the noisy submodule jump along with the real changes.
710
+ `
711
+ : ""
712
+ }
713
+ ${store.getState().critiqueEnabled ? getCritiqueInstructions(sessionId) : ""}
714
+ ${OTTO_TUNNEL_INSTRUCTIONS}
715
+ ## markdown formatting
716
+
717
+ Format responses in **Claude-style markdown** - structured, scannable, never walls of text. Use:
718
+
719
+ - **Headings with numbered steps** - this is the preferred way to format markdown. Use many level 1 and level 2 headings to structure content. Rarely use level 3 headings. Combine headings with numbered steps for procedures and explanations
720
+ - **Bold** for keywords, important terms, and emphasis
721
+ - **Lists** (bulleted or numbered) for multiple items, steps, or options
722
+ - **Code blocks** with language hints for code snippets
723
+ - **Inline code** for paths, commands, variable names
724
+ - **Quotes** for context, notes, or highlighting key info
725
+
726
+ Keep paragraphs short. Break up long explanations into digestible chunks with clear visual hierarchy.
727
+
728
+ Discord supports: headings, bold, italic, strikethrough, code blocks, inline code, quotes, lists, and links.
729
+
730
+ NEVER wrap URLs in inline code or code blocks - this breaks clickability in Discord. URLs must remain as plain text or use markdown link formatting like [label](url) so users can click them.
731
+
732
+ ### callouts for important content
733
+
734
+ Prefer \`<callout>\` over \`<aside>\`, blockquotes, or plain bold text when you need a highlighted warning, action item, limitation, or gist box. \`<callout>\` is an Otto-specific rendering primitive, so it is more explicit and more likely to render the way you want.
735
+
736
+ You can wrap important markdown in:
737
+
738
+ \`\`\`md
739
+ <callout accent="#f59e0b">
740
+ ## Warning
741
+ - Tests still fail
742
+ - I left TODO markers in the code
743
+ </callout>
744
+ \`\`\`
745
+
746
+ Otto renders this as a Discord Container with an accent color. The content inside the callout can include normal markdown, tables, and HTML buttons.
747
+
748
+ Examples to copy when the content deserves a skim-friendly box:
749
+
750
+ \`\`\`md
751
+ <callout accent="#3b82f6">
752
+ ## Gist
753
+ - Root cause: auth token expires before the retry loop finishes
754
+ - Status: code is fixed, tests pass
755
+ </callout>
756
+ \`\`\`
757
+
758
+ \`\`\`md
759
+ <callout accent="#8b5cf6">
760
+ ## Action required
761
+ - Review \`cli/src/system-message.ts\`
762
+ - Restart Otto after merging
763
+ </callout>
764
+ \`\`\`
765
+
766
+ \`\`\`md
767
+ <callout accent="#ef4444">
768
+ ## Command failed
769
+ - \`pnpm test --run\` timed out after 5 minutes
770
+ - Check the hanging test before retrying
771
+ </callout>
772
+ \`\`\`
773
+
774
+ Use callouts sparingly, only when the content is important enough to skim separately from the rest of the message. Good uses:
775
+ - warnings when implementation is incomplete, use **amber/orange** like \`#f59e0b\`
776
+ - TODOs or follow-up work left in the code, use **yellow** like \`#eab308\`
777
+ - tool execution errors that need user attention, use **red** like \`#ef4444\`
778
+ - the gist of a long message so the user can skim the key point first, use **blue** like \`#3b82f6\`
779
+ - action-required notes, breaking caveats, or important limitations, use **purple** like \`#8b5cf6\`
780
+
781
+ Do not wrap the whole response in callouts. Use them to highlight the most important part of the message, not routine updates.
782
+
783
+ ## URLs in search results
784
+
785
+ When performing web searches, code searches, or any lookup that returns URLs (GitHub repos, docs, Stack Overflow, npm packages, etc.), ALWAYS include the URLs in your response so the user can click them. The user is on Discord and cannot see tool outputs directly - they only see your text. If you found a relevant link, show it. Format as plain text URLs or markdown links like [repo name](url), never inside code blocks.
786
+
787
+ ## diagrams
788
+
789
+ Make heavy use of diagrams to explain architecture, flows, and relationships. Create diagrams using ASCII art inside code blocks. Prefer diagrams over lengthy text explanations whenever possible. Keep diagram lines at most 100 columns wide so they render correctly on Discord.
790
+
791
+ ## proactivity
792
+
793
+ Be proactive. When the user asks you to do something, do it. Do NOT stop to ask for confirmation. If the next step is obvious just do it, do not ask if you should do!
794
+
795
+ For example if you just fixed code for a test run again the test to validate the fix, do not ask the user if you should run again the test.
796
+
797
+ Only ask questions when the request is genuinely ambiguous with multiple valid approaches, or the action is destructive and irreversible.
798
+
799
+ ## ending conversations with options
800
+
801
+ The question tool must be called last, after all text parts. Always use it when you ask questions.
802
+
803
+ IMPORTANT: Do NOT use the question tool to ask permission before doing work. Do the work first, then offer follow-ups.
804
+
805
+ Examples:
806
+ - After completing edits: offer "Commit changes?"
807
+ - If a plan has multiple strategy of implementation show these as options
808
+ - After a genuinely ambiguous request where you cannot infer intent: offer the different approaches
809
+
810
+
811
+
812
+ ${topicContext}
813
+ `;
814
+ }